diff options
author | ume <ume@FreeBSD.org> | 2000-07-05 19:34:43 +0000 |
---|---|---|
committer | ume <ume@FreeBSD.org> | 2000-07-05 19:34:43 +0000 |
commit | d3c71f0efbb39b37472b918609511858e32a05de (patch) | |
tree | b33ca97e6c4ce735407a4ea6a42248f1d8abdf04 | |
parent | ade2fdc9b1701bac09b782a5085218e81f59686d (diff) | |
download | FreeBSD-src-d3c71f0efbb39b37472b918609511858e32a05de.zip FreeBSD-src-d3c71f0efbb39b37472b918609511858e32a05de.tar.gz |
IPv6 support.
This is required for forthcoming IPv6 ready installer.
Obtained from: KAME
-rw-r--r-- | lib/libftpio/Makefile | 3 | ||||
-rw-r--r-- | lib/libftpio/ftp.errors | 3 | ||||
-rw-r--r-- | lib/libftpio/ftpio.3 | 21 | ||||
-rw-r--r-- | lib/libftpio/ftpio.c | 349 | ||||
-rw-r--r-- | lib/libftpio/ftpio.h | 3 |
5 files changed, 301 insertions, 78 deletions
diff --git a/lib/libftpio/Makefile b/lib/libftpio/Makefile index 7709dea..b3a749b 100644 --- a/lib/libftpio/Makefile +++ b/lib/libftpio/Makefile @@ -1,12 +1,13 @@ # $FreeBSD$ LIB= ftpio -SHLIB_MAJOR= 5 +SHLIB_MAJOR= 6 SHLIB_MINOR= 0 SRCS= ftpio.c ftperr.c INCS= ftpio.h CFLAGS+= -I${.CURDIR} -Wall +CFLAGS+= -DINET6 MAN3= ftpio.3 CLEANFILES= ftperr.c diff --git a/lib/libftpio/ftp.errors b/lib/libftpio/ftp.errors index 7ae4445..c03eeac 100644 --- a/lib/libftpio/ftp.errors +++ b/lib/libftpio/ftp.errors @@ -18,7 +18,8 @@ 221 Service closing control connection 225 Data connection open; no transfer in progress 226 Requested file action successful -227 Entering Passive Mode +227 Entering Passive Mode +229 Entering Extended Passive Mode 230 User logged in, proceed 250 Requested file action okay, completed 257 File/directory created diff --git a/lib/libftpio/ftpio.3 b/lib/libftpio/ftpio.3 index b20b8f5..510324d 100644 --- a/lib/libftpio/ftpio.3 +++ b/lib/libftpio/ftpio.3 @@ -39,7 +39,10 @@ .Nm ftpPassive , .Nm ftpVerbose , .Nm ftpGetURL , -.Nm ftpPutURL +.Nm ftpPutURL , +.Nm ftpLoginAf , +.Nm ftpGetURLAf , +.Nm ftpPutURLAf .Nd FTPIO User library .Sh SYNOPSIS .Fd #include <ftpio.h> @@ -71,6 +74,12 @@ .Fn ftpGetURL "char *url, char *user, char *passwd, int *retcode" .Ft FILE * .Fn ftpPutURL "char *url, char *user, char *passwd, int *retcode" +.Ft int +.Fn ftpLoginAf "char *host" "int af" "char *user" "char *passwd" "int ftp_port" "int verbose" "int *retcode" +.Ft FILE * +.Fn ftpGetURLAf "char *url" "int af" "char *user" "char *passwd" "int *retcode" +.Ft FILE * +.Fn ftpPutURLAf "char *url" "int af" "char *user" "char *passwd" "int *retcode" .Sh DESCRIPTION These functions implement a high-level library for managing FTP connections. @@ -186,6 +195,16 @@ operations except that no server stream is ever returned - the connection to the server closes when the file has been completely written. Use the lower-level routines if multiple puts are required as it will be far more efficient. +.Pp +.Fn ftpLoginAf , +.Fn ftpGetURLAf , +.Fn ftpPutURLAf +are same as +.Fn ftpLogin , +.Fn ftpGetURL , +.Fn ftpPutURL +except that they are able to specify address family +.Fa af . .Sh ENVIRONMENT .Bl -tag -width FTP_PASSIVE_MODE -offset 123 .It Ev FTP_TIMEOUT diff --git a/lib/libftpio/ftpio.c b/lib/libftpio/ftpio.c index ceb428c..0c5c0ef 100644 --- a/lib/libftpio/ftpio.c +++ b/lib/libftpio/ftpio.c @@ -58,13 +58,14 @@ static __inline char *get_a_line(FTP_t ftp); static int get_a_number(FTP_t ftp, char **q); static int botch(char *func, char *botch_state); static int cmd(FTP_t ftp, const char *fmt, ...); -static int ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int verbose); +static int ftp_login_session(FTP_t ftp, char *host, int af, char *user, char *passwd, int port, int verbose); static int ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto); static int ftp_close(FTP_t ftp); static int get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret); static void ftp_timeout(int sig); static void ftp_set_timeout(void); static void ftp_clear_timeout(void); +static void ai_unmapped(struct addrinfo *); /* Global status variable - ick */ @@ -79,6 +80,8 @@ int FtpTimedOut; #define FTP_QUIT_HAPPY 221 #define FTP_TRANSFER_HAPPY 226 #define FTP_PASSIVE_HAPPY 227 +#define FTP_LPASSIVE_HAPPY 228 +#define FTP_EPASSIVE_HAPPY 229 #define FTP_CHDIR_HAPPY 250 /* FTP unhappy status codes */ @@ -267,6 +270,16 @@ ftpGet(FILE *fp, char *file, off_t *seekto) FILE * ftpLogin(char *host, char *user, char *passwd, int port, int verbose, int *retcode) { +#ifdef INET6 + return ftpLoginAf(host, AF_UNSPEC, user, passwd, port, verbose, retcode); +#else + return ftpLoginAf(host, AF_INET, user, passwd, port, verbose, retcode); +#endif +} + +FILE * +ftpLoginAf(char *host, int af, char *user, char *passwd, int port, int verbose, int *retcode) +{ FTP_t n; FILE *fp; @@ -277,7 +290,7 @@ ftpLogin(char *host, char *user, char *passwd, int port, int verbose, int *retco n = ftp_new(); fp = NULL; - if (n && ftp_login_session(n, host, user, passwd, port, verbose) == SUCCESS) { + if (n && ftp_login_session(n, host, af, user, passwd, port, verbose) == SUCCESS) { fp = funopen(n, ftp_read_method, ftp_write_method, NULL, ftp_close_method); /* BSD 4.4 function! */ fp->_file = n->fd_ctrl; } @@ -323,11 +336,27 @@ ftpPassive(FILE *fp, int st) if (ftp->is_passive == st) return SUCCESS; - i = cmd(ftp, "PASV"); - if (i < 0) - return i; - if (i != FTP_PASSIVE_HAPPY) - return FAILURE; + switch (ftp->addrtype) { + case AF_INET: + i = cmd(ftp, "PASV"); + if (i < 0) + return i; + if (i != FTP_PASSIVE_HAPPY) + return FAILURE; + break; + case AF_INET6: + i = cmd(ftp, "EPSV"); + if (i < 0) + return i; + if (i != FTP_EPASSIVE_HAPPY) { + i = cmd(ftp, "LPSV"); + if (i < 0) + return i; + if (i != FTP_LPASSIVE_HAPPY) + return FAILURE; + } + break; + } ftp->is_passive = !ftp->is_passive; return SUCCESS; } @@ -335,6 +364,16 @@ ftpPassive(FILE *fp, int st) FILE * ftpGetURL(char *url, char *user, char *passwd, int *retcode) { +#ifdef INET6 + return ftpGetURLAf(url, AF_UNSPEC, user, passwd, retcode); +#else + return ftpGetURLAf(url, AF_INET, user, passwd, retcode); +#endif +} + +FILE * +ftpGetURLAf(char *url, int af, char *user, char *passwd, int *retcode) +{ char host[255], name[255]; int port; FILE *fp2; @@ -364,7 +403,7 @@ ftpGetURL(char *url, char *user, char *passwd, int *retcode) prev_host = NULL; } } - fp = ftpLogin(host, user, passwd, port, 0, retcode); + fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode); if (fp) { fp2 = ftpGet(fp, name, NULL); if (!fp2) { @@ -385,6 +424,17 @@ ftpGetURL(char *url, char *user, char *passwd, int *retcode) FILE * ftpPutURL(char *url, char *user, char *passwd, int *retcode) { +#ifdef INET6 + return ftpPutURLAf(url, AF_UNSPEC, user, passwd, retcode); +#else + return ftpPutURLAf(url, AF_INET, user, passwd, retcode); +#endif + +} + +FILE * +ftpPutURLAf(char *url, int af, char *user, char *passwd, int *retcode) +{ char host[255], name[255]; int port; static FILE *fp = NULL; @@ -397,7 +447,7 @@ ftpPutURL(char *url, char *user, char *passwd, int *retcode) fp = NULL; } if (get_url_info(url, host, &port, name) == SUCCESS) { - fp = ftpLogin(host, user, passwd, port, 0, retcode); + fp = ftpLoginAf(host, af, user, passwd, port, 0, retcode); if (fp) { fp2 = ftpPut(fp, name); if (!fp2) { @@ -672,12 +722,13 @@ cmd(FTP_t ftp, const char *fmt, ...) } static int -ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int verbose) +ftp_login_session(FTP_t ftp, char *host, int af, + char *user, char *passwd, int port, int verbose) { - struct hostent *he = NULL; - struct sockaddr_in sin; + char pbuf[10]; + struct addrinfo hints, *res, *res0; + int err; int s; - unsigned long temp; int i; if (networkInit() != SUCCESS) @@ -698,30 +749,36 @@ ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int if (!port) port = 21; - temp = inet_addr(host); - if (temp != INADDR_NONE) { - ftp->addrtype = sin.sin_family = AF_INET; - sin.sin_addr.s_addr = temp; - } - else { - he = gethostbyname(host); - if (!he) { - ftp->error = 0; - return FAILURE; - } - ftp->addrtype = sin.sin_family = he->h_addrtype; - bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length); + snprintf(pbuf, sizeof(pbuf), "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + err = getaddrinfo(host, pbuf, &hints, &res0); + if (err) { + ftp->error = 0; + return FAILURE; } - sin.sin_port = htons(port); + s = -1; + for (res = res0; res; res = res->ai_next) { + ai_unmapped(res); + ftp->addrtype = res->ai_family; - if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) { - ftp->error = -1; - return FAILURE; - } + if ((s = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) < 0) + continue; + + if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { + (void)close(s); + s = -1; + continue; + } - if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { - (void)close(s); + break; + } + freeaddrinfo(res0); + if (s < 0) { ftp->error = errno; return FAILURE; } @@ -745,11 +802,14 @@ ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int static int ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto) { - int i,s; + int i,l,s; char *q; unsigned char addr[64]; - struct sockaddr_in sin; - u_long a; + union sockaddr_cmn { + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } sin; + char *cmdstr; if (!fp) return FAILURE; @@ -764,35 +824,101 @@ ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t } if (ftp->is_passive) { - if (ftp->is_verbose) - fprintf(stderr, "Sending PASV\n"); - if (writes(ftp->fd_ctrl, "PASV\r\n")) { - ftp_close(ftp); - if (FtpTimedOut) - ftp->error = FTP_TIMED_OUT; - return FTP_TIMED_OUT; - } - i = get_a_number(ftp, &q); - if (check_code(ftp, i, FTP_PASSIVE_HAPPY)) { - ftp_close(ftp); - return i; + if (ftp->addrtype == AF_INET) { + if (ftp->is_verbose) + fprintf(stderr, "Sending PASV\n"); + if (writes(ftp->fd_ctrl, "PASV\r\n")) { + ftp_close(ftp); + if (FtpTimedOut) + ftp->error = FTP_TIMED_OUT; + return FTP_TIMED_OUT; + } + i = get_a_number(ftp, &q); + if (check_code(ftp, i, FTP_PASSIVE_HAPPY)) { + ftp_close(ftp); + return i; + } + cmdstr = "PASV"; + } else { + if (ftp->is_verbose) + fprintf(stderr, "Sending EPSV\n"); + if (writes(ftp->fd_ctrl, "EPSV\r\n")) { + ftp_close(ftp); + if (FtpTimedOut) + ftp->error = FTP_TIMED_OUT; + return FTP_TIMED_OUT; + } + i = get_a_number(ftp, &q); + if (check_code(ftp, i, FTP_EPASSIVE_HAPPY)) { + if (ftp->is_verbose) + fprintf(stderr, "Sending LPSV\n"); + if (writes(ftp->fd_ctrl, "LPSV\r\n")) { + ftp_close(ftp); + if (FtpTimedOut) + ftp->error = FTP_TIMED_OUT; + return FTP_TIMED_OUT; + } + i = get_a_number(ftp, &q); + if (check_code(ftp, i, FTP_LPASSIVE_HAPPY)) { + ftp_close(ftp); + return i; + } + cmdstr = "LPSV"; + } else + cmdstr = "EPSV"; } - while (*q && !isdigit(*q)) + while (*q && *q != '(') /* ) */ q++; if (!*q) { ftp_close(ftp); return FAILURE; } - q--; - for (i = 0; i < 6; i++) { + if (strcmp(cmdstr, "PASV") == 0 || strcmp(cmdstr, "LPSV") == 0) { + l = (ftp->addrtype == AF_INET ? 6 : 21); + for (i = 0; i < l; i++) { + q++; + addr[i] = strtol(q, &q, 10); + } + + sin.sin4.sin_family = ftp->addrtype; + if (ftp->addrtype == AF_INET6) { + sin.sin6.sin6_len = sizeof(struct sockaddr_in6); + bcopy(addr + 2, (char *)&sin.sin6.sin6_addr, 16); + bcopy(addr + 19, (char *)&sin.sin6.sin6_port, 2); + } else { + sin.sin4.sin_len = sizeof(struct sockaddr_in); + bcopy(addr, (char *)&sin.sin4.sin_addr, 4); + bcopy(addr + 4, (char *)&sin.sin4.sin_port, 2); + } + } else if (strcmp(cmdstr, "EPSV") == 0) { + int port; + int sinlen; q++; - addr[i] = strtol(q, &q, 10); + if (sscanf(q, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], + &port, &addr[3]) != 5 + || addr[0] != addr[1] || addr[0] != addr[2] || addr[0] != addr[3]) { + ftp_close(ftp); + return FAILURE; + } + sinlen = sizeof(sin); + if (getpeername(ftp->fd_ctrl, (struct sockaddr *)&sin, &sinlen) < 0) { + ftp_close(ftp); + return FAILURE; + } + switch (sin.sin4.sin_family) { + case AF_INET: + sin.sin4.sin_port = htons(port); + break; + case AF_INET6: + sin.sin6.sin6_port = htons(port); + break; + default: + ftp_close(ftp); + return FAILURE; + } } - sin.sin_family = ftp->addrtype; - bcopy(addr, (char *)&sin.sin_addr, 4); - bcopy(addr + 4, (char *)&sin.sin_port, 2); - if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + if (connect(s, (struct sockaddr *)&sin, sin.sin4.sin_len) < 0) { (void)close(s); return FAILURE; } @@ -817,39 +943,85 @@ ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t else { int fd,portrange; +#ifdef IPV6_PORTRANGE + if (ftp->addrtype == AF_INET6) { + portrange = IPV6_PORTRANGE_HIGH; + if (setsockopt(s, IPPROTO_IPV6, IPV6_PORTRANGE, (char *) + &portrange, sizeof(portrange)) < 0) { + close(s); + return FAILURE; + } + } +#endif #ifdef IP_PORTRANGE - portrange = IP_PORTRANGE_HIGH; - if (setsockopt(s, IPPROTO_IP, IP_PORTRANGE, (char *) - &portrange, sizeof(portrange)) < 0) { - close(s); - return FAILURE; - }; + if (ftp->addrtype == AF_INET) { + portrange = IP_PORTRANGE_HIGH; + if (setsockopt(s, IPPROTO_IP, IP_PORTRANGE, (char *) + &portrange, sizeof(portrange)) < 0) { + close(s); + return FAILURE; + } + } #endif i = sizeof sin; getsockname(ftp->fd_ctrl, (struct sockaddr *)&sin, &i); - sin.sin_port = 0; - i = sizeof sin; + sin.sin4.sin_port = 0; + i = ((struct sockaddr *)&sin)->sa_len; if (bind(s, (struct sockaddr *)&sin, i) < 0) { close(s); return FAILURE; } + i = sizeof sin; getsockname(s,(struct sockaddr *)&sin,&i); if (listen(s, 1) < 0) { close(s); return FAILURE; } - a = ntohl(sin.sin_addr.s_addr); - i = cmd(ftp, "PORT %d,%d,%d,%d,%d,%d", - (a >> 24) & 0xff, - (a >> 16) & 0xff, - (a >> 8) & 0xff, - a & 0xff, - (ntohs(sin.sin_port) >> 8) & 0xff, - ntohs(sin.sin_port) & 0xff); - if (check_code(ftp, i, FTP_PORT_HAPPY)) { - close(s); - return i; + if (sin.sin4.sin_family == AF_INET) { + u_long a; + a = ntohl(sin.sin4.sin_addr.s_addr); + i = cmd(ftp, "PORT %d,%d,%d,%d,%d,%d", + (a >> 24) & 0xff, + (a >> 16) & 0xff, + (a >> 8) & 0xff, + a & 0xff, + (ntohs(sin.sin4.sin_port) >> 8) & 0xff, + ntohs(sin.sin4.sin_port) & 0xff); + if (check_code(ftp, i, FTP_PORT_HAPPY)) { + close(s); + return i; + } + } else { +#define UC(b) (((int)b)&0xff) + char *a; + char hname[INET6_ADDRSTRLEN]; + + if (getnameinfo((struct sockaddr *)&sin, sin.sin6.sin6_len, + hname, sizeof(hname), + NULL, 0, NI_NUMERICHOST) != 0) { + goto try_lprt; + } + i = cmd(ftp, "EPRT |%d|%s|%d|", 2, hname, + htons(sin.sin6.sin6_port)); + if (check_code(ftp, i, FTP_PORT_HAPPY)) { +try_lprt: + a = (char *)&sin.sin6.sin6_addr; + i = cmd(ftp, +"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, + (ntohs(sin.sin4.sin_port) >> 8) & 0xff, + ntohs(sin.sin4.sin_port) & 0xff); + if (check_code(ftp, i, FTP_PORT_HAPPY)) { + close(s); + return i; + } + } } if (seekto && *seekto) { i = cmd(ftp, "REST %d", *seekto); @@ -881,3 +1053,30 @@ ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t else return FAILURE; } + +static void +ai_unmapped(struct addrinfo *ai) +{ + struct sockaddr_in6 *sin6; + struct sockaddr_in sin; + + if (ai->ai_family != AF_INET6) + return; + if (ai->ai_addrlen != sizeof(struct sockaddr_in6) || + sizeof(sin) > ai->ai_addrlen) + return; + sin6 = (struct sockaddr_in6 *)ai->ai_addr; + if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + return; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], + sizeof(sin.sin_addr)); + sin.sin_port = sin6->sin6_port; + + ai->ai_family = AF_INET; + memcpy(ai->ai_addr, &sin, sin.sin_len); + ai->ai_addrlen = sin.sin_len; +} diff --git a/lib/libftpio/ftpio.h b/lib/libftpio/ftpio.h index 26d4f90..ed6fd82 100644 --- a/lib/libftpio/ftpio.h +++ b/lib/libftpio/ftpio.h @@ -63,6 +63,9 @@ extern FILE *ftpGetURL(char *url, char *user, char *passwd, int *retcode); extern FILE *ftpPutURL(char *url, char *user, char *passwd, int *retcode); extern time_t ftpGetModtime(FILE *fp, char *s); extern const char *ftpErrString(int error); +extern FILE *ftpLoginAf(char *host, int af, char *user, char *passwd, int port, int verbose, int *retcode); +extern FILE *ftpGetURLAf(char *url, int af, char *user, char *passwd, int *retcode); +extern FILE *ftpPutURLAf(char *url, int af, char *user, char *passwd, int *retcode); __END_DECLS #endif /* _FTP_H_INCLUDE */ |