diff options
| author | Yoshinobu Inoue <shin@FreeBSD.org> | 2000-01-27 09:28:38 +0000 |
|---|---|---|
| committer | Yoshinobu Inoue <shin@FreeBSD.org> | 2000-01-27 09:28:38 +0000 |
| commit | 4dd8b5ab798f9e3b8b60f5fa2c9ffb19b3774021 (patch) | |
| tree | e0caee013c23189a72cb874040a86616a5f64a03 /libexec/ftpd/ftpd.c | |
| parent | 404d72b9f6ab0c20ebd426ff73f13563352f0452 (diff) | |
Notes
Diffstat (limited to 'libexec/ftpd/ftpd.c')
| -rw-r--r-- | libexec/ftpd/ftpd.c | 503 |
1 files changed, 415 insertions, 88 deletions
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 0f9d20c6b5aa..cd72ed277756 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -110,15 +110,20 @@ static const char rcsid[] = static char version[] = "Version 6.00LS"; #undef main +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + extern off_t restart_point; extern char cbuf[]; -struct sockaddr_in server_addr; -struct sockaddr_in ctrl_addr; -struct sockaddr_in data_source; -struct sockaddr_in data_dest; -struct sockaddr_in his_addr; -struct sockaddr_in pasv_addr; +union sockunion server_addr; +union sockunion ctrl_addr; +union sockunion data_source; +union sockunion data_dest; +union sockunion his_addr; +union sockunion pasv_addr; int daemon_mode; int data; @@ -155,9 +160,11 @@ char *hostname; #ifdef VIRTUAL_HOSTING char *ftpuser; +int epsvall = 0; + static struct ftphost { struct ftphost *next; - struct in_addr hostaddr; + union sockunion hostaddr; char *hostname; char *anonuser; char *statfile; @@ -176,7 +183,6 @@ char *tty = ttyline; /* for klogin */ static int auth_pam __P((struct passwd**, const char*)); #endif -struct in_addr bind_address; char *pid_file = NULL; /* @@ -200,7 +206,7 @@ char proctitle[LINE_MAX]; /* initial part of title */ #ifdef SKEY int pwok = 0; -char addr_string[20]; /* XXX */ +char addr_string[INET6_ADDRSTRLEN]; /* XXX */ #endif #define LOGCMD(cmd, file) \ @@ -224,13 +230,13 @@ char addr_string[20]; /* XXX */ #ifdef VIRTUAL_HOSTING static void inithosts __P((void)); -static void selecthost __P((struct in_addr *)); +static void selecthost __P((union sockunion *)); #endif static void ack __P((char *)); static void myoob __P((int)); static int checkuser __P((char *, char *, int)); static FILE *dataconn __P((char *, off_t, char *)); -static void dolog __P((struct sockaddr_in *)); +static void dolog __P((struct sockaddr *)); static char *curdir __P((void)); static void end_login __P((void)); static FILE *getdatasock __P((char *)); @@ -266,6 +272,10 @@ main(argc, argv, envp) int addrlen, ch, on = 1, tos; char *cp, line[LINE_MAX]; FILE *fd; + int error; + char *bindname = NULL; + int family = AF_UNSPEC; + int enable_v4 = 0; tzset(); /* in case no timezone database in ~ftp */ @@ -280,8 +290,7 @@ main(argc, argv, envp) #endif /* OLD_SETPROCTITLE */ - bind_address.s_addr = htonl(INADDR_ANY); - while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) { + while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:46")) != -1) { switch (ch) { case 'D': daemon_mode++; @@ -320,8 +329,7 @@ main(argc, argv, envp) break; case 'a': - if (!inet_aton(optarg, &bind_address)) - errx(1, "invalid address for -a"); + bindname = optarg; break; case 'p': @@ -347,6 +355,16 @@ main(argc, argv, envp) debug = 1; break; + case '4': + enable_v4 = 1; + if (family == AF_UNSPEC) + family = AF_INET; + break; + + case '6': + family = AF_INET6; + break; + default: warnx("unknown flag -%c ignored", optopt); break; @@ -366,7 +384,7 @@ main(argc, argv, envp) if (daemon_mode) { int ctl_sock, fd; - struct servent *sv; + struct addrinfo hints, *res; /* * Detach from parent. @@ -376,30 +394,56 @@ main(argc, argv, envp) exit(1); } (void) signal(SIGCHLD, reapchild); - /* - * Get port number for ftp/tcp. - */ - sv = getservbyname("ftp", "tcp"); - if (sv == NULL) { - syslog(LOG_ERR, "getservbyname for ftp failed"); + /* init bind_sa */ + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = family == AF_UNSPEC ? AF_INET : family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(bindname, "ftp", &hints, &res); + if (error) { + if (family == AF_UNSPEC) { + hints.ai_family = AF_UNSPEC; + error = getaddrinfo(bindname, "ftp", &hints, + &res); + } + if (error == 0 && res->ai_addr != NULL) + family = res->ai_addr->sa_family; + } + if (error) { + syslog(LOG_ERR, gai_strerror(error)); + if (error == EAI_SYSTEM) + syslog(LOG_ERR, strerror(errno)); + exit(1); + } + if (res->ai_addr == NULL) { + syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname); exit(1); } /* * Open a socket, bind it to the FTP port, and start * listening. */ - ctl_sock = socket(AF_INET, SOCK_STREAM, 0); + ctl_sock = socket(family, SOCK_STREAM, 0); if (ctl_sock < 0) { syslog(LOG_ERR, "control socket: %m"); exit(1); } if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "control setsockopt: %m");; - server_addr.sin_family = AF_INET; - server_addr.sin_addr = bind_address; - server_addr.sin_port = sv->s_port; - if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) { + syslog(LOG_ERR, "control setsockopt: %m"); +#ifdef IPV6_BINDV6ONLY + if (family == AF_INET6 && enable_v4 == 0) { + if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, + (char *)&on, sizeof (on)) < 0) + syslog(LOG_ERR, + "control setsockopt(IPV6_BINDV6ONLY): %m"); + } +#endif /* IPV6_BINDV6ONLY */ + memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len); + if (bind(ctl_sock, (struct sockaddr *)&server_addr, + server_addr.su_len) < 0) { syslog(LOG_ERR, "control bind: %m"); exit(1); } @@ -434,7 +478,7 @@ main(argc, argv, envp) * children to handle them. */ while (1) { - addrlen = sizeof(his_addr); + addrlen = server_addr.su_len; fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); if (fork() == 0) { /* child */ @@ -459,7 +503,9 @@ main(argc, argv, envp) syslog(LOG_ERR, "signal: %m"); #ifdef SKEY - strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string)); + getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + addr_string, sizeof(addr_string) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); #endif addrlen = sizeof(ctrl_addr); if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { @@ -468,12 +514,15 @@ main(argc, argv, envp) } #ifdef VIRTUAL_HOSTING /* select our identity from virtual host table */ - selecthost(&ctrl_addr.sin_addr); + selecthost(&ctrl_addr); #endif #ifdef IP_TOS + if (ctrl_addr.su_family == AF_INET) + { tos = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } #endif /* * Disable Nagle on the control channel so that we don't have to wait @@ -482,7 +531,7 @@ main(argc, argv, envp) if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m"); - data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); /* set this here so klogin can use it... */ (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); @@ -497,7 +546,7 @@ main(argc, argv, envp) if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #endif - dolog(&his_addr); + dolog((struct sockaddr *)&his_addr); /* * Set up default state */ @@ -567,9 +616,9 @@ inithosts() { FILE *fp; char *cp; - struct hostent *hp; struct ftphost *hrp, *lhrp; char line[1024]; + struct addrinfo hints, *res, *ai; /* * Fill in the default host information @@ -579,20 +628,28 @@ inithosts() if ((hrp = malloc(sizeof(struct ftphost))) == NULL || (hrp->hostname = strdup(line)) == NULL) fatal("Ran out of memory."); - memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr); - if ((hp = gethostbyname(hrp->hostname)) != NULL) - (void) memcpy(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)); + memset(&hrp->hostaddr, 0, sizeof(hrp->hostaddr)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + getaddrinfo(hrp->hostname, NULL, &hints, &res); + if (res) + memcpy(&hrp->hostaddr, res->ai_addr, res->ai_addrlen); hrp->statfile = _PATH_FTPDSTATFILE; hrp->welcome = _PATH_FTPWELCOME; hrp->loginmsg = _PATH_FTPLOGINMESG; hrp->anonuser = "ftp"; hrp->next = NULL; thishost = firsthost = lhrp = hrp; + freeaddrinfo(res); if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { + int addrsize, error; + void *addr; + struct hostent *hp; + while (fgets(line, sizeof(line), fp) != NULL) { - int i; + int i, hp_error; if ((cp = strchr(line, '\n')) == NULL) { /* ignore long lines */ @@ -606,13 +663,21 @@ inithosts() /* skip comments and empty lines */ if (cp == NULL || line[0] == '#') continue; - /* first, try a standard gethostbyname() */ - if ((hp = gethostbyname(cp)) == NULL) + + hints.ai_flags = 0; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error != NULL) continue; + for (ai = res; ai != NULL && ai->ai_addr != NULL; + ai = ai->ai_next) + { + for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { if (memcmp(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)) == 0) + ai->ai_addr, + ai->ai_addr->sa_len) == 0) break; } if (hrp == NULL) { @@ -628,16 +693,32 @@ inithosts() lhrp = hrp; } (void) memcpy(&hrp->hostaddr, - hp->h_addr_list[0], - sizeof(hrp->hostaddr)); + ai->ai_addr, + ai->ai_addr->sa_len); /* * determine hostname to use. - * force defined name if it is a valid alias + * force defined name if there is a valid alias * otherwise fallback to primary hostname */ - if ((hp = gethostbyaddr((char*)&hrp->hostaddr, - sizeof(hrp->hostaddr), - AF_INET)) != NULL) { + /* XXX: getaddrinfo() can't do alias check */ + switch(hrp->hostaddr.su_family) { + case AF_INET: + addr = &((struct sockaddr_in *)&hrp->hostaddr)->sin_addr; + addrsize = sizeof(struct sockaddr_in); + break; + case AF_INET6: + addr = &((struct sockaddr_in6 *)&hrp->hostaddr)->sin6_addr; + addrsize = sizeof(struct sockaddr_in6); + break; + default: + /* should not reach here */ + free(hrp); + continue; + /* NOTREACHED */ + } + if ((hp = getipnodebyaddr((char*)addr, addrsize, + hrp->hostaddr.su_family, + &hp_error)) != NULL) { if (strcmp(cp, hp->h_name) != 0) { if (hp->h_aliases == NULL) cp = hp->h_name; @@ -652,6 +733,7 @@ inithosts() } } hrp->hostname = strdup(cp); + freehostent(hp); /* ok, now we now peel off the rest */ i = 0; while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) { @@ -673,25 +755,55 @@ inithosts() } ++i; } + /* XXX: re-initialization for getaddrinfo() loop */ + cp = strtok(line, " \t"); + } } (void) fclose(fp); } } static void -selecthost(a) - struct in_addr *a; +selecthost(su) + union sockunion *su; { struct ftphost *hrp; + u_int16_t port; +#ifdef INET6 + struct in6_addr *mapped_in6 = NULL; +#endif + +#ifdef INET6 + /* + * XXX IPv4 mapped IPv6 addr consideraton, + * specified in rfc2373. + */ + if (su->su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) + mapped_in6 = &su->su_sin6.sin6_addr; +#endif hrp = thishost = firsthost; /* default */ + port = su->su_port; + su->su_port = 0; while (hrp != NULL) { - if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { + if (memcmp(su, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { thishost = hrp; break; } +#ifdef INET6 + /* XXX IPv4 mapped IPv6 addr consideraton */ + if (hrp->hostaddr.su_family == AF_INET && mapped_in6 != NULL && + (memcmp(&mapped_in6->s6_addr[12], + &hrp->hostaddr.su_sin.sin_addr, + sizeof(struct in_addr)) == 0)) { + thishost = hrp; + break; + } +#endif hrp = hrp->next; } + su->su_port = port; /* setup static variables as appropriate */ hostname = thishost->hostname; ftpuser = thishost->anonuser; @@ -1107,8 +1219,9 @@ skip: if ((lc = login_getpwclass(pw)) != NULL) { char remote_ip[MAXHOSTNAMELEN]; - strncpy(remote_ip, inet_ntoa(his_addr.sin_addr), - sizeof(remote_ip) - 1); + getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + remote_ip, sizeof(remote_ip) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); remote_ip[sizeof(remote_ip) - 1] = 0; if (!auth_hostok(lc, remotehost, remote_ip)) { syslog(LOG_INFO|LOG_AUTH, @@ -1402,19 +1515,19 @@ getdatasock(mode) if (data >= 0) return (fdopen(data, mode)); (void) seteuid((uid_t)0); - s = socket(AF_INET, SOCK_STREAM, 0); + + s = socket(data_dest.su_family, SOCK_STREAM, 0); if (s < 0) goto bad; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ - data_source.sin_len = sizeof(struct sockaddr_in); - data_source.sin_family = AF_INET; - data_source.sin_addr = ctrl_addr.sin_addr; + data_source = ctrl_addr; + data_source.su_port = htons(20); /* ftp-data port */ for (tries = 1; ; tries++) { if (bind(s, (struct sockaddr *)&data_source, - sizeof(data_source)) >= 0) + data_source.su_len) >= 0) break; if (errno != EADDRINUSE || tries > 10) goto bad; @@ -1422,9 +1535,12 @@ getdatasock(mode) } (void) seteuid((uid_t)pw->pw_uid); #ifdef IP_TOS + if (data_source.su_family == AF_INET) + { on = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } #endif #ifdef TCP_NOPUSH /* @@ -1470,8 +1586,8 @@ dataconn(name, size, mode) else *sizebuf = '\0'; if (pdata >= 0) { - struct sockaddr_in from; - int s, fromlen = sizeof(from); + union sockunion from; + int s, fromlen = ctrl_addr.su_len; struct timeval timeout; fd_set set; @@ -1491,9 +1607,12 @@ dataconn(name, size, mode) (void) close(pdata); pdata = s; #ifdef IP_TOS + if (from.su_family == AF_INET) + { tos = IPTOS_THROUGHPUT; (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)); + } #endif reply(150, "Opening %s mode data connection for '%s'%s.", type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); @@ -1510,14 +1629,18 @@ dataconn(name, size, mode) usedefault = 1; file = getdatasock(mode); if (file == NULL) { - reply(425, "Can't create data socket (%s,%d): %s.", - inet_ntoa(data_source.sin_addr), - ntohs(data_source.sin_port), strerror(errno)); + char hostbuf[BUFSIZ], portbuf[BUFSIZ]; + getnameinfo((struct sockaddr *)&data_source, + data_source.su_len, hostbuf, sizeof(hostbuf) - 1, + portbuf, sizeof(portbuf), + NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID); + reply(425, "Can't create data socket (%s,%s): %s.", + hostbuf, portbuf, strerror(errno)); return (NULL); } data = fileno(file); while (connect(data, (struct sockaddr *)&data_dest, - sizeof(data_dest)) < 0) { + data_dest.su_len) < 0) { if (errno == EADDRINUSE && retry < swaitmax) { sleep((unsigned) swaitint); retry += swaitint; @@ -1768,14 +1891,20 @@ statfilecmd(filename) void statcmd() { - struct sockaddr_in *sin; + union sockunion *su; u_char *a, *p; + char hname[INET6_ADDRSTRLEN]; + int ispassive; lreply(211, "%s FTP server status:", hostname, version); printf(" %s\r\n", version); printf(" Connected to %s", remotehost); - if (!isdigit(remotehost[0])) - printf(" (%s)", inet_ntoa(his_addr.sin_addr)); + if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID)) { + if (strcmp(hname, remotehost) != 0) + printf(" (%s)", hname); + } printf("\r\n"); if (logged_in) { if (guest) @@ -1800,18 +1929,85 @@ statcmd() if (data != -1) printf(" Data connection open\r\n"); else if (pdata != -1) { - printf(" in Passive mode"); - sin = &pasv_addr; + ispassive = 1; + su = &pasv_addr; goto printaddr; } else if (usedefault == 0) { - printf(" PORT"); - sin = &data_dest; + ispassive = 0; + su = &data_dest; printaddr: - a = (u_char *) &sin->sin_addr; - p = (u_char *) &sin->sin_port; #define UC(b) (((int) b) & 0xff) - printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), - UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + if (epsvall) { + printf(" EPSV only mode (EPSV ALL)\r\n"); + goto epsvonly; + } + + /* PORT/PASV */ + if (su->su_family == AF_INET) { + a = (u_char *) &su->su_sin.sin_addr; + p = (u_char *) &su->su_sin.sin_port; + printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", + ispassive ? "PASV" : "PORT", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + } + + /* LPRT/LPSV */ + { + int alen, af, i; + + switch (su->su_family) { + case AF_INET: + a = (u_char *) &su->su_sin.sin_addr; + p = (u_char *) &su->su_sin.sin_port; + alen = sizeof(su->su_sin.sin_addr); + af = 4; + break; + case AF_INET6: + a = (u_char *) &su->su_sin6.sin6_addr; + p = (u_char *) &su->su_sin6.sin6_port; + alen = sizeof(su->su_sin6.sin6_addr); + af = 6; + break; + default: + af = 0; + break; + } + if (af) { + printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", + af, alen); + for (i = 0; i < alen; i++) + printf("%d,", UC(a[i])); + printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); + } + } + +epsvonly:; + /* EPRT/EPSV */ + { + int af; + + switch (su->su_family) { + case AF_INET: + af = 1; + break; + case AF_INET6: + af = 2; + break; + default: + af = 0; + break; + } + if (af) { + if (!getnameinfo((struct sockaddr *)su, su->su_len, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST)) { + printf(" %s |%d|%s|%d|\r\n", + ispassive ? "EPSV" : "EPRT", + af, hname, htons(su->su_port)); + } + } + } #undef UC } else printf(" No data connection\r\n"); @@ -2015,10 +2211,12 @@ renamecmd(from, to) } static void -dolog(sin) - struct sockaddr_in *sin; +dolog(who) + struct sockaddr *who; { - realhostname(remotehost, sizeof(remotehost) - 1, &sin->sin_addr); + int error; + + realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); #ifdef SETPROCTITLE #ifdef VIRTUAL_HOSTING @@ -2039,8 +2237,16 @@ dolog(sin) remotehost, hostname); else #endif + { + char who_name[MAXHOSTNAMELEN]; + + error = getnameinfo(who, who->sa_len, + who_name, sizeof(who_name) - 1, + NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); syslog(LOG_INFO, "connection from %s (%s)", remotehost, - inet_ntoa(sin->sin_addr)); + error == 0 ? who_name : ""); + } } } @@ -2112,7 +2318,7 @@ passive() if (pdata >= 0) /* close old port if one set */ close(pdata); - pdata = socket(AF_INET, SOCK_STREAM, 0); + pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; @@ -2121,7 +2327,7 @@ passive() (void) seteuid((uid_t)0); #ifdef IP_PORTRANGE - { + if (ctrl_addr.su_family == AF_INET) { int on = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; @@ -2132,9 +2338,8 @@ passive() #endif pasv_addr = ctrl_addr; - pasv_addr.sin_port = 0; - if (bind(pdata, (struct sockaddr *)&pasv_addr, - sizeof(pasv_addr)) < 0) + pasv_addr.su_port = 0; + if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) goto pasv_error; (void) seteuid((uid_t)pw->pw_uid); @@ -2144,8 +2349,15 @@ passive() goto pasv_error; if (listen(pdata, 1) < 0) goto pasv_error; - a = (char *) &pasv_addr.sin_addr; - p = (char *) &pasv_addr.sin_port; + if (pasv_addr.su_family == AF_INET) + a = (char *) &pasv_addr.su_sin.sin_addr; + else if (pasv_addr.su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) + a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; + else + goto pasv_error; + + p = (char *) &pasv_addr.su_port; #define UC(b) (((int) b) & 0xff) @@ -2162,6 +2374,121 @@ pasv_error: } /* + * Long Passive defined in RFC 1639. + * 228 Entering Long Passive Mode + * (af, hal, h1, h2, h3,..., pal, p1, p2...) + */ + +void +long_passive(cmd, pf) + char *cmd; + int pf; +{ + int len; + char *p, *a; + + if (pdata >= 0) /* close old port if one set */ + close(pdata); + + if (pf != PF_UNSPEC) { + if (ctrl_addr.su_family != pf) { + switch (ctrl_addr.su_family) { + case AF_INET: + pf = 1; + break; + case AF_INET6: + pf = 2; + break; + default: + pf = 0; + break; + } + /* + * XXX + * only EPRT/EPSV ready clients will understand this + */ + if (strcmp(cmd, "EPSV") == 0 && pf) { + reply(522, "Network protocol mismatch, " + "use (%d)", pf); + } else + reply(501, "Network protocol mismatch"); /*XXX*/ + + return; + } + } + + pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); + if (pdata < 0) { + perror_reply(425, "Can't open passive connection"); + return; + } + + (void) seteuid((uid_t)0); + + pasv_addr = ctrl_addr; + pasv_addr.su_port = 0; + len = pasv_addr.su_len; + + if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) + goto pasv_error; + + (void) seteuid((uid_t)pw->pw_uid); + + if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + goto pasv_error; + if (listen(pdata, 1) < 0) + goto pasv_error; + +#define UC(b) (((int) b) & 0xff) + + if (strcmp(cmd, "LPSV") == 0) { + p = (char *)&pasv_addr.su_port; + switch (pasv_addr.su_family) { + case AF_INET: + a = (char *) &pasv_addr.su_sin.sin_addr; + v4_reply: + reply(228, +"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + 2, UC(p[0]), UC(p[1])); + return; + case AF_INET6: + if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { + a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; + goto v4_reply; + } + a = (char *) &pasv_addr.su_sin6.sin6_addr; + reply(228, +"Entering Long Passive Mode " +"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", + 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), + UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), + UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), + 2, UC(p[0]), UC(p[1])); + return; + } + } else if (strcmp(cmd, "EPSV") == 0) { + switch (pasv_addr.su_family) { + case AF_INET: + case AF_INET6: + reply(229, "Entering Extended Passive Mode (|||%d|)", + ntohs(pasv_addr.su_port)); + return; + } + } else { + /* more proper error code? */ + } + +pasv_error: + (void) seteuid((uid_t)pw->pw_uid); + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +/* * Generate unique name for file with basename "local". * The file named "local" is already known to exist. * Generates failure reply on error. |
