--- commands.c.orig Mon Dec 28 09:42:46 1998 +++ commands.c Fri Aug 4 17:13:25 2000 @@ -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: /tmp/pcvs/ports/chinese/telnet/files/patch-aa,v 1.1 2000-08-04 16:33:54 kevlo Exp $ */ #ifndef lint @@ -50,6 +52,7 @@ #include #endif /* CRAY */ +#include #include #include #include @@ -58,6 +61,7 @@ #include #include +#include #include "general.h" @@ -74,7 +78,7 @@ # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include - +#include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 @@ -93,6 +97,8 @@ extern char **genget(); extern int Ambiguous(); +static int switch_af(struct addrinfo **aip); + static call(); typedef struct { @@ -2092,26 +2098,98 @@ } #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 + +#ifdef INET6 +/* + * When an Address Family related error happend, check if retry with + * another AF is possible or not. + * Return 1, if retry with another af is OK. Else, return 0. + */ +static int +switch_af(aip) + struct addrinfo **aip; +{ + int nextaf; + struct addrinfo *ai; + + ai = *aip; + nextaf = (ai->ai_family == AF_INET) ? AF_INET6 : AF_INET; + do + ai=ai->ai_next; + while (ai != NULL && ai->ai_family != nextaf); + *aip = ai; + if (*aip != NULL) { + return 1; + } + return 0; +} +#endif int tn(argc, argv) int argc; char *argv[]; { - register struct hostent *host = 0; - struct sockaddr_in sin; - struct servent *sp = 0; - unsigned long temp; - extern char *inet_ntoa(); -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) 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; - - /* clear the socket address prior to use */ - bzero((char *)&sin, sizeof(sin)); + char *src_addr = NULL; + struct addrinfo hints, *res, *res0 = NULL, *src_res, *src_res0 = NULL; + int error = 0, af_error = 0; if (connected) { printf("?Already connected to %s\n", hostname); @@ -2144,6 +2222,14 @@ autologin = 1; continue; } + if (strcmp(*argv, "-s") == 0) { + --argc; ++argv; + if (argc == 0) + goto usage; + src_addr = *argv++; + --argc; + continue; + } if (hostp == 0) { hostp = *argv++; --argc; @@ -2155,116 +2241,138 @@ continue; } usage: - printf("usage: telnet [-l user] [-a] host-name [port]\n"); + printf("usage: telnet [-l user] [-a] [-s src_addr] host-name [port]\n"); setuid(getuid()); return 0; } if (hostp == 0) goto usage; -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + if (src_addr != NULL) { + 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, &src_res); + if (error == EAI_NODATA) { + hints.ai_flags = 0; + error = getaddrinfo(src_addr, 0, &hints, &src_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)); + setuid(getuid()); + return 0; + } + src_res0 = src_res; + } 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++; + 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) { + 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()); + goto fail; + } + if (hints.ai_flags == AI_NUMERICHOST) { + /* hostname has numeric */ + int gni_err = 1; + + if (doaddrlookup) + gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len, + _hostname, sizeof(_hostname) - 1, NULL, 0, + NI_NAMEREQD); + if (gni_err != 0) + (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1); + _hostname[sizeof(_hostname)-1] = '\0'; + hostname = _hostname; + } else { + /* hostname has FQDN */ + 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; + } + res0 = res; + af_again: + if (srcroute != 0) { + static char hostbuf[BUFSIZ]; + + if (af_error == 0) { /* save intermediate hostnames for retry */ + strncpy(hostbuf, hostp, BUFSIZ - 1); + hostbuf[BUFSIZ - 1] = '\0'; + } else + hostp = hostbuf; srp = 0; - temp = sourceroute(hostp, &srp, &srlen); - if (temp == 0) { - herror(srp); + result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt); + if (result == 0) { +#ifdef INET6 + if (family == AF_UNSPEC && af_error == 0 && + switch_af(&res) == 1) { + af_error = 1; + goto af_again; + } +#endif setuid(getuid()); - return 0; - } else if (temp == -1) { + goto fail; + } else if (result == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); - return 0; - } else { - sin.sin_addr.s_addr = temp; - sin.sin_family = AF_INET; + goto fail; } - } 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)); do { - net = socket(AF_INET, SOCK_STREAM, 0); + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setuid(getuid()); if (net < 0) { +#ifdef INET6 + if (family == AF_UNSPEC && af_error == 0 && + switch_af(&res) == 1) { + af_error = 1; + goto af_again; + } +#endif perror("telnet: socket"); - return 0; + goto fail; } -#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"))) @@ -2284,30 +2392,63 @@ perror("setsockopt (SO_DEBUG)"); } - 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]) { - int oerrno = errno; - - fprintf(stderr, "telnet: connect to address %s: ", - inet_ntoa(sin.sin_addr)); - errno = oerrno; - perror((char *)0); - host->h_addr_list++; - memcpy((caddr_t)&sin.sin_addr, - host->h_addr_list[0], host->h_length); + if (src_addr != NULL) { + for (src_res = src_res0; src_res != 0; src_res = src_res->ai_next) + if (src_res->ai_family == res->ai_family) + break; + if (src_res == NULL) + src_res = src_res0; + if (bind(net, src_res->ai_addr, src_res->ai_addrlen) == -1) { +#ifdef INET6 + if (family == AF_UNSPEC && af_error == 0 && + switch_af(&res) == 1) { + af_error = 1; + (void) NetClose(net); + goto af_again; + } +#endif + perror("bind"); + (void) NetClose(net); + goto fail; + } + } +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) { + (void) NetClose(net); + goto fail; + } + if (setpolicy(net, res, ipsec_policy_out) < 0) { + (void) NetClose(net); + goto fail; + } +#endif + + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + struct addrinfo *next; + + next = res->ai_next; + /* If already an af failed, only try same af. */ + if (af_error != 0) + while (next != NULL && next->ai_family != res->ai_family) + next = next->ai_next; + warn("connect to address %s", sockaddr_ntop(res->ai_addr)); + if (next != NULL) { + res = next; (void) NetClose(net); continue; } -#endif /* defined(h_addr) */ - perror("telnet: Unable to connect to remote host"); - return 0; + warnx("Unable to connect to remote host"); + (void) NetClose(net); + goto fail; } connected++; #if defined(AUTHENTICATION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) */ } while (connected == 0); + freeaddrinfo(res0); + if (src_res0 != NULL) + freeaddrinfo(src_res0); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; @@ -2331,6 +2472,12 @@ (void) NetClose(net); ExitString("Connection closed by foreign host.\n",1); /*NOTREACHED*/ + fail: + if (res0 != NULL) + freeaddrinfo(res0); + if (src_res0 != NULL) + freeaddrinfo(src_res0); + return 0; } #define HELPINDENT (sizeof ("connect")) @@ -2646,8 +2793,6 @@ fclose(rcfile); } -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) - /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst @@ -2661,6 +2806,10 @@ * 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 @@ -2670,9 +2819,18 @@ * 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 @@ -2686,21 +2844,32 @@ * *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 + ALIGNBYTES]; /*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; /* @@ -2708,23 +2877,46 @@ * at least 7 bytes for the option. */ if (cpp == NULL || lenp == NULL) - return((unsigned long)-1); - if (*cpp != NULL && *lenp < 7) - return((unsigned long)-1); + return -1; + if (*cpp != NULL) { + switch (res->ai_family) { + case AF_INET: + if (*lenp < 7) + return -1; + break; +#ifdef INET6 + case AF_INET6: + if (*lenp < CMSG_SPACE(sizeof(struct ip6_rthdr) + + sizeof(struct in6_addr))) + return -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 = (char *)ALIGN(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 @@ -2745,19 +2937,26 @@ #endif if (*cp != '@') - return((unsigned long)-1); + return -1; #ifndef sysV88 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 == ',') { @@ -2766,7 +2965,11 @@ cp2++; } else if (c == '@') { *cp2++ = '\0'; - } else if (c == ':') { + } else if ( +#ifdef INET6 + ai->ai_family != AF_INET6 && +#endif + c == ':') { *cp2++ = '\0'; } else continue; @@ -2775,21 +2978,32 @@ 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], host->h_length); -#else - memcpy((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_NODATA) { + 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 @@ -2797,14 +3011,31 @@ /* * Check to make sure there is space for next address */ - if (lsrp + 4 > lsrep) - return((unsigned long)-1); - } +#ifdef INET6 + if (res->ai_family == AF_INET6) { + if (((char *)CMSG_DATA(cmsg) + + sizeof(struct ip6_rthdr) + + ((inet6_rthdr_segments(cmsg) + 1) * + sizeof(struct in6_addr))) > ep) + return -1; + } else +#endif + if (lsrp + 4 > ep) + return -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; *lenp = 0; - return((unsigned long)-1); + return -1; } *lsrp++ = IPOPT_NOP; /* 32 bit word align it */ *lenp = lsrp - *cpp; @@ -2813,11 +3044,12 @@ if (ipopt.io_len <= 5) { /* Is 3 better ? */ *cpp = 0; *lenp = 0; - return((unsigned long)-1); + return -1; } *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif - return(sin_addr.s_addr); + } + freeaddrinfo(res); + return 1; } -#endif