From 4dd8b5ab798f9e3b8b60f5fa2c9ffb19b3774021 Mon Sep 17 00:00:00 2001 From: Yoshinobu Inoue Date: Thu, 27 Jan 2000 09:28:38 +0000 Subject: [PATCH] another tcp apps IPv6 updates.(should be make world safe) ftp, telnet, ftpd, faithd also telnet related sync with crypto, secure, kerberosIV Obtained from: KAME project --- contrib/telnet/telnet/commands.c | 432 ++++++---- contrib/telnet/telnet/externs.h | 10 + contrib/telnet/telnet/main.c | 45 +- contrib/telnet/telnet/telnet.1 | 12 +- contrib/telnet/telnetd/telnetd.8 | 5 +- contrib/telnet/telnetd/telnetd.c | 90 ++- crypto/telnet/telnet/commands.c | 432 ++++++---- crypto/telnet/telnet/externs.h | 10 + crypto/telnet/telnet/main.c | 45 +- crypto/telnet/telnet/telnet.1 | 12 +- crypto/telnet/telnetd/telnetd.8 | 5 +- crypto/telnet/telnetd/telnetd.c | 90 ++- kerberos5/libexec/telnetd/Makefile | 2 +- kerberos5/usr.bin/telnet/Makefile | 6 +- kerberosIV/libexec/telnetd/Makefile | 2 +- kerberosIV/usr.bin/telnet/Makefile | 6 +- libexec/ftpd/Makefile | 1 + libexec/ftpd/extern.h | 16 + libexec/ftpd/ftpcmd.y | 368 ++++++++- libexec/ftpd/ftpd.8 | 23 +- libexec/ftpd/ftpd.c | 503 +++++++++--- libexec/ftpd/logwtmp.c | 23 +- libexec/ftpd/popen.c | 1 + secure/libexec/telnetd/Makefile | 2 +- secure/usr.bin/telnet/Makefile | 3 + usr.bin/ftp/Makefile | 1 + usr.bin/ftp/extern.h | 2 +- usr.bin/ftp/fetch.c | 107 ++- usr.bin/ftp/ftp.1 | 22 +- usr.bin/ftp/ftp.c | 418 ++++++++-- usr.bin/ftp/ftp_var.h | 11 +- usr.bin/ftp/main.c | 69 +- usr.bin/ftp/util.c | 17 +- usr.bin/telnet/Makefile | 6 +- usr.bin/telnet/commands.c | 432 ++++++---- usr.bin/telnet/externs.h | 10 + usr.bin/telnet/main.c | 46 +- usr.bin/telnet/telnet.1 | 11 +- usr.sbin/Makefile | 1 + usr.sbin/faithd/Makefile | 24 + usr.sbin/faithd/README | 140 ++++ usr.sbin/faithd/faithd.8 | 256 ++++++ usr.sbin/faithd/faithd.c | 837 ++++++++++++++++++++ usr.sbin/faithd/faithd.h | 70 ++ usr.sbin/faithd/ftp.c | 1139 +++++++++++++++++++++++++++ usr.sbin/faithd/rsh.c | 210 +++++ usr.sbin/faithd/tcp.c | 300 +++++++ usr.sbin/faithd/test/faithd.rb | 312 ++++++++ 48 files changed, 5716 insertions(+), 869 deletions(-) create mode 100644 usr.sbin/faithd/Makefile create mode 100644 usr.sbin/faithd/README create mode 100644 usr.sbin/faithd/faithd.8 create mode 100644 usr.sbin/faithd/faithd.c create mode 100644 usr.sbin/faithd/faithd.h create mode 100644 usr.sbin/faithd/ftp.c create mode 100644 usr.sbin/faithd/rsh.c create mode 100644 usr.sbin/faithd/tcp.c create mode 100644 usr.sbin/faithd/test/faithd.rb diff --git a/contrib/telnet/telnet/commands.c b/contrib/telnet/telnet/commands.c index f6bece2aa32a..dd83669d1d9b 100644 --- a/contrib/telnet/telnet/commands.c +++ b/contrib/telnet/telnet/commands.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef lint @@ -83,6 +85,7 @@ static const char sccsid[] = "@(#)commands.c 8.4 (Berkeley) 5/30/95"; # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include +#include #ifndef MAXHOSTNAMELEN @@ -2270,27 +2273,76 @@ ayt_status() } #endif -unsigned long inet_addr(); +static const char * +sockaddr_ntop(sa) + struct sockaddr *sa; +{ + void *addr; + static char addrbuf[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET: + addr = &((struct sockaddr_in *)sa)->sin_addr; + break; +#ifdef INET6 + case AF_INET6: + addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + break; +#endif + default: + return NULL; + } + inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); + return addrbuf; +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +static int +setpolicy(net, res, policy) + int net; + struct addrinfo *res; + char *policy; +{ + char *buf; + int level; + int optname; + + if (policy == NULL) + return 0; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + printf("%s\n", ipsec_strerror()); + return -1; + } + level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; + if (setsockopt(net, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); +} +#endif int tn(argc, argv) int argc; char *argv[]; { - register struct hostent *host = 0; - struct sockaddr_in sin, src_sin; - struct servent *sp = 0; - unsigned long temp; - extern char *inet_ntoa(); -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + struct sockaddr_storage ss, src_ss; char *srp = 0, *strrchr(); - unsigned long sourceroute(), srlen; -#endif + int proto, opt; + int sourceroute(), srlen; + int srcroute = 0, result; char *cmd, *hostp = 0, *portp = 0, *user = 0; char *src_addr = NULL; + struct addrinfo hints, *res; + int error = 0; /* clear the socket address prior to use */ - memset((char *)&sin, 0, sizeof(sin)); + memset((char *)&ss, 0, sizeof(ss)); if (connected) { printf("?Already connected to %s\n", hostname); @@ -2350,126 +2402,106 @@ tn(argc, argv) goto usage; if (src_addr != NULL) { - bzero((char *)&src_sin, sizeof(src_sin)); - src_sin.sin_family = AF_INET; - if (!inet_aton(src_addr, &src_sin.sin_addr)) { - host = gethostbyname2(src_addr, AF_INET); - if (host == NULL) { - herror(src_addr); - return 0; - } - if (host->h_length != sizeof(src_sin.sin_addr)) { - fprintf(stderr, "telnet: gethostbyname2: invalid address\n"); - return 0; - } - memcpy((void *)&src_sin.sin_addr, (void *)host->h_addr_list[0], - sizeof(src_sin.sin_addr)); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, 0, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &res); } + if (error != 0) { + fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", src_addr, strerror(errno)); + return 0; + } + memcpy((void *)&src_ss, (void *)res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } - -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (hostp[0] == '@' || hostp[0] == '!') { - if ((hostname = strrchr(hostp, ':')) == NULL) + if ( +#ifdef INET6 + family == AF_INET6 || +#endif + (hostname = strrchr(hostp, ':')) == NULL) hostname = strrchr(hostp, '@'); hostname++; - srp = 0; - temp = sourceroute(hostp, &srp, &srlen); - if (temp == 0) { - herror(srp); + srcroute = 1; + } else + hostname = hostp; + if (!portp) { + telnetport = 1; + portp = "telnet"; + } else if (*portp == '-') { + portp++; + telnetport = 1; + } else + telnetport = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error == 0) { + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + 0); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else if (error == EAI_NONAME) { + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error != 0) { + fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", hostname, strerror(errno)); setuid(getuid()); return 0; - } else if (temp == -1) { + } + memcpy((void *)&ss, (void *)res->ai_addr, res->ai_addrlen); + if (srcroute != 0) + (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1); + else if (res->ai_canonname != NULL) + strcpy(_hostname, res->ai_canonname); + else + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } + if (srcroute != 0) { + srp = 0; + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { + setuid(getuid()); + freeaddrinfo(res); + return 0; + } else if (result == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); + freeaddrinfo(res); return 0; - } else { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; } - } else { -#endif - temp = inet_addr(hostp); - if (temp != INADDR_NONE) { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - if (doaddrlookup) - host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); - if (host) - (void) strncpy(_hostname, host->h_name, sizeof(_hostname)); - else - (void) strncpy(_hostname, hostp, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - host = gethostbyname(hostp); - if (host) { - sin.sin_family = host->h_addrtype; -#if defined(h_addr) /* In 4.3, this is a #define */ - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); -#else /* defined(h_addr) */ - memmove((caddr_t)&sin.sin_addr, host->h_addr, host->h_length); -#endif /* defined(h_addr) */ - strncpy(_hostname, host->h_name, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - herror(hostp); - setuid(getuid()); - return 0; - } - } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) } -#endif - if (portp) { - if (*portp == '-') { - portp++; - telnetport = 1; - } else - telnetport = 0; - sin.sin_port = atoi(portp); - if (sin.sin_port == 0) { - sp = getservbyname(portp, "tcp"); - if (sp) - sin.sin_port = sp->s_port; - else { - printf("%s: bad port number\n", portp); - setuid(getuid()); - return 0; - } - } else { -#if !defined(htons) - u_short htons P((unsigned short)); -#endif /* !defined(htons) */ - sin.sin_port = htons(sin.sin_port); - } - } else { - if (sp == 0) { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) { - fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); - setuid(getuid()); - return 0; - } - sin.sin_port = sp->s_port; - } - telnetport = 1; - } - printf("Trying %s...\n", inet_ntoa(sin.sin_addr)); + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); do { - net = socket(AF_INET, SOCK_STREAM, 0); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setuid(getuid()); if (net < 0) { perror("telnet: socket"); return 0; } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0) - perror("setsockopt (IP_OPTIONS)"); -#endif + if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0) + perror("setsockopt (source route)"); #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (res->ai_family == PF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) @@ -2490,28 +2522,31 @@ tn(argc, argv) } if (src_addr != NULL) { - if (bind(net, (struct sockaddr *)&src_sin, sizeof(src_sin)) == -1) { + if (bind(net, (struct sockaddr *)&src_ss, + ((struct sockaddr *)&src_ss)->sa_len) == -1) { perror("bind"); return 0; } } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) + return 0; + if (setpolicy(net, res, ipsec_policy_out) < 0) + return 0; +#endif - if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { -#if defined(h_addr) /* In 4.3, this is a #define */ - if (host && host->h_addr_list[1]) { + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + if (res->ai_next) { int oerrno = errno; fprintf(stderr, "telnet: connect to address %s: ", - inet_ntoa(sin.sin_addr)); + sockaddr_ntop(res->ai_addr)); errno = oerrno; perror((char *)0); - host->h_addr_list++; - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); + res = res->ai_next; (void) NetClose(net); continue; } -#endif /* defined(h_addr) */ perror("telnet: Unable to connect to remote host"); return 0; } @@ -2520,6 +2555,7 @@ tn(argc, argv) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ } while (connected == 0); + freeaddrinfo(res); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; @@ -2861,8 +2897,6 @@ cmdrc(m1, m2) fclose(rcfile); } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst @@ -2876,6 +2910,10 @@ cmdrc(m1, m2) * be the address to connect() to. * * Arguments: + * + * res: ponter to addrinfo structure which contains sockaddr to + * the host to connect to. + * * arg: pointer to route list to decipher * * cpp: If *cpp is not equal to NULL, this is a @@ -2885,9 +2923,18 @@ cmdrc(m1, m2) * lenp: pointer to an integer that contains the * length of *cpp if *cpp != NULL. * + * protop: pointer to an integer that should be filled in with + * appropriate protocol for setsockopt, as socket + * protocol family. + * + * optp: pointer to an integer that should be filled in with + * appropriate option for setsockopt, as socket protocol + * family. + * * Return values: * - * Returns the address of the host to connect to. If the + * If the return value is 1, then all operations are + * successful. If the * return value is -1, there was a syntax error in the * option, either unknown characters, or too many hosts. * If the return value is 0, one of the hostnames in the @@ -2901,21 +2948,32 @@ cmdrc(m1, m2) * *lenp: This will be filled in with how long the option * pointed to by *cpp is. * + * *protop: This will be filled in with appropriate protocol for + * setsockopt, as socket protocol family. + * + * *optp: This will be filled in with appropriate option for + * setsockopt, as socket protocol family. */ - unsigned long -sourceroute(arg, cpp, lenp) +int +sourceroute(ai, arg, cpp, lenp, protop, optp) + struct addrinfo *ai; char *arg; char **cpp; int *lenp; + int *protop; + int *optp; { - static char lsr[44]; + static char buf[1024]; /*XXX*/ + struct cmsghdr *cmsg; #ifdef sysV88 static IOPTN ipopt; #endif - char *cp, *cp2, *lsrp, *lsrep; + char *cp, *cp2, *lsrp, *ep; register int tmp; - struct in_addr sin_addr; - register struct hostent *host = 0; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct addrinfo hints, *res; + int error; register char c; /* @@ -2924,22 +2982,46 @@ sourceroute(arg, cpp, lenp) */ if (cpp == NULL || lenp == NULL) return((unsigned long)-1); - if (*cpp != NULL && *lenp < 7) - return((unsigned long)-1); + if (*cpp != NULL) { + switch (res->ai_family) { + case AF_INET: + if (*lenp < 7) + return((unsigned long)-1); + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < (sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return((unsigned long)-1); + break; +#endif + } + } /* * Decide whether we have a buffer passed to us, * or if we need to use our own static buffer. */ if (*cpp) { lsrp = *cpp; - lsrep = lsrp + *lenp; + ep = lsrp + *lenp; } else { - *cpp = lsrp = lsr; - lsrep = lsrp + 44; + *cpp = lsrp = buf; + ep = lsrp + 1024; } cp = arg; +#ifdef INET6 + if (ai->ai_family == AF_INET6) { + cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0); + if (*cp != '@') + return -1; + *protop = IPPROTO_IPV6; + *optp = IPV6_PKTOPTIONS; + } else +#endif + { /* * Next, decide whether we have a loose source * route or a strict source route, and fill in @@ -2966,13 +3048,20 @@ sourceroute(arg, cpp, lenp) lsrp++; /* skip over length, we'll fill it in later */ *lsrp++ = 4; #endif + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + } cp++; - - sin_addr.s_addr = 0; - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; for (c = 0;;) { - if (c == ':') + if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') cp2 = 0; else for (cp2 = cp; (c = *cp2); cp2++) { if (c == ',') { @@ -2981,7 +3070,11 @@ sourceroute(arg, cpp, lenp) cp2++; } else if (c == '@') { *cp2++ = '\0'; - } else if (c == ':') { + } else if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { *cp2++ = '\0'; } else continue; @@ -2990,21 +3083,32 @@ sourceroute(arg, cpp, lenp) if (!c) cp2 = 0; - if ((tmp = inet_addr(cp)) != -1) { - sin_addr.s_addr = tmp; - } else if ((host = gethostbyname(cp))) { -#if defined(h_addr) - memmove((caddr_t)&sin_addr, - host->h_addr_list[0], host->h_length); -#else - memmove((caddr_t)&sin_addr, host->h_addr, host->h_length); -#endif - } else { + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(cp, NULL, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", cp, + strerror(errno)); *cpp = cp; return(0); } - memmove(lsrp, (char *)&sin_addr, 4); +#ifdef INET6 + if (res->ai_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + inet6_rthdr_add(cmsg, &sin6->sin6_addr, + IPV6_RTHDR_LOOSE); + } else +#endif + { + sin = (struct sockaddr_in *)res->ai_addr; + memcpy(lsrp, (char *)&sin->sin_addr, 4); lsrp += 4; + } if (cp2) cp = cp2; else @@ -3012,9 +3116,27 @@ sourceroute(arg, cpp, lenp) /* * Check to make sure there is space for next address */ - if (lsrp + 4 > lsrep) +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)cmsg + + sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) return((unsigned long)-1); + } else +#endif + if (lsrp + 4 > ep) + return((unsigned long)-1); + freeaddrinfo(res); } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + *lenp = cmsg->cmsg_len; + } else +#endif + { #ifndef sysV88 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { *cpp = 0; @@ -3033,6 +3155,10 @@ sourceroute(arg, cpp, lenp) *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif - return(sin_addr.s_addr); + } + freeaddrinfo(res); + return 1; } -#endif + + + diff --git a/contrib/telnet/telnet/externs.h b/contrib/telnet/telnet/externs.h index 0c6894f53d4e..46253d9a6048 100644 --- a/contrib/telnet/telnet/externs.h +++ b/contrib/telnet/telnet/externs.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)externs.h 8.3 (Berkeley) 5/30/95 + * $FreeBSD$ */ #ifndef BSD @@ -87,6 +88,14 @@ typedef unsigned char cc_t; #include #endif +#if defined(IPSEC) +#include +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + #ifndef _POSIX_VDISABLE # ifdef sun # include /* pick up VDISABLE definition, mayby */ @@ -116,6 +125,7 @@ extern int autologin, /* Autologin enabled */ skiprc, /* Don't process the ~/.telnetrc file */ eight, /* use eight bit mode (binary in and/or out */ + family, /* address family of peer */ flushout, /* flush output */ connected, /* Are we connected to the other side? */ globalmode, /* Mode tty should be in */ diff --git a/contrib/telnet/telnet/main.c b/contrib/telnet/telnet/main.c index 2dc05fd73eb0..c3d0f5d74ba9 100644 --- a/contrib/telnet/telnet/main.c +++ b/contrib/telnet/telnet/main.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef lint @@ -42,6 +44,7 @@ static const char sccsid[] = "@(#)main.c 8.3 (Berkeley) 5/30/95"; #endif /* not lint */ #include +#include #include #include "ring.h" @@ -70,6 +73,13 @@ void init_telnet(void); void init_sys(void); void init_3270(void); +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +int family = AF_UNSPEC; + /* * Initialize variables. */ @@ -95,10 +105,10 @@ usage() fprintf(stderr, "Usage: %s %s%s%s%s\n", prompt, #ifdef AUTHENTICATION - "[-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", "\n\t[-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", #else - "[-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", "\n\t[-n tracefile] ", #endif #if defined(TN3270) && defined(unix) @@ -112,6 +122,9 @@ usage() #else "[-r] [-s src_addr] ", #endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "[-P policy]" +#endif #ifdef ENCRYPTION "[-x] [host-name [port]]" #else /* ENCRYPTION */ @@ -156,8 +169,24 @@ main(argc, argv) rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; autologin = -1; - while ((ch = getopt(argc, argv, "8EKLNS:X:acde:fFk:l:n:rs:t:x")) != EOF) { +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define IPSECOPT "P:" +#else +#define IPSECOPT +#endif + while ((ch = getopt(argc, argv, + "468EKLNS:X:acde:fFk:l:n:rs:t:x" IPSECOPT)) != -1) +#undef IPSECOPT + { switch(ch) { + case '4': + family = AF_INET; + break; +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif case '8': eight = 3; /* binary output and input */ break; @@ -299,6 +328,16 @@ main(argc, argv) prompt); #endif /* ENCRYPTION */ break; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + case 'P': + if (!strncmp("in", optarg, 2)) + ipsec_policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + ipsec_policy_out = strdup(optarg); + else + usage(); + break; +#endif case '?': default: usage(); diff --git a/contrib/telnet/telnet/telnet.1 b/contrib/telnet/telnet/telnet.1 index 984146232215..3378d3a4ad92 100644 --- a/contrib/telnet/telnet/telnet.1 +++ b/contrib/telnet/telnet/telnet.1 @@ -30,8 +30,9 @@ .\" SUCH DAMAGE. .\" .\" @(#)telnet.1 8.6 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNET 1 .Os BSD 4.2 .Sh NAME @@ -549,9 +550,10 @@ will attempt to contact a .Tn TELNET server at the default port. The host specification may be either a host name (see -.Xr hosts 5 ) -or an Internet address specified in the \*(Lqdot notation\*(Rq (see -.Xr inet 3 ) . +.Xr hosts 5 ) , +an Internet address specified in the \*(Lqdot notation\*(Rq (see +.Xr inet 3 ) , +or IPv6 host name or IPv6 coloned-hexadecimal addreess. The .Op Fl l option may be used to specify the user name @@ -1367,6 +1369,8 @@ The .Nm Telnet command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. .Sh NOTES .Pp On some remote systems, echo has to be turned off manually when in diff --git a/contrib/telnet/telnetd/telnetd.8 b/contrib/telnet/telnetd/telnetd.8 index e03f290daaa9..4e004a4edede 100644 --- a/contrib/telnet/telnetd/telnetd.8 +++ b/contrib/telnet/telnetd/telnetd.8 @@ -30,8 +30,9 @@ .\" SUCH DAMAGE. .\" .\" @(#)telnetd.8 8.4 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNETD 8 .Os BSD 4.2 .Sh NAME @@ -610,3 +611,5 @@ never sends .Tn TELNET .Dv IAC GA (go ahead) commands. +.Sh HISTORY +IPv6 support was added by WIDE/KAME project. diff --git a/contrib/telnet/telnetd/telnetd.c b/contrib/telnet/telnetd/telnetd.c index 57e6ed2a7a79..6d7bcde5fc4f 100644 --- a/contrib/telnet/telnetd/telnetd.c +++ b/contrib/telnet/telnetd/telnetd.c @@ -70,6 +70,12 @@ static const char rcsid[] = #include #include # endif /* SO_SEC_MULTI */ + +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + int secflag; char tty_dev[16]; struct secdev dv; @@ -128,7 +134,7 @@ char ptyibuf2[BUFSIZ]; # include int readstream(int p, char *ibuf, int bufsize); -void doit(struct sockaddr_in *who); +void doit(struct sockaddr *who); int terminaltypeok(char *s); void startslave(char *host, int autologin, char *autoname); @@ -145,7 +151,7 @@ int debug = 0; int keepalive = 1; char *altlogin; -void doit __P((struct sockaddr_in *)); +void doit __P((struct sockaddr *)); int terminaltypeok __P((char *)); void startslave __P((char *, int, char *)); extern void usage P((void)); @@ -157,6 +163,7 @@ extern void usage P((void)); */ char valid_opts[] = { 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', + '4', '6', #ifdef AUTHENTICATION 'a', ':', 'X', ':', #endif @@ -184,11 +191,13 @@ char valid_opts[] = { '\0' }; - int +int family = AF_INET; + +int main(argc, argv) char *argv[]; { - struct sockaddr_in from; + struct sockaddr_storage from; int on = 1, fromlen; register int ch; #if defined(IPPROTO_IP) && defined(IP_TOS) @@ -406,6 +415,16 @@ main(argc, argv) break; #endif /* AUTHENTICATION */ + case '4': + family = AF_INET; + break; + +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif + default: warnx("%c: unknown option", ch); /* FALLTHROUGH */ @@ -419,43 +438,41 @@ main(argc, argv) argv += optind; if (debug) { - int s, ns, foo; - struct servent *sp; - static struct sockaddr_in sin = { AF_INET }; + int s, ns, foo, error; + char *service = "telnet"; + struct addrinfo hints, *res; if (argc > 1) { usage(); /* NOT REACHED */ - } else if (argc == 1) { - if ((sp = getservbyname(*argv, "tcp"))) { - sin.sin_port = sp->s_port; - } else { - sin.sin_port = atoi(*argv); - if ((int)sin.sin_port <= 0) { - warnx("%s: bad port #", *argv); - usage(); - /* NOT REACHED */ - } - sin.sin_port = htons((u_short)sin.sin_port); - } - } else { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) - errx(1, "tcp/telnet: unknown service"); - sin.sin_port = sp->s_port; + } else if (argc == 1) + service = *argv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(NULL, service, &hints, &res); + + if (error) { + errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); + if (error == EAI_SYSTEM) + errx(1, "tcp/%s: %s\n", service, strerror(errno)); + usage(); } - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) err(1, "socket"); (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) err(1, "bind"); if (listen(s, 1) < 0) err(1, "listen"); - foo = sizeof sin; - ns = accept(s, (struct sockaddr *)&sin, &foo); + foo = res->ai_addrlen; + ns = accept(s, res->ai_addr, &foo); if (ns < 0) err(1, "accept"); (void) dup2(ns, 0); @@ -537,7 +554,7 @@ main(argc, argv) } #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (from.ss_family == AF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) @@ -553,7 +570,7 @@ main(argc, argv) } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ net = 0; - doit(&from); + doit((struct sockaddr *)&from); /* NOTREACHED */ return(0); } /* end of main */ @@ -821,8 +838,9 @@ char user_name[256]; */ void doit(who) - struct sockaddr_in *who; + struct sockaddr *who; { + int err; int ptynum; /* @@ -865,16 +883,18 @@ doit(who) #endif /* _SC_CRAY_SECURE_SYS */ /* get name of connected client */ - if (realhostname(remote_hostname, sizeof(remote_hostname) - 1, - &who->sin_addr) == HOSTNAME_INVALIDADDR && registerd_host_only) + if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, + who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) fatal(net, "Couldn't resolve your address into a host name.\r\n\ Please contact your net administrator"); remote_hostname[sizeof(remote_hostname) - 1] = '\0'; trimdomain(remote_hostname, UT_HOSTSIZE); if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) - strncpy(remote_hostname, inet_ntoa(who->sin_addr), - sizeof(remote_hostname) - 1); + err = getnameinfo(who, who->sa_len, remote_hostname, + sizeof(remote_hostname), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX: do 'err' check */ (void) gethostname(host_name, sizeof(host_name) - 1); host_name[sizeof(host_name) - 1] = '\0'; diff --git a/crypto/telnet/telnet/commands.c b/crypto/telnet/telnet/commands.c index f6bece2aa32a..dd83669d1d9b 100644 --- a/crypto/telnet/telnet/commands.c +++ b/crypto/telnet/telnet/commands.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef lint @@ -83,6 +85,7 @@ static const char sccsid[] = "@(#)commands.c 8.4 (Berkeley) 5/30/95"; # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include +#include #ifndef MAXHOSTNAMELEN @@ -2270,27 +2273,76 @@ ayt_status() } #endif -unsigned long inet_addr(); +static const char * +sockaddr_ntop(sa) + struct sockaddr *sa; +{ + void *addr; + static char addrbuf[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET: + addr = &((struct sockaddr_in *)sa)->sin_addr; + break; +#ifdef INET6 + case AF_INET6: + addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + break; +#endif + default: + return NULL; + } + inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); + return addrbuf; +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +static int +setpolicy(net, res, policy) + int net; + struct addrinfo *res; + char *policy; +{ + char *buf; + int level; + int optname; + + if (policy == NULL) + return 0; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + printf("%s\n", ipsec_strerror()); + return -1; + } + level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; + if (setsockopt(net, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); +} +#endif int tn(argc, argv) int argc; char *argv[]; { - register struct hostent *host = 0; - struct sockaddr_in sin, src_sin; - struct servent *sp = 0; - unsigned long temp; - extern char *inet_ntoa(); -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + struct sockaddr_storage ss, src_ss; char *srp = 0, *strrchr(); - unsigned long sourceroute(), srlen; -#endif + int proto, opt; + int sourceroute(), srlen; + int srcroute = 0, result; char *cmd, *hostp = 0, *portp = 0, *user = 0; char *src_addr = NULL; + struct addrinfo hints, *res; + int error = 0; /* clear the socket address prior to use */ - memset((char *)&sin, 0, sizeof(sin)); + memset((char *)&ss, 0, sizeof(ss)); if (connected) { printf("?Already connected to %s\n", hostname); @@ -2350,126 +2402,106 @@ tn(argc, argv) goto usage; if (src_addr != NULL) { - bzero((char *)&src_sin, sizeof(src_sin)); - src_sin.sin_family = AF_INET; - if (!inet_aton(src_addr, &src_sin.sin_addr)) { - host = gethostbyname2(src_addr, AF_INET); - if (host == NULL) { - herror(src_addr); - return 0; - } - if (host->h_length != sizeof(src_sin.sin_addr)) { - fprintf(stderr, "telnet: gethostbyname2: invalid address\n"); - return 0; - } - memcpy((void *)&src_sin.sin_addr, (void *)host->h_addr_list[0], - sizeof(src_sin.sin_addr)); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, 0, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &res); } + if (error != 0) { + fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", src_addr, strerror(errno)); + return 0; + } + memcpy((void *)&src_ss, (void *)res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } - -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (hostp[0] == '@' || hostp[0] == '!') { - if ((hostname = strrchr(hostp, ':')) == NULL) + if ( +#ifdef INET6 + family == AF_INET6 || +#endif + (hostname = strrchr(hostp, ':')) == NULL) hostname = strrchr(hostp, '@'); hostname++; - srp = 0; - temp = sourceroute(hostp, &srp, &srlen); - if (temp == 0) { - herror(srp); + srcroute = 1; + } else + hostname = hostp; + if (!portp) { + telnetport = 1; + portp = "telnet"; + } else if (*portp == '-') { + portp++; + telnetport = 1; + } else + telnetport = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error == 0) { + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + 0); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else if (error == EAI_NONAME) { + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error != 0) { + fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", hostname, strerror(errno)); setuid(getuid()); return 0; - } else if (temp == -1) { + } + memcpy((void *)&ss, (void *)res->ai_addr, res->ai_addrlen); + if (srcroute != 0) + (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1); + else if (res->ai_canonname != NULL) + strcpy(_hostname, res->ai_canonname); + else + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } + if (srcroute != 0) { + srp = 0; + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { + setuid(getuid()); + freeaddrinfo(res); + return 0; + } else if (result == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); + freeaddrinfo(res); return 0; - } else { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; } - } else { -#endif - temp = inet_addr(hostp); - if (temp != INADDR_NONE) { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - if (doaddrlookup) - host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); - if (host) - (void) strncpy(_hostname, host->h_name, sizeof(_hostname)); - else - (void) strncpy(_hostname, hostp, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - host = gethostbyname(hostp); - if (host) { - sin.sin_family = host->h_addrtype; -#if defined(h_addr) /* In 4.3, this is a #define */ - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); -#else /* defined(h_addr) */ - memmove((caddr_t)&sin.sin_addr, host->h_addr, host->h_length); -#endif /* defined(h_addr) */ - strncpy(_hostname, host->h_name, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - herror(hostp); - setuid(getuid()); - return 0; - } - } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) } -#endif - if (portp) { - if (*portp == '-') { - portp++; - telnetport = 1; - } else - telnetport = 0; - sin.sin_port = atoi(portp); - if (sin.sin_port == 0) { - sp = getservbyname(portp, "tcp"); - if (sp) - sin.sin_port = sp->s_port; - else { - printf("%s: bad port number\n", portp); - setuid(getuid()); - return 0; - } - } else { -#if !defined(htons) - u_short htons P((unsigned short)); -#endif /* !defined(htons) */ - sin.sin_port = htons(sin.sin_port); - } - } else { - if (sp == 0) { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) { - fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); - setuid(getuid()); - return 0; - } - sin.sin_port = sp->s_port; - } - telnetport = 1; - } - printf("Trying %s...\n", inet_ntoa(sin.sin_addr)); + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); do { - net = socket(AF_INET, SOCK_STREAM, 0); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setuid(getuid()); if (net < 0) { perror("telnet: socket"); return 0; } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0) - perror("setsockopt (IP_OPTIONS)"); -#endif + if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0) + perror("setsockopt (source route)"); #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (res->ai_family == PF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) @@ -2490,28 +2522,31 @@ tn(argc, argv) } if (src_addr != NULL) { - if (bind(net, (struct sockaddr *)&src_sin, sizeof(src_sin)) == -1) { + if (bind(net, (struct sockaddr *)&src_ss, + ((struct sockaddr *)&src_ss)->sa_len) == -1) { perror("bind"); return 0; } } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) + return 0; + if (setpolicy(net, res, ipsec_policy_out) < 0) + return 0; +#endif - if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { -#if defined(h_addr) /* In 4.3, this is a #define */ - if (host && host->h_addr_list[1]) { + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + if (res->ai_next) { int oerrno = errno; fprintf(stderr, "telnet: connect to address %s: ", - inet_ntoa(sin.sin_addr)); + sockaddr_ntop(res->ai_addr)); errno = oerrno; perror((char *)0); - host->h_addr_list++; - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); + res = res->ai_next; (void) NetClose(net); continue; } -#endif /* defined(h_addr) */ perror("telnet: Unable to connect to remote host"); return 0; } @@ -2520,6 +2555,7 @@ tn(argc, argv) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ } while (connected == 0); + freeaddrinfo(res); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; @@ -2861,8 +2897,6 @@ cmdrc(m1, m2) fclose(rcfile); } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst @@ -2876,6 +2910,10 @@ cmdrc(m1, m2) * be the address to connect() to. * * Arguments: + * + * res: ponter to addrinfo structure which contains sockaddr to + * the host to connect to. + * * arg: pointer to route list to decipher * * cpp: If *cpp is not equal to NULL, this is a @@ -2885,9 +2923,18 @@ cmdrc(m1, m2) * lenp: pointer to an integer that contains the * length of *cpp if *cpp != NULL. * + * protop: pointer to an integer that should be filled in with + * appropriate protocol for setsockopt, as socket + * protocol family. + * + * optp: pointer to an integer that should be filled in with + * appropriate option for setsockopt, as socket protocol + * family. + * * Return values: * - * Returns the address of the host to connect to. If the + * If the return value is 1, then all operations are + * successful. If the * return value is -1, there was a syntax error in the * option, either unknown characters, or too many hosts. * If the return value is 0, one of the hostnames in the @@ -2901,21 +2948,32 @@ cmdrc(m1, m2) * *lenp: This will be filled in with how long the option * pointed to by *cpp is. * + * *protop: This will be filled in with appropriate protocol for + * setsockopt, as socket protocol family. + * + * *optp: This will be filled in with appropriate option for + * setsockopt, as socket protocol family. */ - unsigned long -sourceroute(arg, cpp, lenp) +int +sourceroute(ai, arg, cpp, lenp, protop, optp) + struct addrinfo *ai; char *arg; char **cpp; int *lenp; + int *protop; + int *optp; { - static char lsr[44]; + static char buf[1024]; /*XXX*/ + struct cmsghdr *cmsg; #ifdef sysV88 static IOPTN ipopt; #endif - char *cp, *cp2, *lsrp, *lsrep; + char *cp, *cp2, *lsrp, *ep; register int tmp; - struct in_addr sin_addr; - register struct hostent *host = 0; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct addrinfo hints, *res; + int error; register char c; /* @@ -2924,22 +2982,46 @@ sourceroute(arg, cpp, lenp) */ if (cpp == NULL || lenp == NULL) return((unsigned long)-1); - if (*cpp != NULL && *lenp < 7) - return((unsigned long)-1); + if (*cpp != NULL) { + switch (res->ai_family) { + case AF_INET: + if (*lenp < 7) + return((unsigned long)-1); + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < (sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return((unsigned long)-1); + break; +#endif + } + } /* * Decide whether we have a buffer passed to us, * or if we need to use our own static buffer. */ if (*cpp) { lsrp = *cpp; - lsrep = lsrp + *lenp; + ep = lsrp + *lenp; } else { - *cpp = lsrp = lsr; - lsrep = lsrp + 44; + *cpp = lsrp = buf; + ep = lsrp + 1024; } cp = arg; +#ifdef INET6 + if (ai->ai_family == AF_INET6) { + cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0); + if (*cp != '@') + return -1; + *protop = IPPROTO_IPV6; + *optp = IPV6_PKTOPTIONS; + } else +#endif + { /* * Next, decide whether we have a loose source * route or a strict source route, and fill in @@ -2966,13 +3048,20 @@ sourceroute(arg, cpp, lenp) lsrp++; /* skip over length, we'll fill it in later */ *lsrp++ = 4; #endif + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + } cp++; - - sin_addr.s_addr = 0; - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; for (c = 0;;) { - if (c == ':') + if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') cp2 = 0; else for (cp2 = cp; (c = *cp2); cp2++) { if (c == ',') { @@ -2981,7 +3070,11 @@ sourceroute(arg, cpp, lenp) cp2++; } else if (c == '@') { *cp2++ = '\0'; - } else if (c == ':') { + } else if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { *cp2++ = '\0'; } else continue; @@ -2990,21 +3083,32 @@ sourceroute(arg, cpp, lenp) if (!c) cp2 = 0; - if ((tmp = inet_addr(cp)) != -1) { - sin_addr.s_addr = tmp; - } else if ((host = gethostbyname(cp))) { -#if defined(h_addr) - memmove((caddr_t)&sin_addr, - host->h_addr_list[0], host->h_length); -#else - memmove((caddr_t)&sin_addr, host->h_addr, host->h_length); -#endif - } else { + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(cp, NULL, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", cp, + strerror(errno)); *cpp = cp; return(0); } - memmove(lsrp, (char *)&sin_addr, 4); +#ifdef INET6 + if (res->ai_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + inet6_rthdr_add(cmsg, &sin6->sin6_addr, + IPV6_RTHDR_LOOSE); + } else +#endif + { + sin = (struct sockaddr_in *)res->ai_addr; + memcpy(lsrp, (char *)&sin->sin_addr, 4); lsrp += 4; + } if (cp2) cp = cp2; else @@ -3012,9 +3116,27 @@ sourceroute(arg, cpp, lenp) /* * Check to make sure there is space for next address */ - if (lsrp + 4 > lsrep) +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)cmsg + + sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) return((unsigned long)-1); + } else +#endif + if (lsrp + 4 > ep) + return((unsigned long)-1); + freeaddrinfo(res); } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + *lenp = cmsg->cmsg_len; + } else +#endif + { #ifndef sysV88 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { *cpp = 0; @@ -3033,6 +3155,10 @@ sourceroute(arg, cpp, lenp) *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif - return(sin_addr.s_addr); + } + freeaddrinfo(res); + return 1; } -#endif + + + diff --git a/crypto/telnet/telnet/externs.h b/crypto/telnet/telnet/externs.h index 0c6894f53d4e..46253d9a6048 100644 --- a/crypto/telnet/telnet/externs.h +++ b/crypto/telnet/telnet/externs.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)externs.h 8.3 (Berkeley) 5/30/95 + * $FreeBSD$ */ #ifndef BSD @@ -87,6 +88,14 @@ typedef unsigned char cc_t; #include #endif +#if defined(IPSEC) +#include +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + #ifndef _POSIX_VDISABLE # ifdef sun # include /* pick up VDISABLE definition, mayby */ @@ -116,6 +125,7 @@ extern int autologin, /* Autologin enabled */ skiprc, /* Don't process the ~/.telnetrc file */ eight, /* use eight bit mode (binary in and/or out */ + family, /* address family of peer */ flushout, /* flush output */ connected, /* Are we connected to the other side? */ globalmode, /* Mode tty should be in */ diff --git a/crypto/telnet/telnet/main.c b/crypto/telnet/telnet/main.c index 2dc05fd73eb0..c3d0f5d74ba9 100644 --- a/crypto/telnet/telnet/main.c +++ b/crypto/telnet/telnet/main.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef lint @@ -42,6 +44,7 @@ static const char sccsid[] = "@(#)main.c 8.3 (Berkeley) 5/30/95"; #endif /* not lint */ #include +#include #include #include "ring.h" @@ -70,6 +73,13 @@ void init_telnet(void); void init_sys(void); void init_3270(void); +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +int family = AF_UNSPEC; + /* * Initialize variables. */ @@ -95,10 +105,10 @@ usage() fprintf(stderr, "Usage: %s %s%s%s%s\n", prompt, #ifdef AUTHENTICATION - "[-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", "\n\t[-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", #else - "[-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", "\n\t[-n tracefile] ", #endif #if defined(TN3270) && defined(unix) @@ -112,6 +122,9 @@ usage() #else "[-r] [-s src_addr] ", #endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "[-P policy]" +#endif #ifdef ENCRYPTION "[-x] [host-name [port]]" #else /* ENCRYPTION */ @@ -156,8 +169,24 @@ main(argc, argv) rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; autologin = -1; - while ((ch = getopt(argc, argv, "8EKLNS:X:acde:fFk:l:n:rs:t:x")) != EOF) { +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define IPSECOPT "P:" +#else +#define IPSECOPT +#endif + while ((ch = getopt(argc, argv, + "468EKLNS:X:acde:fFk:l:n:rs:t:x" IPSECOPT)) != -1) +#undef IPSECOPT + { switch(ch) { + case '4': + family = AF_INET; + break; +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif case '8': eight = 3; /* binary output and input */ break; @@ -299,6 +328,16 @@ main(argc, argv) prompt); #endif /* ENCRYPTION */ break; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + case 'P': + if (!strncmp("in", optarg, 2)) + ipsec_policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + ipsec_policy_out = strdup(optarg); + else + usage(); + break; +#endif case '?': default: usage(); diff --git a/crypto/telnet/telnet/telnet.1 b/crypto/telnet/telnet/telnet.1 index 984146232215..3378d3a4ad92 100644 --- a/crypto/telnet/telnet/telnet.1 +++ b/crypto/telnet/telnet/telnet.1 @@ -30,8 +30,9 @@ .\" SUCH DAMAGE. .\" .\" @(#)telnet.1 8.6 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNET 1 .Os BSD 4.2 .Sh NAME @@ -549,9 +550,10 @@ will attempt to contact a .Tn TELNET server at the default port. The host specification may be either a host name (see -.Xr hosts 5 ) -or an Internet address specified in the \*(Lqdot notation\*(Rq (see -.Xr inet 3 ) . +.Xr hosts 5 ) , +an Internet address specified in the \*(Lqdot notation\*(Rq (see +.Xr inet 3 ) , +or IPv6 host name or IPv6 coloned-hexadecimal addreess. The .Op Fl l option may be used to specify the user name @@ -1367,6 +1369,8 @@ The .Nm Telnet command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. .Sh NOTES .Pp On some remote systems, echo has to be turned off manually when in diff --git a/crypto/telnet/telnetd/telnetd.8 b/crypto/telnet/telnetd/telnetd.8 index e03f290daaa9..4e004a4edede 100644 --- a/crypto/telnet/telnetd/telnetd.8 +++ b/crypto/telnet/telnetd/telnetd.8 @@ -30,8 +30,9 @@ .\" SUCH DAMAGE. .\" .\" @(#)telnetd.8 8.4 (Berkeley) 6/1/94 +.\" $FreeBSD$ .\" -.Dd June 1, 1994 +.Dd January 27, 2000 .Dt TELNETD 8 .Os BSD 4.2 .Sh NAME @@ -610,3 +611,5 @@ never sends .Tn TELNET .Dv IAC GA (go ahead) commands. +.Sh HISTORY +IPv6 support was added by WIDE/KAME project. diff --git a/crypto/telnet/telnetd/telnetd.c b/crypto/telnet/telnetd/telnetd.c index 57e6ed2a7a79..6d7bcde5fc4f 100644 --- a/crypto/telnet/telnetd/telnetd.c +++ b/crypto/telnet/telnetd/telnetd.c @@ -70,6 +70,12 @@ static const char rcsid[] = #include #include # endif /* SO_SEC_MULTI */ + +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + int secflag; char tty_dev[16]; struct secdev dv; @@ -128,7 +134,7 @@ char ptyibuf2[BUFSIZ]; # include int readstream(int p, char *ibuf, int bufsize); -void doit(struct sockaddr_in *who); +void doit(struct sockaddr *who); int terminaltypeok(char *s); void startslave(char *host, int autologin, char *autoname); @@ -145,7 +151,7 @@ int debug = 0; int keepalive = 1; char *altlogin; -void doit __P((struct sockaddr_in *)); +void doit __P((struct sockaddr *)); int terminaltypeok __P((char *)); void startslave __P((char *, int, char *)); extern void usage P((void)); @@ -157,6 +163,7 @@ extern void usage P((void)); */ char valid_opts[] = { 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', + '4', '6', #ifdef AUTHENTICATION 'a', ':', 'X', ':', #endif @@ -184,11 +191,13 @@ char valid_opts[] = { '\0' }; - int +int family = AF_INET; + +int main(argc, argv) char *argv[]; { - struct sockaddr_in from; + struct sockaddr_storage from; int on = 1, fromlen; register int ch; #if defined(IPPROTO_IP) && defined(IP_TOS) @@ -406,6 +415,16 @@ main(argc, argv) break; #endif /* AUTHENTICATION */ + case '4': + family = AF_INET; + break; + +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif + default: warnx("%c: unknown option", ch); /* FALLTHROUGH */ @@ -419,43 +438,41 @@ main(argc, argv) argv += optind; if (debug) { - int s, ns, foo; - struct servent *sp; - static struct sockaddr_in sin = { AF_INET }; + int s, ns, foo, error; + char *service = "telnet"; + struct addrinfo hints, *res; if (argc > 1) { usage(); /* NOT REACHED */ - } else if (argc == 1) { - if ((sp = getservbyname(*argv, "tcp"))) { - sin.sin_port = sp->s_port; - } else { - sin.sin_port = atoi(*argv); - if ((int)sin.sin_port <= 0) { - warnx("%s: bad port #", *argv); - usage(); - /* NOT REACHED */ - } - sin.sin_port = htons((u_short)sin.sin_port); - } - } else { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) - errx(1, "tcp/telnet: unknown service"); - sin.sin_port = sp->s_port; + } else if (argc == 1) + service = *argv; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(NULL, service, &hints, &res); + + if (error) { + errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); + if (error == EAI_SYSTEM) + errx(1, "tcp/%s: %s\n", service, strerror(errno)); + usage(); } - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) err(1, "socket"); (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) err(1, "bind"); if (listen(s, 1) < 0) err(1, "listen"); - foo = sizeof sin; - ns = accept(s, (struct sockaddr *)&sin, &foo); + foo = res->ai_addrlen; + ns = accept(s, res->ai_addr, &foo); if (ns < 0) err(1, "accept"); (void) dup2(ns, 0); @@ -537,7 +554,7 @@ main(argc, argv) } #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (from.ss_family == AF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) @@ -553,7 +570,7 @@ main(argc, argv) } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ net = 0; - doit(&from); + doit((struct sockaddr *)&from); /* NOTREACHED */ return(0); } /* end of main */ @@ -821,8 +838,9 @@ char user_name[256]; */ void doit(who) - struct sockaddr_in *who; + struct sockaddr *who; { + int err; int ptynum; /* @@ -865,16 +883,18 @@ doit(who) #endif /* _SC_CRAY_SECURE_SYS */ /* get name of connected client */ - if (realhostname(remote_hostname, sizeof(remote_hostname) - 1, - &who->sin_addr) == HOSTNAME_INVALIDADDR && registerd_host_only) + if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, + who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) fatal(net, "Couldn't resolve your address into a host name.\r\n\ Please contact your net administrator"); remote_hostname[sizeof(remote_hostname) - 1] = '\0'; trimdomain(remote_hostname, UT_HOSTSIZE); if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) - strncpy(remote_hostname, inet_ntoa(who->sin_addr), - sizeof(remote_hostname) - 1); + err = getnameinfo(who, who->sa_len, remote_hostname, + sizeof(remote_hostname), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX: do 'err' check */ (void) gethostname(host_name, sizeof(host_name) - 1); host_name[sizeof(host_name) - 1] = '\0'; diff --git a/kerberos5/libexec/telnetd/Makefile b/kerberos5/libexec/telnetd/Makefile index 4928c8de6bc5..221bdb48d945 100644 --- a/kerberos5/libexec/telnetd/Makefile +++ b/kerberos5/libexec/telnetd/Makefile @@ -8,7 +8,7 @@ MAN8= telnetd.8 CFLAGS+= -DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS -DOLD_ENVIRON \ -DENV_HACK -DAUTHENTICATION -DENCRYPTION \ - -I${TELNETDIR} + -I${TELNETDIR} -DINET6 SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c authenc.c diff --git a/kerberos5/usr.bin/telnet/Makefile b/kerberos5/usr.bin/telnet/Makefile index eb41f26a3c34..fb5219bfb9b1 100644 --- a/kerberos5/usr.bin/telnet/Makefile +++ b/kerberos5/usr.bin/telnet/Makefile @@ -4,15 +4,15 @@ PROG= telnet CFLAGS+= -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DSKEY \ -DENCRYPTION -DAUTHENTICATION -DKRB4 \ - -I${TELNETDIR} + -I${TELNETDIR} -DIPSEC -DINET6 SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c \ telnet.c terminal.c tn3270.c utilities.c DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBKRB} ${LIBCRYPT} \ - ${LIBCOM_ERR} + ${LIBCOM_ERR} ${LIBIPSEC} LDADD= -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes \ - -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp + -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp -lipsec .include diff --git a/kerberosIV/libexec/telnetd/Makefile b/kerberosIV/libexec/telnetd/Makefile index 4928c8de6bc5..221bdb48d945 100644 --- a/kerberosIV/libexec/telnetd/Makefile +++ b/kerberosIV/libexec/telnetd/Makefile @@ -8,7 +8,7 @@ MAN8= telnetd.8 CFLAGS+= -DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS -DOLD_ENVIRON \ -DENV_HACK -DAUTHENTICATION -DENCRYPTION \ - -I${TELNETDIR} + -I${TELNETDIR} -DINET6 SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c authenc.c diff --git a/kerberosIV/usr.bin/telnet/Makefile b/kerberosIV/usr.bin/telnet/Makefile index eb41f26a3c34..fb5219bfb9b1 100644 --- a/kerberosIV/usr.bin/telnet/Makefile +++ b/kerberosIV/usr.bin/telnet/Makefile @@ -4,15 +4,15 @@ PROG= telnet CFLAGS+= -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DSKEY \ -DENCRYPTION -DAUTHENTICATION -DKRB4 \ - -I${TELNETDIR} + -I${TELNETDIR} -DIPSEC -DINET6 SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c \ telnet.c terminal.c tn3270.c utilities.c DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBKRB} ${LIBCRYPT} \ - ${LIBCOM_ERR} + ${LIBCOM_ERR} ${LIBIPSEC} LDADD= -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes \ - -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp + -L${KRBOBJDIR} -lkrb -lcrypt -lcom_err -lmp -lipsec .include diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index c23e85b6078a..4ddfb53a3a42 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -7,6 +7,7 @@ SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c skey-stuff.c CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -DVIRTUAL_HOSTING -Wall \ -I${.CURDIR}/../../contrib-crypto/telnet +CFLAGS+=-DINET6 -g YFLAGS= LDADD= -lskey -lmd -lcrypt -lutil diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index 9888cb6946b6..684703eee2f8 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -49,6 +49,7 @@ void makedir __P((char *)); void nack __P((char *)); void pass __P((char *)); void passive __P((void)); +void long_passive __P((char *, int)); void perror_reply __P((int, char *)); void pwd __P((void)); void removedir __P((char *)); @@ -71,3 +72,18 @@ int yyparse __P((void)); char *skey_challenge __P((char *, struct passwd *, int)); #endif int ls_main __P((int, char **)); + +struct sockaddr_in; +struct sockaddr_in6; +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y index e085e29f57d6..bb5bff97ff86 100644 --- a/libexec/ftpd/ftpcmd.y +++ b/libexec/ftpd/ftpcmd.y @@ -58,6 +58,7 @@ static const char rcsid[] = #include #include #include +#include #include #include #include @@ -71,7 +72,7 @@ static const char rcsid[] = #include "extern.h" -extern struct sockaddr_in data_dest, his_addr; +extern union sockunion data_dest, his_addr; extern int logged_in; extern struct passwd *pw; extern int guest; @@ -98,6 +99,8 @@ static int cmd_bytesz; char cbuf[512]; char *fromname; +extern int epsvall; + %} %union { @@ -108,6 +111,7 @@ char *fromname; %token A B C E F I L N P R S T + ALL SP CRLF COMMA @@ -118,6 +122,7 @@ char *fromname; ABOR DELE CWD LIST NLST SITE STAT HELP NOOP MKD RMD PWD CDUP STOU SMNT SYST SIZE MDTM + LPRT LPSV EPRT EPSV UMASK IDLE CHMOD @@ -128,7 +133,8 @@ char *fromname; %type check_login octal_number byte_size %type struct_code mode_code type_code form_code -%type pathstring pathname password username +%type pathstring pathname password username ext_arg +%type ALL %start cmd_list @@ -157,31 +163,194 @@ cmd } | PORT check_login SP host_port CRLF { - if ($2) { - if (paranoid && - ((ntohs(data_dest.sin_port) < - IPPORT_RESERVED) || - memcmp(&data_dest.sin_addr, - &his_addr.sin_addr, - sizeof(data_dest.sin_addr)))) { - usedefault = 1; - reply(500, - "Illegal PORT range rejected."); - } else { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; - } - reply(200, "PORT command successful."); - } + if (epsvall) { + reply(501, "no PORT allowed after EPSV ALL"); + goto port_done; } + if (!$2) + goto port_done; + if (port_check("PORT") == 1) + goto port_done; +#ifdef INET6 + if ((his_addr.su_family != AF_INET6 || + !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { + /* shoud never happen */ + usedefault = 1; + reply(500, "Invalid address rejected."); + goto port_done; + } + port_check_v6("pcmd"); +#endif + port_done: + } + | LPRT check_login SP host_long_port CRLF + { + if (epsvall) { + reply(501, "no LPRT allowed after EPSV ALL"); + goto lprt_done; + } + if (!$2) + goto lprt_done; + if (port_check("LPRT") == 1) + goto lprt_done; +#ifdef INET6 + if (his_addr.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + goto lprt_done; + } + if (port_check_v6("LPRT") == 1) + goto lprt_done; +#endif + lprt_done: + } + | EPRT check_login SP STRING CRLF + { + char delim; + char *tmp = NULL; + char *p, *q; + char *result[3]; + struct addrinfo hints; + struct addrinfo *res; + int i; + + if (epsvall) { + reply(501, "no EPRT allowed after EPSV ALL"); + goto eprt_done; + } + if (!$2) + goto eprt_done; + + memset(&data_dest, 0, sizeof(data_dest)); + tmp = strdup($4); + if (debug) + syslog(LOG_DEBUG, "%s", tmp); + if (!tmp) { + fatal("not enough core"); + /*NOTREACHED*/ + } + p = tmp; + delim = p[0]; + p++; + memset(result, 0, sizeof(result)); + for (i = 0; i < 3; i++) { + q = strchr(p, delim); + if (!q || *q != delim) { + parsefail: + reply(500, + "Invalid argument, rejected."); + if (tmp) + free(tmp); + usedefault = 1; + goto eprt_done; + } + *q++ = '\0'; + result[i] = p; + if (debug) + syslog(LOG_DEBUG, "%d: %s", i, p); + p = q; + } + + /* some more sanity check */ + p = result[0]; + while (*p) { + if (!isdigit(*p)) + goto parsefail; + p++; + } + p = result[2]; + while (*p) { + if (!isdigit(*p)) + goto parsefail; + p++; + } + + /* grab address */ + memset(&hints, 0, sizeof(hints)); + if (atoi(result[0]) == 1) + hints.ai_family = PF_INET; +#ifdef INET6 + else if (atoi(result[0]) == 2) + hints.ai_family = PF_INET6; +#endif + else + hints.ai_family = PF_UNSPEC; /*XXX*/ + hints.ai_socktype = SOCK_STREAM; + i = getaddrinfo(result[1], result[2], &hints, &res); + if (i) + goto parsefail; + memcpy(&data_dest, res->ai_addr, res->ai_addrlen); +#ifdef INET6 + if (his_addr.su_family == AF_INET6 + && data_dest.su_family == AF_INET6) { + /* XXX more sanity checks! */ + data_dest.su_sin6.sin6_scope_id = + his_addr.su_sin6.sin6_scope_id; + } +#endif + free(tmp); + tmp = NULL; + + if (port_check("EPRT") == 1) + goto eprt_done; +#ifdef INET6 + if (his_addr.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + goto eprt_done; + } + if (port_check_v6("EPRT") == 1) + goto eprt_done; +#endif + eprt_done:; } | PASV check_login CRLF { - if ($2) + if (epsvall) + reply(501, "no PASV allowed after EPSV ALL"); + else if ($2) passive(); } + | LPSV check_login CRLF + { + if (epsvall) + reply(501, "no LPSV allowed after EPSV ALL"); + else if ($2) + long_passive("LPSV", PF_UNSPEC); + } + | EPSV check_login SP NUMBER CRLF + { + if ($2) { + int pf; + switch ($4) { + case 1: + pf = PF_INET; + break; +#ifdef INET6 + case 2: + pf = PF_INET6; + break; +#endif + default: + pf = -1; /*junk value*/ + break; + } + long_passive("EPSV", pf); + } + } + | EPSV check_login SP ALL CRLF + { + if ($2) { + reply(200, + "EPSV ALL command successful."); + epsvall++; + } + } + | EPSV check_login CRLF + { + if ($2) + long_passive("EPSV", PF_UNSPEC); + } | TYPE SP type_code CRLF { switch (cmd_type) { @@ -576,15 +745,61 @@ host_port { char *a, *p; - data_dest.sin_len = sizeof(struct sockaddr_in); - data_dest.sin_family = AF_INET; - p = (char *)&data_dest.sin_port; + data_dest.su_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_sin.sin_port; p[0] = $9; p[1] = $11; - a = (char *)&data_dest.sin_addr; + a = (char *)&data_dest.su_sin.sin_addr; a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; } ; +host_long_port + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_len = sizeof(struct sockaddr_in6); + data_dest.su_family = AF_INET6; + p = (char *)&data_dest.su_port; + p[0] = $39; p[1] = $41; + a = (char *)&data_dest.su_sin6.sin6_addr; + a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; + a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; + a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; + a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; + if (his_addr.su_family == AF_INET6) { + /* XXX more sanity checks! */ + data_dest.su_sin6.sin6_scope_id = + his_addr.su_sin6.sin6_scope_id; + } + if ($1 != 6 || $3 != 16 || $37 != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER + { + char *a, *p; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); + data_dest.su_family = AF_INET; + p = (char *)&data_dest.su_port; + p[0] = $15; p[1] = $17; + a = (char *)&data_dest.su_sin.sin_addr; + a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; + if ($1 != 4 || $3 != 4 || $13 != 2) + memset(&data_dest, 0, sizeof(data_dest)); + } + ; + form_code : N { @@ -774,7 +989,11 @@ struct tab cmdtab[] = { /* In order defined in RFC 765 */ { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, { "PORT", PORT, ARGS, 1, " b0, b1, b2, b3, b4" }, + { "LPRT", LPRT, ARGS, 1, " af, hal, h1, h2, h3,..., pal, p1, p2..." }, + { "EPRT", EPRT, STR1, 1, " |af|addr|port|" }, { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, + { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, + { "EPSV", EPSV, ARGS, 1, "[ af|ALL]" }, { "TYPE", TYPE, ARGS, 1, " [ A | E | I | L ]" }, { "STRU", STRU, ARGS, 1, "(specify file structure)" }, { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, @@ -829,8 +1048,11 @@ static char *copy __P((char *)); static void help __P((struct tab *, char *)); static struct tab * lookup __P((struct tab *, char *)); +static int port_check __P((const char *)); +static int port_check_v6 __P((const char *)); static void sizecmd __P((char *)); static void toolong __P((int)); +static void v4map_data_dest __P((void)); static int yylex __P((void)); static struct tab * @@ -1085,6 +1307,11 @@ yylex() cbuf[cpos] = c; return (NUMBER); } + if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 + && !isalnum(cbuf[cpos + 3])) { + cpos += 3; + return ALL; + } switch (cbuf[cpos++]) { case '\n': @@ -1289,3 +1516,94 @@ sizecmd(filename) reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); } } + +/* Return 1, if port check is done. Return 0, if not yet. */ +static int +port_check(pcmd) + const char *pcmd; +{ + if (his_addr.su_family == AF_INET) { + if (data_dest.su_family != AF_INET) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return 1; + } + if (paranoid && + ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || + memcmp(&data_dest.su_sin.sin_addr, + &his_addr.su_sin.sin_addr, + sizeof(data_dest.su_sin.sin_addr)))) { + usedefault = 1; + reply(500, "Illegal PORT range rejected."); + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", pcmd); + } + return 1; + } + return 0; +} + +#ifdef INET6 +/* Return 1, if port check is done. Return 0, if not yet. */ +static int +port_check_v6(pcmd) + const char *pcmd; +{ + if (his_addr.su_family == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) + /* Convert data_dest into v4 mapped sockaddr.*/ + v4map_data_dest(); + if (data_dest.su_family != AF_INET6) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return 1; + } + if (paranoid && + ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || + memcmp(&data_dest.su_sin6.sin6_addr, + &his_addr.su_sin6.sin6_addr, + sizeof(data_dest.su_sin6.sin6_addr)))) { + usedefault = 1; + reply(500, "Illegal PORT range rejected."); + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "%s command successful.", pcmd); + } + return 1; + } + return 0; +} + +static void +v4map_data_dest() +{ + struct in_addr savedaddr; + int savedport; + + if (data_dest.su_family != AF_INET) { + usedefault = 1; + reply(500, "Invalid address rejected."); + return; + } + + savedaddr = data_dest.su_sin.sin_addr; + savedport = data_dest.su_port; + + memset(&data_dest, 0, sizeof(data_dest)); + data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); + data_dest.su_sin6.sin6_family = AF_INET6; + data_dest.su_sin6.sin6_port = savedport; + memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); + memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], + (caddr_t)&savedaddr, sizeof(savedaddr)); +} +#endif diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index 8f2cca788ad5..fb10aa64de42 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -32,7 +32,7 @@ .\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 .\" $FreeBSD$ .\" -.Dd April 19, 1994 +.Dd January 27, 2000 .Dt FTPD 8 .Os BSD 4.2 .Sh NAME @@ -41,6 +41,8 @@ Internet File Transfer Protocol server .Sh SYNOPSIS .Nm ftpd +.Op Fl 4 +.Op Fl 6 .Op Fl d .Op Fl l Op Fl l .Op Fl A @@ -135,6 +137,20 @@ When .Fl D is specified, write the daemon's process ID to .Ar file . +.It Fl 6 +When +.Fl D +is specified, accept connections via AF_INET6 socket. +.It Fl 4 +When +.Fl D +is specified, accept IPv4 connections. +When +.Fl 6 +is also specified, accept IPv4 connection via AF_INET6 socket. +When +.Fl 6 +is not specified, accept IPv4 connection via AF_INET socket. .It Fl A Allow only anonymous ftp access. .El @@ -203,6 +219,10 @@ The case of the requests is ignored. .It XMKD Ta "make a directory (deprecated)" .It XPWD Ta "print the current working directory (deprecated)" .It XRMD Ta "remove a directory (deprecated)" +.It LPSV Ta "prepare for server-to-server transfer, multiprotocol" +.It LPRT Ta "specify data connection port, multiprotocol" +.It EPSV Ta "prepare for server-to-server transfer, multiprotocol" +.It EPRT Ta "specify data connection port, multiprotocol" .El .Pp The following non-standard or @@ -453,3 +473,4 @@ The .Nm command appeared in .Bx 4.2 . +IPv6 support was added in WIDE Hydrangea IPv6 stack kit. 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 @@ pass(passwd) 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) @@ -2161,6 +2373,121 @@ passive() return; } +/* + * 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. diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c index d3f2ec56c883..2ac57d13d270 100644 --- a/libexec/ftpd/logwtmp.c +++ b/libexec/ftpd/logwtmp.c @@ -43,6 +43,7 @@ static const char rcsid[] = #include #include #include +#include #include #include @@ -68,15 +69,23 @@ ftpd_logwtmp(line, name, host) struct stat buf; if (strlen(host) > UT_HOSTSIZE) { - struct hostent *hp = gethostbyname(host); + struct addrinfo hints, *res; + int error; + static char hostbuf[BUFSIZ]; - if (hp != NULL) { - struct in_addr in; - - memmove(&in, hp->h_addr, sizeof(in)); - host = inet_ntoa(in); - } else + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(host, NULL, &hints, &res); + if (error) host = "invalid hostname"; + else { + getnameinfo(res->ai_addr, res->ai_addrlen, + hostbuf, sizeof(hostbuf), NULL, 0, + NI_NUMERICHOST); + host = hostbuf; + if (strlen(host) > UT_HOSTSIZE) + host[UT_HOSTSIZE] = '\0'; + } } if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c index 606d17a421d6..82cfd581f8db 100644 --- a/libexec/ftpd/popen.c +++ b/libexec/ftpd/popen.c @@ -44,6 +44,7 @@ static const char rcsid[] = #include #include +#include #include #include diff --git a/secure/libexec/telnetd/Makefile b/secure/libexec/telnetd/Makefile index 230e4e79bf53..e13f48dcad78 100644 --- a/secure/libexec/telnetd/Makefile +++ b/secure/libexec/telnetd/Makefile @@ -8,7 +8,7 @@ MAN8= telnetd.8 CFLAGS+= -DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS -DOLD_ENVIRON \ -DENV_HACK -DAUTHENTICATION -DENCRYPTION \ - -I${TELNETDIR} + -I${TELNETDIR} -DINET6 SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c authenc.c diff --git a/secure/usr.bin/telnet/Makefile b/secure/usr.bin/telnet/Makefile index b7683443d5a6..f0db4cb8cb28 100644 --- a/secure/usr.bin/telnet/Makefile +++ b/secure/usr.bin/telnet/Makefile @@ -4,12 +4,15 @@ PROG= telnet CFLAGS+= -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DSKEY \ -DENCRYPTION -DAUTHENTICATION -I${TELNETDIR} +CFLAGS+= -DIPSEC -DINET6 SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c \ telnet.c terminal.c tn3270.c utilities.c DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBDES} ${LIBCRYPT} ${LIBMP} +DPADD+= ${LIBIPSEC} LDADD= -ltermcap -L${TELNETOBJDIR} -ltelnet -ldes -lcrypt -lmp +LDADD+= -lipsec .include diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile index bbd82e253aa6..b5a59cc8330e 100644 --- a/usr.bin/ftp/Makefile +++ b/usr.bin/ftp/Makefile @@ -10,6 +10,7 @@ PROG= ftp SRCS= cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c ruserpass.c \ util.c +CFLAGS+=-DINET6 -g LDADD+= -ledit -ltermcap DPADD+= ${LIBEDIT} ${LIBTERMCAP} diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h index a8ac97b8c866..7841f7b02c55 100644 --- a/usr.bin/ftp/extern.h +++ b/usr.bin/ftp/extern.h @@ -73,7 +73,7 @@ int getreply __P((int)); int globulize __P((char **)); char *gunique __P((const char *)); void help __P((int, char **)); -char *hookup __P((const char *, int)); +char *hookup __P((const char *, char *)); void idle __P((int, char **)); int initconn __P((void)); void intr __P((void)); diff --git a/usr.bin/ftp/fetch.c b/usr.bin/ftp/fetch.c index 1d73c0f9655e..62b64e9e35dd 100644 --- a/usr.bin/ftp/fetch.c +++ b/usr.bin/ftp/fetch.c @@ -57,6 +57,7 @@ __RCSID_SOURCE("$NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $"); #include #include +#include #include #include #include @@ -67,6 +68,11 @@ __RCSID_SOURCE("$NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $"); #include "ftp_var.h" +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + static int url_get __P((const char *, const char *)); void aborthttp __P((int)); @@ -91,9 +97,11 @@ url_get(origline, proxyenv) const char *origline; const char *proxyenv; { - struct sockaddr_in sin; + struct addrinfo hints; + struct addrinfo *res; + char nameinfo[2 * INET6_ADDRSTRLEN + 1]; int i, out, isftpurl; - u_int16_t port; + char *port; volatile int s; size_t len; char c, *cp, *ep, *portnum, *path, buf[4096]; @@ -101,6 +109,7 @@ url_get(origline, proxyenv) char *line, *proxy, *host; volatile sig_t oldintr; off_t hashbytes; + int error; s = -1; proxy = NULL; @@ -172,68 +181,69 @@ url_get(origline, proxyenv) path = line; } - portnum = strchr(host, ':'); /* find portnum */ - if (portnum != NULL) + if (*host == '[' && (portnum = strrchr(host, ']'))) { /* IPv6 URL */ *portnum++ = '\0'; - + host++; + if (*portnum == ':') + portnum++; + else + portnum = NULL; + } else { + portnum = strrchr(host, ':'); /* find portnum */ + if (portnum != NULL) + *portnum++ = '\0'; + } + if (debug) printf("host %s, port %s, path %s, save as %s.\n", host, portnum, path, savefile); - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - - if (isdigit((unsigned char)host[0])) { - if (inet_aton(host, &sin.sin_addr) == 0) { - warnx("Invalid IP address: %s", host); - goto cleanup_url_get; - } - } else { - struct hostent *hp; - - hp = gethostbyname(host); - if (hp == NULL) { - warnx("%s: %s", host, hstrerror(h_errno)); - goto cleanup_url_get; - } - if (hp->h_addrtype != AF_INET) { - warnx("%s: not an Internet address?", host); - goto cleanup_url_get; - } - memcpy(&sin.sin_addr, hp->h_addr, hp->h_length); - } - if (! EMPTYSTRING(portnum)) { - char *ep; - long nport; - - nport = strtol(portnum, &ep, 10); - if (nport < 1 || nport > 0xffff || *ep != '\0') { - warnx("Invalid port: %s", portnum); - goto cleanup_url_get; - } - port = htons(nport); + port = portnum; } else port = httpport; - sin.sin_port = port; - s = socket(AF_INET, SOCK_STREAM, 0); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hints, &res); + if (error) { + warnx("%s: %s", host, gai_strerror(error)); + if (error = EAI_SYSTEM) + warnx("%s: %s", host, strerror(errno)); + goto cleanup_url_get; + } + + while (1) + { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) { warn("Can't create socket"); goto cleanup_url_get; } if (dobind && bind(s, (struct sockaddr *)&bindto, - sizeof(bindto)) == -1) { - warn("Can't bind to %s", inet_ntoa(bindto.sin_addr)); + ((struct sockaddr *)&bindto)->sa_len) == -1) { + getnameinfo((struct sockaddr *)&bindto, + ((struct sockaddr *)&bindto)->sa_len, + nameinfo, sizeof(nameinfo), NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + /* XXX check error? */ + warn("Can't bind to %s", nameinfo); goto cleanup_url_get; } - if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) { - warn("Can't connect to %s", host); + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + close(s); + res = res->ai_next; + if (res) + continue; goto cleanup_url_get; } + break; + } + /* * Construct and send the request. We're expecting a return * status of "200". Proxy requests don't want leading /. @@ -645,3 +655,14 @@ auto_fetch(argc, argv) disconnect(0, NULL); return (rval); } + +int +isurl(p) + const char *p; +{ + if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 + || strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { + return 1; + } + return 0; +} diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1 index 59b371f34b7b..a112709981fe 100644 --- a/usr.bin/ftp/ftp.1 +++ b/usr.bin/ftp/ftp.1 @@ -34,7 +34,7 @@ .\" .\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 .\" -.Dd August 18, 1997 +.Dd January 27, 2000 .Dt FTP 1 .Os BSD 4.2 .Sh NAME @@ -61,6 +61,7 @@ is the user interface to the standard File Transfer Protocol. The program allows a user to transfer files to and from a remote network site. +The version supports IPv6 (Internet protocol version 6), as well as IPv4. .Pp The latter three usage formats will fetch a file using either the HTTP or FTP protocols into the current directory. @@ -682,7 +683,18 @@ through a gateway router or host that controls the directionality of traffic. (Note that though ftp servers are required to support the .Dv PASV -command by RFC 1123, some do not.) +command by RFC 1123, some do not. +Please note that if you are connecting to IPv6 ftp server, +the program will use +.Dv EPSV/EPRT +pair and +.Dv LPSV/LPRT +pair, +instead of +.Dv PASV +and +.Dv PORT . +The meaning is the same.) .It Ic preserve Toggle preservation of modification times on retrieved files. .It Ic progress @@ -1402,6 +1414,8 @@ fetching of files, ftp and http URLs, and modification time preservation were implemented in .Nx 1.3 by Luke Mewburn, with assistance from Jason Thorpe. +.Pp +IPv6 support was added by WIDE/KAME Project. .Sh BUGS Correct execution of many commands depends upon proper behavior by the remote server. @@ -1416,3 +1430,7 @@ to and from .Bx 4.2 servers using the ascii type. Avoid this problem by using the binary image type. +.Pp +Proxying functionalities, such as +.Ev ftp_proxy , +may not work for IPv6 connection. diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c index d2edd014d54b..3c7da3feb2cf 100644 --- a/usr.bin/ftp/ftp.c +++ b/usr.bin/ftp/ftp.c @@ -71,68 +71,93 @@ __RCSID_SOURCE("$NetBSD: ftp.c,v 1.29.2.1 1997/11/18 01:01:04 mellon Exp $"); #include "ftp_var.h" -struct sockaddr_in hisctladdr; -struct sockaddr_in data_addr; +/* wrapper for KAME-special getnameinfo() */ +#ifndef NI_WITHSCOPEID +#define NI_WITHSCOPEID 0 +#endif + +extern int h_errno; + int data = -1; int abrtflag = 0; jmp_buf ptabort; int ptabflg; int ptflag = 0; -struct sockaddr_in myctladdr; - FILE *cin, *cout; +union sockunion { + struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; + } su_si; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; +#define su_len su_si.si_len +#define su_family su_si.si_family +#define su_port su_si.si_port + +union sockunion myctladdr, hisctladdr, data_addr; + char * hookup(host, port) const char *host; - int port; + char *port; { - struct hostent *hp = NULL; - int s, len, tos; + int s, len, tos, error; + struct addrinfo hints, *res, *res0; static char hostnamebuf[MAXHOSTNAMELEN]; - memset((void *)&hisctladdr, 0, sizeof(hisctladdr)); - if (inet_aton(host, &hisctladdr.sin_addr) != 0) { - hisctladdr.sin_family = AF_INET; - (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf)); - } else { - hp = gethostbyname(host); - if (hp == NULL) { - warnx("%s: %s", host, hstrerror(h_errno)); - code = -1; - return ((char *) 0); - } - hisctladdr.sin_family = hp->h_addrtype; - memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], - MIN(hp->h_length,sizeof(hisctladdr.sin_addr))); - (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf)); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(host, port, &hints, &res0); + if (error) { + warnx("%s: %s", host, gai_strerror(error)); + if (error == EAI_SYSTEM) + warnx("%s: %s", host, strerror(errno)); + code = -1; + return (0); + } + + res = res0; + if (res->ai_canonname) + hostname = res->ai_canonname; + else { + (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf)); + hostname = hostnamebuf; } - hostnamebuf[sizeof(hostnamebuf) - 1] = '\0'; - hostname = hostnamebuf; - hisctladdr.sin_port = port; while (1) { - if ((s = socket(hisctladdr.sin_family, SOCK_STREAM, 0)) == -1) { + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s < 0) { warn("socket"); code = -1; return (0); } - if (dobind && bind(s, (struct sockaddr *)&bindto, - sizeof(bindto)) == -1) { + if (dobind && + bind(s, (struct sockaddr *)&bindto, + ((struct sockaddr *)&bindto)->sa_len) == -1) { warn("bind"); - code = -1; - goto bad; + goto next; } - if (connect(s, (struct sockaddr *)&hisctladdr, - sizeof(hisctladdr)) == 0) + if (connect(s, res->ai_addr, res->ai_addrlen) == 0) break; - if (hp && *++hp->h_addr_list) { - warnc(errno, "connect to address %s", - inet_ntoa(hisctladdr.sin_addr)); - memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], - MIN(hp->h_length,sizeof(hisctladdr.sin_addr))); - printf("Trying %s...\n", - inet_ntoa(hisctladdr.sin_addr)); + next: + if (res->ai_next) { + char hname[INET6_ADDRSTRLEN]; + getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + warn("connect to address %s", hname); + res = res->ai_next; + getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID); + printf("Trying %s...\n", hname); (void)close(s); continue; } @@ -140,6 +165,7 @@ hookup(host, port) code = -1; goto bad; } + memcpy(&hisctladdr, res->ai_addr, res->ai_addrlen); len = sizeof(myctladdr); if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) { warn("getsockname"); @@ -147,9 +173,12 @@ hookup(host, port) goto bad; } #ifdef IP_TOS - tos = IPTOS_LOWDELAY; + if (myctladdr.su_family == AF_INET) + { + tos = IPTOS_LOWDELAY; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif cin = fdopen(s, "r"); cout = fdopen(s, "w"); @@ -324,11 +353,25 @@ getreply(expecteof) } if (dig < 4 && isdigit((unsigned char)c)) code = code * 10 + (c - '0'); - if (!pflag && code == 227) - pflag = 1; - if (dig > 4 && pflag == 1 && isdigit((unsigned char)c)) + switch (pflag) { + case 0: + if (code == 227 || code == 228) { + /* result for PASV/LPSV */ + pflag = 1; + /* fall through */ + } else if (code == 229) { + /* result for EPSV */ + pflag = 1; + pflag = 100; + break; + } else + break; + case 1: + if (!(dig > 4 && isdigit((unsigned char)c))) + break; pflag = 2; - if (pflag == 2) { + /* fall through */ + case 2: if (c != '\r' && c != ')' && pt < &pasv[sizeof(pasv)-1]) *pt++ = c; @@ -336,6 +379,11 @@ getreply(expecteof) *pt = '\0'; pflag = 3; } + break; + case 100: + if (dig > 4 && c == '(') + pflag = 2; + break; } if (dig == 4 && c == '-') { if (continuation) @@ -1080,17 +1128,29 @@ initconn() char *p, *a; int result, len, tmpno = 0; int on = 1; - int a0, a1, a2, a3, p0, p1; - int ports; + int error, ports; + u_int af; + u_int hal, h[16]; + u_int pal, prt[2]; + char *pasvcmd; + +#ifdef INET6 + if (myctladdr.su_family == AF_INET6 + && (IN6_IS_ADDR_LINKLOCAL(&myctladdr.su_sin6.sin6_addr) + || IN6_IS_ADDR_SITELOCAL(&myctladdr.su_sin6.sin6_addr))) { + warnx("use of scoped address can be troublesome"); + } +#endif if (passivemode) { - data = socket(AF_INET, SOCK_STREAM, 0); + data_addr = myctladdr; + data = socket(data_addr.su_family, SOCK_STREAM, 0); if (data < 0) { warn("socket"); return (1); } if (dobind && bind(data, (struct sockaddr *)&bindto, - sizeof(bindto)) == -1) { + ((struct sockaddr *)&bindto)->sa_len) == -1) { warn("bind"); goto bad; } @@ -1098,47 +1158,166 @@ initconn() setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)) < 0) warn("setsockopt (ignored)"); - if (command("PASV") != COMPLETE) { + switch (data_addr.su_family) { + case AF_INET: + result = command(pasvcmd = "EPSV"); + if (code / 10 == 22 && code != 229) { + puts("wrong server: return code must be 229"); + result = COMPLETE + 1; + } + if (result != COMPLETE) + result = command(pasvcmd = "PASV"); + break; +#ifdef INET6 + case AF_INET6: + result = command(pasvcmd = "EPSV"); + if (code / 10 == 22 && code != 229) { + puts("wrong server: return code must be 229"); + result = COMPLETE + 1; + } + if (result != COMPLETE) + result = command(pasvcmd = "LPSV"); + break; +#endif + default: + result = COMPLETE + 1; + } + if (result != COMPLETE) { puts("Passive mode refused."); goto bad; } +#define pack2(var, offset) \ + (((var[(offset) + 0] & 0xff) << 8) | ((var[(offset) + 1] & 0xff) << 0)) +#define pack4(var, offset) \ + (((var[(offset) + 0] & 0xff) << 24) | ((var[(offset) + 1] & 0xff) << 16) \ + | ((var[(offset) + 2] & 0xff) << 8) | ((var[(offset) + 3] & 0xff) << 0)) /* * What we've got at this point is a string of comma * separated one-byte unsigned integer values. + * In PASV case, * The first four are the an IP address. The fifth is * the MSB of the port number, the sixth is the LSB. * From that we'll prepare a sockaddr_in. + * In other case, the format is more complicated. */ + if (strcmp(pasvcmd, "PASV") == 0) { + if (code / 10 == 22 && code != 227) { + puts("wrong server: return code must be 227"); + error = 1; + goto bad; + } + error = sscanf(pasv, "%d,%d,%d,%d,%d,%d", + &h[0], &h[1], &h[2], &h[3], + &prt[0], &prt[1]); + if (error == 6) { + error = 0; + data_addr.su_sin.sin_addr.s_addr = + htonl(pack4(h, 0)); + } else + error = 1; + } else if (strcmp(pasvcmd, "LPSV") == 0) { + if (code / 10 == 22 && code != 228) { + puts("wrong server: return code must be 228"); + error = 1; + goto bad; + } + switch (data_addr.su_family) { + case AF_INET: + error = sscanf(pasv, +"%d,%d,%d,%d,%d,%d,%d,%d,%d", + &af, &hal, + &h[0], &h[1], &h[2], &h[3], + &pal, &prt[0], &prt[1]); + if (error == 9 && af == 4 && hal == 4 && pal == 2) { + error = 0; + data_addr.su_sin.sin_addr.s_addr = + htonl(pack4(h, 0)); + } else + error = 1; + break; +#ifdef INET6 + case AF_INET6: + error = sscanf(pasv, +"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &af, &hal, + &h[0], &h[1], &h[2], &h[3], + &h[4], &h[5], &h[6], &h[7], + &h[8], &h[9], &h[10], &h[11], + &h[12], &h[13], &h[14], &h[15], + &pal, &prt[0], &prt[1]); + if (error != 21 || af != 6 || hal != 16 || pal != 2) { + error = 1; + break; + } - if (sscanf(pasv, "%d,%d,%d,%d,%d,%d", - &a0, &a1, &a2, &a3, &p0, &p1) != 6) { + error = 0; + { + u_int32_t *p32; + p32 = (u_int32_t *)&data_addr.su_sin6.sin6_addr; + p32[0] = htonl(pack4(h, 0)); + p32[1] = htonl(pack4(h, 4)); + p32[2] = htonl(pack4(h, 8)); + p32[3] = htonl(pack4(h, 12)); + } + break; +#endif + default: + error = 1; + } + } else if (strcmp(pasvcmd, "EPSV") == 0) { + char delim[4]; + char *tcpport; + + prt[0] = 0; + if (code / 10 == 22 && code != 229) { + puts("wrong server: return code must be 229"); + error = 1; + goto bad; + } + error = sscanf(pasv, "%c%c%c%d%c", + &delim[0], &delim[1], &delim[2], + &prt[1], &delim[3]); + if (error != 5) { + error = 1; + goto epsv_done; + } + if (delim[0] != delim[1] || delim[0] != delim[2] + || delim[0] != delim[3]) { + error = 1; + goto epsv_done; + } + + data_addr = hisctladdr; + /* quickhack */ + prt[0] = (prt[1] & 0xff00) >> 8; + prt[1] &= 0xff; + error = 0; +epsv_done: + } else + error = 1; + + if (error) { puts( "Passive mode address scan failure. Shouldn't happen!"); goto bad; - } + }; - memset(&data_addr, 0, sizeof(data_addr)); - data_addr.sin_family = AF_INET; - a = (char *)&data_addr.sin_addr.s_addr; - a[0] = a0 & 0xff; - a[1] = a1 & 0xff; - a[2] = a2 & 0xff; - a[3] = a3 & 0xff; - p = (char *)&data_addr.sin_port; - p[0] = p0 & 0xff; - p[1] = p1 & 0xff; + data_addr.su_port = htons(pack2(prt, 0)); if (connect(data, (struct sockaddr *)&data_addr, - sizeof(data_addr)) < 0) { + data_addr.su_len) < 0) { warn("connect"); goto bad; } #ifdef IP_TOS + if (data_addr.su_family == AF_INET) + { on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif return (0); } @@ -1146,10 +1325,10 @@ initconn() noport: data_addr = myctladdr; if (sendport) - data_addr.sin_port = 0; /* let system pick one */ + data_addr.su_port = 0; /* let system pick one */ if (data != -1) (void)close(data); - data = socket(AF_INET, SOCK_STREAM, 0); + data = socket(data_addr.su_family, SOCK_STREAM, 0); if (data < 0) { warn("socket"); if (tmpno) @@ -1163,12 +1342,27 @@ initconn() goto bad; } #ifdef IP_PORTRANGE + if (data_addr.su_family == AF_INET) + { + ports = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT; if (setsockopt(data, IPPROTO_IP, IP_PORTRANGE, (char *)&ports, sizeof(ports)) < 0) warn("setsockopt PORTRANGE (ignored)"); + } #endif - if (bind(data, (struct sockaddr *)&data_addr, sizeof(data_addr)) < 0) { +#ifdef INET6 +#ifdef IPV6_PORTRANGE + if (data_addr.su_family == AF_INET6) { + ports = restricted_data_ports ? IPV6_PORTRANGE_HIGH + : IPV6_PORTRANGE_DEFAULT; + if (setsockopt(data, IPPROTO_IPV6, IPV6_PORTRANGE, + (char *)&ports, sizeof(ports)) < 0) + warn("setsockopt PORTRANGE (ignored)"); + } +#endif +#endif + if (bind(data, (struct sockaddr *)&data_addr, data_addr.su_len) < 0) { warn("bind"); goto bad; } @@ -1184,13 +1378,79 @@ initconn() if (listen(data, 1) < 0) warn("listen"); if (sendport) { - a = (char *)&data_addr.sin_addr; - p = (char *)&data_addr.sin_port; + char hname[INET6_ADDRSTRLEN]; + int af; + struct sockaddr_in data_addr4; + union sockunion *daddr; + +#ifdef INET6 + if (data_addr.su_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&data_addr.su_sin6.sin6_addr)) { + memset(&data_addr4, 0, sizeof(data_addr4)); + data_addr4.sin_len = sizeof(struct sockaddr_in); + data_addr4.sin_family = AF_INET; + data_addr4.sin_port = data_addr.su_port; + memcpy((caddr_t)&data_addr4.sin_addr, + (caddr_t)&data_addr.su_sin6.sin6_addr.s6_addr[12], + sizeof(struct in_addr)); + daddr = (union sockunion *)&data_addr4; + } else +#endif + daddr = &data_addr; + + + #define UC(b) (((int)b)&0xff) - result = - command("PORT %d,%d,%d,%d,%d,%d", - UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), - UC(p[0]), UC(p[1])); + + switch (daddr->su_family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + af = (daddr->su_family == AF_INET) ? 1 : 2; + if (getnameinfo((struct sockaddr *)daddr, + daddr->su_len, hname, + sizeof(hname) - 1, NULL, 0, + NI_NUMERICHOST|NI_WITHSCOPEID)) { + result = ERROR; + } else { + result = command("EPRT |%d|%s|%d|", + af, hname, ntohs(daddr->su_port)); + } + break; + default: + result = COMPLETE + 1; + break; + } + if (result == COMPLETE) + goto skip_port; + + p = (char *)&daddr->su_port; + switch (daddr->su_family) { + case AF_INET: + a = (char *)&daddr->su_sin.sin_addr; + result = command("PORT %d,%d,%d,%d,%d,%d", + UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]), + UC(p[0]), UC(p[1])); + break; +#ifdef INET6 + case AF_INET6: + a = (char *)&daddr->su_sin6.sin6_addr; + result = command( +"LPRT %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])); + break; +#endif + default: + result = COMPLETE + 1; /* xxx */ + } + skip_port: + if (result == ERROR && sendport == -1) { sendport = 0; tmpno = 1; @@ -1201,9 +1461,12 @@ initconn() if (tmpno) sendport = 1; #ifdef IP_TOS + if (data_addr.su_family == AF_INET) + { on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif return (0); bad: @@ -1217,10 +1480,10 @@ FILE * dataconn(lmode) const char *lmode; { - struct sockaddr_in from; + union sockunion from; int s, fromlen, tos; - fromlen = sizeof(from); + fromlen = myctladdr.su_len; if (passivemode) return (fdopen(data, lmode)); @@ -1234,9 +1497,12 @@ dataconn(lmode) (void)close(data); data = s; #ifdef IP_TOS + if (data_addr.su_family == AF_INET) + { tos = IPTOS_THROUGHPUT; if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) warn("setsockopt TOS (ignored)"); + } #endif return (fdopen(data, lmode)); } @@ -1267,8 +1533,8 @@ pswitch(flag) static struct comvars { int connect; char name[MAXHOSTNAMELEN]; - struct sockaddr_in mctl; - struct sockaddr_in hctl; + union sockunion mctl; + union sockunion hctl; FILE *in; FILE *out; int tpe; diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h index d26d22df7f14..cd9b0bddb916 100644 --- a/usr.bin/ftp/ftp_var.h +++ b/usr.bin/ftp/ftp_var.h @@ -41,6 +41,7 @@ */ #include +#include #include #include #include @@ -96,7 +97,7 @@ int preserve; /* preserve modification time on files */ int progress; /* display transfer progress bar */ int code; /* return/reply code for ftp command */ int crflag; /* if 1, strip car. rets. on ascii gets */ -char pasv[64]; /* passive port for proxy data connection */ +char pasv[BUFSIZ]; /* passive port for proxy data connection */ int passivemode; /* passive mode enabled */ int restricted_data_ports; /* enable quarantine FTP area */ char *altarg; /* argv[1] with no shell-like preprocessing */ @@ -138,12 +139,12 @@ char *hostname; /* name of host connected to */ int unix_server; /* server is unix, can use binary for ascii */ int unix_proxy; /* proxy is unix, can use binary for ascii */ -u_int16_t ftpport; /* port number to use for ftp connections */ -u_int16_t httpport; /* port number to use for http connections */ -u_int16_t gateport; /* port number to use for gateftp connections */ +char *ftpport; /* port number to use for ftp connections */ +char *httpport; /* port number to use for http connections */ +char *gateport; /* port number to use for gateftp connections */ int dobind; /* bind to specific address */ -struct sockaddr_in bindto; /* address to bind to */ +struct sockaddr_storage bindto; /* address to bind to */ jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c index 371730849e34..aeb74878c8a7 100644 --- a/usr.bin/ftp/main.c +++ b/usr.bin/ftp/main.c @@ -58,6 +58,7 @@ __RCSID_SOURCE("$NetBSD: main.c,v 1.26 1997/10/14 16:31:22 christos Exp $"); #include #include +#include #include #include #include @@ -86,33 +87,14 @@ main(argc, argv) (void) setlocale(LC_ALL, ""); - sp = getservbyname("ftp", "tcp"); - if (sp == 0) - ftpport = htons(FTP_PORT); /* good fallback */ - else - ftpport = sp->s_port; - sp = getservbyname("http", "tcp"); - if (sp == 0) - httpport = htons(HTTP_PORT); /* good fallback */ - else - httpport = sp->s_port; - gateport = 0; + ftpport = "ftp"; + httpport = "http"; + gateport = NULL; cp = getenv("FTPSERVERPORT"); - if (cp != NULL) { - port = strtol(cp, &ep, 10); - if (port < 1 || port > 0xffff || *ep != '\0') - warnx("bad FTPSERVERPORT port number: %s (ignored)", - cp); - else - gateport = htons(port); - } - if (gateport == 0) { - sp = getservbyname("ftpgate", "tcp"); - if (sp == 0) - gateport = htons(GATE_PORT); - else - gateport = sp->s_port; - } + if (cp != NULL) + gateport = cp; + if (!gateport) + gateport = "ftpgate"; doglob = 1; interactive = 1; autologin = 1; @@ -203,11 +185,7 @@ main(argc, argv) break; case 'P': - port = strtol(optarg, &ep, 10); - if (port < 1 || port > 0xffff || *ep != '\0') - warnx("bad port number: %s (ignored)", optarg); - else - ftpport = htons(port); + ftpport = optarg; break; case 's': @@ -244,17 +222,24 @@ main(argc, argv) sendport = -1; /* not using ports */ if (dobind) { - memset((void *)&bindto, 0, sizeof(bindto)); - if (inet_aton(src_addr, &bindto.sin_addr) == 1) - bindto.sin_family = AF_INET; - else { - struct hostent *hp = gethostbyname(src_addr); - if (hp == NULL) - errx(1, "%s: %s", src_addr, hstrerror(h_errno)); - bindto.sin_family = hp->h_addrtype; - memcpy(&bindto.sin_addr, hp->h_addr_list[0], - MIN(hp->h_length,sizeof(bindto.sin_addr))); + struct addrinfo hints; + struct addrinfo *res; + char *ftpdataport = "ftp-data"; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, NULL, &hints, &res); + if (error) { + fprintf(stderr, "%s: %s", src_addr, + gai_strerror(error)); + if (error == EAI_SYSTEM) + errx(1, "%s", strerror(errno)); + exit(1); } + memcpy(&bindto, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } /* @@ -280,7 +265,7 @@ main(argc, argv) #endif if (argc > 0) { - if (strchr(argv[0], ':') != NULL) { + if (isurl(argv[0])) { anonftp = 1; /* Handle "automatic" transfers. */ rval = auto_fetch(argc, argv); if (rval >= 0) /* -1 == connected and cd-ed */ diff --git a/usr.bin/ftp/util.c b/usr.bin/ftp/util.c index a34ca3424dbd..a62565c85f0e 100644 --- a/usr.bin/ftp/util.c +++ b/usr.bin/ftp/util.c @@ -76,7 +76,7 @@ setpeer(argc, argv) char *argv[]; { char *host; - u_int16_t port; + char *port; if (connected) { printf("Already connected to %s, use close first.\n", @@ -95,19 +95,8 @@ setpeer(argc, argv) port = gateport; else port = ftpport; - if (argc > 2) { - char *ep; - long nport; - - nport = strtol(argv[2], &ep, 10); - if (nport < 1 || nport > 0xffff || *ep != '\0') { - printf("%s: bad port number '%s'.\n", argv[0], argv[2]); - printf("usage: %s host-name [port]\n", argv[0]); - code = -1; - return; - } - port = htons(nport); - } + if (argc > 2) + port = strdup(argv[2]); if (gatemode) { if (gateserver == NULL || *gateserver == '\0') diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile index 658a2b7fb298..b4faf30769ad 100644 --- a/usr.bin/telnet/Makefile +++ b/usr.bin/telnet/Makefile @@ -31,6 +31,7 @@ # SUCH DAMAGE. # # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ # PROG= telnet @@ -39,11 +40,12 @@ CFLAGS+=-DKLUDGELINEMODE -DUSE_TERMIO #-DAUTHENTICATION -DENCRYPTION CFLAGS+=-DENV_HACK CFLAGS+=-DSKEY CFLAGS+=-I${.CURDIR}/../../lib +CFLAGS+=-DIPSEC -DINET6 #CFLAGS+= -DKRB4 -DPADD= ${LIBTERMCAP} ${LIBTELNET} -LDADD= -ltermcap -ltelnet +DPADD= ${LIBTERMCAP} ${LIBTELNET} ${LIBIPSEC} +LDADD= -ltermcap -ltelnet -lipsec #DPADD+= ${LIBKRB} ${LIBDES} #LDADD+= -lkrb -ldes diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c index a4677e0135e4..a640becaa168 100644 --- a/usr.bin/telnet/commands.c +++ b/usr.bin/telnet/commands.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef lint @@ -76,7 +78,7 @@ static char sccsid[] = "@(#)commands.c 8.2 (Berkeley) 12/15/93"; # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include - +#include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 @@ -2094,24 +2096,76 @@ ayt_status() } #endif +static const char * +sockaddr_ntop(sa) + struct sockaddr *sa; +{ + void *addr; + static char addrbuf[INET6_ADDRSTRLEN]; + + switch (sa->sa_family) { + case AF_INET: + addr = &((struct sockaddr_in *)sa)->sin_addr; + break; +#ifdef INET6 + case AF_INET6: + addr = &((struct sockaddr_in6 *)sa)->sin6_addr; + break; +#endif + default: + return NULL; + } + inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); + return addrbuf; +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +static int +setpolicy(net, res, policy) + int net; + struct addrinfo *res; + char *policy; +{ + char *buf; + int level; + int optname; + + if (policy == NULL) + return 0; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + printf("%s\n", ipsec_strerror()); + return -1; + } + level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; + if (setsockopt(net, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); +} +#endif + int tn(argc, argv) int argc; char *argv[]; { - register struct hostent *host = 0; - struct sockaddr_in sin, src_sin; - struct servent *sp = 0; - unsigned long temp; -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + struct sockaddr_storage ss, src_ss; char *srp = 0, *strrchr(); - unsigned long sourceroute(), srlen; -#endif + int proto, opt; + int sourceroute(), srlen; + int srcroute = 0, result; char *cmd, *hostp = 0, *portp = 0, *user = 0; char *src_addr = NULL; + struct addrinfo hints, *res; + int error = 0; /* clear the socket address prior to use */ - bzero((char *)&sin, sizeof(sin)); + memset((char *)&ss, 0, sizeof(ss)); if (connected) { printf("?Already connected to %s\n", hostname); @@ -2171,128 +2225,106 @@ tn(argc, argv) goto usage; if (src_addr != NULL) { - bzero((char *)&src_sin, sizeof(src_sin)); - src_sin.sin_family = AF_INET; - if (!inet_aton(src_addr, &src_sin.sin_addr)) { - host = gethostbyname2(src_addr, AF_INET); - if (host == NULL) { - herror(src_addr); - return 0; - } - if (host->h_length != sizeof(src_sin.sin_addr)) { - fprintf(stderr, "telnet: gethostbyname2: invalid address\n"); - return 0; - } - memcpy((void *)&src_sin.sin_addr, (void *)host->h_addr_list[0], - sizeof(src_sin.sin_addr)); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(src_addr, 0, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &res); } + if (error != 0) { + fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", src_addr, strerror(errno)); + return 0; + } + memcpy((void *)&src_ss, (void *)res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); } - -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (hostp[0] == '@' || hostp[0] == '!') { - if ((hostname = strrchr(hostp, ':')) == NULL) + if ( +#ifdef INET6 + family == AF_INET6 || +#endif + (hostname = strrchr(hostp, ':')) == NULL) hostname = strrchr(hostp, '@'); hostname++; - srp = 0; - temp = sourceroute(hostp, &srp, &srlen); - if (temp == 0) { - herror(srp); + srcroute = 1; + } else + hostname = hostp; + if (!portp) { + telnetport = 1; + portp = "telnet"; + } else if (*portp == '-') { + portp++; + telnetport = 1; + } else + telnetport = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error == 0) { + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + 0); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else if (error == EAI_NONAME) { + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, portp, &hints, &res); + if (error != 0) { + fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", hostname, strerror(errno)); setuid(getuid()); return 0; - } else if (temp == -1) { + } + memcpy((void *)&ss, (void *)res->ai_addr, res->ai_addrlen); + if (srcroute != 0) + (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1); + else if (res->ai_canonname != NULL) + strcpy(_hostname, res->ai_canonname); + else + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } + if (srcroute != 0) { + srp = 0; + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { + setuid(getuid()); + freeaddrinfo(res); + return 0; + } else if (result == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); + freeaddrinfo(res); return 0; - } else { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; } - } else { -#endif - temp = inet_addr(hostp); - if (temp != INADDR_NONE) { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; - if (doaddrlookup) - host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); - if (host) - (void) strncpy(_hostname, host->h_name, sizeof(_hostname)); - else - (void) strncpy(_hostname, hostp, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - host = gethostbyname(hostp); - if (host) { - sin.sin_family = host->h_addrtype; -#if defined(h_addr) /* In 4.3, this is a #define */ - memmove((caddr_t)&sin.sin_addr, - host->h_addr_list[0], - MIN(host->h_length, sizeof(sin.sin_addr))); -#else /* defined(h_addr) */ - memmove((caddr_t)&sin.sin_addr, host->h_addr, - MIN(host->h_length, sizeof(sin.sin_addr))); -#endif /* defined(h_addr) */ - strncpy(_hostname, host->h_name, sizeof(_hostname)); - _hostname[sizeof(_hostname)-1] = '\0'; - hostname = _hostname; - } else { - herror(hostp); - setuid(getuid()); - return 0; - } - } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) } -#endif - if (portp) { - if (*portp == '-') { - portp++; - telnetport = 1; - } else - telnetport = 0; - sin.sin_port = atoi(portp); - if (sin.sin_port == 0) { - sp = getservbyname(portp, "tcp"); - if (sp) - sin.sin_port = sp->s_port; - else { - printf("%s: bad port number\n", portp); - setuid(getuid()); - return 0; - } - } else { -#if !defined(htons) - u_short htons P((unsigned short)); -#endif /* !defined(htons) */ - sin.sin_port = htons(sin.sin_port); - } - } else { - if (sp == 0) { - sp = getservbyname("telnet", "tcp"); - if (sp == 0) { - fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); - setuid(getuid()); - return 0; - } - sin.sin_port = sp->s_port; - } - telnetport = 1; - } - printf("Trying %s...\n", inet_ntoa(sin.sin_addr)); + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); do { - net = socket(AF_INET, SOCK_STREAM, 0); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setuid(getuid()); if (net < 0) { perror("telnet: socket"); return 0; } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0) - perror("setsockopt (IP_OPTIONS)"); -#endif + if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0) + perror("setsockopt (source route)"); #if defined(IPPROTO_IP) && defined(IP_TOS) - { + if (res->ai_family == PF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) @@ -2313,28 +2345,31 @@ tn(argc, argv) } if (src_addr != NULL) { - if (bind(net, (struct sockaddr *)&src_sin, sizeof(src_sin)) == -1) { + if (bind(net, (struct sockaddr *)&src_ss, + ((struct sockaddr *)&src_ss)->sa_len) == -1) { perror("bind"); return 0; } } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) + return 0; + if (setpolicy(net, res, ipsec_policy_out) < 0) + return 0; +#endif - if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { -#if defined(h_addr) /* In 4.3, this is a #define */ - if (host && host->h_addr_list[1]) { + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + if (res->ai_next) { int oerrno = errno; fprintf(stderr, "telnet: connect to address %s: ", - inet_ntoa(sin.sin_addr)); + sockaddr_ntop(res->ai_addr)); errno = oerrno; perror((char *)0); - host->h_addr_list++; - memcpy((caddr_t)&sin.sin_addr, host->h_addr_list[0], - MIN(host->h_length, sizeof(sin.sin_addr))); + res = res->ai_next; (void) NetClose(net); continue; } -#endif /* defined(h_addr) */ perror("telnet: Unable to connect to remote host"); return 0; } @@ -2343,6 +2378,7 @@ tn(argc, argv) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) */ } while (connected == 0); + freeaddrinfo(res); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; @@ -2681,8 +2717,6 @@ cmdrc(m1, m2) fclose(rcfile); } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst @@ -2696,6 +2730,10 @@ cmdrc(m1, m2) * be the address to connect() to. * * Arguments: + * + * res: ponter to addrinfo structure which contains sockaddr to + * the host to connect to. + * * arg: pointer to route list to decipher * * cpp: If *cpp is not equal to NULL, this is a @@ -2705,9 +2743,18 @@ cmdrc(m1, m2) * lenp: pointer to an integer that contains the * length of *cpp if *cpp != NULL. * + * protop: pointer to an integer that should be filled in with + * appropriate protocol for setsockopt, as socket + * protocol family. + * + * optp: pointer to an integer that should be filled in with + * appropriate option for setsockopt, as socket protocol + * family. + * * Return values: * - * Returns the address of the host to connect to. If the + * If the return value is 1, then all operations are + * successful. If the * return value is -1, there was a syntax error in the * option, either unknown characters, or too many hosts. * If the return value is 0, one of the hostnames in the @@ -2721,21 +2768,32 @@ cmdrc(m1, m2) * *lenp: This will be filled in with how long the option * pointed to by *cpp is. * + * *protop: This will be filled in with appropriate protocol for + * setsockopt, as socket protocol family. + * + * *optp: This will be filled in with appropriate option for + * setsockopt, as socket protocol family. */ - unsigned long -sourceroute(arg, cpp, lenp) +int +sourceroute(ai, arg, cpp, lenp, protop, optp) + struct addrinfo *ai; char *arg; char **cpp; int *lenp; + int *protop; + int *optp; { - static char lsr[44]; + static char buf[1024]; /*XXX*/ + struct cmsghdr *cmsg; #ifdef sysV88 static IOPTN ipopt; #endif - char *cp, *cp2, *lsrp, *lsrep; + char *cp, *cp2, *lsrp, *ep; register int tmp; - struct in_addr sin_addr; - register struct hostent *host = 0; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct addrinfo hints, *res; + int error; register char c; /* @@ -2744,22 +2802,46 @@ sourceroute(arg, cpp, lenp) */ if (cpp == NULL || lenp == NULL) return((unsigned long)-1); - if (*cpp != NULL && *lenp < 7) - return((unsigned long)-1); + if (*cpp != NULL) { + switch (res->ai_family) { + case AF_INET: + if (*lenp < 7) + return((unsigned long)-1); + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < (sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return((unsigned long)-1); + break; +#endif + } + } /* * Decide whether we have a buffer passed to us, * or if we need to use our own static buffer. */ if (*cpp) { lsrp = *cpp; - lsrep = lsrp + *lenp; + ep = lsrp + *lenp; } else { - *cpp = lsrp = lsr; - lsrep = lsrp + 44; + *cpp = lsrp = buf; + ep = lsrp + 1024; } cp = arg; +#ifdef INET6 + if (ai->ai_family == AF_INET6) { + cmsg = inet6_rthdr_init(*cpp, IPV6_RTHDR_TYPE_0); + if (*cp != '@') + return -1; + *protop = IPPROTO_IPV6; + *optp = IPV6_PKTOPTIONS; + } else +#endif + { /* * Next, decide whether we have a loose source * route or a strict source route, and fill in @@ -2786,13 +2868,20 @@ sourceroute(arg, cpp, lenp) lsrp++; /* skip over length, we'll fill it in later */ *lsrp++ = 4; #endif + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + } cp++; - - sin_addr.s_addr = 0; - + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; for (c = 0;;) { - if (c == ':') + if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') cp2 = 0; else for (cp2 = cp; c = *cp2; cp2++) { if (c == ',') { @@ -2801,7 +2890,11 @@ sourceroute(arg, cpp, lenp) cp2++; } else if (c == '@') { *cp2++ = '\0'; - } else if (c == ':') { + } else if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { *cp2++ = '\0'; } else continue; @@ -2810,22 +2903,32 @@ sourceroute(arg, cpp, lenp) if (!c) cp2 = 0; - if ((tmp = inet_addr(cp)) != -1) { - sin_addr.s_addr = tmp; - } else if (host = gethostbyname(cp)) { -#if defined(h_addr) - memcpy((caddr_t)&sin_addr, host->h_addr_list[0], - MIN(host->h_length,sizeof(sin_addr))); -#else - memcpy((caddr_t)&sin_addr, host->h_addr, - MIN(host->h_length,sizeof(sin_addr))); -#endif - } else { + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(cp, NULL, &hints, &res); + if (error == EAI_NONAME) { + hints.ai_flags = 0; + error = getaddrinfo(cp, NULL, &hints, &res); + } + if (error != 0) { + fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); + if (error == EAI_SYSTEM) + fprintf(stderr, "%s: %s\n", cp, + strerror(errno)); *cpp = cp; return(0); } - memcpy(lsrp, (char *)&sin_addr, 4); +#ifdef INET6 + if (res->ai_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + inet6_rthdr_add(cmsg, &sin6->sin6_addr, + IPV6_RTHDR_LOOSE); + } else +#endif + { + sin = (struct sockaddr_in *)res->ai_addr; + memcpy(lsrp, (char *)&sin->sin_addr, 4); lsrp += 4; + } if (cp2) cp = cp2; else @@ -2833,9 +2936,27 @@ sourceroute(arg, cpp, lenp) /* * Check to make sure there is space for next address */ - if (lsrp + 4 > lsrep) +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)cmsg + + sizeof(struct cmsghdr) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) return((unsigned long)-1); + } else +#endif + if (lsrp + 4 > ep) + return((unsigned long)-1); + freeaddrinfo(res); } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + *lenp = cmsg->cmsg_len; + } else +#endif + { #ifndef sysV88 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { *cpp = 0; @@ -2854,6 +2975,7 @@ sourceroute(arg, cpp, lenp) *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif - return(sin_addr.s_addr); + } + freeaddrinfo(res); + return 1; } -#endif diff --git a/usr.bin/telnet/externs.h b/usr.bin/telnet/externs.h index a8c3f24f57fd..10e89acdee36 100644 --- a/usr.bin/telnet/externs.h +++ b/usr.bin/telnet/externs.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)externs.h 8.2 (Berkeley) 12/15/93 + * $FreeBSD$ */ #ifndef BSD @@ -86,6 +87,14 @@ typedef unsigned char cc_t; #endif #include +#if defined(IPSEC) +#include +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + #ifndef _POSIX_VDISABLE # ifdef sun # include /* pick up VDISABLE definition, mayby */ @@ -115,6 +124,7 @@ extern int autologin, /* Autologin enabled */ skiprc, /* Don't process the ~/.telnetrc file */ eight, /* use eight bit mode (binary in and/or out */ + family, /* address family of peer */ flushout, /* flush output */ connected, /* Are we connected to the other side? */ globalmode, /* Mode tty should be in */ diff --git a/usr.bin/telnet/main.c b/usr.bin/telnet/main.c index 298389c7eafd..9126738c848b 100644 --- a/usr.bin/telnet/main.c +++ b/usr.bin/telnet/main.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $FreeBSD$ */ #ifndef lint @@ -42,6 +44,7 @@ static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #include +#include #include "ring.h" #include "externs.h" @@ -56,6 +59,13 @@ static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 12/15/93"; #define FORWARD #endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +int family = AF_UNSPEC; + /* * Initialize variables. */ @@ -81,10 +91,10 @@ usage() fprintf(stderr, "Usage: %s %s%s%s%s\n", prompt, #ifdef AUTHENTICATION - "[-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c] [-d]", "\n\t[-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", #else - "[-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", "\n\t[-n tracefile] ", #endif #if defined(TN3270) && defined(unix) @@ -97,6 +107,9 @@ usage() # endif #else "[-r] [-s src_addr] ", +#endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "[-P policy]" #endif "[host-name [port]]" ); @@ -138,8 +151,25 @@ main(argc, argv) rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; autologin = -1; - while ((ch = getopt(argc, argv, "8EKLNS:X:acde:fFk:l:n:rs:t:x")) != -1) { +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define IPSECOPT "P:" +#else +#define IPSECOPT +#endif + while ((ch = getopt(argc, argv, + "468EKLNS:X:acde:fFk:l:n:rs:t:x" IPSECOPT)) != -1) +#undef IPSECOPT + { + switch(ch) { + case '4': + family = AF_INET; + break; +#ifdef INET6 + case '6': + family = AF_INET6; + break; +#endif case '8': eight = 3; /* binary output and input */ break; @@ -276,6 +306,16 @@ main(argc, argv) "%s: Warning: -x ignored, no ENCRYPT support.\n", prompt); break; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + case 'P': + if (!strncmp("in", optarg, 2)) + ipsec_policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + ipsec_policy_out = strdup(optarg); + else + usage(); + break; +#endif case '?': default: usage(); diff --git a/usr.bin/telnet/telnet.1 b/usr.bin/telnet/telnet.1 index ca2e968be6fd..dfa4fcfe75a9 100644 --- a/usr.bin/telnet/telnet.1 +++ b/usr.bin/telnet/telnet.1 @@ -32,7 +32,7 @@ .\" @(#)telnet.1 8.5 (Berkeley) 3/1/94 .\" $FreeBSD$ .\" -.Dd March 1, 1994 +.Dd January 27, 2000 .Dt TELNET 1 .Os BSD 4.2 .Sh NAME @@ -564,9 +564,10 @@ will attempt to contact a .Tn TELNET server at the default port. The host specification may be either a host name (see -.Xr hosts 5 ) -or an Internet address specified in the \*(Lqdot notation\*(Rq (see -.Xr inet 3 ) . +.Xr hosts 5 ) , +an Internet address specified in the \*(Lqdot notation\*(Rq (see +.Xr inet 3 ) , +or IPv6 host name or IPv6 coloned-hexadecimal addreess. The .Fl l option may be used to specify the user name @@ -1390,6 +1391,8 @@ The .Nm command appeared in .Bx 4.2 . +.Pp +IPv6 support was added by WIDE/KAME project. .Sh NOTES .Pp On some remote systems, echo has to be turned off manually when in diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index abe3b5bd3d9b..5e7433d87691 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -24,6 +24,7 @@ SUBDIR= IPXrouted \ dev_mkdb \ diskpart \ edquota \ + faithd \ fdcontrol \ fdformat \ fdwrite \ diff --git a/usr.sbin/faithd/Makefile b/usr.sbin/faithd/Makefile new file mode 100644 index 000000000000..c40c21de0c44 --- /dev/null +++ b/usr.sbin/faithd/Makefile @@ -0,0 +1,24 @@ +# Copyright (c) 1996 WIDE Project. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modifications, are permitted provided that the above copyright notice +# and this paragraph are duplicated in all such forms and that any +# documentation, advertising materials, and other materials related to +# such distribution and use acknowledge that the software was developed +# by the WIDE Project, Japan. The name of the Project may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' +# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT +# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE. +# $FreeBSD$ + +PROG= faithd +SRCS= faithd.c tcp.c ftp.c rsh.c +MAN8= faithd.8 +#CFLAGS+= -DFAITH4 +CFLAGS+= -Wall +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +.include diff --git a/usr.sbin/faithd/README b/usr.sbin/faithd/README new file mode 100644 index 000000000000..f8e47532dd33 --- /dev/null +++ b/usr.sbin/faithd/README @@ -0,0 +1,140 @@ +Configuring FAITH IPv6-to-IPv4 TCP relay + +Kazu Yamamoto and Jun-ichiro itojun Hagino +$Id: README,v 1.1.1.1 1999/08/08 23:29:27 itojun Exp $ +$FreeBSD$ + + +Introduction +============ + +FAITH is a IPv6-to-IPv4 TCP relay. It performs tcp relay just as some of +firewall-oriented gateway does, but between IPv6 and IPv4 with address +translation. +TCP connections has to be made from IPv6 node to IPv4 node. FAITH will +not relay connections for the opposite direction. +To perform relays, FAITH daemon needs to be executed on a router between +your local IPv6 site and outside IPv4 network. The daemon needs to be +invoked per each TCP services (TCP port number). + + IPv4 node "dest" = 123.4.5.6 + | + [[[[ outside IPv4 ocean ]]]] + | + node that runs FAITH-daemon (usually a router) + | + ==+=====+===+==== IPv6, or IPv4/v6 network in your site ^ + | | | connection + clients IPv6 node "src" | + +You will have to allocate an IPv6 address prefix to map IPv4 addresses into. +The following description uses 3ffe:0501:1234:ffff:: as example. +Please use a prefix which belongs to your site. +FAITH will make it possible to make a IPv6 TCP connection From IPv6 node +"src", toward IPv4 node "dest", by specifying FAITH-mapped address +3ffe:0501:1234:ffff::123.4.5.6 +(which is, 3ffe:0501:1234:ffff:0000:0000:7b04:0506). +The address mapping can be performed by hand:-), by speical nameserver on +the network, or by special resolver on the source node. + + +Setup +===== + +The following example assumes: +- You have assigned 3ffe:0501:1234:ffff:: as FAITH adderss prefix. +- You are willing to provide IPv6-to IPv4 TCP relay for telnet. + +<> + +(1) If you have IPv6 TCP server for the "telnet" service, i.e. telnetd via + inet6d, disable that daemon. Comment out the line from "inet6d.conf" + and send the HUP signal to "inet6d". + +(2) Execute sysctl as root to enable FAITH support in the kernel. + + # sysctl -w net.inet6.ip6.keepfaith=1 + +(3) Route packets toward FAITH prefix into "faith0" interface. + + # ifconfig faith0 up + # route add -inet6 3ffe:0501:1234:ffff:: -prefixlen 64 -interface faith0 + + or, on platforms that has problem with "-interface": + # ifconfig faith0 up + # route add -inet6 3ffe:0501:1234:ffff:: -prefixlen 64 \ + fe80:q::xxxx:yyyy:zzzz:wwww + (the last one is link-local address assigned for faith0) + +(4) Execute "faithd" by root as follows: + + # faithd telnet /usr/local/v6/libexec/telnetd telnetd + + 1st argument is a service name you are willing to provide TCP relay. + (it can be specified either by number "23" or by string "telnet") + 2nd argument is a path name for local IPv6 TCP server. If there is a + connection toward the router itself, this program will be invoked. + 3rd and the following arguments are arguments for the local IPv6 TCP + server. (3rd argument is typically the program name without its path.) + + More examples: + + # faithd login /usr/local/v6/libexec/rlogin rlogind + # faithd shell /usr/local/v6/libexec/rshd rshd + # faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l + # faithd ssh + + +<> + +(4) Make sure that packets whose destinations match the prefix can +reach from the IPv6 host to the translating router. + +<> + +There are two ways to translate IPv4 address to IPv6 address: + (a) Faked by DNS + (b) Faked by /etc/hosts. + +(5.a) Install "newbie" and set up FAITH mode. See kit/ports/newbie of + KAME package. KAME package is obtained from www.kame.net. + +(5.b) Add an entry into /etc/hosts so that you can resolve hostname into +faked IPv6 addrss. For example, add the following line for www.freebsd.org: + + 3ffe:0501:1234:ffff::204.216.27.21 www.freebsd.org + +<> + +(6) To see if "faithd" works, watch "/var/log/daemon". Note: please +setup "/etc/syslog.conf" so that LOG_DAEMON messages are to be stored +in "/var/log/daemon". + + + daemon.* /var/log/daemon + + +Advanced configuration +====================== + +If you would like to restrict IPv4 destination for translation, you may +want to do the following: + + # route add -inet6 3ffe:0501:1234:ffff::123.0.0.0 -prefixlen 104 \ + -interface faith0 + +By this way, you can restrict IPv4 destination to 123.0.0.0/8. +You may also want to reject packets toward 3ffe:0501:1234:ffff::/64 which +is not in 3ffe:0501:1234:ffff::123.0.0.0/104. This will be left as excerside +for the reader. + +By doing this, you will be able to provide your IPv4 web server to outside +IPv6 customers, without risks of unwanted open relays. + + [[[[ IPv6 network outside ]]]] | + | | connection + node that runs FAITH-daemon (usually a router) v + | + ========+======== IPv4/v6 network in your site + | (123.0.0.0/8) + IPv4 web server diff --git a/usr.sbin/faithd/faithd.8 b/usr.sbin/faithd/faithd.8 new file mode 100644 index 000000000000..2f62ed3e5f2a --- /dev/null +++ b/usr.sbin/faithd/faithd.8 @@ -0,0 +1,256 @@ +.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: faithd.8,v 1.3 1999/10/07 04:22:14 itojun Exp $ +.\" $FreeBSD$ +.\" +.Dd January 27, 2000 +.Dt FAITHD 8 +.Os KAME +.Sh NAME +.Nm faithd +.Nd FAITH IPv6/v4 translator daemon +.Sh SYNOPSIS +.Nm faithd +.Op Fl dp +.Oo +.Ar service +.Oo +.Ar serverpath +.Op Ar serverargs +.Oc +.Oc +.Sh DESCRIPTION +.Nm +provides IPv6/v4 TCP relay for the specified +.Ar service . +.Pp +.Nm +must be invoked on IPv4/v6 dual stack router. +The router must be configured to capture all the TCP traffic +toward reserved IPv6 address prefix, by using +.Xr route 8 +and +.Xr sysctl 8 +commands. +.Nm +will daemonize itself on invocation. +.Pp +.Nm +will listen to TCPv6 port +.Ar service . +If TCPv6 traffic to port +.Ar service +is found, +.Nm +will relay the TCPv6 traffic to TCPv4. +Destination for relayed TCPv4 connection will be determined by the +last 4 octets of the original IPv6 destination. +For example, if +.Li 3ffe:0501:4819:ffff:: +is reserved for +.Nm faithd , +and the TCPv6 destination address is +.Li 3ffe:0501:4819:ffff::0a01:0101 , +the traffic will be relayed to IPv4 destination +.Li 10.1.1.1 . +.Pp +If +.Ar service +is not given, +.Li telnet +is assumed, and +.Nm +will relay TCP traffic on TCP port +.Li telnet . +With +.Ar service , +.Nm +will work as TCP relaying daemon for specified +.Ar service +as described above. +.Pp +Since +.Nm +listens to TCP port +.Ar service , +it is not possible to run local TCP daemons for port +.Ar service +on the router, using +.Xr inetd 8 +or other standard mechanisms. +By specifying +.Ar serverpath +to +.Nm faithd , +you can run local daemons on the router. +.Nm +will invoke local daemon at +.Ar serverpath +if the destination address is local interface address, +and will perform translation to IPv4 TCP in other cases. +You can also specify +.Ar serverargs +for the arguments for the local daemon. +.Pp +To use +.Nm +translation service, +an IPv6 address prefix must be reserved for mapping IPv4 addresses into. +Kernel must be properly configured to route all the TCP connection +toward the reserved IPv6 address prefix into the +.Dv faith +pseudo interface, by using +.Xr route 8 +command. +Also, +.Xr sysctl 8 +should be used to configure +.Dv net.inet6.ip6.keepfaith +to +.Dv 1 . +.Pp +If +.Fl d +is given, debugging information will be generated using +.Xr syslog 3 . +If +.Fl p +is given, +.Nm +will use privileged TCP port number as source port, +for IPv4 TCP connection toward final destination. +For relaying +.Xr ftp 1 +and +.Xr rlogin 1 , +.Fl p +is not necessary as special program code is supplied. +.Pp +.Nm +will relay both normal and out-of-band TCP data. +It is capable of emulating TCP half close as well. +.Nm +includes special support for protocols used by +.Xr ftp 1 +and +.Xr rlogin 1 . +When translating FTP protocol, +.Nm +translates network level addresses in +.Li PORT/LPRT/EPRT +and +.Li PASV/LPSV/EPSV +commands. +For RLOGIN protocol, +.Nm +will relay back connection from +.Xr rlogind 8 +on the server to +.Xr rlogin 1 +on client. +.Pp +Inactive sessions will be disconnected in 30 minutes, +to avoid stale sessions from chewing up resources. +This may be inappropriate for some of the services +.Po +should this be configurable? +.Pc . +.\" +.Sh EXAMPLES +Before invoking +.Nm faithd , +.Xr faith 4 +interface has to be configured properly. +.Pp +To translate +.Li telnet +service, and provide no local telnet service, invoke +.Nm +as either of the following: +.Bd -literal -offset +# faithd +# faithd telnet +.Ed +.Pp +If you would like to provide local telnet service via +.Xr telnetd 8 +on +.Pa /usr/local/v6/libexec/telnetd , +user the following command line: +.Bd -literal -offset +# faithd telnet /usr/local/v6/libexec/telnetd telnetd +.Ed +.Pp +If you would like to pass extra arguments to the local daemon: +.Bd -literal -offset +# faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l +.Ed +.Pp +Here are some other examples: +.Bd -literal -offset +# faithd login /usr/local/v6/libexec/rlogin rlogind +# faithd shell /usr/local/v6/libexec/rshd rshd +# faithd sshd +.Ed +.\" +.Sh RETURN VALUES +.Nm +exits with +.Dv EXIT_SUCCESS +.Pq 0 +on success, and +.Dv EXIT_FAILURE +.Pq 1 +on error. +.\" +.Sh SEE ALSO +.Xr faith 4 , +.Xr route 8 , +.Xr sysctl 8 +.Rs +.%A Jun-ichiro itojun Hagino +.%A Kazu Yamamoto +.%T "An IPv6-to-IPv4 transport relay translator" +.%R internet draft +.%N draft-ietf-ngtrans-tcpudp-relay-00.txt +.%O work in progress material +.Re +.\" +.Sh SECURITY NOTICE +It is very insecure to use +.Xr rhosts 5 +and other IP-address based authentication, for connections relayed by +.Nm +.Po +and any other TCP relaying services +.Pc . +.\" +.Sh HISTORY +The +.Nm +command first appeared in WIDE Hydrangea IPv6 protocol stack kit. diff --git a/usr.sbin/faithd/faithd.c b/usr.sbin/faithd/faithd.c new file mode 100644 index 000000000000..ee6c8dfefc10 --- /dev/null +++ b/usr.sbin/faithd/faithd.c @@ -0,0 +1,837 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * User level translator from IPv6 to IPv4. + * + * Usage: faithd [ ...] + * e.g. faithd telnet /usr/local/v6/sbin/telnetd telnetd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef IFT_FAITH +# define USE_ROUTE +# include +# include +# include +#endif + +#include +#include +#include + +#ifdef FAITH4 +#include +#include +#ifndef FAITH_NS +#define FAITH_NS "FAITH_NS" +#endif +#endif + +#include "faithd.h" + +char *serverpath = NULL; +char *serverarg[MAXARGV + 1]; +static char *faithdname = NULL; +char logname[BUFSIZ]; +char procname[BUFSIZ]; +struct myaddrs { + struct myaddrs *next; + struct sockaddr *addr; +}; +struct myaddrs *myaddrs = NULL; +static char *service; +#ifdef USE_ROUTE +static int sockfd = 0; +#endif +int dflag = 0; +static int pflag = 0; + +int main __P((int, char **)); +static void play_service __P((int)); +static void play_child __P((int, struct sockaddr *)); +static int faith_prefix __P((struct sockaddr *)); +static int map6to4 __P((struct sockaddr_in6 *, struct sockaddr_in *)); +#ifdef FAITH4 +static int map4to6 __P((struct sockaddr_in *, struct sockaddr_in6 *)); +#endif +static void sig_child __P((int)); +static void sig_terminate __P((int)); +static void start_daemon __P((void)); +static unsigned int if_maxindex __P((void)); +static void grab_myaddrs __P((void)); +static void free_myaddrs __P((void)); +static void update_myaddrs __P((void)); +static void usage __P((void)); + +int +main(int argc, char *argv[]) +{ + struct addrinfo hints, *res; + int s_wld, error, i, serverargc, on = 1; + int family = AF_INET6; + int c; +#ifdef FAITH_NS + char *ns; +#endif /* FAITH_NS */ + extern int optind; + extern char *optarg; + + /* + * Initializing stuff + */ + + faithdname = strrchr(argv[0], '/'); + if (faithdname) + faithdname++; + else + faithdname = argv[0]; + + while ((c = getopt(argc, argv, "dp46")) != -1) { + switch (c) { + case 'd': + dflag++; + break; + case 'p': + pflag++; + break; +#ifdef FAITH4 + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; +#endif + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + +#ifdef FAITH_NS + if ((ns = getenv(FAITH_NS)) != NULL) { + struct sockaddr_storage ss; + struct addrinfo hints, *res; + char serv[NI_MAXSERV]; + + memset(&ss, 0, sizeof(ss)); + memset(&hints, 0, sizeof(hints)); + snprintf(serv, sizeof(serv), "%u", NAMESERVER_PORT); + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(ns, serv, &hints, &res) == 0) { + res_init(); + memcpy(&_res_ext.nsaddr, res->ai_addr, res->ai_addrlen); + _res.nscount = 1; + } + } +#endif /* FAITH_NS */ + +#ifdef USE_ROUTE + grab_myaddrs(); +#endif + + switch (argc) { + case 0: + serverpath = DEFAULT_PATH; + serverarg[0] = DEFAULT_NAME; + serverarg[1] = NULL; + service = DEFAULT_PORT_NAME; + break; + default: + serverargc = argc - NUMARG; + if (serverargc > MAXARGV) + exit_error("too many augments"); + + serverpath = malloc(strlen(argv[NUMPRG])); + strcpy(serverpath, argv[NUMPRG]); + for (i = 0; i < serverargc; i++) { + serverarg[i] = malloc(strlen(argv[i + NUMARG])); + strcpy(serverarg[i], argv[i + NUMARG]); + } + serverarg[i] = NULL; + /* fall throuth */ + case 1: /* no local service */ + service = argv[NUMPRT]; + break; + } + + /* + * Opening wild card socket for this service. + */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(NULL, service, &hints, &res); + if (error) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); + if (error == EAI_SYSTEM) + exit_error("getaddrinfo: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s_wld == -1) + exit_error("socket: %s", ERRSTR); + +#ifdef IPV6_FAITH + if (res->ai_family == AF_INET6) { + error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR); + } +#endif +#ifdef FAITH4 +#ifdef IP_FAITH + if (res->ai_family == AF_INET) { + error = setsockopt(s_wld, IPPROTO_IP, IP_FAITH, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IP_FAITH): %s", ERRSTR); + } +#endif +#endif /* FAITH4 */ + + error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(SO_REUSEADDR): %s", ERRSTR); + + error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR); + + error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen); + if (error == -1) + exit_error("bind: %s", ERRSTR); + + error = listen(s_wld, 5); + if (error == -1) + exit_error("listen: %s", ERRSTR); + +#ifdef USE_ROUTE + sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC); + if (sockfd < 0) { + exit_error("socket(PF_ROUTE): %s", ERRSTR); + /*NOTREACHED*/ + } +#endif + + /* + * Everything is OK. + */ + + start_daemon(); + + snprintf(logname, sizeof(logname), "faithd %s", service); + snprintf(procname, sizeof(procname), "accepting port %s", service); + openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + syslog(LOG_INFO, "Staring faith daemon for %s port", service); + + play_service(s_wld); + /*NOTRECHED*/ + exit(1); /*pacify gcc*/ +} + +static void +play_service(int s_wld) +{ + struct sockaddr_storage srcaddr; + int len; + int s_src; + pid_t child_pid; + fd_set rfds; + int error; + int maxfd; + + /* + * Wait, accept, fork, faith.... + */ +again: + setproctitle(procname); + + FD_ZERO(&rfds); + FD_SET(s_wld, &rfds); + maxfd = s_wld; +#ifdef USE_ROUTE + if (sockfd) { + FD_SET(sockfd, &rfds); + maxfd = (maxfd < sockfd) ? sockfd : maxfd; + } +#endif + + error = select(maxfd + 1, &rfds, NULL, NULL, NULL); + if (error < 0) { + if (errno == EINTR) + goto again; + exit_failure("select: %s", ERRSTR); + /*NOTREACHED*/ + } + +#ifdef USE_ROUTE + if (FD_ISSET(sockfd, &rfds)) { + update_myaddrs(); + } +#endif + if (FD_ISSET(s_wld, &rfds)) { + len = sizeof(srcaddr); + s_src = accept(s_wld, (struct sockaddr *)&srcaddr, + &len); + if (s_src == -1) + exit_failure("socket: %s", ERRSTR); + + child_pid = fork(); + + if (child_pid == 0) { + /* child process */ + close(s_wld); + closelog(); + openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + play_child(s_src, (struct sockaddr *)&srcaddr); + exit_failure("should never reach here"); + } else { + /* parent process */ + close(s_src); + if (child_pid == -1) + syslog(LOG_ERR, "can't fork"); + } + } + goto again; +} + +static void +play_child(int s_src, struct sockaddr *srcaddr) +{ + struct sockaddr_storage dstaddr6; + struct sockaddr_storage dstaddr4; + char src[MAXHOSTNAMELEN]; + char dst6[MAXHOSTNAMELEN]; + char dst4[MAXHOSTNAMELEN]; + int len = sizeof(dstaddr6); + int s_dst, error, hport, nresvport, on = 1; + struct timeval tv; + struct sockaddr *sa4; + + tv.tv_sec = 1; + tv.tv_usec = 0; + + getnameinfo(srcaddr, srcaddr->sa_len, + src, sizeof(src), NULL, 0, NI_NUMERICHOST); + syslog(LOG_INFO, "accepted a client from %s", src); + + error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len); + if (error == -1) + exit_failure("getsockname: %s", ERRSTR); + + getnameinfo((struct sockaddr *)&dstaddr6, len, + dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST); + syslog(LOG_INFO, "the client is connecting to %s", dst6); + + if (!faith_prefix((struct sockaddr *)&dstaddr6)) { + if (serverpath) { + /* + * Local service + */ + syslog(LOG_INFO, "executing local %s", serverpath); + dup2(s_src, 0); + close(s_src); + dup2(0, 1); + dup2(0, 2); + execv(serverpath, serverarg); + syslog(LOG_ERR, "execv %s: %s", serverpath, ERRSTR); + _exit(EXIT_FAILURE); + } else { + close(s_src); + exit_success("no local service for %s", service); + } + } + + /* + * Act as a translator + */ + + switch (((struct sockaddr *)&dstaddr6)->sa_family) { + case AF_INET6: + if (!map6to4((struct sockaddr_in6 *)&dstaddr6, + (struct sockaddr_in *)&dstaddr4)) { + close(s_src); + exit_error("map6to4 failed"); + } + syslog(LOG_INFO, "translating from v6 to v4"); + break; +#ifdef FAITH4 + case AF_INET: + if (!map4to6((struct sockaddr_in *)&dstaddr6, + (struct sockaddr_in6 *)&dstaddr4)) { + close(s_src); + exit_error("map4to6 failed"); + } + syslog(LOG_INFO, "translating from v4 to v6"); + break; +#endif + default: + close(s_src); + exit_error("family not supported"); + /*NOTREACHED*/ + } + + sa4 = (struct sockaddr *)&dstaddr4; + getnameinfo(sa4, sa4->sa_len, + dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST); + syslog(LOG_INFO, "the translator is connecting to %s", dst4); + + setproctitle("port %s, %s -> %s", service, src, dst4); + + if (sa4->sa_family == AF_INET6) + hport = ntohs(((struct sockaddr_in6 *)&dstaddr4)->sin6_port); + else /* AF_INET */ + hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port); + + switch (hport) { + case RLOGIN_PORT: + case RSH_PORT: + s_dst = rresvport_af(&nresvport, sa4->sa_family); + break; + default: + if (pflag) + s_dst = rresvport_af(&nresvport, sa4->sa_family); + else + s_dst = socket(sa4->sa_family, SOCK_STREAM, 0); + break; + } + if (s_dst == -1) + exit_failure("socket: %s", ERRSTR); + + error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR); + + error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (error == -1) + exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + if (error == -1) + exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR); + + error = connect(s_dst, sa4, sa4->sa_len); + if (error == -1) + exit_failure("connect: %s", ERRSTR); + + switch (hport) { + case FTP_PORT: + ftp_relay(s_src, s_dst); + break; + case RSH_PORT: + rsh_relay(s_src, s_dst); + break; + default: + tcp_relay(s_src, s_dst, service); + break; + } + + /* NOTREACHED */ +} + +/* 0: non faith, 1: faith */ +static int +faith_prefix(struct sockaddr *dst) +{ +#ifndef USE_ROUTE + int mib[4], size; + struct in6_addr faith_prefix; + struct sockaddr_in6 *dst6 = (struct sockaddr_in *)dst; + + if (dst->sa_family != AF_INET6) + return 0; + + mib[0] = CTL_NET; + mib[1] = PF_INET6; + mib[2] = IPPROTO_IPV6; + mib[3] = IPV6CTL_FAITH_PREFIX; + size = sizeof(struct in6_addr); + if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) + exit_error("sysctl: %s", ERRSTR); + + if (memcmp(dst, &faith_prefix, + sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) { + return 1; + } + return 0; +#else + struct myaddrs *p; + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin4; + struct sockaddr_in6 *dst6; + struct sockaddr_in *dst4; + struct sockaddr_in dstmap; + + dst6 = (struct sockaddr_in6 *)dst; + if (dst->sa_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&dst6->sin6_addr)) { + /* ugly... */ + memset(&dstmap, 0, sizeof(dstmap)); + dstmap.sin_family = AF_INET; + dstmap.sin_len = sizeof(dstmap); + memcpy(&dstmap.sin_addr, &dst6->sin6_addr.s6_addr[12], + sizeof(dstmap.sin_addr)); + dst = (struct sockaddr *)&dstmap; + } + + dst6 = (struct sockaddr_in6 *)dst; + dst4 = (struct sockaddr_in *)dst; + + for (p = myaddrs; p; p = p->next) { + sin6 = (struct sockaddr_in6 *)p->addr; + sin4 = (struct sockaddr_in *)p->addr; + + if (p->addr->sa_len != dst->sa_len + || p->addr->sa_family != dst->sa_family) + continue; + + switch (dst->sa_family) { + case AF_INET6: + if (sin6->sin6_scope_id == dst6->sin6_scope_id + && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &dst6->sin6_addr)) + return 0; + break; + case AF_INET: + if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr) + return 0; + break; + } + } + return 1; +#endif +} + +/* 0: non faith, 1: faith */ +static int +map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4) +{ + memset(dst4, 0, sizeof(*dst4)); + dst4->sin_len = sizeof(*dst4); + dst4->sin_family = AF_INET; + dst4->sin_port = dst6->sin6_port; + memcpy(&dst4->sin_addr, &dst6->sin6_addr.s6_addr[12], + sizeof(dst4->sin_addr)); + + if (dst4->sin_addr.s_addr == INADDR_ANY + || dst4->sin_addr.s_addr == INADDR_BROADCAST + || IN_MULTICAST(dst4->sin_addr.s_addr)) + return 0; + + return 1; +} + +#ifdef FAITH4 +/* 0: non faith, 1: faith */ +static int +map4to6(struct sockaddr_in *dst4, struct sockaddr_in6 *dst6) +{ + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + struct addrinfo hints, *res; + int ai_errno; + + if (getnameinfo((struct sockaddr *)dst4, dst4->sin_len, host, sizeof(host), + serv, sizeof(serv), NI_NAMEREQD|NI_NUMERICSERV) != 0) + return 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if ((ai_errno = getaddrinfo(host, serv, &hints, &res)) != 0) { + syslog(LOG_INFO, "%s %s: %s", host, serv, gai_strerror(ai_errno)); + if (ai_errno == EAI_SYSTEM) + syslog(LOG_INFO, "%s %s: %s", host, serv, + strerror(errno)); + return 0; + } + + memcpy(dst6, res->ai_addr, res->ai_addrlen); + + freeaddrinfo(res); + + return 1; +} +#endif /* FAITH4 */ + +static void +sig_child(int sig) +{ + int status; + pid_t pid; + + pid = wait3(&status, WNOHANG, (struct rusage *)0); + if (pid && status) + syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status); +} + +void +sig_terminate(int sig) +{ + syslog(LOG_INFO, "Terminating faith daemon"); + exit(EXIT_SUCCESS); +} + +static void +start_daemon(void) +{ + if (daemon(0, 0) == -1) + exit_error("daemon: %s", ERRSTR); + + if (signal(SIGCHLD, sig_child) == SIG_ERR) + exit_failure("signal CHLD: %s", ERRSTR); + + if (signal(SIGTERM, sig_terminate) == SIG_ERR) + exit_failure("signal TERM: %s", ERRSTR); +} + +void +exit_error(const char *fmt, ...) +{ + va_list ap; + char buf[BUFSIZ]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fprintf(stderr, "%s\n", buf); + exit(EXIT_FAILURE); +} + +void +exit_failure(const char *fmt, ...) +{ + va_list ap; + char buf[BUFSIZ]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + syslog(LOG_ERR, buf); + exit(EXIT_FAILURE); +} + +void +exit_success(const char *fmt, ...) +{ + va_list ap; + char buf[BUFSIZ]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + syslog(LOG_INFO, buf); + exit(EXIT_SUCCESS); +} + +#ifdef USE_ROUTE +static unsigned int +if_maxindex() +{ + struct if_nameindex *p, *p0; + unsigned int max = 0; + + p0 = if_nameindex(); + for (p = p0; p && p->if_index && p->if_name; p++) { + if (max < p->if_index) + max = p->if_index; + } + if_freenameindex(p0); + return max; +} + +static void +grab_myaddrs() +{ + int s; + unsigned int maxif; + struct ifreq *iflist; + struct ifconf ifconf; + struct ifreq *ifr, *ifr_end; + struct myaddrs *p; + struct sockaddr_in6 *sin6; + + maxif = if_maxindex() + 1; + iflist = (struct ifreq *)malloc(maxif * BUFSIZ); /* XXX */ + if (!iflist) { + exit_failure("not enough core"); + /*NOTREACHED*/ + } + + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + exit_failure("socket(SOCK_DGRAM)"); + /*NOTREACHED*/ + } + memset(&ifconf, 0, sizeof(ifconf)); + ifconf.ifc_req = iflist; + ifconf.ifc_len = maxif * BUFSIZ; /* XXX */ + if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) { + exit_failure("ioctl(SIOCGIFCONF)"); + /*NOTREACHED*/ + } + close(s); + + /* Look for this interface in the list */ + ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len); + for (ifr = ifconf.ifc_req; + ifr < ifr_end; + ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + + ifr->ifr_addr.sa_len)) { + switch (ifr->ifr_addr.sa_family) { + case AF_INET: + case AF_INET6: + p = (struct myaddrs *)malloc(sizeof(struct myaddrs) + + ifr->ifr_addr.sa_len); + if (!p) { + exit_failure("not enough core"); + /*NOTREACHED*/ + } + memcpy(p + 1, &ifr->ifr_addr, ifr->ifr_addr.sa_len); + p->next = myaddrs; + p->addr = (struct sockaddr *)(p + 1); +#ifdef __KAME__ + if (ifr->ifr_addr.sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)p->addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) + || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { + sin6->sin6_scope_id = + ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); + sin6->sin6_addr.s6_addr[2] = 0; + sin6->sin6_addr.s6_addr[3] = 0; + } + } +#endif + myaddrs = p; + if (dflag) { + char hbuf[NI_MAXHOST]; + getnameinfo(p->addr, p->addr->sa_len, + hbuf, sizeof(hbuf), NULL, 0, + NI_NUMERICHOST); + syslog(LOG_INFO, "my interface: %s %s", hbuf, ifr->ifr_name); + } + break; + default: + break; + } + } + + free(iflist); +} + +static void +free_myaddrs() +{ + struct myaddrs *p, *q; + + p = myaddrs; + while (p) { + q = p->next; + free(p); + p = q; + } + myaddrs = NULL; +} + +static void +update_myaddrs() +{ + char msg[BUFSIZ]; + int len; + struct rt_msghdr *rtm; + + len = read(sockfd, msg, sizeof(msg)); + if (len < 0) { + syslog(LOG_ERR, "read(PF_ROUTE) failed"); + return; + } + rtm = (struct rt_msghdr *)msg; + if (len < 4 || len < rtm->rtm_msglen) { + syslog(LOG_ERR, "read(PF_ROUTE) short read"); + return; + } + if (rtm->rtm_version != RTM_VERSION) { + syslog(LOG_ERR, "routing socket version mismatch"); + close(sockfd); + sockfd = 0; + return; + } + switch (rtm->rtm_type) { + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_IFINFO: + break; + default: + return; + } + /* XXX more filters here? */ + + syslog(LOG_INFO, "update interface address list"); + free_myaddrs(); + grab_myaddrs(); +} +#endif /*USE_ROUTE*/ + +static void +usage() +{ + fprintf(stderr, "usage: %s [-dp] [service [serverpath [serverargs]]]\n", + faithdname); + exit(0); +} diff --git a/usr.sbin/faithd/faithd.h b/usr.sbin/faithd/faithd.h new file mode 100644 index 000000000000..55566edc030c --- /dev/null +++ b/usr.sbin/faithd/faithd.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +extern char logname[]; +extern int dflag; + +extern void tcp_relay __P((int, int, const char *)); +extern void ftp_relay __P((int, int)); +extern int ftp_active __P((int, int, int *, int *)); +extern int ftp_passive __P((int, int, int *, int *)); +extern void rsh_relay __P((int, int)); +extern void rsh_dual_relay __P((int, int)); +extern void exit_error __P((const char *fmt, ...)); +extern void exit_success __P((const char *fmt, ...)); +extern void exit_failure __P((const char *fmt, ...)); + +#define DEFAULT_PORT_NAME "telnet" +#define DEFAULT_PATH "/usr/local/v6/libexec/telnetd" +#define DEFAULT_NAME "telnetd" + +#define FTP_PORT 21 +#define RLOGIN_PORT 513 +#define RSH_PORT 514 + +#define RETURN_SUCCESS 0 +#define RETURN_FAILURE 1 + +#define YES 1 +#define NO 0 + +#define MSS 2048 +#define MAXARGV 20 + +#define NUMPRT 0 +#define NUMPRG 1 +#define NUMARG 2 + +#define UC(b) (((int)b)&0xff) + +#define ERRSTR strerror(errno) + +#define FAITH_TIMEOUT (30 * 60) /*second*/ diff --git a/usr.sbin/faithd/ftp.c b/usr.sbin/faithd/ftp.c new file mode 100644 index 000000000000..fcb25e32de57 --- /dev/null +++ b/usr.sbin/faithd/ftp.c @@ -0,0 +1,1139 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "faithd.h" + +static char rbuf[MSS]; +static char sbuf[MSS]; +static int passivemode = 0; +static int wport4 = -1; /* listen() to active */ +static int wport6 = -1; /* listen() to passive */ +static int port4 = -1; /* active: inbound passive: outbound */ +static int port6 = -1; /* active: outbound passive: inbound */ +static struct sockaddr_storage data4; /* server data address */ +static struct sockaddr_storage data6; /* client data address */ +static int epsvall = 0; + +#ifdef FAITH4 +enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV }; +#else +enum state { NONE, LPRT, EPRT, LPSV, EPSV }; +#endif + +static int ftp_activeconn __P((void)); +static int ftp_passiveconn __P((void)); +static int ftp_copy __P((int, int)); +static int ftp_copyresult __P((int, int, enum state)); +static int ftp_copycommand __P((int, int, enum state *)); + +void +ftp_relay(int ctl6, int ctl4) +{ + fd_set readfds; + int error; + enum state state = NONE; + struct timeval tv; + + syslog(LOG_INFO, "starting ftp control connection"); + + for (;;) { + FD_ZERO(&readfds); + FD_SET(ctl4, &readfds); + FD_SET(ctl6, &readfds); + if (0 <= port4) + FD_SET(port4, &readfds); + if (0 <= port6) + FD_SET(port6, &readfds); +#if 0 + if (0 <= wport4) + FD_SET(wport4, &readfds); + if (0 <= wport6) + FD_SET(wport6, &readfds); +#endif + tv.tv_sec = FAITH_TIMEOUT; + tv.tv_usec = 0; + + error = select(256, &readfds, NULL, NULL, &tv); + if (error == -1) + exit_failure("select: %s", ERRSTR); + else if (error == 0) + exit_failure("connection timeout"); + + /* + * The order of the following checks does (slightly) matter. + * It is important to visit all checks (do not use "continue"), + * otherwise some of the pipe may become full and we cannot + * relay correctly. + */ + if (FD_ISSET(ctl6, &readfds)) { + /* + * copy control connection from the client. + * command translation is necessary. + */ + error = ftp_copycommand(ctl6, ctl4, &state); + + switch (error) { + case -1: + goto bad; + case 0: + close(ctl4); + close(ctl6); + exit_success("terminating ftp control connection"); + /*NOTREACHED*/ + default: + break; + } + } + if (FD_ISSET(ctl4, &readfds)) { + /* + * copy control connection from the server + * translation of result code is necessary. + */ + error = ftp_copyresult(ctl4, ctl6, state); + + switch (error) { + case -1: + goto bad; + case 0: + close(ctl4); + close(ctl6); + exit_success("terminating ftp control connection"); + /*NOTREACHED*/ + default: + break; + } + } + if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) { + /* + * copy data connection. + * no special treatment necessary. + */ + if (FD_ISSET(port4, &readfds)) + error = ftp_copy(port4, port6); + switch (error) { + case -1: + goto bad; + case 0: + close(port4); + close(port6); + port4 = port6 = -1; + syslog(LOG_INFO, "terminating data connection"); + break; + default: + break; + } + } + if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) { + /* + * copy data connection. + * no special treatment necessary. + */ + if (FD_ISSET(port6, &readfds)) + error = ftp_copy(port6, port4); + switch (error) { + case -1: + goto bad; + case 0: + close(port4); + close(port6); + port4 = port6 = -1; + syslog(LOG_INFO, "terminating data connection"); + break; + default: + break; + } + } +#if 0 + if (wport4 && FD_ISSET(wport4, &readfds)) { + /* + * establish active data connection from the server. + */ + ftp_activeconn(); + } + if (wport6 && FD_ISSET(wport6, &readfds)) { + /* + * establish passive data connection from the client. + */ + ftp_passiveconn(); + } +#endif + } + + bad: + exit_failure(ERRSTR); +} + +static int +ftp_activeconn() +{ + int n; + int error; + fd_set set; + struct timeval timeout; + struct sockaddr *sa; + + /* get active connection from server */ + FD_ZERO(&set); + FD_SET(wport4, &set); + timeout.tv_sec = 120; + timeout.tv_usec = -1; + n = sizeof(data4); + if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 + || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) { + close(wport4); + wport4 = -1; + syslog(LOG_INFO, "active mode data connection failed"); + return -1; + } + + /* ask active connection to client */ + sa = (struct sockaddr *)&data6; + port6 = socket(sa->sa_family, SOCK_STREAM, 0); + if (port6 == -1) { + close(port4); + close(wport4); + port4 = wport4 = -1; + syslog(LOG_INFO, "active mode data connection failed"); + return -1; + } + error = connect(port6, sa, sa->sa_len); + if (port6 == -1) { + close(port6); + close(port4); + close(wport4); + port6 = port4 = wport4 = -1; + syslog(LOG_INFO, "active mode data connection failed"); + return -1; + } + + syslog(LOG_INFO, "active mode data connection established"); + return 0; +} + +static int +ftp_passiveconn() +{ + int n; + int error; + fd_set set; + struct timeval timeout; + struct sockaddr *sa; + + /* get passive connection from client */ + FD_ZERO(&set); + FD_SET(wport6, &set); + timeout.tv_sec = 120; + timeout.tv_usec = 0; + n = sizeof(data6); + if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 + || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) { + close(wport6); + wport6 = -1; + syslog(LOG_INFO, "passive mode data connection failed"); + return -1; + } + + /* ask passive connection to server */ + sa = (struct sockaddr *)&data4; + port4 = socket(sa->sa_family, SOCK_STREAM, 0); + if (port4 == -1) { + close(wport6); + close(port6); + wport6 = port6 = -1; + syslog(LOG_INFO, "passive mode data connection failed"); + return -1; + } + error = connect(port4, sa, sa->sa_len); + if (port4 == -1) { + close(wport6); + close(port4); + close(port6); + wport6 = port4 = port6 = -1; + syslog(LOG_INFO, "passive mode data connection failed"); + return -1; + } + + syslog(LOG_INFO, "passive mode data connection established"); + return 0; +} + +static int +ftp_copy(int src, int dst) +{ + int error, atmark; + int n; + + /* OOB data handling */ + error = ioctl(src, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(src, rbuf, 1); + if (n == -1) + goto bad; + send(dst, rbuf, n, MSG_OOB); +#if 0 + n = read(src, rbuf, sizeof(rbuf)); + if (n == -1) + goto bad; + write(dst, rbuf, n); + return n; +#endif + } + + n = read(src, rbuf, sizeof(rbuf)); + switch (n) { + case -1: + case 0: + return n; + default: + write(dst, rbuf, n); + return n; + } + + bad: + exit_failure(ERRSTR); + /*NOTREACHED*/ + return 0; /* to make gcc happy */ +} + +static int +ftp_copyresult(int src, int dst, enum state state) +{ + int error, atmark; + int n; + char *param; + int code; + + /* OOB data handling */ + error = ioctl(src, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(src, rbuf, 1); + if (n == -1) + goto bad; + send(dst, rbuf, n, MSG_OOB); +#if 0 + n = read(src, rbuf, sizeof(rbuf)); + if (n == -1) + goto bad; + write(dst, rbuf, n); + return n; +#endif + } + + n = read(src, rbuf, sizeof(rbuf)); + if (n <= 0) + return n; + rbuf[n] = '\0'; + + /* + * parse argument + */ + { + char *p; + int i; + + p = rbuf; + for (i = 0; i < 3; i++) { + if (!isdigit(*p)) { + /* invalid reply */ + write(dst, rbuf, n); + return n; + } + p++; + } + if (!isspace(*p)) { + /* invalid reply */ + write(dst, rbuf, n); + return n; + } + code = atoi(rbuf); + param = p; + /* param points to first non-command token, if any */ + while (*param && isspace(*param)) + param++; + if (!*param) + param = NULL; + } + + switch (state) { + case NONE: + if (!passivemode && rbuf[0] == '1') { + if (ftp_activeconn() < 0) { + n = snprintf(rbuf, sizeof(rbuf), + "425 Cannot open data connetion\r\n"); + } + } + write(dst, rbuf, n); + return n; + case LPRT: + case EPRT: + /* expecting "200 PORT command successful." */ + if (code == 200) { + char *p; + + p = strstr(rbuf, "PORT"); + if (p) { + p[0] = (state == LPRT) ? 'L' : 'E'; + p[1] = 'P'; + } + } else { + close(wport4); + wport4 = -1; + } + write(dst, rbuf, n); + return n; +#ifdef FAITH4 + case PORT: + /* expecting "200 EPRT command successful." */ + if (code == 200) { + char *p; + + p = strstr(rbuf, "EPRT"); + if (p) { + p[0] = 'P'; + p[1] = 'O'; + } + } else { + close(wport4); + wport4 = -1; + } + write(dst, rbuf, n); + return n; +#endif + case LPSV: + case EPSV: + /* expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)" */ + if (code != 227) { +passivefail0: + close(wport6); + wport6 = -1; + write(dst, rbuf, n); + return n; + } + + { + unsigned int ho[4], po[2]; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + u_short port; + char *p; + + /* + * PASV result -> LPSV/EPSV result + */ + p = param; + while (*p && *p != '(') + p++; + if (!*p) + goto passivefail0; /*XXX*/ + p++; + n = sscanf(p, "%u,%u,%u,%u,%u,%u", + &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]); + if (n != 6) + goto passivefail0; /*XXX*/ + + /* keep PORT parameter */ + memset(&data4, 0, sizeof(data4)); + sin = (struct sockaddr_in *)&data4; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = 0; + for (n = 0; n < 4; n++) { + sin->sin_addr.s_addr |= + htonl((ho[n] & 0xff) << ((3 - n) * 8)); + } + sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); + + /* get ready for passive data connection */ + memset(&data6, 0, sizeof(data6)); + sin6 = (struct sockaddr_in6 *)&data6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0); + if (wport6 == -1) { +passivefail: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate from PASV\r\n"); + write(src, sbuf, n); + return n; + } +#ifdef IPV6_FAITH + { + int on = 1; + error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH, + &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR); + } +#endif + error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + error = listen(wport6, 1); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + + /* transmit LPSV or EPSV */ + /* + * addr from dst, port from wport6 + */ + n = sizeof(data6); + error = getsockname(wport6, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + sin6 = (struct sockaddr_in6 *)&data6; + port = sin6->sin6_port; + + n = sizeof(data6); + error = getsockname(dst, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail; + } + sin6 = (struct sockaddr_in6 *)&data6; + sin6->sin6_port = port; + + if (state == LPSV) { + char *a, *p; + + a = (char *)&sin6->sin6_addr; + p = (char *)&sin6->sin6_port; + n = snprintf(sbuf, sizeof(sbuf), +"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)\r\n", + 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])); + write(dst, sbuf, n); + passivemode = 1; + return n; + } else { + n = snprintf(sbuf, sizeof(sbuf), +"229 Entering Extended Passive Mode (|||%d|)\r\n", + ntohs(sin6->sin6_port)); + write(dst, sbuf, n); + passivemode = 1; + return n; + } + } +#ifdef FAITH4 + case PASV: + /* expecting "229 Entering Extended Passive Mode (|||x|)" */ + if (code != 229) { +passivefail1: + close(wport6); + wport6 = -1; + write(dst, rbuf, n); + return n; + } + + { + u_short port; + char *p; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + /* + * EPSV result -> PORT result + */ + p = param; + while (*p && *p != '(') + p++; + if (!*p) + goto passivefail1; /*XXX*/ + p++; + n = sscanf(p, "|||%hu|", &port); + if (n != 1) + goto passivefail1; /*XXX*/ + + /* keep EPRT parameter */ + n = sizeof(data4); + error = getpeername(src, (struct sockaddr *)&data4, &n); + if (error == -1) + goto passivefail1; /*XXX*/ + sin6 = (struct sockaddr_in6 *)&data4; + sin6->sin6_port = htons(port); + + /* get ready for passive data connection */ + memset(&data6, 0, sizeof(data6)); + sin = (struct sockaddr_in *)&data6; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + wport6 = socket(sin->sin_family, SOCK_STREAM, 0); + if (wport6 == -1) { +passivefail2: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate from EPSV\r\n"); + write(src, sbuf, n); + return n; + } +#ifdef IP_FAITH + { + int on = 1; + error = setsockopt(wport6, IPPROTO_IP, IP_FAITH, + &on, sizeof(on)); + if (error == -1) + exit_error("setsockopt(IP_FAITH): %s", ERRSTR); + } +#endif + error = bind(wport6, (struct sockaddr *)sin, sin->sin_len); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + error = listen(wport6, 1); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + + /* transmit PORT */ + /* + * addr from dst, port from wport6 + */ + n = sizeof(data6); + error = getsockname(wport6, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + sin = (struct sockaddr_in *)&data6; + port = sin->sin_port; + + n = sizeof(data6); + error = getsockname(dst, (struct sockaddr *)&data6, &n); + if (error == -1) { + close(wport6); + wport6 = -1; + goto passivefail2; + } + sin = (struct sockaddr_in *)&data6; + sin->sin_port = port; + + { + char *a, *p; + + a = (char *)&sin->sin_addr; + p = (char *)&sin->sin_port; + n = snprintf(sbuf, sizeof(sbuf), +"227 Entering Passive Mode (%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])); + write(dst, sbuf, n); + passivemode = 1; + return n; + } + } +#endif /* FAITH4 */ + } + + bad: + exit_failure(ERRSTR); + /*NOTREACHED*/ + return 0; /* to make gcc happy */ +} + +static int +ftp_copycommand(int src, int dst, enum state *state) +{ + int error, atmark; + int n; + unsigned int af, hal, ho[16], pal, po[2]; + char *a, *p; + char cmd[5], *param; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + enum state nstate; + char ch; + + /* OOB data handling */ + error = ioctl(src, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(src, rbuf, 1); + if (n == -1) + goto bad; + send(dst, rbuf, n, MSG_OOB); +#if 0 + n = read(src, rbuf, sizeof(rbuf)); + if (n == -1) + goto bad; + write(dst, rbuf, n); + return n; +#endif + } + + n = read(src, rbuf, sizeof(rbuf)); + if (n <= 0) + return n; + rbuf[n] = '\0'; + + if (n < 4) { + write(dst, rbuf, n); + return n; + } + + /* + * parse argument + */ + { + char *p, *q; + int i; + + p = rbuf; + q = cmd; + for (i = 0; i < 4; i++) { + if (!isalpha(*p)) { + /* invalid command */ + write(dst, rbuf, n); + return n; + } + *q++ = islower(*p) ? toupper(*p) : *p; + p++; + } + if (!isspace(*p)) { + /* invalid command */ + write(dst, rbuf, n); + return n; + } + *q = '\0'; + param = p; + /* param points to first non-command token, if any */ + while (*param && isspace(*param)) + param++; + if (!*param) + param = NULL; + } + + *state = NONE; + + if (strcmp(cmd, "LPRT") == 0 && param) { + /* + * LPRT -> PORT + */ + nstate = LPRT; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + if (epsvall) { + n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", + cmd); + write(src, sbuf, n); + return n; + } + + n = sscanf(param, +"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", + &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3], + &ho[4], &ho[5], &ho[6], &ho[7], + &ho[8], &ho[9], &ho[10], &ho[11], + &ho[12], &ho[13], &ho[14], &ho[15], + &pal, &po[0], &po[1]); + if (n != 21 || af != 6 || hal != 16|| pal != 2) { + n = snprintf(sbuf, sizeof(sbuf), + "501 illegal parameter to LPRT\r\n"); + write(src, sbuf, n); + return n; + } + + /* keep LPRT parameter */ + memset(&data6, 0, sizeof(data6)); + sin6 = (struct sockaddr_in6 *)&data6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + for (n = 0; n < 16; n++) + sin6->sin6_addr.s6_addr[n] = ho[n]; + sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); + +sendport: + /* get ready for active data connection */ + n = sizeof(data4); + error = getsockname(dst, (struct sockaddr *)&data4, &n); + if (error == -1) { +lprtfail: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate to PORT\r\n"); + write(src, sbuf, n); + return n; + } + if (((struct sockaddr *)&data4)->sa_family != AF_INET) + goto lprtfail; + sin = (struct sockaddr_in *)&data4; + sin->sin_port = 0; + wport4 = socket(sin->sin_family, SOCK_STREAM, 0); + if (wport4 == -1) + goto lprtfail; + error = bind(wport4, (struct sockaddr *)sin, sin->sin_len); + if (error == -1) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + error = listen(wport4, 1); + if (error == -1) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + + /* transmit PORT */ + n = sizeof(data4); + error = getsockname(wport4, (struct sockaddr *)&data4, &n); + if (error == -1) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + if (((struct sockaddr *)&data4)->sa_family != AF_INET) { + close(wport4); + wport4 = -1; + goto lprtfail; + } + sin = (struct sockaddr_in *)&data4; + a = (char *)&sin->sin_addr; + p = (char *)&sin->sin_port; + n = snprintf(sbuf, sizeof(sbuf), "PORT %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])); + write(dst, sbuf, n); + *state = nstate; + passivemode = 0; + return n; + } else if (strcmp(cmd, "EPRT") == 0 && param) { + /* + * EPRT -> PORT + */ + char *afp, *hostp, *portp; + struct addrinfo hints, *res; + + nstate = EPRT; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + if (epsvall) { + n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", + cmd); + write(src, sbuf, n); + return n; + } + + p = param; + ch = *p++; /* boundary character */ + afp = p; + while (*p && *p != ch) + p++; + if (!*p) { +eprtparamfail: + n = snprintf(sbuf, sizeof(sbuf), + "501 illegal parameter to EPRT\r\n"); + write(src, sbuf, n); + return n; + } + *p++ = '\0'; + hostp = p; + while (*p && *p != ch) + p++; + if (!*p) + goto eprtparamfail; + *p++ = '\0'; + portp = p; + while (*p && *p != ch) + p++; + if (!*p) + goto eprtparamfail; + *p++ = '\0'; + + n = sscanf(afp, "%d", &af); + if (n != 1 || af != 2) { + n = snprintf(sbuf, sizeof(sbuf), + "501 unsupported address family to EPRT\r\n"); + write(src, sbuf, n); + return n; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + error = getaddrinfo(hostp, portp, &hints, &res); + if (error) { + n = snprintf(sbuf, sizeof(sbuf), + "501 EPRT: %s", gai_strerror(error)); + if (error == EAI_SYSTEM) + n += snprintf(sbuf, sizeof(sbuf), + ": %s", strerror(errno)); + n += snprintf(sbuf, sizeof(sbuf), "\r\n"); + + write(src, sbuf, n); + return n; + } + if (res->ai_next) { + n = snprintf(sbuf, sizeof(sbuf), + "501 EPRT: %s resolved to multiple addresses\r\n", hostp); + write(src, sbuf, n); + return n; + } + + memcpy(&data6, res->ai_addr, res->ai_addrlen); + + goto sendport; + } else if (strcmp(cmd, "LPSV") == 0 && !param) { + /* + * LPSV -> PASV + */ + nstate = LPSV; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + if (epsvall) { + n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n", + cmd); + write(src, sbuf, n); + return n; + } + + /* transmit PASV */ + n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); + write(dst, sbuf, n); + *state = LPSV; + passivemode = 0; /* to be set to 1 later */ + return n; + } else if (strcmp(cmd, "EPSV") == 0 && !param) { + /* + * EPSV -> PASV + */ + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n"); + write(dst, sbuf, n); + *state = EPSV; + passivemode = 0; /* to be set to 1 later */ + return n; + } else if (strcmp(cmd, "EPSV") == 0 && param + && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) { + /* + * EPSV ALL + */ + epsvall = 1; + n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n"); + write(src, sbuf, n); + return n; +#ifdef FAITH4 + } else if (strcmp(cmd, "PORT") == 0 && param) { + /* + * PORT -> EPRT + */ + char host[NI_MAXHOST], serv[NI_MAXSERV]; + + nstate = PORT; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + p = param; + n = sscanf(p, "%u,%u,%u,%u,%u,%u", + &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]); + if (n != 6) { + n = snprintf(sbuf, sizeof(sbuf), + "501 illegal parameter to PORT\r\n"); + write(src, sbuf, n); + return n; + } + + memset(&data6, 0, sizeof(data6)); + sin = (struct sockaddr_in *)&data6; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = htonl( + ((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) | + ((ho[2] & 0xff) << 8) | (ho[3] & 0xff)); + sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff)); + + /* get ready for active data connection */ + n = sizeof(data4); + error = getsockname(dst, (struct sockaddr *)&data4, &n); + if (error == -1) { +portfail: + n = snprintf(sbuf, sizeof(sbuf), + "500 could not translate to EPRT\r\n"); + write(src, sbuf, n); + return n; + } + if (((struct sockaddr *)&data4)->sa_family != AF_INET6) + goto portfail; + + ((struct sockaddr_in6 *)&data4)->sin6_port = 0; + sa = (struct sockaddr *)&data4; + wport4 = socket(sa->sa_family, SOCK_STREAM, 0); + if (wport4 == -1) + goto portfail; + error = bind(wport4, sa, sa->sa_len); + if (error == -1) { + close(wport4); + wport4 = -1; + goto portfail; + } + error = listen(wport4, 1); + if (error == -1) { + close(wport4); + wport4 = -1; + goto portfail; + } + + /* transmit EPRT */ + n = sizeof(data4); + error = getsockname(wport4, (struct sockaddr *)&data4, &n); + if (error == -1) { + close(wport4); + wport4 = -1; + goto portfail; + } + af = 2; + sa = (struct sockaddr *)&data4; + if (getnameinfo(sa, sa->sa_len, host, sizeof(host), + serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) { + close(wport4); + wport4 = -1; + goto portfail; + } + n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv); + write(dst, sbuf, n); + *state = nstate; + passivemode = 0; + return n; + } else if (strcmp(cmd, "PASV") == 0 && !param) { + /* + * PASV -> EPSV + */ + + nstate = PASV; + + close(wport4); + close(wport6); + close(port4); + close(port6); + wport4 = wport6 = port4 = port6 = -1; + + /* transmit EPSV */ + n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n"); + write(dst, sbuf, n); + *state = PASV; + passivemode = 0; /* to be set to 1 later */ + return n; +#else /* FAITH4 */ + } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) { + /* + * reject PORT/PASV + */ + n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd); + write(src, sbuf, n); + return n; +#endif /* FAITH4 */ + } else if (passivemode + && (strcmp(cmd, "STOR") == 0 + || strcmp(cmd, "STOU") == 0 + || strcmp(cmd, "RETR") == 0 + || strcmp(cmd, "LIST") == 0 + || strcmp(cmd, "NLST") == 0 + || strcmp(cmd, "APPE") == 0)) { + /* + * commands with data transfer. need to care about passive + * mode data connection. + */ + + if (ftp_passiveconn() < 0) { + n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n"); + write(src, sbuf, n); + } else { + /* simply relay the command */ + write(dst, rbuf, n); + } + + *state = NONE; + return n; + } else { + /* simply relay it */ + *state = NONE; + write(dst, rbuf, n); + return n; + } + + bad: + exit_failure(ERRSTR); + /*NOTREACHED*/ + return 0; /* to make gcc happy */ +} diff --git a/usr.sbin/faithd/rsh.c b/usr.sbin/faithd/rsh.c new file mode 100644 index 000000000000..c6e83572be9a --- /dev/null +++ b/usr.sbin/faithd/rsh.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "faithd.h" + +char rshbuf[MSS]; + +int s_ctl, s_ctl6, s_rcv, s_snd; +int half; + +void +rsh_relay(int s_src, int s_dst) +{ + ssize_t n; + fd_set readfds; + int error; + struct timeval tv; + + FD_ZERO(&readfds); + FD_SET(s_src, &readfds); + tv.tv_sec = FAITH_TIMEOUT; + tv.tv_usec = 0; + error = select(256, &readfds, NULL, NULL, &tv); + if (error == -1) + exit_failure("select %d: %s", s_src, ERRSTR); + else if (error == 0) + exit_failure("connecion timeout"); + + n = read(s_src, rshbuf, sizeof(rshbuf)); + if (rshbuf[0] != 0) { + rsh_dual_relay(s_src, s_dst); + /* NOTREACHED */ + } + write(s_dst, rshbuf, n); + tcp_relay(s_src, s_dst, "rsh"); + /* NOTREACHED */ +} + +static void +relay(int src, int dst) +{ + int error; + ssize_t n; + int atmark; + + error = ioctl(s_rcv, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + n = read(s_rcv, rshbuf, 1); + if (n == 1) + send(s_snd, rshbuf, 1, MSG_OOB); + return; + } + + n = read(s_rcv, rshbuf, sizeof(rshbuf)); + + switch (n) { + case -1: + exit_failure(ERRSTR); + case 0: + if (s_rcv == src) { + /* half close */ + shutdown(dst, 1); + half = YES; + break; + } + close(src); + close(dst); + close(s_ctl); + close(s_ctl6); + exit_success("terminating rsh/contorol connections"); + break; + default: + write(s_snd, rshbuf, n); + } +} + +void +rsh_dual_relay(int s_src, int s_dst) +{ + fd_set readfds; + int len, s_wld, error; + struct sockaddr_storage ctladdr6; + struct sockaddr_storage ctladdr; + int port6 = 0, lport, lport6; + char *p; + struct timeval tv; + struct sockaddr *sa; + + half = NO; + s_rcv = s_src; + s_snd = s_dst; + syslog(LOG_INFO, "starting rsh connection"); + + for (p = rshbuf; *p; p++) + port6 = port6 * 10 + *p - '0'; + + len = sizeof(ctladdr6); + getpeername(s_src, (struct sockaddr *)&ctladdr6, &len); + if (((struct sockaddr *)&ctladdr6)->sa_family == AF_INET6) + ((struct sockaddr_in6 *)&ctladdr6)->sin6_port = htons(port6); + else + ((struct sockaddr_in *)&ctladdr6)->sin_port = htons(port6); + + s_wld = rresvport(&lport); + if (s_wld == -1) goto bad; + error = listen(s_wld, 1); + if (error == -1) goto bad; + snprintf(rshbuf, sizeof(rshbuf), "%d", lport); + write(s_dst, rshbuf, strlen(rshbuf)+1); + + len = sizeof(ctladdr); + s_ctl = accept(s_wld, (struct sockaddr *)&ctladdr, &len); + if (s_ctl == -1) goto bad; + close(s_wld); + + sa = (struct sockaddr *)&ctladdr6; + s_ctl6 = rresvport_af(&lport6, sa->sa_family); + if (s_ctl6 == -1) goto bad; + error = connect(s_ctl6, sa, sa->sa_len); + if (error == -1) goto bad; + + syslog(LOG_INFO, "starting rsh control connection"); + + for (;;) { + FD_ZERO(&readfds); + if (half == NO) + FD_SET(s_src, &readfds); + FD_SET(s_dst, &readfds); + FD_SET(s_ctl, &readfds); + FD_SET(s_ctl6, &readfds); + tv.tv_sec = FAITH_TIMEOUT; + tv.tv_usec = 0; + + error = select(256, &readfds, NULL, NULL, &tv); + if (error == -1) + exit_failure("select 4 sockets: %s", ERRSTR); + else if (error == 0) + exit_failure("connecion timeout"); + + if (half == NO && FD_ISSET(s_src, &readfds)) { + s_rcv = s_src; + s_snd = s_dst; + relay(s_src, s_dst); + } + if (FD_ISSET(s_dst, &readfds)) { + s_rcv = s_dst; + s_snd = s_src; + relay(s_src, s_dst); + } + if (FD_ISSET(s_ctl, &readfds)) { + s_rcv = s_ctl; + s_snd = s_ctl6; + relay(s_src, s_dst); + } + if (FD_ISSET(s_ctl6, &readfds)) { + s_rcv = s_ctl6; + s_snd = s_ctl; + relay(s_src, s_dst); + } + } + /* NOTREACHED */ + + bad: + exit_failure(ERRSTR); +} diff --git a/usr.sbin/faithd/tcp.c b/usr.sbin/faithd/tcp.c new file mode 100644 index 000000000000..e1e1b328c2b1 --- /dev/null +++ b/usr.sbin/faithd/tcp.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "faithd.h" + +static char tcpbuf[16*1024]; + /* bigger than MSS and may be lesser than window size */ +static int tblen, tboff, oob_exists; +static fd_set readfds, writefds, exceptfds; +static char atmark_buf[2]; +static pid_t cpid = (pid_t)0; +static pid_t ppid = (pid_t)0; +static time_t child_lastactive = (time_t)0; +static time_t parent_lastactive = (time_t)0; + +static void sig_ctimeout __P((int)); +static void sig_child __P((int)); +static void notify_inactive __P((void)); +static void notify_active __P((void)); +static void send_data __P((int, int, const char *, int)); +static void relay __P((int, int, const char *, int)); + +/* + * Inactivity timer: + * - child side (ppid != 0) will send SIGUSR1 to parent every (FAITH_TIMEOUT/4) + * second if traffic is active. if traffic is inactive, don't send SIGUSR1. + * - parent side (ppid == 0) will check the last SIGUSR1 it have seen. + */ +static void +sig_ctimeout(int sig) +{ + /* parent side: record notification from the child */ + if (dflag) + syslog(LOG_DEBUG, "activity timer from child"); + child_lastactive = time(NULL); +} + +/* parent will terminate if child dies. */ +static void +sig_child(int sig) +{ + int status; + pid_t pid; + + pid = wait3(&status, WNOHANG, (struct rusage *)0); + if (pid && status) + syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status); + exit_failure("terminate connection due to child termination"); +} + +static void +notify_inactive() +{ + time_t t; + + /* only on parent side... */ + if (ppid) + return; + + /* parent side should check for timeout. */ + t = time(NULL); + if (dflag) { + syslog(LOG_DEBUG, "parent side %sactive, child side %sactive", + (FAITH_TIMEOUT < t - parent_lastactive) ? "in" : "", + (FAITH_TIMEOUT < t - child_lastactive) ? "in" : ""); + } + + if (FAITH_TIMEOUT < t - child_lastactive + && FAITH_TIMEOUT < t - parent_lastactive) { + /* both side timeouted */ + signal(SIGCHLD, SIG_DFL); + kill(cpid, SIGTERM); + wait(NULL); + exit_failure("connection timeout"); + /* NOTREACHED */ + } +} + +static void +notify_active() +{ + if (ppid) { + /* child side: notify parent of active traffic */ + time_t t; + t = time(NULL); + if (FAITH_TIMEOUT / 4 < t - child_lastactive) { + if (kill(ppid, SIGUSR1) < 0) { + exit_failure("terminate connection due to parent termination"); + /* NOTREACHED */ + } + child_lastactive = t; + } + } else { + /* parent side */ + parent_lastactive = time(NULL); + } +} + +static void +send_data(int s_rcv, int s_snd, const char *service, int direction) +{ + int cc; + + if (oob_exists) { + cc = send(s_snd, atmark_buf, 1, MSG_OOB); + if (cc == -1) + goto retry_or_err; + oob_exists = 0; + FD_SET(s_rcv, &exceptfds); + } + + for (; tboff < tblen; tboff += cc) { + cc = write(s_snd, tcpbuf + tboff, tblen - tboff); + if (cc < 0) + goto retry_or_err; + } +#ifdef DEBUG + if (tblen) { + if (tblen >= sizeof(tcpbuf)) + tblen = sizeof(tcpbuf) - 1; + tcpbuf[tblen] = '\0'; + syslog(LOG_DEBUG, "from %s (%dbytes): %s", + direction == 1 ? "client" : "server", tblen, tcpbuf); + } +#endif /* DEBUG */ + tblen = 0; tboff = 0; + FD_CLR(s_snd, &writefds); + FD_SET(s_rcv, &readfds); + return; + retry_or_err: + if (errno != EAGAIN) + exit_failure("writing relay data failed: %s", ERRSTR); + FD_SET(s_snd, &writefds); +} + +static void +relay(int s_rcv, int s_snd, const char *service, int direction) +{ + int atmark, error, maxfd; + struct timeval tv; + fd_set oreadfds, owritefds, oexceptfds; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + fcntl(s_snd, F_SETFD, O_NONBLOCK); + oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds; + FD_SET(s_rcv, &readfds); FD_SET(s_rcv, &exceptfds); + oob_exists = 0; + maxfd = (s_rcv > s_snd) ? s_rcv : s_snd; + + for (;;) { + tv.tv_sec = FAITH_TIMEOUT / 4; + tv.tv_usec = 0; + oreadfds = readfds; + owritefds = writefds; + oexceptfds = exceptfds; + error = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv); + if (error == -1) { + if (errno == EINTR) + continue; + exit_failure("select: %s", ERRSTR); + } else if (error == 0) { + readfds = oreadfds; + writefds = owritefds; + exceptfds = oexceptfds; + notify_inactive(); + continue; + } + + /* activity notification */ + notify_active(); + + if (FD_ISSET(s_rcv, &exceptfds)) { + error = ioctl(s_rcv, SIOCATMARK, &atmark); + if (error != -1 && atmark == 1) { + int cc; + oob_read_retry: + cc = read(s_rcv, atmark_buf, 1); + if (cc == 1) { + FD_CLR(s_rcv, &exceptfds); + FD_SET(s_snd, &writefds); + oob_exists = 1; + } else if (cc == -1) { + if (errno == EINTR) + goto oob_read_retry; + exit_failure("reading oob data failed" + ": %s", + ERRSTR); + } + } + } + if (FD_ISSET(s_rcv, &readfds)) { + relaydata_read_retry: + tblen = read(s_rcv, tcpbuf, sizeof(tcpbuf)); + tboff = 0; + + switch (tblen) { + case -1: + if (errno == EINTR) + goto relaydata_read_retry; + exit_failure("reading relay data failed: %s", + ERRSTR); + /* NOTREACHED */ + case 0: + /* to close opposite-direction relay process */ + shutdown(s_snd, 0); + + close(s_rcv); + close(s_snd); + exit_success("terminating %s relay", service); + /* NOTREACHED */ + default: + FD_CLR(s_rcv, &readfds); + FD_SET(s_snd, &writefds); + break; + } + } + if (FD_ISSET(s_snd, &writefds)) + send_data(s_rcv, s_snd, service, direction); + } +} + +void +tcp_relay(int s_src, int s_dst, const char *service) +{ + syslog(LOG_INFO, "starting %s relay", service); + + child_lastactive = parent_lastactive = time(NULL); + + cpid = fork(); + switch (cpid) { + case -1: + exit_failure("tcp_relay: can't fork grand child: %s", ERRSTR); + /* NOTREACHED */ + case 0: + /* child process: relay going traffic */ + ppid = getppid(); + /* this is child so reopen log */ + closelog(); + openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON); + relay(s_src, s_dst, service, 1); + /* NOTREACHED */ + default: + /* parent process: relay coming traffic */ + ppid = (pid_t)0; + signal(SIGUSR1, sig_ctimeout); + signal(SIGCHLD, sig_child); + relay(s_dst, s_src, service, 0); + /* NOTREACHED */ + } +} diff --git a/usr.sbin/faithd/test/faithd.rb b/usr.sbin/faithd/test/faithd.rb new file mode 100644 index 000000000000..683b5041adc1 --- /dev/null +++ b/usr.sbin/faithd/test/faithd.rb @@ -0,0 +1,312 @@ +# faithd, ruby version. requires v6-enabled ruby. +# +# highly experimental (not working right at all) and very limited +# functionality. +# +# $Id: faithd.rb,v 1.1.1.1 1999/08/08 23:29:31 itojun Exp $ +# $FreeBSD$ + +require "socket" +require "thread" + +# XXX should be derived from system headers +IPPROTO_IPV6 = 41 +IPV6_FAITH = 29 +DEBUG = true +DEBUG_LOOPBACK = true + +# TODO: OOB data handling +def tcpcopy(s1, s2, m) + STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG + buf = "" + while TRUE + begin + buf = s1.sysread(100) + s2.syswrite(buf) + rescue EOFError + break + rescue IOError + break + end + end + STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG + s1.shutdown(0) + s2.shutdown(1) +end + +def relay_ftp_passiveconn(s6, s4, dport6, dport4) + Thread.start do + d6 = TCPserver.open("::", dport6).accept + d4 = TCPsocket.open(s4.getpeer[3], dport4) + t = [] + t[0] = Thread.start do + tcpcopy(d6, d4) + end + t[1] = Thread.start do + tcpcopy(d4, d6) + end + for i in t + i.join + end + d4.close + d6.close + end +end + +def ftp_parse_2428(line) + if (line[0] != line[line.length - 1]) + return nil + end + t = line.split(line[0 .. 0]) # as string + if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/) + return nil + end + return t[1 .. 3] +end + +def relay_ftp_command(s6, s4, state) + STDERR.print "relay_ftp_command start\n" if DEBUG + while TRUE + begin + STDERR.print "s6.gets\n" if DEBUG + line = s6.gets + STDERR.print "line is #{line}\n" if DEBUG + if line == nil + return nil + end + + # translate then copy + STDERR.print "line is #{line}\n" if DEBUG + if (line =~ /^EPSV\r\n/i) + STDERR.print "EPSV -> PASV\n" if DEBUG + line = "PASV\n" + state = "EPSV" + elsif (line =~ /^EPRT\s+(.+)\r\n/i) + t = ftp_parse_2428($1) + if t == nil + s6.puts "501 illegal parameter to EPRT\r\n" + next + end + + # some tricks should be here + s6.puts "501 illegal parameter to EPRT\r\n" + next + end + STDERR.print "fail: send #{line} as is\n" if DEBUG + s4.puts(line) + break + rescue EOFError + return nil + rescue IOError + return nil + end + end + STDERR.print "relay_ftp_command finish\n" if DEBUG + return state +end + +def relay_ftp_status(s4, s6, state) + STDERR.print "relay_ftp_status start\n" if DEBUG + while TRUE + begin + line = s4.gets + if line == nil + return nil + end + + # translate then copy + s6.puts(line) + + next if line =~ /^\d\d\d-/ + next if line !~ /^\d/ + + # special post-processing + case line + when /^221 / # result to QUIT + s4.shutdown(0) + s6.shutdown(1) + end + + break if (line =~ /^\d\d\d /) + rescue EOFError + return nil + rescue IOError + return nil + end + end + STDERR.print "relay_ftp_status finish\n" if DEBUG + return state +end + +def relay_ftp(sock, name) + STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG + while TRUE + STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG + s = sock.accept + STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG + Thread.start do + threads = [] + STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG + s6 = s + dest6 = s.addr[3] + if !DEBUG_LOOPBACK + t = s.getsockname.unpack("x8 x12 C4") + dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" + port4 = s.addr[1] + else + dest4 = "127.0.0.1" + port4 = "ftp" + end + if DEBUG + STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG + end + STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG + s4 = TCPsocket.open(dest4, port4) + STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG + state = 0 + while TRUE + # translate status line + state = relay_ftp_status(s4, s6, state) + break if state == nil + # translate command line + state = relay_ftp_command(s6, s4, state) + break if state == nil + end + STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG + s4.close + STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG + s6.close + STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG + end + end + STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG +end + +def relay_tcp(sock, name) + STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG + while TRUE + STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG + s = sock.accept + STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG + Thread.start do + threads = [] + STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG + s6 = s + dest6 = s.addr[3] + if !DEBUG_LOOPBACK + t = s.getsockname.unpack("x8 x12 C4") + dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}" + port4 = s.addr[1] + else + dest4 = "127.0.0.1" + port4 = "telnet" + end + if DEBUG + STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG + end + STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG + s4 = TCPsocket.open(dest4, port4) + STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG + [0, 1].each do |i| + threads[i] = Thread.start do + if (i == 0) + tcpcopy(s6, s4) + else + tcpcopy(s4, s6) + end + end + end + STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG + for i in threads + STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG + i.join + STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG + end + STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG + s4.close + STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG + s6.close + STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG + end + end + STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG +end + +def usage() + STDERR.print "usage: #{$0} [-f] port...\n" +end + +#------------------------------------------------------------ + +$mode = "tcp" + +while ARGV[0] =~ /^-/ do + case ARGV[0] + when /^-f/ + $mode = "ftp" + else + usage() + exit 0 + end + ARGV.shift +end + +if ARGV.length == 0 + usage() + exit 1 +end + +ftpport = Socket.getservbyname("ftp") + +res = [] +for port in ARGV + t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM, + nil, Socket::AI_PASSIVE) + if (t.size <= 0) + STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n" + exit 1 + end + res += t +end + +sockpool = [] +names = [] +listenthreads = [] + +res.each do |i| + s = TCPserver.new(i[3], i[1]) + n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ") + if i[6] == IPPROTO_IPV6 + s.setsockopt(i[6], IPV6_FAITH, 1) + end + s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) + sockpool.push s + names.push n +end + +if DEBUG + (0 .. sockpool.size - 1).each do |i| + STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG + end +end + +(0 .. sockpool.size - 1).each do |i| + listenthreads[i] = Thread.start do + if DEBUG + STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG + end + STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG + case $mode + when "tcp" + relay_tcp(sockpool[i], names[i]) + when "ftp" + relay_ftp(sockpool[i], names[i]) + end + end +end + +for i in listenthreads + i.join +end + +exit 0