diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/connect.c | 147 | ||||
-rw-r--r-- | lib/url.c | 17 | ||||
-rw-r--r-- | lib/urldata.h | 5 |
3 files changed, 106 insertions, 63 deletions
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, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, 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 (CURL_INADDR_NONE == in) { + failf(data,"couldn't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } /* end of inet_addr */ - if ( h ) { - Curl_addrinfo *addr = h->addr; + if ( h ) { + Curl_addrinfo *addr = h->addr; + sock = addr->ai_addr; + socksize = addr->ai_addrlen; + } + else + return CURLE_HTTP_PORT_FAILED; - Curl_resolv_unlock(data, h); - /* we don't need it anymore after this function has returned */ + } + 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; - 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 + sock = (struct sockaddr *)&me; + socksize = sizeof(struct sockaddr); - bindworked = TRUE; + } + else + /* no local kind of binding was requested */ + return CURLE_OK; - size = sizeof(add); - if(getsockname(sockfd, (struct sockaddr *) &add, - (socklen_t *)&size)<0) { - failf(data, "getsockname() failed"); - return CURLE_HTTP_PORT_FAILED; - } - } + do { - 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; - } + /* 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 - } /* end of if h */ - else { - failf(data,"couldn't find my own IP address (%s)", myhost); + 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; } - } /* end of inet_addr */ - - else { - failf(data, "couldn't find my own IP address (%s)", myhost); - 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); - return CURLE_OK; - - } /* end of device selection support */ -#else - (void)conn; - (void)sockfd; -#endif /* end of HAVE_INET_NTOA */ - + 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 */ @@ -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 */ |