Add more dual stack consideration.

-Should retry as much as possible when some of source
     routing intermediate hosts' address families missmatch
     happened.
     (such as when a host has only A record, and another host
     has each of A and AAAA record.)

    -Should retry as much as possible when dest addr and
     source addr(specified with -s option) address family
     missmatch happend

Approved by: jkh


git-svn-id: http://svn0.us-east.freebsd.org/base/head/contrib/telnet@57125 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f
This commit is contained in:
shin 2000-02-10 20:06:36 +00:00
parent 92a0e6dadf
commit d2ec198a69
1 changed files with 95 additions and 27 deletions

View File

@ -108,6 +108,7 @@ extern int Ambiguous();
static int help(int argc, char *argv[]);
static int call();
static void cmdrc(char *m1, char *m2);
static int switch_af(struct addrinfo **aip);
int quit(void);
@ -2324,6 +2325,32 @@ setpolicy(net, res, policy)
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
@ -2331,18 +2358,14 @@ tn(argc, argv)
int argc;
char *argv[];
{
struct sockaddr_storage ss, src_ss;
char *srp = 0, *strrchr();
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 *)&ss, 0, sizeof(ss));
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);
@ -2406,19 +2429,19 @@ tn(argc, argv)
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(src_addr, 0, &hints, &res);
error = getaddrinfo(src_addr, 0, &hints, &src_res);
if (error == EAI_NONAME) {
hints.ai_flags = 0;
error = getaddrinfo(src_addr, 0, &hints, &res);
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;
}
memcpy((void *)&src_ss, (void *)res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
src_res0 = src_res;
}
if (hostp[0] == '@' || hostp[0] == '!') {
if (
@ -2464,9 +2487,8 @@ tn(argc, argv)
if (error == EAI_SYSTEM)
fprintf(stderr, "%s: %s\n", hostname, strerror(errno));
setuid(getuid());
return 0;
goto fail;
}
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)
@ -2480,20 +2502,33 @@ tn(argc, argv)
if (error == EAI_SYSTEM)
fprintf(stderr, "%s: %s\n", hostname, strerror(errno));
setuid(getuid());
return 0;
goto fail;
}
res0 = res;
if (srcroute != 0) {
char hostbuf[BUFSIZ];
strncpy(hostbuf, hostp, BUFSIZ - 1);
hostbuf[BUFSIZ - 1] = '\0';
af_again:
if (af_error != 0)
hostp = hostbuf;
srp = 0;
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());
freeaddrinfo(res);
return 0;
goto fail;
} else if (result == -1) {
printf("Bad source route option: %s\n", hostp);
setuid(getuid());
freeaddrinfo(res);
return 0;
goto fail;
}
}
printf("Trying %s...\n", sockaddr_ntop(res->ai_addr));
@ -2501,8 +2536,15 @@ tn(argc, argv)
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 (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0)
perror("setsockopt (source route)");
@ -2528,40 +2570,60 @@ tn(argc, argv)
}
if (src_addr != NULL) {
if (bind(net, (struct sockaddr *)&src_ss,
((struct sockaddr *)&src_ss)->sa_len) == -1) {
for (src_res = src_res0; src_res != 0; src_res = src_res->ai_next)
if (src_res->ai_family != res->ai_family)
continue;
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;
goto af_again;
}
#endif
perror("bind");
return 0;
goto fail;
}
}
#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
if (setpolicy(net, res, ipsec_policy_in) < 0)
return 0;
goto fail;
if (setpolicy(net, res, ipsec_policy_out) < 0)
return 0;
goto fail;
#endif
if (connect(net, res->ai_addr, res->ai_addrlen) < 0) {
if (res->ai_next) {
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;
if (next != NULL) {
int oerrno = errno;
fprintf(stderr, "telnet: connect to address %s: ",
sockaddr_ntop(res->ai_addr));
errno = oerrno;
perror((char *)0);
res = res->ai_next;
res = next;
(void) NetClose(net);
continue;
}
perror("telnet: Unable to connect to remote host");
return 0;
goto fail;
}
connected++;
#if defined(AUTHENTICATION) || defined(ENCRYPTION)
auth_encrypt_connect(connected);
#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
} while (connected == 0);
freeaddrinfo(res);
freeaddrinfo(res0);
if (src_res0 != NULL)
freeaddrinfo(src_res0);
cmdrc(hostp, hostname);
if (autologin && user == NULL) {
struct passwd *pw;
@ -2585,6 +2647,12 @@ tn(argc, argv)
(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"))