aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/connect.c147
-rw-r--r--lib/url.c17
-rw-r--r--lib/urldata.h5
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 */
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 */