IPv6 support.

This commit is contained in:
ume 2000-05-20 18:23:51 +00:00
parent 0636bdc7c2
commit 1db238984d
7 changed files with 279 additions and 69 deletions

View File

@ -3,6 +3,7 @@
MAINTAINER= des@freebsd.org
LIB= fetch
CFLAGS+= -I. -Wall -pedantic
CFLAGS+= -DINET6
.if !defined(DEBUG)
CFLAGS+= -DNDEBUG
.endif

View File

@ -51,10 +51,10 @@
* Error messages for resolver errors
*/
static struct fetcherr _netdb_errlist[] = {
{ HOST_NOT_FOUND, FETCH_RESOLV, "Host not found" },
{ TRY_AGAIN, FETCH_TEMP, "Transient resolver failure" },
{ NO_RECOVERY, FETCH_RESOLV, "Non-recoverable resolver failure" },
{ NO_DATA, FETCH_RESOLV, "No address record" },
{ EAI_NODATA, FETCH_RESOLV, "Host not found" },
{ EAI_AGAIN, FETCH_TEMP, "Transient resolver failure" },
{ EAI_FAIL, FETCH_RESOLV, "Non-recoverable resolver failure" },
{ EAI_NONAME, FETCH_RESOLV, "No address record" },
{ -1, FETCH_UNKNOWN, "Unknown resolver error" }
};
@ -200,11 +200,11 @@ _fetch_info(char *fmt, ...)
* Establish a TCP connection to the specified port on the specified host.
*/
int
_fetch_connect(char *host, int port, int verbose)
_fetch_connect(char *host, int port, int af, int verbose)
{
struct sockaddr_in sin;
struct hostent *he;
int sd;
char pbuf[10];
struct addrinfo hints, *res, *res0;
int sd, err;
#ifndef NDEBUG
fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port);
@ -213,29 +213,33 @@ _fetch_connect(char *host, int port, int verbose)
if (verbose)
_fetch_info("looking up %s", host);
/* look up host name */
if ((he = gethostbyname(host)) == NULL) {
_netdb_seterr(h_errno);
/* look up host name and set up socket address structure */
snprintf(pbuf, sizeof(pbuf), "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {
_netdb_seterr(err);
return -1;
}
if (verbose)
_fetch_info("connecting to %s:%d", host, port);
/* set up socket address structure */
bzero(&sin, sizeof sin);
bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
sin.sin_family = he->h_addrtype;
sin.sin_port = htons(port);
/* try to connect */
if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
_fetch_syserr();
return -1;
}
if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) {
_fetch_syserr();
sd = -1;
for (res = res0; res; res = res->ai_next) {
if ((sd = socket(res->ai_family, res->ai_socktype,
res->ai_protocol)) < 0)
continue;
if (connect(sd, res->ai_addr, res->ai_addrlen) >= 0)
break;
close(sd);
sd = -1;
}
if (sd < 0) {
_fetch_syserr();
return -1;
}

View File

@ -41,7 +41,7 @@ struct fetcherr {
void _fetch_seterr(struct fetcherr *p, int e);
void _fetch_syserr(void);
int _fetch_info(char *fmt, ...);
int _fetch_connect(char *host, int port, int verbose);
int _fetch_connect(char *host, int port, int af, int verbose);
int _fetch_getln(int fd, char **buf, size_t *size, size_t *len);
int _fetch_add_entry(struct url_ent **p, int *size, int *len,
char *name, struct url_stat *stat);

View File

@ -286,9 +286,18 @@ fetchParseURL(char *URL)
} else p = URL;
/* hostname */
for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
if (i < MAXHOSTNAMELEN)
u->host[i++] = *p;
#ifdef INET6
if (*p == '[' && (q = strchr(p + 1, ']')) != NULL &&
(*++q == '\0' || *q == '/' || *q == ':')) {
if ((i = q - p - 2) > MAXHOSTNAMELEN)
i = MAXHOSTNAMELEN;
strncpy(u->host, ++p, i);
p = q;
} else
#endif
for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
if (i < MAXHOSTNAMELEN)
u->host[i++] = *p;
/* port */
if (*p == ':') {

View File

@ -83,6 +83,8 @@
#define FTP_FILE_STATUS 213
#define FTP_SERVICE_READY 220
#define FTP_PASSIVE_MODE 227
#define FTP_LPASSIVE_MODE 228
#define FTP_EPASSIVE_MODE 229
#define FTP_LOGGED_IN 230
#define FTP_FILE_ACTION_OK 250
#define FTP_NEED_PASSWORD 331
@ -105,6 +107,27 @@ static int last_code;
#define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \
&& isdigit(foo[2]) && foo[3] == '-')
/* translate IPv4 mapped IPv6 address to IPv4 address */
static void
unmappedaddr(struct sockaddr_in6 *sin6)
{
struct sockaddr_in *sin4;
u_int32_t addr;
int port;
if (sin6->sin6_family != AF_INET6 ||
!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
return;
sin4 = (struct sockaddr_in *)sin6;
addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
port = sin6->sin6_port;
memset(sin4, 0, sizeof(struct sockaddr_in));
sin4->sin_addr.s_addr = addr;
sin4->sin_port = port;
sin4->sin_family = AF_INET;
sin4->sin_len = sizeof(struct sockaddr_in);
}
/*
* Get server response
*/
@ -181,7 +204,9 @@ static FILE *
_ftp_transfer(int cd, char *oper, char *file,
char *mode, off_t offset, char *flags)
{
struct sockaddr_in sin;
struct sockaddr_storage sin;
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin4;
int pasv, high, verbose;
int e, sd = -1;
socklen_t l;
@ -217,36 +242,79 @@ _ftp_transfer(int cd, char *oper, char *file,
/* s now points to file name */
/* find our own address, bind, and listen */
l = sizeof sin;
if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1)
goto sysouch;
if (sin.ss_family == AF_INET6)
unmappedaddr((struct sockaddr_in6 *)&sin);
/* open data socket */
if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
if ((sd = socket(sin.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
_fetch_syserr();
return NULL;
}
if (pasv) {
u_char addr[6];
u_char addr[64];
char *ln, *p;
int i;
int port;
/* send PASV command */
if (verbose)
_fetch_info("setting passive mode");
if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
switch (sin.ss_family) {
case AF_INET:
if ((e = _ftp_cmd(cd, "PASV")) != FTP_PASSIVE_MODE)
goto ouch;
break;
case AF_INET6:
if ((e = _ftp_cmd(cd, "EPSV")) != FTP_EPASSIVE_MODE) {
if (e == -1)
goto ouch;
if ((e = _ftp_cmd(cd, "LPSV")) != FTP_LPASSIVE_MODE)
goto ouch;
}
break;
default:
e = 999; /* XXX: error code should be prepared */
goto ouch;
}
/*
* Find address and port number. The reply to the PASV command
* is IMHO the one and only weak point in the FTP protocol.
*/
ln = last_reply;
for (p = ln + 3; *p && !isdigit(*p); p++)
for (p = ln + 3; *p && *p != '('; p++)
/* nothing */ ;
for (i = 0; *p, i < 6; i++, p++)
addr[i] = strtol(p, &p, 10);
if (i < 6) {
if (!*p) {
e = 999;
goto ouch;
}
p++;
switch (e) {
case FTP_PASSIVE_MODE:
case FTP_LPASSIVE_MODE:
l = (e == FTP_PASSIVE_MODE ? 6 : 21);
for (i = 0; *p && i < l; i++, p++)
addr[i] = strtol(p, &p, 10);
if (i < l) {
e = 999;
goto ouch;
}
break;
case FTP_EPASSIVE_MODE:
if (sscanf(p, "%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]) {
e = 999;
goto ouch;
}
break;
}
/* seek to required offset */
if (offset)
@ -257,13 +325,36 @@ _ftp_transfer(int cd, char *oper, char *file,
l = sizeof sin;
if (getpeername(cd, (struct sockaddr *)&sin, &l) == -1)
goto sysouch;
bcopy(addr, (char *)&sin.sin_addr, 4);
bcopy(addr + 4, (char *)&sin.sin_port, 2);
if (sin.ss_family == AF_INET6)
unmappedaddr((struct sockaddr_in6 *)&sin);
switch (sin.ss_family) {
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&sin;
if (e == FTP_EPASSIVE_MODE)
sin6->sin6_port = htons(port);
else {
bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);
bcopy(addr + 19, (char *)&sin6->sin6_port, 2);
}
break;
case AF_INET:
sin4 = (struct sockaddr_in *)&sin;
if (e == FTP_EPASSIVE_MODE)
sin4->sin_port = htons(port);
else {
bcopy(addr, (char *)&sin4->sin_addr, 4);
bcopy(addr + 4, (char *)&sin4->sin_port, 2);
}
break;
default:
e = 999; /* XXX: error code should be prepared */
break;
}
/* connect to data port */
if (verbose)
_fetch_info("opening data connection");
if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1)
if (connect(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
goto sysouch;
/* make the server initiate the transfer */
@ -277,19 +368,30 @@ _ftp_transfer(int cd, char *oper, char *file,
u_int32_t a;
u_short p;
int arg, d;
char *ap;
char hname[INET6_ADDRSTRLEN];
/* find our own address, bind, and listen */
l = sizeof sin;
if (getsockname(cd, (struct sockaddr *)&sin, &l) == -1)
goto sysouch;
sin.sin_port = 0;
arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
(char *)&arg, sizeof arg) == -1)
goto sysouch;
switch (sin.ss_family) {
case AF_INET6:
((struct sockaddr_in6 *)&sin)->sin6_port = 0;
#ifdef IPV6_PORTRANGE
arg = high ? IPV6_PORTRANGE_HIGH : IPV6_PORTRANGE_DEFAULT;
if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,
(char *)&arg, sizeof(arg)) == -1)
goto sysouch;
#endif
break;
case AF_INET:
((struct sockaddr_in *)&sin)->sin_port = 0;
arg = high ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
(char *)&arg, sizeof arg) == -1)
goto sysouch;
break;
}
if (verbose)
_fetch_info("binding data socket");
if (bind(sd, (struct sockaddr *)&sin, l) == -1)
if (bind(sd, (struct sockaddr *)&sin, sin.ss_len) == -1)
goto sysouch;
if (listen(sd, 1) == -1)
goto sysouch;
@ -297,12 +399,46 @@ _ftp_transfer(int cd, char *oper, char *file,
/* find what port we're on and tell the server */
if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1)
goto sysouch;
a = ntohl(sin.sin_addr.s_addr);
p = ntohs(sin.sin_port);
e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
(a >> 24) & 0xff, (a >> 16) & 0xff,
(a >> 8) & 0xff, a & 0xff,
(p >> 8) & 0xff, p & 0xff);
switch (sin.ss_family) {
case AF_INET:
sin4 = (struct sockaddr_in *)&sin;
a = ntohl(sin4->sin_addr.s_addr);
p = ntohs(sin4->sin_port);
e = _ftp_cmd(cd, "PORT %d,%d,%d,%d,%d,%d",
(a >> 24) & 0xff, (a >> 16) & 0xff,
(a >> 8) & 0xff, a & 0xff,
(p >> 8) & 0xff, p & 0xff);
break;
case AF_INET6:
#define UC(b) (((int)b)&0xff)
e = -1;
sin6 = (struct sockaddr_in6 *)&sin;
if (getnameinfo((struct sockaddr *)&sin, sin.ss_len,
hname, sizeof(hname),
NULL, 0, NI_NUMERICHOST) == 0) {
e = _ftp_cmd(cd, "EPRT |%d|%s|%d|", 2, hname,
htons(sin6->sin6_port));
if (e == -1)
goto ouch;
}
if (e != FTP_OK) {
ap = (char *)&sin6->sin6_addr;
e = _ftp_cmd(cd,
"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(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),
UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),
UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),
UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),
2,
(ntohs(sin6->sin6_port) >> 8) & 0xff,
ntohs(sin6->sin6_port) & 0xff);
}
break;
default:
e = 999; /* XXX: error code should be prepared */
goto ouch;
}
if (e != FTP_OK)
goto ouch;
@ -326,13 +462,15 @@ _ftp_transfer(int cd, char *oper, char *file,
sysouch:
_fetch_syserr();
close(sd);
if (sd >= 0)
close(sd);
return NULL;
ouch:
if (e != -1)
_ftp_seterr(e);
close(sd);
if (sd >= 0)
close(sd);
return NULL;
}
@ -343,14 +481,30 @@ static int
_ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
{
int cd, e, pp = 0, direct, verbose;
#ifdef INET6
int af = AF_UNSPEC;
#else
int af = AF_INET;
#endif
char *p, *q;
direct = (flags && strchr(flags, 'd'));
verbose = (flags && strchr(flags, 'v'));
if ((flags && strchr(flags, '4')))
af = AF_INET;
else if ((flags && strchr(flags, '6')))
af = AF_INET6;
/* check for proxy */
if (!direct && (p = getenv("FTP_PROXY")) != NULL) {
if ((q = strchr(p, ':')) != NULL) {
char c = 0;
#ifdef INET6
if (*p != '[' || (q = strchr(p + 1, ']')) == NULL ||
(*++q != '\0' && *q != ':'))
#endif
q = strchr(p, ':');
if (q != NULL && *q == ':') {
if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) {
/* XXX we should emit some kind of warning */
}
@ -367,14 +521,22 @@ _ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
else
pp = FTP_DEFAULT_PORT;
}
if (q)
if (q) {
#ifdef INET6
if (q > p && *p == '[' && *(q - 1) == ']') {
p++;
q--;
}
#endif
c = *q;
*q = 0;
cd = _fetch_connect(p, pp, verbose);
}
cd = _fetch_connect(p, pp, af, verbose);
if (q)
*q = ':';
*q = c;
} else {
/* no proxy, go straight to target */
cd = _fetch_connect(host, port, verbose);
cd = _fetch_connect(host, port, af, verbose);
p = NULL;
}

View File

@ -18,7 +18,8 @@
221 OK Service closing control connection
225 OK Data connection open; no transfer in progress
226 OK Requested file action successful
227 OK Entering Passive Mode
227 OK Entering Passive Mode
229 OK Entering Extended Passive Mode
230 OK User logged in, proceed
250 OK Requested file action okay, completed
257 OK File/directory created

View File

@ -61,6 +61,7 @@
* SUCH DAMAGE. */
#include <sys/param.h>
#include <sys/socket.h>
#include <err.h>
#include <ctype.h>
@ -302,12 +303,21 @@ FILE *
_http_connect(struct url *URL, char *flags)
{
int direct, sd = -1, verbose;
#ifdef INET6
int af = AF_UNSPEC;
#else
int af = AF_INET;
#endif
size_t len;
char *px;
FILE *f;
direct = (flags && strchr(flags, 'd'));
verbose = (flags && strchr(flags, 'v'));
if ((flags && strchr(flags, '4')))
af = AF_INET;
else if ((flags && strchr(flags, '6')))
af = AF_INET6;
/* check port */
if (!URL->port) {
@ -331,7 +341,12 @@ _http_connect(struct url *URL, char *flags)
int port = 0;
/* measure length */
len = strcspn(px, ":");
#ifdef INET6
if (px[0] != '[' ||
(len = strcspn(px, "]")) >= strlen(px) ||
(px[++len] != '\0' && px[len] != ':'))
#endif
len = strcspn(px, ":");
/* get port (XXX atoi is a little too tolerant perhaps?) */
if (px[len] == ':') {
@ -360,20 +375,26 @@ _http_connect(struct url *URL, char *flags)
}
/* get host name */
#ifdef INET6
if (len > 1 && px[0] == '[' && px[len - 1] == ']') {
px++;
len -= 2;
}
#endif
if (len >= MAXHOSTNAMELEN)
len = MAXHOSTNAMELEN - 1;
strncpy(host, px, len);
host[len] = 0;
/* connect */
sd = _fetch_connect(host, port, verbose);
sd = _fetch_connect(host, port, af, verbose);
}
/* if no proxy is configured or could be contacted, try direct */
if (sd == -1) {
if (strcasecmp(URL->scheme, "ftp") == 0)
goto ouch;
if ((sd = _fetch_connect(URL->host, URL->port, verbose)) == -1)
if ((sd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1)
goto ouch;
}
@ -399,15 +420,27 @@ _http_request(FILE *f, char *op, struct url *URL, char *flags)
int e, verbose;
char *ln, *p;
size_t len;
char *host;
#ifdef INET6
char hbuf[MAXHOSTNAMELEN + 1];
#endif
verbose = (flags && strchr(flags, 'v'));
host = URL->host;
#ifdef INET6
if (strchr(URL->host, ':')) {
snprintf(hbuf, sizeof(hbuf), "[%s]", URL->host);
host = hbuf;
}
#endif
/* send request (proxies require absolute form, so use that) */
if (verbose)
_fetch_info("requesting %s://%s:%d%s",
URL->scheme, URL->host, URL->port, URL->doc);
URL->scheme, host, URL->port, URL->doc);
_http_cmd(f, "%s %s://%s:%d%s HTTP/1.1" ENDL,
op, URL->scheme, URL->host, URL->port, URL->doc);
op, URL->scheme, host, URL->port, URL->doc);
/* start sending headers away */
if (URL->user[0] || URL->pwd[0]) {
@ -417,7 +450,7 @@ _http_request(FILE *f, char *op, struct url *URL, char *flags)
_http_cmd(f, "Authorization: Basic %s" ENDL, auth_str);
free(auth_str);
}
_http_cmd(f, "Host: %s:%d" ENDL, URL->host, URL->port);
_http_cmd(f, "Host: %s:%d" ENDL, host, URL->port);
_http_cmd(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname);
if (URL->offset)
_http_cmd(f, "Range: bytes=%lld-" ENDL, URL->offset);