aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/ftp.c263
-rw-r--r--lib/hostip.c23
-rw-r--r--lib/hostip.h5
-rw-r--r--lib/url.c71
-rw-r--r--lib/urldata.h4
5 files changed, 358 insertions, 8 deletions
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 */