From 2fbf94b0f309de9bd9153274bb475abc744afb0a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 30 Jan 2006 08:24:07 +0000 Subject: [PATCH] Added CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE to libcurl. Set with the curl tool with --local-port. Plain and simply set the range of ports to bind the local end of connections to. Implemented on to popular demand. Not extensively tested. Please let me know how it works. --- CHANGES | 4 + RELEASE-NOTES | 6 +- docs/curl.1 | 55 ++++++------ docs/libcurl/curl_easy_setopt.3 | 15 +++- include/curl/curl.h | 10 ++- lib/connect.c | 155 +++++++++++++++++++------------- lib/url.c | 17 +++- lib/urldata.h | 5 +- src/main.c | 32 ++++++- 9 files changed, 196 insertions(+), 103 deletions(-) diff --git a/CHANGES b/CHANGES index bfdb1495d..d61a42b29 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,10 @@ Changelog Daniel (30 January 2006) +- Added CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE to libcurl. Set with the + curl tool with --local-port. Plain and simply set the range of ports to bind + the local end of connections to. Implemented on to popular demand. + - Based on an error report by Philippe Vaucher, we no longer count a retried connection setup as a follow-redirect. It turns out 1) this fails when a FTP connection is re-setup and 2) it does make the max-redirs counter behave diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 151ce6175..ef9589cb9 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -5,12 +5,14 @@ Curl and libcurl 7.15.2 Available command line options: 109 Available curl_easy_setopt() options: 125 Number of public functions in libcurl: 46 - Amount of public web site mirrors: 28 + Amount of public web site mirrors: 30 Number of known libcurl bindings: 32 Number of contributors: 474 This release includes the following changes: + o CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE (--local-port) added + o Dropped support for the LPRT ftp command o Gopher is now officially abandoned as a protocol (lib)curl tries to support. o curl_global_init() and curl_global_cleanup() are now using a refcount so that it is now legal to call them multiple times. See updated info for @@ -22,7 +24,7 @@ This release includes the following bugfixes: redirect and thus prevents a weird error that would occur if a FTP connection died on an attempted re-use o Try PASV after failing to connect to the port the EPSV response contained - o -P [IP] with ipv6-enabled curl + o -P [IP] with non-local address with ipv6-enabled curl o -P [hostname] with ipv6-disabled curl o libcurl.m4 was updated o configure no longer warns if the current path contains a space diff --git a/docs/curl.1 b/docs/curl.1 index 598b15de8..52949c34f 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -5,7 +5,7 @@ .\" * | (__| |_| | _ <| |___ .\" * \___|\___/|_| \_\_____| .\" * -.\" * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. +.\" * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. .\" * .\" * This software is licensed as described in the file COPYING, which .\" * you should have received as part of this distribution. The terms @@ -100,7 +100,7 @@ most secure one the remote site claims it supports. This is done by first doing a request and checking the response-headers, thus inducing an extra network round-trip. This is used instead of setting a specific authentication method, which you can do with \fI--basic\fP, \fI--digest\fP, \fI--ntlm\fP, and -\fI--negotiate\fP. (Added in 7.10.6) +\fI--negotiate\fP. Note that using --anyauth is not recommended if you do uploads from stdin, since it may require data to be sent twice and then the client must be able to @@ -140,7 +140,7 @@ If this option is used twice, the second one will disable ASCII usage. (HTTP) Tells curl to use HTTP Basic authentication. This is the default and this option is usually pointless, unless you use it to override a previously set option that sets a different authentication method (such as \fI--ntlm\fP, -\fI--digest\fP and \fI--negotiate\fP). (Added in 7.10.6) +\fI--digest\fP and \fI--negotiate\fP). If this option is used several times, the following occurrences make no difference. @@ -243,7 +243,7 @@ append data. prevents the password from being sent over the wire in clear text. Use this in combination with the normal \fI-u/--user\fP option to set user name and password. See also \fI--ntlm\fP, \fI--negotiate\fP and \fI--anyauth\fP for -related options. (Added in curl 7.10.6) +related options. If this option is used several times, the following occurrences make no difference. @@ -253,7 +253,7 @@ active FTP transfers. Curl will normally always first attempt to use EPRT, then LPRT before using PORT, but with this option, it will use PORT right away. EPRT and LPRT are extensions to the original FTP protocol, may not work on all servers but enable more functionality in a better way than the -traditional PORT command. (Added in 7.10.5) +traditional PORT command. If this option is used several times, each occurrence will toggle this on/off. .IP "--disable-epsv" @@ -351,7 +351,7 @@ If this option is used twice, the second will override the previous use. .IP "--ftp-create-dirs" (FTP) When an FTP URL/operation uses a path that doesn't currently exist on the server, the standard behavior of curl is to fail. Using this option, curl -will instead attempt to create missing directories. (Added in 7.10.7) +will instead attempt to create missing directories. If this option is used twice, the second will again disable directory creation. .IP "--ftp-pasv" @@ -484,15 +484,14 @@ If this option is used twice, the second will again disable header only. (HTTP) When curl is told to read cookies from a given file, this option will make it discard all "session cookies". This will basically have the same effect as if a new session is started. Typical browsers always discard session -cookies when they're closed down. (Added in 7.9.7) +cookies when they're closed down. If this option is used several times, each occurrence will toggle this on/off. .IP "-k/--insecure" (SSL) This option explicitly allows curl to perform "insecure" SSL connections -and transfers. Starting with curl 7.10, all SSL connections will be attempted -to be made secure by using the CA certificate bundle installed by -default. This makes all connections considered "insecure" to fail unless -\fI-k/--insecure\fP is used. +and transfers. All SSL connections are attempted to be made secure by using +the CA certificate bundle installed by default. This makes all connections +considered "insecure" to fail unless \fI-k/--insecure\fP is used. If this option is used twice, the second time will again disable it. .IP "--key " @@ -559,8 +558,6 @@ If you are also using the \fI-Y/--speed-limit\fP option, that option will take precedence and might cripple the rate-limiting slightly, to help keeping the speed-limit logic working. -This option was introduced in curl 7.10. - If this option is used several times, the last one will be used. .IP "-l/--list-only" (FTP) @@ -574,6 +571,11 @@ list only files in their response to NLST; they do not include subdirectories and symbolic links. If this option is used twice, the second will again disable list only. +.IP "--local-port [-num]" +Set a prefered number or range of local port numbers to use for the +connection(s). Note that port numbers by nature is a scarce resource that +will be busy at times so setting this range to something too narrow might +cause unnecessary connection setup failures. (Added in 7.15.2) .IP "-L/--location" (HTTP/HTTPS) If the server reports that the requested page has moved to a different location (indicated with a Location: header and a 3XX response code) @@ -637,7 +639,7 @@ Very similar to \fI--netrc\fP, but this option makes the .netrc usage designed by Microsoft and is used in their web applications. It is primarily meant as a support for Kerberos5 authentication but may be also used along with another authentication methods. For more information see IETF draft -draft-brezak-spnego-http-04.txt. (Added in 7.10.6) +draft-brezak-spnego-http-04.txt. This option requires that the library was built with GSSAPI support. This is not very common. Use \fI-V/--version\fP to see if your version supports @@ -662,7 +664,7 @@ designed by Microsoft and is used by IIS web servers. It is a proprietary protocol, reversed engineered by clever people and implemented in curl based on their efforts. This kind of behavior should not be endorsed, you should encourage everyone who uses NTLM to switch to a public and documented -authentication method instead. Such as Digest. (Added in 7.10.6) +authentication method instead. Such as Digest. If you want to enable NTLM for your proxy authentication, then use \fI--proxy-ntlm\fP. @@ -702,8 +704,8 @@ You may use this option as many times as you have number of URLs. If this option is used several times, the last one will be used. .IP "--proxy-anyauth" Tells curl to pick a suitable authentication method when communicating with -the given proxy. This will cause an extra request/response round-trip. Added -in curl 7.13.2. +the given proxy. This will cause an extra request/response round-trip. (Added +in 7.13.2) If this option is used twice, the second will again disable the proxy use-any authentication. @@ -894,13 +896,10 @@ this is used on a http(s) server, the PUT command will be used. Use the file name "-" (a single dash) to use stdin instead of a given file. -Before 7.10.8, when this option was used several times, the last one was used. - -In curl 7.10.8 and later, you can specify one -T for each URL on the command -line. Each -T + URL pair specifies what to upload and to where. curl also -supports "globbing" of the -T argument, meaning that you can upload multiple -files to a single URL by using the same URL globbing style supported in the -URL, like this: +You can specify one -T for each URL on the command line. Each -T + URL pair +specifies what to upload and to where. curl also supports "globbing" of the -T +argument, meaning that you can upload multiple files to a single URL by using +the same URL globbing style supported in the URL, like this: curl -T "{file1,file2}" http://www.uploadtothissite.com @@ -911,7 +910,6 @@ curl -T "img[1-1000].png" ftp://ftp.picturemania.com/upload/ Enables a full trace dump of all incoming and outgoing data, including descriptive information, to the given output file. Use "-" as filename to have the output sent to stdout. -(Added in 7.9.7) If this option is used several times, the last one will be used. .IP "--trace-ascii " @@ -922,7 +920,6 @@ the output sent to stdout. This is very similar to \fI--trace\fP, but leaves out the hex part and only shows the ASCII part of the dump. It makes smaller output that might be easier to read for untrained humans. -(Added in 7.9.7) If this option is used several times, the last one will be used. .IP "--trace-time" @@ -1083,7 +1080,7 @@ The average download speed that curl measured for the complete download. The average upload speed that curl measured for the complete upload. .TP .B content_type -The Content-Type of the requested document, if there was any. (Added in 7.9.5) +The Content-Type of the requested document, if there was any. .TP .B num_connects Number of new connects made in the recent transfer. (Added in 7.12.3) @@ -1182,11 +1179,11 @@ Note that not all FTP server allow 3rd party transfers. (Added in 7.13.0) .IP "-4/--ipv4" If libcurl is capable of resolving an address to multiple IP versions (which it is if it is ipv6-capable), this option tells libcurl to resolve names to -IPv4 addresses only. (Added in 7.10.8) +IPv4 addresses only. .IP "-6/--ipv6" If libcurl is capable of resolving an address to multiple IP versions (which it is if it is ipv6-capable), this option tells libcurl to resolve names to -IPv6 addresses only. (Added in 7.10.8) +IPv6 addresses only. .IP "-#/--progress-bar" Make curl display progress information as a progress bar instead of the default statistics. diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 60068f661..25e7e7707 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -21,7 +21,7 @@ .\" * $Id$ .\" ************************************************************************** .\" -.TH curl_easy_setopt 3 "27 Oct 2005" "libcurl 7.14.2" "libcurl Manual" +.TH curl_easy_setopt 3 "28 Jan 2006" "libcurl 7.15.2" "libcurl Manual" .SH NAME curl_easy_setopt \- set options for a curl easy handle .SH SYNOPSIS @@ -335,6 +335,19 @@ don't want this tunneling option. Pass a char * as parameter. This set the interface name to use as outgoing network interface. The name can be an interface name, an IP address or a host name. +.IP CURLOPT_LOCALPORT +Pass a long. This sets the local port number of the socket used for +connection. This can be used in combination with \fICURLOPT_INTERFACE\fP and +you are recommended to use \fICURLOPT_LOCALPORTRANGE\fP as well when this is +set. Note that port numbers are only valid 1 - 65535. (Added in 7.15.2) +.IP CURLOPT_LOCALPORTRANGE +Pass a long. This is the number of attempts libcurl should do to find a +working local port number. It starts with the given \fICURLOPT_LOCALPORT\fP +and adds one to the number for each retry. Setting this value to 1 or below +will make libcurl do only one try for exact port number. Note that port +numbers by nature is a scarce resource that will be busy at times so setting +this value to something too low might cause unnecessary connection setup +failures. (Added in 7.15.2) .IP CURLOPT_DNS_CACHE_TIMEOUT Pass a long, this sets the timeout in seconds. Name resolves will be kept in memory for this number of seconds. Set to zero (0) to completely disable diff --git a/include/curl/curl.h b/include/curl/curl.h index 0ac3f28ea..95c478a07 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -915,6 +915,14 @@ typedef enum { /* Select "file method" to use when doing FTP */ CINIT(FTP_FILEMETHOD, LONG, 138), + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/connect.c b/lib/connect.c index 2fb08d628..902721c80 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -98,6 +98,7 @@ #include "memory.h" #include "select.h" #include "url.h" /* for Curl_safefree() */ +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ /* The last #include file should be: */ #include "memdebug.h" @@ -239,16 +240,20 @@ int waitconnect(curl_socket_t sockfd, /* socket */ static CURLcode bindlocal(struct connectdata *conn, curl_socket_t sockfd) { -#ifdef HAVE_INET_NTOA - bool bindworked = FALSE; struct SessionHandle *data = conn->data; + struct sockaddr_in me; + struct sockaddr *sock = NULL; /* bind to this address */ + socklen_t socksize; /* size of the data sock points to */ + unsigned short port = data->set.localport; /* use this port number, 0 for + "random" */ + /* how many port numbers to try to bind to, increasing one at a time */ + int portnum = data->set.localportrange; /************************************************************* * Select device to bind socket to *************************************************************/ - if (strlen(data->set.device)<255) { + if (data->set.device && (strlen(data->set.device)<255) ) { struct Curl_dns_entry *h=NULL; - size_t size; char myhost[256] = ""; in_addr_t in; int rc; @@ -266,8 +271,10 @@ static CURLcode bindlocal(struct connectdata *conn, if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); - if(h) + if(h) { was_iface = TRUE; + Curl_resolv_unlock(data, h); + } } if(!was_iface) { @@ -279,9 +286,11 @@ static CURLcode bindlocal(struct connectdata *conn, if(rc == CURLRESOLV_PENDING) (void)Curl_wait_for_resolv(conn, &h); - if(h) + if(h) { /* we know data->set.device is shorter than the myhost array */ strcpy(myhost, data->set.device); + Curl_resolv_unlock(data, h); + } } if(! *myhost) { @@ -295,7 +304,7 @@ static CURLcode bindlocal(struct connectdata *conn, return CURLE_HTTP_PORT_FAILED; } - infof(data, "We bind local end to %s\n", myhost); + infof(data, "Bind local address to %s\n", myhost); #ifdef SO_BINDTODEVICE /* I am not sure any other OSs than Linux that provide this feature, and @@ -323,60 +332,79 @@ static CURLcode bindlocal(struct connectdata *conn, #endif in=inet_addr(myhost); - if (CURL_INADDR_NONE != in) { - - if ( h ) { - Curl_addrinfo *addr = h->addr; - - Curl_resolv_unlock(data, h); - /* we don't need it anymore after this function has returned */ - - if( bind(sockfd, addr->ai_addr, (socklen_t)addr->ai_addrlen) >= 0) { - /* we succeeded to bind */ -#ifdef ENABLE_IPV6 - struct sockaddr_in6 add; -#else - struct sockaddr_in add; -#endif - - bindworked = TRUE; - - size = sizeof(add); - if(getsockname(sockfd, (struct sockaddr *) &add, - (socklen_t *)&size)<0) { - failf(data, "getsockname() failed"); - return CURLE_HTTP_PORT_FAILED; - } - } - - if(!bindworked) { - data->state.os_errno = Curl_ourerrno(); - failf(data, "bind failure: %s", - Curl_strerror(conn, data->state.os_errno)); - return CURLE_HTTP_PORT_FAILED; - } - - } /* end of if h */ - else { - failf(data,"couldn't find my own IP address (%s)", myhost); - return CURLE_HTTP_PORT_FAILED; - } + if (CURL_INADDR_NONE == in) { + failf(data,"couldn't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; } /* end of inet_addr */ - else { - failf(data, "couldn't find my own IP address (%s)", myhost); - return CURLE_HTTP_PORT_FAILED; + if ( h ) { + Curl_addrinfo *addr = h->addr; + sock = addr->ai_addr; + socksize = addr->ai_addrlen; } + else + return CURLE_HTTP_PORT_FAILED; + } + else if(port) { + /* if a local port number is requested but no local IP, extract the + address from the socket */ + memset(&me, 0, sizeof(struct sockaddr)); + me.sin_family = AF_INET; + me.sin_addr.s_addr = INADDR_ANY; + + sock = (struct sockaddr *)&me; + socksize = sizeof(struct sockaddr); + + } + else + /* no local kind of binding was requested */ return CURLE_OK; - } /* end of device selection support */ -#else - (void)conn; - (void)sockfd; -#endif /* end of HAVE_INET_NTOA */ + do { + /* Set port number to bind to, 0 makes the system pick one */ + if(sock->sa_family == AF_INET) + ((struct sockaddr_in *)sock)->sin_port = htons(port); +#ifdef ENABLE_IPV6 + else + ((struct sockaddr_in6 *)sock)->sin6_port = htons(port); +#endif + + if( bind(sockfd, sock, socksize) >= 0) { + /* we succeeded to bind */ + struct Curl_sockaddr_storage add; + unsigned short port; + size_t size; + + size = sizeof(add); + if(getsockname(sockfd, (struct sockaddr *) &add, + (socklen_t *)&size)<0) { + failf(data, "getsockname() failed"); + return CURLE_HTTP_PORT_FAILED; + } + if(((struct sockaddr *)&add)->sa_family == AF_INET) + port = ntohs(((struct sockaddr_in *)&add)->sin_port); +#ifdef ENABLE_IPV6 + else + port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port); +#endif + infof(data, "Local port: %d\n", port); + return CURLE_OK; + } + if(--portnum > 0) { + infof(data, "Bind to local port %d failed, trying next\n", port); + port++; /* try next port */ + } + else + break; + } while(1); + + data->state.os_errno = Curl_ourerrno(); + failf(data, "bind failure: %s", + Curl_strerror(conn, data->state.os_errno)); return CURLE_HTTP_PORT_FAILED; + } /* @@ -618,6 +646,8 @@ static void nosigpipe(struct connectdata *conn, infof(data, "Could not set SO_NOSIGPIPE: %s\n", Curl_strerror(conn, Curl_ourerrno())); } +#else +#define nosigpipe(x,y) #endif /* singleipconnect() connects to the given IP only, and it may return without @@ -634,6 +664,7 @@ singleipconnect(struct connectdata *conn, bool isconnected; struct SessionHandle *data = conn->data; curl_socket_t sockfd; + CURLcode res; sockfd = socket(ai->ai_family, conn->socktype, ai->ai_protocol); if (sockfd == CURL_SOCKET_BAD) @@ -647,17 +678,13 @@ singleipconnect(struct connectdata *conn, if(data->set.tcp_nodelay) tcpnodelay(conn, sockfd); -#ifdef SO_NOSIGPIPE nosigpipe(conn, sockfd); -#endif - if(conn->data->set.device) { - /* user selected to bind the outgoing socket to a specified "device" - before doing connect */ - CURLcode res = bindlocal(conn, sockfd); - if(res) { - sclose(sockfd); /* close socket and bail out */ - return CURL_SOCKET_BAD; - } + + /* possibly bind the local end to an IP, interface or port */ + res = bindlocal(conn, sockfd); + if(res) { + sclose(sockfd); /* close socket and bail out */ + return CURL_SOCKET_BAD; } /* set socket non-blocking */ diff --git a/lib/url.c b/lib/url.c index a4bd0f509..f17213f79 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1222,13 +1222,26 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, */ data->set.crlf = va_arg(param, long)?TRUE:FALSE; break; + case CURLOPT_INTERFACE: /* - * Set what interface to bind to when performing an operation and thus - * what from-IP your connection will use. + * Set what interface or address/hostname to bind the socket to when + * performing an operation and thus what from-IP your connection will use. */ data->set.device = va_arg(param, char *); break; + case CURLOPT_LOCALPORT: + /* + * Set what local port to bind the socket to when performing an operation. + */ + data->set.localport = (unsigned short) va_arg(param, long); + break; + case CURLOPT_LOCALPORTRANGE: + /* + * Set number of local ports to try, starting with CURLOPT_LOCALPORT. + */ + data->set.localportrange = (int) va_arg(param, long); + break; case CURLOPT_KRB4LEVEL: /* * A string that defines the krb4 security level. diff --git a/lib/urldata.h b/lib/urldata.h index c3a76b45c..96494a7ce 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -970,7 +970,10 @@ struct UserDefined { of strlen(), and then the data *may* be binary (contain zero bytes) */ char *ftpport; /* port to send with the FTP PORT command */ - char *device; /* network interface to use */ + char *device; /* local network interface/address to use */ + unsigned short localport; /* local port number to bind to */ + int localportrange; /* number of additional port numbers to test in case the + 'localport' one can't be bind()ed */ curl_write_callback fwrite; /* function that stores the output */ curl_write_callback fwrite_header; /* function that stores headers */ curl_read_callback fread; /* function that reads the input */ diff --git a/src/main.c b/src/main.c index 38eb9f0a1..549a4d892 100644 --- a/src/main.c +++ b/src/main.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -271,6 +271,8 @@ struct Configurable { char *headerfile; char *ftpport; char *iface; + int localport; + int localportrange; unsigned short porttouse; char *range; long low_speed_limit; @@ -519,12 +521,13 @@ static void help(void) " -i/--include Include protocol headers in the output (H/F)", " -I/--head Show document info only", " -j/--junk-session-cookies Ignore session cookies read from file (H)", - " --interface Specify network interface to use", + " --interface Specify network interface/address to use", " --krb4 Enable krb4 with specified security level (F)", " -k/--insecure Allow connections to SSL sites without certs (H)", " -K/--config Specify which config file to read", " -l/--list-only List only names of an FTP directory (F)", " --limit-rate Limit transfer speed to this rate", + " --local-port [-num] Force use of these local port numbers\n", " -L/--location Follow Location: hints (H)", " --location-trusted Follow Location: and send authentication even ", " to other hostnames (H)", @@ -1261,7 +1264,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ { char letter; char subletter=0; /* subletters can only occur on long options */ - + int rc; /* generic return code variable */ const char *parse=NULL; unsigned int j; time_t now; @@ -1326,6 +1329,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$p", "ignore-content-length", FALSE}, {"$q", "ftp-skip-pasv-ip", FALSE}, {"$r", "ftp-method", TRUE}, + {"$s", "local-port", TRUE}, {"0", "http1.0", FALSE}, {"1", "tlsv1", FALSE}, @@ -1739,6 +1743,22 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ case 'r': /* --ftp-method (undocumented at this point) */ config->ftp_filemethod = ftpfilemethod(config, nextarg); break; + case 's': /* --local-port */ + rc = sscanf(nextarg, "%d - %d", + &config->localport, + &config->localportrange); + if(!rc) + return PARAM_BAD_USE; + else if(rc == 1) + config->localportrange = 1; /* default number of ports to try */ + else { + config->localportrange -= config->localport; + if(config->localportrange < 1) { + warnf(config, "bad range input\n"); + return PARAM_BAD_USE; + } + } + break; } break; case '#': /* --progress-bar */ @@ -3972,6 +3992,12 @@ operate(struct Configurable *config, int argc, char *argv[]) /* curl 7.15.1 */ curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod); + /* curl 7.15.2 */ + if(config->localport) { + curl_easy_setopt(curl, CURLOPT_LOCALPORT, config->localport); + curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, config->localportrange); + } + retry_numretries = config->req_retry; retrystart = curlx_tvnow();