summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/telnet/telnet/commands.c428
-rw-r--r--contrib/telnet/telnet/externs.h10
-rw-r--r--contrib/telnet/telnet/main.c45
-rw-r--r--contrib/telnet/telnet/telnet.112
-rw-r--r--contrib/telnet/telnetd/telnetd.85
-rw-r--r--contrib/telnet/telnetd/telnetd.c90
-rw-r--r--crypto/telnet/telnet/commands.c428
-rw-r--r--crypto/telnet/telnet/externs.h10
-rw-r--r--crypto/telnet/telnet/main.c45
-rw-r--r--crypto/telnet/telnet/telnet.112
-rw-r--r--crypto/telnet/telnetd/telnetd.85
-rw-r--r--crypto/telnet/telnetd/telnetd.c90
-rw-r--r--kerberos5/libexec/telnetd/Makefile2
-rw-r--r--kerberos5/usr.bin/telnet/Makefile6
-rw-r--r--kerberosIV/libexec/telnetd/Makefile2
-rw-r--r--kerberosIV/usr.bin/telnet/Makefile6
-rw-r--r--libexec/ftpd/Makefile1
-rw-r--r--libexec/ftpd/extern.h16
-rw-r--r--libexec/ftpd/ftpcmd.y364
-rw-r--r--libexec/ftpd/ftpd.823
-rw-r--r--libexec/ftpd/ftpd.c503
-rw-r--r--libexec/ftpd/logwtmp.c23
-rw-r--r--libexec/ftpd/popen.c1
-rw-r--r--secure/libexec/telnetd/Makefile2
-rw-r--r--secure/usr.bin/telnet/Makefile3
-rw-r--r--usr.bin/ftp/Makefile1
-rw-r--r--usr.bin/ftp/extern.h2
-rw-r--r--usr.bin/ftp/fetch.c107
-rw-r--r--usr.bin/ftp/ftp.122
-rw-r--r--usr.bin/ftp/ftp.c416
-rw-r--r--usr.bin/ftp/ftp_var.h11
-rw-r--r--usr.bin/ftp/main.c69
-rw-r--r--usr.bin/ftp/util.c17
-rw-r--r--usr.bin/telnet/Makefile6
-rw-r--r--usr.bin/telnet/commands.c428
-rw-r--r--usr.bin/telnet/externs.h10
-rw-r--r--usr.bin/telnet/main.c46
-rw-r--r--usr.bin/telnet/telnet.111
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/faithd/Makefile24
-rw-r--r--usr.sbin/faithd/README140
-rw-r--r--usr.sbin/faithd/faithd.8256
-rw-r--r--usr.sbin/faithd/faithd.c837
-rw-r--r--usr.sbin/faithd/faithd.h70
-rw-r--r--usr.sbin/faithd/ftp.c1139
-rw-r--r--usr.sbin/faithd/rsh.c210
-rw-r--r--usr.sbin/faithd/tcp.c300
-rw-r--r--usr.sbin/faithd/test/faithd.rb312
48 files changed, 5707 insertions, 860 deletions
diff --git a/contrib/telnet/telnet/commands.c b/contrib/telnet/telnet/commands.c
index f6bece2..dd83669 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 <netinet/ip.h>
+#include <netinet/ip6.h>
#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");
+ 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_sin.sin_addr, (void *)host->h_addr_list[0],
- sizeof(src_sin.sin_addr));
}
+ 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++;
+ 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;
+ }
+ 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;
- temp = sourceroute(hostp, &srp, &srlen);
- if (temp == 0) {
- herror(srp);
+ result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt);
+ if (result == 0) {
setuid(getuid());
+ freeaddrinfo(res);
return 0;
- } else if (temp == -1) {
+ } 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 0c6894f..46253d9 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 <strings.h>
#endif
+#if defined(IPSEC)
+#include <netinet6/ipsec.h>
+#if defined(IPSEC_POLICY_IPSEC)
+extern char *ipsec_policy_in;
+extern char *ipsec_policy_out;
+#endif
+#endif
+
#ifndef _POSIX_VDISABLE
# ifdef sun
# include <sys/param.h> /* 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 2dc05fd..c3d0f5d 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 <sys/types.h>
+#include <sys/socket.h>
#include <stdlib.h>
#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 9841462..3378d3a 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 e03f290..4e004a4 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 57e6ed2..6d7bcde 100644
--- a/contrib/telnet/telnetd/telnetd.c
+++ b/contrib/telnet/telnetd/telnetd.c
@@ -70,6 +70,12 @@ static const char rcsid[] =
#include <sys/secparm.h>
#include <sys/usrv.h>
# 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 <termcap.h>
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 f6bece2..dd83669 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 <netinet/ip.h>
+#include <netinet/ip6.h>
#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");
+ 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_sin.sin_addr, (void *)host->h_addr_list[0],
- sizeof(src_sin.sin_addr));
}
+ 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++;
+ 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;
+ }
+ 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;
- temp = sourceroute(hostp, &srp, &srlen);
- if (temp == 0) {
- herror(srp);
+ result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt);
+ if (result == 0) {
setuid(getuid());
+ freeaddrinfo(res);
return 0;
- } else if (temp == -1) {
+ } 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 0c6894f..46253d9 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 <strings.h>
#endif
+#if defined(IPSEC)
+#include <netinet6/ipsec.h>
+#if defined(IPSEC_POLICY_IPSEC)
+extern char *ipsec_policy_in;
+extern char *ipsec_policy_out;
+#endif
+#endif
+
#ifndef _POSIX_VDISABLE
# ifdef sun
# include <sys/param.h> /* 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 2dc05fd..c3d0f5d 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 <sys/types.h>
+#include <sys/socket.h>
#include <stdlib.h>
#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 9841462..3378d3a 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 e03f290..4e004a4 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 57e6ed2..6d7bcde 100644
--- a/crypto/telnet/telnetd/telnetd.c
+++ b/crypto/telnet/telnetd/telnetd.c
@@ -70,6 +70,12 @@ static const char rcsid[] =
#include <sys/secparm.h>
#include <sys/usrv.h>
# 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 <termcap.h>
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 4928c8d..221bdb4 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 eb41f26..fb5219b 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 <bsd.prog.mk>
diff --git a/kerberosIV/libexec/telnetd/Makefile b/kerberosIV/libexec/telnetd/Makefile
index 4928c8d..221bdb4 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 eb41f26..fb5219b 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 <bsd.prog.mk>
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile
index c23e85b..4ddfb53 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 9888cb6..684703e 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 e085e29..bb5bff9 100644
--- a/libexec/ftpd/ftpcmd.y
+++ b/libexec/ftpd/ftpcmd.y
@@ -58,6 +58,7 @@ static const char rcsid[] =
#include <ctype.h>
#include <errno.h>
#include <glob.h>
+#include <netdb.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
@@ -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 <i> check_login octal_number byte_size
%type <i> struct_code mode_code type_code form_code
-%type <s> pathstring pathname password username
+%type <s> pathstring pathname password username ext_arg
+%type <s> 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;
+ 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,
- "Illegal PORT range rejected.");
- } else {
- usedefault = 0;
- if (pdata >= 0) {
- (void) close(pdata);
- pdata = -1;
- }
- reply(200, "PORT command successful.");
+ "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, "<sp> b0, b1, b2, b3, b4" },
+ { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
+ { "EPRT", EPRT, STR1, 1, "<sp> |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, "[<sp> af|ALL]" },
{ "TYPE", TYPE, ARGS, 1, "<sp> [ 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 8f2cca7..fb10aa6 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 0f9d20c..cd72ed2 100644
--- a/libexec/ftpd/ftpd.c
+++ b/libexec/ftpd/ftpd.c
@@ -110,15 +110,20 @@ static const char rcsid[] =
static char version[] = "Version 6.00LS";
#undef main
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#endif
+
extern off_t restart_point;
extern char cbuf[];
-struct sockaddr_in server_addr;
-struct sockaddr_in ctrl_addr;
-struct sockaddr_in data_source;
-struct sockaddr_in data_dest;
-struct sockaddr_in his_addr;
-struct sockaddr_in pasv_addr;
+union sockunion server_addr;
+union sockunion ctrl_addr;
+union sockunion data_source;
+union sockunion data_dest;
+union sockunion his_addr;
+union sockunion pasv_addr;
int daemon_mode;
int data;
@@ -155,9 +160,11 @@ char *hostname;
#ifdef VIRTUAL_HOSTING
char *ftpuser;
+int epsvall = 0;
+
static struct ftphost {
struct ftphost *next;
- struct in_addr hostaddr;
+ union sockunion hostaddr;
char *hostname;
char *anonuser;
char *statfile;
@@ -176,7 +183,6 @@ char *tty = ttyline; /* for klogin */
static int auth_pam __P((struct passwd**, const char*));
#endif
-struct in_addr bind_address;
char *pid_file = NULL;
/*
@@ -200,7 +206,7 @@ char proctitle[LINE_MAX]; /* initial part of title */
#ifdef SKEY
int pwok = 0;
-char addr_string[20]; /* XXX */
+char addr_string[INET6_ADDRSTRLEN]; /* XXX */
#endif
#define LOGCMD(cmd, file) \
@@ -224,13 +230,13 @@ char addr_string[20]; /* XXX */
#ifdef VIRTUAL_HOSTING
static void inithosts __P((void));
-static void selecthost __P((struct in_addr *));
+static void selecthost __P((union sockunion *));
#endif
static void ack __P((char *));
static void myoob __P((int));
static int checkuser __P((char *, char *, int));
static FILE *dataconn __P((char *, off_t, char *));
-static void dolog __P((struct sockaddr_in *));
+static void dolog __P((struct sockaddr *));
static char *curdir __P((void));
static void end_login __P((void));
static FILE *getdatasock __P((char *));
@@ -266,6 +272,10 @@ main(argc, argv, envp)
int addrlen, ch, on = 1, tos;
char *cp, line[LINE_MAX];
FILE *fd;
+ int error;
+ char *bindname = NULL;
+ int family = AF_UNSPEC;
+ int enable_v4 = 0;
tzset(); /* in case no timezone database in ~ftp */
@@ -280,8 +290,7 @@ main(argc, argv, envp)
#endif /* OLD_SETPROCTITLE */
- bind_address.s_addr = htonl(INADDR_ANY);
- while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) {
+ while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:46")) != -1) {
switch (ch) {
case 'D':
daemon_mode++;
@@ -320,8 +329,7 @@ main(argc, argv, envp)
break;
case 'a':
- if (!inet_aton(optarg, &bind_address))
- errx(1, "invalid address for -a");
+ bindname = optarg;
break;
case 'p':
@@ -347,6 +355,16 @@ main(argc, argv, envp)
debug = 1;
break;
+ case '4':
+ enable_v4 = 1;
+ if (family == AF_UNSPEC)
+ family = AF_INET;
+ break;
+
+ case '6':
+ family = AF_INET6;
+ break;
+
default:
warnx("unknown flag -%c ignored", optopt);
break;
@@ -366,7 +384,7 @@ main(argc, argv, envp)
if (daemon_mode) {
int ctl_sock, fd;
- struct servent *sv;
+ struct addrinfo hints, *res;
/*
* Detach from parent.
@@ -376,30 +394,56 @@ main(argc, argv, envp)
exit(1);
}
(void) signal(SIGCHLD, reapchild);
- /*
- * Get port number for ftp/tcp.
- */
- sv = getservbyname("ftp", "tcp");
- if (sv == NULL) {
- syslog(LOG_ERR, "getservbyname for ftp failed");
+ /* init bind_sa */
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = family == AF_UNSPEC ? AF_INET : family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(bindname, "ftp", &hints, &res);
+ if (error) {
+ if (family == AF_UNSPEC) {
+ hints.ai_family = AF_UNSPEC;
+ error = getaddrinfo(bindname, "ftp", &hints,
+ &res);
+ }
+ if (error == 0 && res->ai_addr != NULL)
+ family = res->ai_addr->sa_family;
+ }
+ if (error) {
+ syslog(LOG_ERR, gai_strerror(error));
+ if (error == EAI_SYSTEM)
+ syslog(LOG_ERR, strerror(errno));
+ exit(1);
+ }
+ if (res->ai_addr == NULL) {
+ syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname);
exit(1);
}
/*
* Open a socket, bind it to the FTP port, and start
* listening.
*/
- ctl_sock = socket(AF_INET, SOCK_STREAM, 0);
+ ctl_sock = socket(family, SOCK_STREAM, 0);
if (ctl_sock < 0) {
syslog(LOG_ERR, "control socket: %m");
exit(1);
}
if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on)) < 0)
- syslog(LOG_ERR, "control setsockopt: %m");;
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr = bind_address;
- server_addr.sin_port = sv->s_port;
- if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
+ syslog(LOG_ERR, "control setsockopt: %m");
+#ifdef IPV6_BINDV6ONLY
+ if (family == AF_INET6 && enable_v4 == 0) {
+ if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char *)&on, sizeof (on)) < 0)
+ syslog(LOG_ERR,
+ "control setsockopt(IPV6_BINDV6ONLY): %m");
+ }
+#endif /* IPV6_BINDV6ONLY */
+ memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len);
+ if (bind(ctl_sock, (struct sockaddr *)&server_addr,
+ server_addr.su_len) < 0) {
syslog(LOG_ERR, "control bind: %m");
exit(1);
}
@@ -434,7 +478,7 @@ main(argc, argv, envp)
* children to handle them.
*/
while (1) {
- addrlen = sizeof(his_addr);
+ addrlen = server_addr.su_len;
fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen);
if (fork() == 0) {
/* child */
@@ -459,7 +503,9 @@ main(argc, argv, envp)
syslog(LOG_ERR, "signal: %m");
#ifdef SKEY
- strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string));
+ getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ addr_string, sizeof(addr_string) - 1, NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
#endif
addrlen = sizeof(ctrl_addr);
if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
@@ -468,12 +514,15 @@ main(argc, argv, envp)
}
#ifdef VIRTUAL_HOSTING
/* select our identity from virtual host table */
- selecthost(&ctrl_addr.sin_addr);
+ selecthost(&ctrl_addr);
#endif
#ifdef IP_TOS
+ if (ctrl_addr.su_family == AF_INET)
+ {
tos = IPTOS_LOWDELAY;
if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
#endif
/*
* Disable Nagle on the control channel so that we don't have to wait
@@ -482,7 +531,7 @@ main(argc, argv, envp)
if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m");
- data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+ data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
/* set this here so klogin can use it... */
(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
@@ -497,7 +546,7 @@ main(argc, argv, envp)
if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
syslog(LOG_ERR, "fcntl F_SETOWN: %m");
#endif
- dolog(&his_addr);
+ dolog((struct sockaddr *)&his_addr);
/*
* Set up default state
*/
@@ -567,9 +616,9 @@ inithosts()
{
FILE *fp;
char *cp;
- struct hostent *hp;
struct ftphost *hrp, *lhrp;
char line[1024];
+ struct addrinfo hints, *res, *ai;
/*
* Fill in the default host information
@@ -579,20 +628,28 @@ inithosts()
if ((hrp = malloc(sizeof(struct ftphost))) == NULL ||
(hrp->hostname = strdup(line)) == NULL)
fatal("Ran out of memory.");
- memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr);
- if ((hp = gethostbyname(hrp->hostname)) != NULL)
- (void) memcpy(&hrp->hostaddr,
- hp->h_addr_list[0],
- sizeof(hrp->hostaddr));
+ memset(&hrp->hostaddr, 0, sizeof(hrp->hostaddr));
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ getaddrinfo(hrp->hostname, NULL, &hints, &res);
+ if (res)
+ memcpy(&hrp->hostaddr, res->ai_addr, res->ai_addrlen);
hrp->statfile = _PATH_FTPDSTATFILE;
hrp->welcome = _PATH_FTPWELCOME;
hrp->loginmsg = _PATH_FTPLOGINMESG;
hrp->anonuser = "ftp";
hrp->next = NULL;
thishost = firsthost = lhrp = hrp;
+ freeaddrinfo(res);
if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
+ int addrsize, error;
+ void *addr;
+ struct hostent *hp;
+
while (fgets(line, sizeof(line), fp) != NULL) {
- int i;
+ int i, hp_error;
if ((cp = strchr(line, '\n')) == NULL) {
/* ignore long lines */
@@ -606,13 +663,21 @@ inithosts()
/* skip comments and empty lines */
if (cp == NULL || line[0] == '#')
continue;
- /* first, try a standard gethostbyname() */
- if ((hp = gethostbyname(cp)) == NULL)
+
+ hints.ai_flags = 0;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(cp, NULL, &hints, &res);
+ if (error != NULL)
continue;
+ for (ai = res; ai != NULL && ai->ai_addr != NULL;
+ ai = ai->ai_next)
+ {
+
for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
if (memcmp(&hrp->hostaddr,
- hp->h_addr_list[0],
- sizeof(hrp->hostaddr)) == 0)
+ ai->ai_addr,
+ ai->ai_addr->sa_len) == 0)
break;
}
if (hrp == NULL) {
@@ -628,16 +693,32 @@ inithosts()
lhrp = hrp;
}
(void) memcpy(&hrp->hostaddr,
- hp->h_addr_list[0],
- sizeof(hrp->hostaddr));
+ ai->ai_addr,
+ ai->ai_addr->sa_len);
/*
* determine hostname to use.
- * force defined name if it is a valid alias
+ * force defined name if there is a valid alias
* otherwise fallback to primary hostname
*/
- if ((hp = gethostbyaddr((char*)&hrp->hostaddr,
- sizeof(hrp->hostaddr),
- AF_INET)) != NULL) {
+ /* XXX: getaddrinfo() can't do alias check */
+ switch(hrp->hostaddr.su_family) {
+ case AF_INET:
+ addr = &((struct sockaddr_in *)&hrp->hostaddr)->sin_addr;
+ addrsize = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addr = &((struct sockaddr_in6 *)&hrp->hostaddr)->sin6_addr;
+ addrsize = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ /* should not reach here */
+ free(hrp);
+ continue;
+ /* NOTREACHED */
+ }
+ if ((hp = getipnodebyaddr((char*)addr, addrsize,
+ hrp->hostaddr.su_family,
+ &hp_error)) != NULL) {
if (strcmp(cp, hp->h_name) != 0) {
if (hp->h_aliases == NULL)
cp = hp->h_name;
@@ -652,6 +733,7 @@ inithosts()
}
}
hrp->hostname = strdup(cp);
+ freehostent(hp);
/* ok, now we now peel off the rest */
i = 0;
while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) {
@@ -673,25 +755,55 @@ inithosts()
}
++i;
}
+ /* XXX: re-initialization for getaddrinfo() loop */
+ cp = strtok(line, " \t");
+ }
}
(void) fclose(fp);
}
}
static void
-selecthost(a)
- struct in_addr *a;
+selecthost(su)
+ union sockunion *su;
{
struct ftphost *hrp;
+ u_int16_t port;
+#ifdef INET6
+ struct in6_addr *mapped_in6 = NULL;
+#endif
+
+#ifdef INET6
+ /*
+ * XXX IPv4 mapped IPv6 addr consideraton,
+ * specified in rfc2373.
+ */
+ if (su->su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr))
+ mapped_in6 = &su->su_sin6.sin6_addr;
+#endif
hrp = thishost = firsthost; /* default */
+ port = su->su_port;
+ su->su_port = 0;
while (hrp != NULL) {
- if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) {
+ if (memcmp(su, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) {
+ thishost = hrp;
+ break;
+ }
+#ifdef INET6
+ /* XXX IPv4 mapped IPv6 addr consideraton */
+ if (hrp->hostaddr.su_family == AF_INET && mapped_in6 != NULL &&
+ (memcmp(&mapped_in6->s6_addr[12],
+ &hrp->hostaddr.su_sin.sin_addr,
+ sizeof(struct in_addr)) == 0)) {
thishost = hrp;
break;
}
+#endif
hrp = hrp->next;
}
+ su->su_port = port;
/* setup static variables as appropriate */
hostname = thishost->hostname;
ftpuser = thishost->anonuser;
@@ -1107,8 +1219,9 @@ skip:
if ((lc = login_getpwclass(pw)) != NULL) {
char remote_ip[MAXHOSTNAMELEN];
- strncpy(remote_ip, inet_ntoa(his_addr.sin_addr),
- sizeof(remote_ip) - 1);
+ getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ remote_ip, sizeof(remote_ip) - 1, NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
remote_ip[sizeof(remote_ip) - 1] = 0;
if (!auth_hostok(lc, remotehost, remote_ip)) {
syslog(LOG_INFO|LOG_AUTH,
@@ -1402,19 +1515,19 @@ getdatasock(mode)
if (data >= 0)
return (fdopen(data, mode));
(void) seteuid((uid_t)0);
- s = socket(AF_INET, SOCK_STREAM, 0);
+
+ s = socket(data_dest.su_family, SOCK_STREAM, 0);
if (s < 0)
goto bad;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof(on)) < 0)
goto bad;
/* anchor socket to avoid multi-homing problems */
- data_source.sin_len = sizeof(struct sockaddr_in);
- data_source.sin_family = AF_INET;
- data_source.sin_addr = ctrl_addr.sin_addr;
+ data_source = ctrl_addr;
+ data_source.su_port = htons(20); /* ftp-data port */
for (tries = 1; ; tries++) {
if (bind(s, (struct sockaddr *)&data_source,
- sizeof(data_source)) >= 0)
+ data_source.su_len) >= 0)
break;
if (errno != EADDRINUSE || tries > 10)
goto bad;
@@ -1422,9 +1535,12 @@ getdatasock(mode)
}
(void) seteuid((uid_t)pw->pw_uid);
#ifdef IP_TOS
+ if (data_source.su_family == AF_INET)
+ {
on = IPTOS_THROUGHPUT;
if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
#endif
#ifdef TCP_NOPUSH
/*
@@ -1470,8 +1586,8 @@ dataconn(name, size, mode)
else
*sizebuf = '\0';
if (pdata >= 0) {
- struct sockaddr_in from;
- int s, fromlen = sizeof(from);
+ union sockunion from;
+ int s, fromlen = ctrl_addr.su_len;
struct timeval timeout;
fd_set set;
@@ -1491,9 +1607,12 @@ dataconn(name, size, mode)
(void) close(pdata);
pdata = s;
#ifdef IP_TOS
+ if (from.su_family == AF_INET)
+ {
tos = IPTOS_THROUGHPUT;
(void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
sizeof(int));
+ }
#endif
reply(150, "Opening %s mode data connection for '%s'%s.",
type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
@@ -1510,14 +1629,18 @@ dataconn(name, size, mode)
usedefault = 1;
file = getdatasock(mode);
if (file == NULL) {
- reply(425, "Can't create data socket (%s,%d): %s.",
- inet_ntoa(data_source.sin_addr),
- ntohs(data_source.sin_port), strerror(errno));
+ char hostbuf[BUFSIZ], portbuf[BUFSIZ];
+ getnameinfo((struct sockaddr *)&data_source,
+ data_source.su_len, hostbuf, sizeof(hostbuf) - 1,
+ portbuf, sizeof(portbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID);
+ reply(425, "Can't create data socket (%s,%s): %s.",
+ hostbuf, portbuf, strerror(errno));
return (NULL);
}
data = fileno(file);
while (connect(data, (struct sockaddr *)&data_dest,
- sizeof(data_dest)) < 0) {
+ data_dest.su_len) < 0) {
if (errno == EADDRINUSE && retry < swaitmax) {
sleep((unsigned) swaitint);
retry += swaitint;
@@ -1768,14 +1891,20 @@ statfilecmd(filename)
void
statcmd()
{
- struct sockaddr_in *sin;
+ union sockunion *su;
u_char *a, *p;
+ char hname[INET6_ADDRSTRLEN];
+ int ispassive;
lreply(211, "%s FTP server status:", hostname, version);
printf(" %s\r\n", version);
printf(" Connected to %s", remotehost);
- if (!isdigit(remotehost[0]))
- printf(" (%s)", inet_ntoa(his_addr.sin_addr));
+ if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ hname, sizeof(hname) - 1, NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID)) {
+ if (strcmp(hname, remotehost) != 0)
+ printf(" (%s)", hname);
+ }
printf("\r\n");
if (logged_in) {
if (guest)
@@ -1800,18 +1929,85 @@ statcmd()
if (data != -1)
printf(" Data connection open\r\n");
else if (pdata != -1) {
- printf(" in Passive mode");
- sin = &pasv_addr;
+ ispassive = 1;
+ su = &pasv_addr;
goto printaddr;
} else if (usedefault == 0) {
- printf(" PORT");
- sin = &data_dest;
+ ispassive = 0;
+ su = &data_dest;
printaddr:
- a = (u_char *) &sin->sin_addr;
- p = (u_char *) &sin->sin_port;
#define UC(b) (((int) b) & 0xff)
- printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
- UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+ if (epsvall) {
+ printf(" EPSV only mode (EPSV ALL)\r\n");
+ goto epsvonly;
+ }
+
+ /* PORT/PASV */
+ if (su->su_family == AF_INET) {
+ a = (u_char *) &su->su_sin.sin_addr;
+ p = (u_char *) &su->su_sin.sin_port;
+ printf(" %s (%d,%d,%d,%d,%d,%d)\r\n",
+ ispassive ? "PASV" : "PORT",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ }
+
+ /* LPRT/LPSV */
+ {
+ int alen, af, i;
+
+ switch (su->su_family) {
+ case AF_INET:
+ a = (u_char *) &su->su_sin.sin_addr;
+ p = (u_char *) &su->su_sin.sin_port;
+ alen = sizeof(su->su_sin.sin_addr);
+ af = 4;
+ break;
+ case AF_INET6:
+ a = (u_char *) &su->su_sin6.sin6_addr;
+ p = (u_char *) &su->su_sin6.sin6_port;
+ alen = sizeof(su->su_sin6.sin6_addr);
+ af = 6;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT",
+ af, alen);
+ for (i = 0; i < alen; i++)
+ printf("%d,", UC(a[i]));
+ printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
+ }
+ }
+
+epsvonly:;
+ /* EPRT/EPSV */
+ {
+ int af;
+
+ switch (su->su_family) {
+ case AF_INET:
+ af = 1;
+ break;
+ case AF_INET6:
+ af = 2;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ if (!getnameinfo((struct sockaddr *)su, su->su_len,
+ hname, sizeof(hname) - 1, NULL, 0,
+ NI_NUMERICHOST)) {
+ printf(" %s |%d|%s|%d|\r\n",
+ ispassive ? "EPSV" : "EPRT",
+ af, hname, htons(su->su_port));
+ }
+ }
+ }
#undef UC
} else
printf(" No data connection\r\n");
@@ -2015,10 +2211,12 @@ renamecmd(from, to)
}
static void
-dolog(sin)
- struct sockaddr_in *sin;
+dolog(who)
+ struct sockaddr *who;
{
- realhostname(remotehost, sizeof(remotehost) - 1, &sin->sin_addr);
+ int error;
+
+ realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len);
#ifdef SETPROCTITLE
#ifdef VIRTUAL_HOSTING
@@ -2039,8 +2237,16 @@ dolog(sin)
remotehost, hostname);
else
#endif
+ {
+ char who_name[MAXHOSTNAMELEN];
+
+ error = getnameinfo(who, who->sa_len,
+ who_name, sizeof(who_name) - 1,
+ NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
syslog(LOG_INFO, "connection from %s (%s)", remotehost,
- inet_ntoa(sin->sin_addr));
+ error == 0 ? who_name : "");
+ }
}
}
@@ -2112,7 +2318,7 @@ passive()
if (pdata >= 0) /* close old port if one set */
close(pdata);
- pdata = socket(AF_INET, SOCK_STREAM, 0);
+ pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
if (pdata < 0) {
perror_reply(425, "Can't open passive connection");
return;
@@ -2121,7 +2327,7 @@ passive()
(void) seteuid((uid_t)0);
#ifdef IP_PORTRANGE
- {
+ if (ctrl_addr.su_family == AF_INET) {
int on = restricted_data_ports ? IP_PORTRANGE_HIGH
: IP_PORTRANGE_DEFAULT;
@@ -2132,9 +2338,8 @@ passive()
#endif
pasv_addr = ctrl_addr;
- pasv_addr.sin_port = 0;
- if (bind(pdata, (struct sockaddr *)&pasv_addr,
- sizeof(pasv_addr)) < 0)
+ pasv_addr.su_port = 0;
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0)
goto pasv_error;
(void) seteuid((uid_t)pw->pw_uid);
@@ -2144,8 +2349,15 @@ passive()
goto pasv_error;
if (listen(pdata, 1) < 0)
goto pasv_error;
- a = (char *) &pasv_addr.sin_addr;
- p = (char *) &pasv_addr.sin_port;
+ if (pasv_addr.su_family == AF_INET)
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ else if (pasv_addr.su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr))
+ a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
+ else
+ goto pasv_error;
+
+ p = (char *) &pasv_addr.su_port;
#define UC(b) (((int) b) & 0xff)
@@ -2162,6 +2374,121 @@ pasv_error:
}
/*
+ * Long Passive defined in RFC 1639.
+ * 228 Entering Long Passive Mode
+ * (af, hal, h1, h2, h3,..., pal, p1, p2...)
+ */
+
+void
+long_passive(cmd, pf)
+ char *cmd;
+ int pf;
+{
+ int len;
+ char *p, *a;
+
+ if (pdata >= 0) /* close old port if one set */
+ close(pdata);
+
+ if (pf != PF_UNSPEC) {
+ if (ctrl_addr.su_family != pf) {
+ switch (ctrl_addr.su_family) {
+ case AF_INET:
+ pf = 1;
+ break;
+ case AF_INET6:
+ pf = 2;
+ break;
+ default:
+ pf = 0;
+ break;
+ }
+ /*
+ * XXX
+ * only EPRT/EPSV ready clients will understand this
+ */
+ if (strcmp(cmd, "EPSV") == 0 && pf) {
+ reply(522, "Network protocol mismatch, "
+ "use (%d)", pf);
+ } else
+ reply(501, "Network protocol mismatch"); /*XXX*/
+
+ return;
+ }
+ }
+
+ pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+
+ (void) seteuid((uid_t)0);
+
+ pasv_addr = ctrl_addr;
+ pasv_addr.su_port = 0;
+ len = pasv_addr.su_len;
+
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0)
+ goto pasv_error;
+
+ (void) seteuid((uid_t)pw->pw_uid);
+
+ if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+
+#define UC(b) (((int) b) & 0xff)
+
+ if (strcmp(cmd, "LPSV") == 0) {
+ p = (char *)&pasv_addr.su_port;
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ v4_reply:
+ reply(228,
+"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
+ 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ 2, UC(p[0]), UC(p[1]));
+ return;
+ case AF_INET6:
+ if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) {
+ a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
+ goto v4_reply;
+ }
+ a = (char *) &pasv_addr.su_sin6.sin6_addr;
+ reply(228,
+"Entering Long Passive Mode "
+"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
+ 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ 2, UC(p[0]), UC(p[1]));
+ return;
+ }
+ } else if (strcmp(cmd, "EPSV") == 0) {
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ case AF_INET6:
+ reply(229, "Entering Extended Passive Mode (|||%d|)",
+ ntohs(pasv_addr.su_port));
+ return;
+ }
+ } else {
+ /* more proper error code? */
+ }
+
+pasv_error:
+ (void) seteuid((uid_t)pw->pw_uid);
+ (void) close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
* Generate unique name for file with basename "local".
* The file named "local" is already known to exist.
* Generates failure reply on error.
diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c
index d3f2ec5..2ac57d1 100644
--- a/libexec/ftpd/logwtmp.c
+++ b/libexec/ftpd/logwtmp.c
@@ -43,6 +43,7 @@ static const char rcsid[] =
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <sys/socket.h>
#include <fcntl.h>
#include <time.h>
@@ -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 606d17a..82cfd58 100644
--- a/libexec/ftpd/popen.c
+++ b/libexec/ftpd/popen.c
@@ -44,6 +44,7 @@ static const char rcsid[] =
#include <sys/types.h>
#include <sys/wait.h>
+#include <netinet/in.h>
#include <errno.h>
#include <glob.h>
diff --git a/secure/libexec/telnetd/Makefile b/secure/libexec/telnetd/Makefile
index 230e4e7..e13f48d 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 b768344..f0db4cb 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 <bsd.prog.mk>
diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile
index bbd82e2..b5a59cc 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 a8ac97b..7841f7b 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 1d73c0f..62b64e9 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 <ctype.h>
#include <err.h>
+#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
@@ -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 @@ parsed_url:
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 59b371f..a112709 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 d2edd01..3c7da3f 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;
+ 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));
- } 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));
+ 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 @@ noport:
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
+#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, sizeof(data_addr)) < 0) {
+ if (bind(data, (struct sockaddr *)&data_addr, data_addr.su_len) < 0) {
warn("bind");
goto bad;
}
@@ -1184,13 +1378,79 @@ noport:
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 @@ noport:
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 d26d22d..cd9b0bd 100644
--- a/usr.bin/ftp/ftp_var.h
+++ b/usr.bin/ftp/ftp_var.h
@@ -41,6 +41,7 @@
*/
#include <sys/param.h>
+#include <sys/socket.h>
#include <setjmp.h>
#include <stringlist.h>
#include <netinet/in.h>
@@ -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 3717308..aeb7487 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 <arpa/inet.h>
#include <err.h>
+#include <errno.h>
#include <locale.h>
#include <netdb.h>
#include <pwd.h>
@@ -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 a34ca34..a62565c 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 658a2b7..b4faf30 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 a4677e0..a640bec 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 <netinet/ip.h>
-
+#include <netinet/ip6.h>
#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);
+ 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;
- }
- 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));
}
+ 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++;
+ 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;
+ }
+ 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;
- temp = sourceroute(hostp, &srp, &srlen);
- if (temp == 0) {
- herror(srp);
+ result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt);
+ if (result == 0) {
setuid(getuid());
+ freeaddrinfo(res);
return 0;
- } else if (temp == -1) {
+ } 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 a8c3f24..10e89ac 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 <strings.h>
+#if defined(IPSEC)
+#include <netinet6/ipsec.h>
+#if defined(IPSEC_POLICY_IPSEC)
+extern char *ipsec_policy_in;
+extern char *ipsec_policy_out;
+#endif
+#endif
+
#ifndef _POSIX_VDISABLE
# ifdef sun
# include <sys/param.h> /* 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 298389c..9126738 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 <sys/types.h>
+#include <sys/socket.h>
#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)
@@ -98,6 +108,9 @@ usage()
#else
"[-r] [-s src_addr] ",
#endif
+#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+ "[-P policy]"
+#endif
"[host-name [port]]"
);
exit(1);
@@ -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 ca2e968..dfa4fcf 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 abe3b5b..5e7433d 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 0000000..c40c21d
--- /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 <bsd.prog.mk>
diff --git a/usr.sbin/faithd/README b/usr.sbin/faithd/README
new file mode 100644
index 0000000..f8e4753
--- /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.
+
+<<On the translating router on which faithd runs>>
+
+(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
+
+
+<<Routing>>
+
+(4) Make sure that packets whose destinations match the prefix can
+reach from the IPv6 host to the translating router.
+
+<<On the IPv6 host>>
+
+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
+
+<<On the translating router on which faithd runs.>>
+
+(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".
+
+ <e.g.>
+ 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 0000000..2f62ed3
--- /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 0000000..ee6c8df
--- /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 [<port> <progpath> <arg1(progname)> <arg2> ...]
+ * e.g. faithd telnet /usr/local/v6/sbin/telnetd telnetd
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <libutil.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <net/if_types.h>
+#ifdef IFT_FAITH
+# define USE_ROUTE
+# include <net/if.h>
+# include <net/route.h>
+# include <net/if_dl.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef FAITH4
+#include <resolv.h>
+#include <arpa/nameser.h>
+#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 0000000..55566ed
--- /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 0000000..fcb25e3
--- /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 <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#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 0000000..c6e8357
--- /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 <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#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 0000000..e1e1b32
--- /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 <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#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 0000000..683b504
--- /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
OpenPOWER on IntegriCloud