aboutsummaryrefslogtreecommitdiff
path: root/lib/ftp.c
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2009-06-29 20:46:01 +0000
committerDaniel Stenberg <daniel@haxx.se>2009-06-29 20:46:01 +0000
commit4f551259ddf79163660fdf44ec57b6ed2f409e50 (patch)
tree42889b2d80a2a39bcbfe8aeef3cd9829de27a6d8 /lib/ftp.c
parent9cb68c6e32f29ad5ba00195a164371006e26467a (diff)
- Markus Koetter made CURLOPT_FTPPORT (and curl's -P/--ftpport) support a port
range if given colon-separated after the host name/address part. Like "192.168.0.1:2000-10000"
Diffstat (limited to 'lib/ftp.c')
-rw-r--r--lib/ftp.c346
1 files changed, 159 insertions, 187 deletions
diff --git a/lib/ftp.c b/lib/ftp.c
index b46402be5..80841ee37 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -290,7 +290,8 @@ static void freedirs(struct ftp_conn *ftpc)
*/
static bool isBadFtpString(const char *string)
{
- return (bool)((NULL != strchr(string, '\r')) || (NULL != strchr(string, '\n')));
+ return (bool)((NULL != strchr(string, '\r')) ||
+ (NULL != strchr(string, '\n')));
}
/***********************************************************************
@@ -882,36 +883,116 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
curl_socket_t portsock= CURL_SOCKET_BAD;
char myhost[256] = "";
-#ifdef ENABLE_IPV6
- /******************************************************************
- * IPv6-specific section
- */
struct Curl_sockaddr_storage ss;
Curl_addrinfo *res, *ai;
curl_socklen_t sslen;
char hbuf[NI_MAXHOST];
struct sockaddr *sa=(struct sockaddr *)&ss;
struct sockaddr_in * const sa4 = (void *)sa;
+#ifdef ENABLE_IPV6
struct sockaddr_in6 * const sa6 = (void *)sa;
+#endif
char tmp[1024];
static const char mode[][5] = { "EPRT", "PORT" };
int rc;
int error;
char *host=NULL;
+ char *string_ftpport = data->set.str[STRING_FTPPORT];
struct Curl_dns_entry *h=NULL;
- unsigned short port = 0;
+ unsigned short port_min = 0;
+ unsigned short port_max = 0;
+ unsigned short port;
- /* Step 1, figure out what address that is requested */
+ char *addr = NULL;
+
+ /* Step 1, figure out what is requested,
+ * accepted format :
+ * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
+ */
if(data->set.str[STRING_FTPPORT] &&
(strlen(data->set.str[STRING_FTPPORT]) > 1)) {
- /* attempt to get the address of the given interface name */
- if(!Curl_if2ip(conn->ip_addr->ai_family, data->set.str[STRING_FTPPORT],
- hbuf, sizeof(hbuf)))
- /* not an interface, use the given string as host name instead */
- host = data->set.str[STRING_FTPPORT];
- else
- host = hbuf; /* use the hbuf for host name */
+
+#ifdef ENABLE_IPV6
+ size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
+ INET6_ADDRSTRLEN : strlen(string_ftpport);
+#else
+ size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
+ INET_ADDRSTRLEN : strlen(string_ftpport);
+#endif
+ char *ip_start = string_ftpport;
+ char *ip_end = NULL;
+ char *port_start = NULL;
+ char *port_sep = NULL;
+
+ addr = malloc(addrlen);
+ memset(addr, 0, addrlen);
+
+
+#ifdef ENABLE_IPV6
+ if(*string_ftpport == '[') {
+ /* [ipv6]:port(-range) */
+ ip_start = string_ftpport + 1;
+ if((ip_end = strchr(string_ftpport, ']')) != NULL )
+ strncpy(addr, ip_start, ip_end - ip_start);
+ } else
+#endif
+ if( *string_ftpport == ':') {
+ /* :port */
+ ip_end = string_ftpport;
+ } else
+ if( (ip_end = strchr(string_ftpport, ':')) != NULL) {
+ /* either ipv6 or (ipv4|domain|interface):port(-range) */
+#ifdef ENABLE_IPV6
+ if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
+ /* ipv6 */
+ port_min = port_max = 0;
+ strcpy(addr, string_ftpport);
+ ip_end = NULL; /* this got no port ! */
+ } else
+#endif
+ {
+ /* (ipv4|domain|interface):port(-range) */
+ strncpy(addr, string_ftpport, ip_end - ip_start );
+ }
+ } else {
+ /* ipv4|interface */
+ strcpy(addr, string_ftpport);
+ }
+
+ /* parse the port */
+ if( ip_end != NULL ) {
+ if((port_start = strchr(ip_end, ':')) != NULL) {
+ port_min = (unsigned short)strtol(port_start+1, NULL, 10);
+ if((port_sep = strchr(port_start, '-')) != NULL) {
+ port_max = (unsigned short)strtol(port_sep + 1, NULL, 10);
+ }
+ else
+ port_max = port_min;
+ }
+ }
+
+ /* correct errors like:
+ * :1234-1230
+ * :-4711 , in this case port_min is (unsigned)-1,
+ * therefore port_min > port_max for all cases
+ * but port_max = (unsigned)-1
+ */
+ if(port_min > port_max )
+ port_min = port_max = 0;
+
+
+ if(*addr != '\0') {
+ /* attempt to get the address of the given interface name */
+ if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
+ hbuf, sizeof(hbuf)))
+ /* not an interface, use the given string as host name instead */
+ host = addr;
+ else
+ host = hbuf; /* use the hbuf for host name */
+ }else
+ /* there was only a port(-range) given, default the host */
+ host = NULL;
} /* data->set.ftpport */
if(!host) {
@@ -922,20 +1003,24 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
if(getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
+ if (addr)
+ free(addr);
return CURLE_FTP_PORT_FAILED;
}
- if(sslen > (curl_socklen_t)sizeof(ss))
- sslen = sizeof(ss);
+ sslen = sizeof(ss);
rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL,
0, NIFLAGS);
if(rc) {
failf(data, "getnameinfo() returned %d", rc);
+ if (addr)
+ free(addr);
return CURLE_FTP_PORT_FAILED;
}
host = hbuf; /* use this host name */
}
+ /* resolv ip/host to ip */
rc = Curl_resolv(conn, host, 0, &h);
if(rc == CURLRESOLV_PENDING)
rc = Curl_wait_for_resolv(conn, &h);
@@ -948,6 +1033,13 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
else
res = NULL; /* failure! */
+ if (addr)
+ free(addr);
+
+ if (res == NULL) {
+ failf(data, "Curl_resolv failed, we can not recover!");
+ return CURLE_FTP_PORT_FAILED;
+ }
/* step 2, create a socket for the requested address */
@@ -974,32 +1066,56 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
/* step 3, bind to a suitable local address */
- /* Try binding the given address. */
- if(bind(portsock, ai->ai_addr, ai->ai_addrlen)) {
-
- /* It failed. Bind the address used for the control connection instead */
- sslen = sizeof(ss);
- if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
- failf(data, "getsockname() failed: %s",
- Curl_strerror(conn, SOCKERRNO) );
- sclose(portsock);
- return CURLE_FTP_PORT_FAILED;
- }
+ memcpy(sa, ai->ai_addr, ai->ai_addrlen);
+ sslen = ai->ai_addrlen;
- /* set port number to zero to make bind() pick "any" */
- if(sa->sa_family == AF_INET)
- sa4->sin_port = 0;
+ for( port = port_min; port <= port_max; ) {
+ if( sa->sa_family == AF_INET )
+ sa4->sin_port = htons(port);
+#ifdef ENABLE_IPV6
else
- sa6->sin6_port = 0;
+ sa6->sin6_port = htons(port);
+#endif
+ /* Try binding the given address. */
+ if(bind(portsock, sa, sslen) ) {
+ /* It failed. */
+ if(errno == EADDRNOTAVAIL) {
+
+ /* The requested bind address is not local
+ * use the address used forthe control connection instead
+ * restart the port loop
+ */
+ failf(data, "bind(port=%i) failed: %s", port,
+ Curl_strerror(conn, SOCKERRNO) );
- if(sslen > (curl_socklen_t)sizeof(ss))
- sslen = sizeof(ss);
+ sslen = sizeof(ss);
+ if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
+ failf(data, "getsockname() failed: %s",
+ Curl_strerror(conn, SOCKERRNO) );
+ sclose(portsock);
+ return CURLE_FTP_PORT_FAILED;
+ }
+ port = port_min;
+ continue;
+ }else
+ if(errno != EADDRINUSE && errno != EACCES) {
+ failf(data, "bind(port=%i) failed: %s", port,
+ Curl_strerror(conn, SOCKERRNO) );
+ sclose(portsock);
+ return CURLE_FTP_PORT_FAILED;
+ }
- if(bind(portsock, sa, sslen)) {
- failf(data, "bind failed: %s", Curl_strerror(conn, SOCKERRNO));
- sclose(portsock);
- return CURLE_FTP_PORT_FAILED;
- }
+ } else
+ break;
+
+ port++;
+ }
+
+ /* maybe all ports were in use already*/
+ if (port > port_max) {
+ failf(data, "bind() failed, we ran out of ports!");
+ sclose(portsock);
+ return CURLE_FTP_PORT_FAILED;
}
/* get the name again after the bind() so that we can extract the
@@ -1039,6 +1155,10 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
/* if disabled, goto next */
continue;
+ if((PORT == fcmd) && sa->sa_family != AF_INET)
+ /* PORT is ipv4 only */
+ continue;
+
switch (sa->sa_family) {
case AF_INET:
port = ntohs(sa4->sin_port);
@@ -1060,7 +1180,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
*/
result = Curl_nbftpsendf(conn, "%s |%d|%s|%d|", mode[fcmd],
- ai->ai_family == AF_INET?1:2,
+ sa->sa_family == AF_INET?1:2,
myhost, port);
if(result)
return result;
@@ -1070,9 +1190,6 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
char *source = myhost;
char *dest = tmp;
- if((PORT == fcmd) && ai->ai_family != AF_INET)
- continue;
-
/* translate x.x.x.x to x,x,x,x */
while(source && *source) {
if(*source == '.')
@@ -1102,151 +1219,6 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = portsock;
-#else
- /******************************************************************
- * IPv4-specific section
- */
- struct sockaddr_in sa;
- unsigned short porttouse;
- bool sa_filled_in = FALSE;
- Curl_addrinfo *addr = NULL;
- unsigned short ip[4];
- bool freeaddr = TRUE;
- curl_socklen_t sslen = sizeof(sa);
- const char *ftpportstr = data->set.str[STRING_FTPPORT];
-
- (void)fcmd; /* not used in the IPv4 code */
- if(ftpportstr) {
- struct in_addr in;
-
- /* First check if the given string is an IP address */
- if(Curl_inet_pton(AF_INET, ftpportstr, &in) > 0) {
- /* this is an IPv4 address */
- addr = Curl_ip2addr(AF_INET, &in, ftpportstr, 0);
- }
- /* otherwise check if the given string is an interface */
- else if(Curl_if2ip(AF_INET, ftpportstr, myhost, sizeof(myhost))) {
- /* The interface to IP conversion provided a dotted address */
- if(Curl_inet_pton(AF_INET, myhost, &in) > 0)
- addr = Curl_ip2addr(AF_INET, &in, myhost, 0);
- }
- else if(strlen(ftpportstr)> 1) {
- /* might be a host name! */
- struct Curl_dns_entry *h=NULL;
- int rc = Curl_resolv(conn, ftpportstr, 0, &h);
- if(rc == CURLRESOLV_PENDING)
- /* BLOCKING */
- rc = Curl_wait_for_resolv(conn, &h);
- if(h) {
- addr = h->addr;
- /* when we return from this function, we can forget about this entry
- so we can unlock it now already */
- Curl_resolv_unlock(data, h);
-
- freeaddr = FALSE; /* make sure we don't free 'addr' in this function
- since it points to a DNS cache entry! */
- } /* (h) */
- else {
- infof(data, "Failed to resolve host name %s\n", ftpportstr);
- }
- } /* strlen */
- } /* ftpportstr */
-
- if(!addr) {
- /* pick a suitable default here */
-
- if(getsockname(conn->sock[FIRSTSOCKET],
- (struct sockaddr *)&sa, &sslen)) {
- failf(data, "getsockname() failed: %s",
- Curl_strerror(conn, SOCKERRNO) );
- return CURLE_FTP_PORT_FAILED;
- }
- if(sslen > (curl_socklen_t)sizeof(sa))
- sslen = sizeof(sa);
-
- sa_filled_in = TRUE; /* the sa struct is filled in */
- }
-
- if(addr || sa_filled_in) {
- portsock = socket(AF_INET, SOCK_STREAM, 0);
- if(CURL_SOCKET_BAD != portsock) {
-
- /* we set the secondary socket variable to this for now, it
- is only so that the cleanup function will close it in case
- we fail before the true secondary stuff is made */
- if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
- sclose(conn->sock[SECONDARYSOCKET]);
- conn->sock[SECONDARYSOCKET] = portsock;
-
- if(!sa_filled_in) {
- memcpy(&sa, addr->ai_addr, sslen);
- sa.sin_addr.s_addr = INADDR_ANY;
- }
-
- sa.sin_port = 0;
- sslen = sizeof(sa);
-
- if(bind(portsock, (struct sockaddr *)&sa, sslen) == 0) {
- /* we succeeded to bind */
- struct sockaddr_in add;
- curl_socklen_t socksize = sizeof(add);
-
- if(getsockname(portsock, (struct sockaddr *) &add,
- &socksize)) {
- failf(data, "getsockname() failed: %s",
- Curl_strerror(conn, SOCKERRNO) );
- return CURLE_FTP_PORT_FAILED;
- }
- porttouse = ntohs(add.sin_port);
-
- if( listen(portsock, 1) < 0 ) {
- failf(data, "listen(2) failed on socket");
- return CURLE_FTP_PORT_FAILED;
- }
- }
- else {
- failf(data, "bind(2) failed on socket");
- return CURLE_FTP_PORT_FAILED;
- }
- }
- else {
- failf(data, "socket(2) failed (%s)");
- return CURLE_FTP_PORT_FAILED;
- }
- }
- else {
- failf(data, "couldn't find IP address to use");
- return CURLE_FTP_PORT_FAILED;
- }
-
- if(sa_filled_in)
- Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
- myhost, sizeof(myhost));
- else
- Curl_printable_address(addr, myhost, sizeof(myhost));
-
- if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
- &ip[0], &ip[1], &ip[2], &ip[3])) {
-
- infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
- ip[0], ip[1], ip[2], ip[3], porttouse);
-
- result=Curl_nbftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
- ip[0], ip[1], ip[2], ip[3],
- porttouse >> 8, porttouse & 255);
- if(result)
- return result;
- }
- else
- return CURLE_FTP_PORT_FAILED;
-
- if(freeaddr)
- Curl_freeaddrinfo(addr);
-
- ftpc->count1 = PORT;
-
-#endif /* end of ipv4-specific code */
-
/* this tcpconnect assignment below is a hackish work-around to make the
multi interface with active FTP work - as it will not wait for a
(passive) connect in Curl_is_connected().