1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-21 23:58:49 -05:00

- 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"
This commit is contained in:
Daniel Stenberg 2009-06-29 20:46:01 +00:00
parent 9cb68c6e32
commit 4f551259dd
5 changed files with 183 additions and 189 deletions

10
CHANGES
View File

@ -6,6 +6,16 @@
Changelog
Daniel Stenberg (29 Jun 2009)
- 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"
- Modified the separators used for CURLOPT_CERTINFO in multi-part outputs. I
don't know how they got wrong in the first place, but using this output
format makes it possible to quite easily separate the string into an array
of multiple items.
Daniel Fandrich (16 June 2009)
- Added a few more compiler warning options for gcc.

View File

@ -9,7 +9,7 @@ Curl and libcurl 7.19.6
This release includes the following changes:
o
o CURLOPT_FTPPORT (and curl's -P/--ftpport) support port ranges
This release includes the following bugfixes:
@ -41,6 +41,6 @@ advice from friends like these:
Yang Tse, Daniel Fandrich, Kamil Dudka, Caolan McNamara, Frank McGeough,
Andre Guibert de Bruet, Mike Crowe, Claes Jakobsson, John E. Malmberg,
Aaron Oneal, Igor Novoseltsev, Eric Wong, Bill Hoffman, Daniel Steinberg,
Fabian Keil, Michal Marek, Reuven Wachtfogel
Fabian Keil, Michal Marek, Reuven Wachtfogel, Markus Koetter
Thanks! (and sorry if I forgot to mention someone)

View File

@ -953,6 +953,12 @@ connection
If this option is used several times, the last one will be used. Disable the
use of PORT with \fI--ftp-pasv\fP. Disable the attempt to use the EPRT command
instead of PORT by using \fI--disable-eprt\fP. EPRT is really PORT++.
Starting in 7.19.5, you can append \&":[start]-[end]\&" to the right of the
address, to tell curl what TCP port range to use. That means you specify a
port range, from a lower to a higher number. A single number works as well,
but do not that it increases the risk of failure since the port may not be
available.
.IP "-q"
If used as the first parameter on the command line, the \fIcurlrc\fP config
file will not be read and used. See the \fI-K/--config\fP for details on the

View File

@ -1070,6 +1070,12 @@ be a plain IP address, a host name, a network interface name (under Unix) or
just a '-' symbol to let the library use your system's default IP
address. Default FTP operations are passive, and thus won't use PORT.
Starting in 7.19.5, you can append \&":[start]-[end]\&" to the right of the
address, to tell libcurl what TCP port range to use. That means you specify a
port range, from a lower to a higher number. A single number works as well,
but do not that it increases the risk of failure since the port may not be
available.
You disable PORT again and go back to using the passive version by setting
this option to NULL.
.IP CURLOPT_QUOTE

346
lib/ftp.c
View File

@ -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)) {
memcpy(sa, ai->ai_addr, ai->ai_addrlen);
sslen = 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;
}
/* 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) {
if(sslen > (curl_socklen_t)sizeof(ss))
sslen = sizeof(ss);
/* 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(bind(portsock, sa, sslen)) {
failf(data, "bind failed: %s", Curl_strerror(conn, SOCKERRNO));
sclose(portsock);
return CURLE_FTP_PORT_FAILED;
}
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;
}
} 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().