diff --git a/CHANGES b/CHANGES index 549a71a54..4278d01b5 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,9 @@ Daniel (5 February 2001) +- Jun-ichiro itojun Hagino brought a big patch that brings IPv6-awareness to + a bunch of different areas within libcurl. + - Robert Weaver told me about the problems the MS VC++ 6.0 compiler has with the 'static' keyword on a number of libcurl functions. I might need to add a patch that redefines static when libcurl is compiled with that compiler. diff --git a/configure.in b/configure.in index 4ad067aea..de707cd21 100644 --- a/configure.in +++ b/configure.in @@ -53,15 +53,9 @@ dnl AC_DEFUN(CURL_CHECK_WORKING_GETADDRINFO,[ AC_CACHE_CHECK(for working getaddrinfo, ac_cv_working_getaddrinfo,[ AC_TRY_RUN( [ -#ifdef HAVE_NETDB_H #include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_SYS_SOCKET_H +#include #include -#endif void main(void) { struct addrinfo hints, *ai; @@ -434,6 +428,10 @@ AC_MSG_CHECKING([if Kerberos4 support is requested]) if test "$want_krb4" = yes then + if test "$ipv6" = "yes"; then + echo krb4 is not compatible with IPv6 + exit 1 + fi AC_MSG_RESULT(yes) dnl Check for & handle argument to --with-krb4 @@ -691,7 +689,8 @@ AC_CHECK_FUNCS( socket \ setvbuf \ sigaction \ signal \ - getpass_r + getpass_r \ + strlcat ) dnl removed 'getpass' check on October 26, 2000 diff --git a/docs/curl.1 b/docs/curl.1 index 62331433d..f57e93fd7 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -788,6 +788,7 @@ If you do find bugs, mail them to curl-bug@haxx.se. - Loic Dachary - Robert Weaver - Ingo Ralf Blum + - Jun-ichiro itojun Hagino .SH WWW http://curl.haxx.se diff --git a/lib/ftp.c b/lib/ftp.c index 0a2c43aaf..069b9d100 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -564,6 +564,9 @@ CURLcode _ftp(struct connectdata *conn) #if defined (HAVE_INET_NTOA_R) char ntoa_buf[64]; #endif +#ifdef ENABLE_IPV6 + struct addrinfo *ai; +#endif struct curl_slist *qitem; /* QUOTE item */ /* the ftp struct is already inited in ftp_connect() */ @@ -702,6 +705,174 @@ CURLcode _ftp(struct connectdata *conn) /* We have chosen to use the PORT command */ if(data->bits.ftp_use_port) { +#ifdef ENABLE_IPV6 + struct addrinfo hints, *res, *ai; + struct sockaddr_storage ss; + int sslen; + char hbuf[NI_MAXHOST]; + char *localaddr; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif + unsigned char *ap; + unsigned char *pp; + int alen, plen; + char portmsgbuf[4096], tmp[4096]; + char *p; + char *mode[] = { "EPRT", "LPRT", "PORT", NULL }; + char **modep; + + /* + * we should use Curl_if2ip? given pickiness of recent ftpd, + * I believe we should use the same address as the control connection. + */ + sslen = sizeof(ss); + if (getsockname(data->firstsocket, (struct sockaddr *)&ss, &sslen) < 0) + return CURLE_FTP_PORT_FAILED; + + if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0, + niflags)) + return CURLE_FTP_PORT_FAILED; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ss.ss_family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if (getaddrinfo(hbuf, "0", &hints, &res)) + return CURLE_FTP_PORT_FAILED; + + portsock = -1; + for (ai = res; ai; ai = ai->ai_next) { + portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (portsock < 0) + continue; + + if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { + close(portsock); + portsock = -1; + continue; + } + + if (listen(portsock, 1) < 0) { + close(portsock); + portsock = -1; + continue; + } + + break; + } + if (portsock < 0) { + failf(data, strerror(errno)); + freeaddrinfo(res); + return CURLE_FTP_PORT_FAILED; + } + + sslen = sizeof(ss); + if (getsockname(portsock, (struct sockaddr *)&ss, &sslen) < 0) { + failf(data, strerror(errno)); + freeaddrinfo(res); + return CURLE_FTP_PORT_FAILED; + } + + for (modep = mode; modep && *modep; modep++) { + int lprtaf, eprtaf; + + switch (ss.ss_family) { + case AF_INET: + ap = (char *)&((struct sockaddr_in *)&ss)->sin_addr; + alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr); + pp = (char *)&((struct sockaddr_in *)&ss)->sin_port; + plen = sizeof(((struct sockaddr_in *)&ss)->sin_port); + lprtaf = 4; + eprtaf = 1; + break; + case AF_INET6: + ap = (char *)&((struct sockaddr_in6 *)&ss)->sin6_addr; + alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr); + pp = (char *)&((struct sockaddr_in6 *)&ss)->sin6_port; + plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port); + lprtaf = 6; + eprtaf = 2; + break; + default: + ap = pp = NULL; + lprtaf = eprtaf = -1; + break; + } + + if (strcmp(*modep, "EPRT") == 0) { + if (eprtaf < 0) + continue; + if (getnameinfo((struct sockaddr *)&ss, sslen, + portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags)) + continue; + /* do not transmit IPv6 scope identifier to the wire */ + if (ss.ss_family == AF_INET6) { + char *q = strchr(portmsgbuf, '%'); + if (q) + *q = '\0'; + } + ftpsendf(data->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf, + portmsgbuf, tmp); + } else if (strcmp(*modep, "LPRT") == 0 || strcmp(*modep, "PORT") == 0) { + int i; + + if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0) + continue; + if (strcmp(*modep, "PORT") == 0 && ss.ss_family != AF_INET) + continue; + + portmsgbuf[0] = '\0'; + if (strcmp(*modep, "LPRT") == 0) { + snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen); + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) { + goto again; + } + } + for (i = 0; i < alen; i++) { + if (portmsgbuf[0]) + snprintf(tmp, sizeof(tmp), ",%u", ap[i]); + else + snprintf(tmp, sizeof(tmp), "%u", ap[i]); + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) { + goto again; + } + } + if (strcmp(*modep, "LPRT") == 0) { + snprintf(tmp, sizeof(tmp), ",%d", plen); + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) + goto again; + } + for (i = 0; i < plen; i++) { + snprintf(tmp, sizeof(tmp), ",%u", pp[i]); + if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) { + goto again; + } + } + ftpsendf(data->firstsocket, conn, "%s %s", *modep, portmsgbuf); + } + + nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + if (nread < 0) + return CURLE_OPERATION_TIMEOUTED; + + if (ftpcode != 200) { + failf(data, "Server does not grok %s", *modep); + continue; + } else + break; +again:; + } + + if (!*modep) { + close(portsock); + freeaddrinfo(res); + return CURLE_FTP_PORT_FAILED; + } + +#else struct sockaddr_in sa; struct hostent *h=NULL; char *hostdataptr=NULL; @@ -809,26 +980,43 @@ CURLcode _ftp(struct connectdata *conn) failf(data, "Server does not grok PORT, try without it!"); return CURLE_FTP_PORT_FAILED; } +#endif /* ENABLE_IPV6 */ } else { /* we use the PASV command */ +#if 0 + char *mode[] = { "EPSV", "LPSV", "PASV", NULL }; + int results[] = { 229, 228, 227, 0 }; +#else + char *mode[] = { "PASV", NULL }; + int results[] = { 227, 0 }; +#endif + int modeoff; - ftpsendf(data->firstsocket, conn, "PASV"); + for (modeoff = 0; mode[modeoff]; modeoff++) { + ftpsendf(data->firstsocket, conn, mode[modeoff]); + nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); + if(nread < 0) + return CURLE_OPERATION_TIMEOUTED; - nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode); - if(nread < 0) - return CURLE_OPERATION_TIMEOUTED; + if (ftpcode == results[modeoff]) + break; + } - if(ftpcode != 227) { + if (!mode[modeoff]) { failf(data, "Odd return code after PASV"); return CURLE_FTP_WEIRD_PASV_REPLY; } - else { + else if (strcmp(mode[modeoff], "PASV") == 0) { int ip[4]; int port[2]; unsigned short newport; /* remote port, not necessary the local one */ unsigned short connectport; /* the local port connect() should use! */ char newhost[32]; +#ifdef ENABLE_IPV6 + struct addrinfo *res; +#else struct hostent *he; +#endif char *str=buf,*ip_addr; char *hostdataptr=NULL; @@ -863,20 +1051,78 @@ CURLcode _ftp(struct connectdata *conn) * proxy again here. We already have the name info for it since the * previous lookup. */ +#ifdef ENABLE_IPV6 + res = conn->res; +#else he = conn->hp; +#endif connectport = (unsigned short)data->port; /* we connect to the proxy's port */ } else { /* normal, direct, ftp connection */ +#ifdef ENABLE_IPV6 + res = Curl_getaddrinfo(data, newhost, newport); + if(!res) +#else he = Curl_gethost(data, newhost, &hostdataptr); - if(!he) { + if(!he) +#endif + { failf(data, "Can't resolve new host %s", newhost); return CURLE_FTP_CANT_GET_HOST; } connectport = newport; /* we connect to the remote port */ } +#ifdef ENABLE_IPV6 + data->secondarysocket = -1; + for (ai = res; ai; ai = ai->ai_next) { + /* XXX for now, we can do IPv4 only */ + if (ai->ai_family != AF_INET) + continue; + + data->secondarysocket = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + if (data->secondarysocket < 0) + continue; + + if(data->bits.verbose) { + char hbuf[NI_MAXHOST]; + char nbuf[NI_MAXHOST]; + char sbuf[NI_MAXSERV]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +#endif + if (getnameinfo(res->ai_addr, res->ai_addrlen, nbuf, sizeof(nbuf), + sbuf, sizeof(sbuf), niflags)) { + snprintf(nbuf, sizeof(nbuf), "?"); + snprintf(sbuf, sizeof(sbuf), "?"); + } + if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), + NULL, 0, 0)) { + infof(data, "Connecting to %s port %s\n", nbuf, sbuf); + } else { + infof(data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf); + } + } + + if (connect(data->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) { + close(data->secondarysocket); + data->secondarysocket = -1; + continue; + } + + break; + } + + if (data->secondarysocket < 0) { + failf(data, strerror(errno)); + return CURLE_FTP_CANT_RECONNECT; + } +#else data->secondarysocket = socket(AF_INET, SOCK_STREAM, 0); memset((char *) &serv_addr, '\0', sizeof(serv_addr)); @@ -971,6 +1217,7 @@ CURLcode _ftp(struct connectdata *conn) } return CURLE_FTP_CANT_RECONNECT; } +#endif /*ENABLE_IPV6*/ if (data->bits.tunnel_thru_httpproxy) { /* We want "seamless" FTP operations through HTTP proxy tunnel */ @@ -979,6 +1226,8 @@ CURLcode _ftp(struct connectdata *conn) if(CURLE_OK != result) return result; } + } else { + return CURLE_FTP_CANT_RECONNECT; } } /* we have the (new) data connection ready */ diff --git a/lib/hostip.c b/lib/hostip.c index cb7bc19a5..c1f3fde11 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -83,6 +83,29 @@ static char *MakeIP(unsigned long num,char *addr, int addr_len) return (addr); } +#ifdef ENABLE_IPV6 +struct addrinfo *Curl_getaddrinfo(struct UrlData *data, + char *hostname, + int port) +{ + struct addrinfo hints, *res; + int error; + char sbuf[NI_MAXSERV]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + snprintf(sbuf, sizeof(sbuf), "%d", port); + error = getaddrinfo(hostname, sbuf, &hints, &res); + if (error) { + infof(data, "getaddrinfo(3) failed for %s\n", hostname); + return NULL; + } + return res; +} +#endif + /* The original code to this function was once stolen from the Dancer source code, written by Bjorn Reese, it has since been patched and modified considerably. */ diff --git a/lib/hostip.h b/lib/hostip.h index 78453487b..d7c9b26f1 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -23,6 +23,11 @@ * $Id$ *****************************************************************************/ +struct addrinfo; +struct addrinfo *Curl_getaddrinfo(struct UrlData *data, + char *hostname, + int port); + struct hostent *Curl_gethost(struct UrlData *data, char *hostname, char **bufp); diff --git a/lib/url.c b/lib/url.c index e37f24163..98d41acb7 100644 --- a/lib/url.c +++ b/lib/url.c @@ -562,8 +562,13 @@ CURLcode curl_disconnect(CURLconnect *c_connect) struct UrlData *data = conn->data; +#ifdef ENABLE_IPV6 + if(conn->res) /* host name info */ + freeaddrinfo(conn->res); +#else if(conn->hostent_buf) /* host name info */ free(conn->hostent_buf); +#endif if(conn->path) /* the URL path part */ free(conn->path); @@ -589,6 +594,9 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) struct sigaction sigact; #endif int urllen; +#ifdef ENABLE_IPV6 + struct addrinfo *ai; +#endif /************************************************************* * Check input data @@ -1189,13 +1197,23 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) data->port = data->remote_port; /* it is the same port */ /* Connect to target host right on */ +#ifdef ENABLE_IPV6 + conn->res = Curl_getaddrinfo(data, conn->name, data->port); + if(!conn->res) +#else conn->hp = Curl_gethost(data, conn->name, &conn->hostent_buf); - if(!conn->hp) { + if(!conn->hp) +#endif + { failf(data, "Couldn't resolve host '%s'", conn->name); return CURLE_COULDNT_RESOLVE_HOST; } } else { +#ifdef ENABLE_IPV6 + failf(data, "proxy yet to be supported"); + return CURLE_OUT_OF_MEMORY; +#else char *prox_portno; char *endofprot; @@ -1244,9 +1262,11 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) } free(proxydup); /* free the duplicate pointer and not the modified */ +#endif } Curl_pgrsTime(data, TIMER_NAMELOOKUP); +#ifndef ENABLE_IPV6 data->firstsocket = socket(AF_INET, SOCK_STREAM, 0); memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr)); @@ -1254,6 +1274,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) conn->hp->h_addr, conn->hp->h_length); conn->serv_addr.sin_family = conn->hp->h_addrtype; conn->serv_addr.sin_port = htons(data->port); +#endif #if !defined(WIN32)||defined(__CYGWIN32__) /* We don't generally like checking for OS-versions, we should make this @@ -1266,6 +1287,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) #define INADDR_NONE (unsigned long) ~0 #endif +#ifndef ENABLE_IPV6 /************************************************************* * Select device to bind socket to *************************************************************/ @@ -1374,10 +1396,31 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) } /* end of device selection support */ #endif /* end of HAVE_INET_NTOA */ #endif /* end of not WIN32 */ +#endif /*ENABLE_IPV6*/ /************************************************************* * Connect to server/proxy *************************************************************/ +#ifdef ENABLE_IPV6 + data->firstsocket = -1; + for (ai = conn->res; ai; ai = ai->ai_next) { + data->firstsocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (data->firstsocket < 0) + continue; + + if (connect(data->firstsocket, ai->ai_addr, ai->ai_addrlen) < 0) { + close(data->firstsocket); + data->firstsocket = -1; + continue; + } + + break; + } + if (data->firstsocket < 0) { + failf(data, strerror(errno)); + return CURLE_COULDNT_CONNECT; + } +#else if (connect(data->firstsocket, (struct sockaddr *) &(conn->serv_addr), sizeof(conn->serv_addr) @@ -1426,6 +1469,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) } return CURLE_COULDNT_CONNECT; } +#endif /************************************************************* * Proxy authentication @@ -1473,11 +1517,31 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect) conn->bytecount = 0; /* Figure out the ip-number and display the first host name it shows: */ +#ifdef ENABLE_IPV6 + { + char hbuf[NI_MAXHOST]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST; +#endif + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, + niflags)) { + snprintf(hbuf, sizeof(hbuf), "?"); + } + if (ai->ai_canonname) { + infof(data, "Connected to %s (%s)\n", ai->ai_canonname, hbuf); + } else { + infof(data, "Connected to %s\n", hbuf); + } + } +#else { struct in_addr in; (void) memcpy(&in.s_addr, *conn->hp->h_addr_list, sizeof (in.s_addr)); infof(data, "Connected to %s (%s)\n", conn->hp->h_name, inet_ntoa(in)); } +#endif #ifdef __EMX__ /* 20000330 mgs @@ -1509,8 +1573,13 @@ CURLcode curl_connect(CURL *curl, CURLconnect **in_connect) if(conn) { if(conn->path) free(conn->path); +#ifdef ENABLE_IPV6 + if(conn->res) + freeaddrinfo(conn->res); +#else if(conn->hostent_buf) free(conn->hostent_buf); +#endif free(conn); *in_connect=NULL; } diff --git a/lib/urldata.h b/lib/urldata.h index e053caffd..325c06b49 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -159,9 +159,13 @@ struct connectdata { #define PROT_LDAP (1<<7) #define PROT_FILE (1<<8) +#ifdef ENABLE_IPV6 + struct addrinfo *res; +#else char *hostent_buf; /* pointer to allocated memory for name info */ struct hostent *hp; struct sockaddr_in serv_addr; +#endif char proto[64]; /* store the protocol string in this buffer */ char gname[257]; /* store the hostname in this buffer */ char *name; /* host name pointer to fool around with */