diff options
author | Daniel Stenberg <daniel@haxx.se> | 2001-02-05 23:04:44 +0000 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2001-02-05 23:04:44 +0000 |
commit | f6e2bfd464a7ffd7ec24194da2a9a1e285387560 (patch) | |
tree | 25f0bcff222cb2da182f070076e45b972380e62e /lib/ftp.c | |
parent | 1ae5dab8fbf1df0d11a7d3aa93e0100c629178bc (diff) |
Jun-ichiro itojun Hagino's IPv6 adjustments
Diffstat (limited to 'lib/ftp.c')
-rw-r--r-- | lib/ftp.c | 263 |
1 files changed, 256 insertions, 7 deletions
@@ -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 */ |