cURL / Mailing Lists / curl-library / Single Mail

curl-library

[PATCH] curl: Support CURLOPT_LOCALADDR to bind to local address.

From: <greearb_at_candelatech.com>
Date: Wed, 16 Jan 2013 12:11:29 -0800

From: Ben Greear <greearb_at_candelatech.com>

This allows a user to bind to both an interface (with CURLOPT_INTERFACE)
and a local IP address. In doing so, it allows the user to work-around
some serious performance issues on machines with lots of virtual interfaces
because curl no longer has to scan all interfaces each time it makes
a connection.

Signed-off-by: Ben Greear <greearb_at_candelatech.com>

---
Similar patch was tested using app that uses the modified lib-curl.
I have not tested the 'curl' executable (nor any OS400 code).
 docs/libcurl/curl_easy_setopt.3 |  3 +++
 include/curl/curl.h             |  3 +++
 include/curl/typecheck-gcc.h    |  1 +
 lib/connect.c                   | 11 +++++++++--
 lib/url.c                       | 35 ++++++++++++++++++++++++++++++-----
 lib/urldata.h                   |  2 ++
 packages/OS400/ccsidcurl.c      |  1 +
 packages/OS400/curl.inc.in      |  2 ++
 src/tool_cfgable.c              |  1 +
 src/tool_cfgable.h              |  1 +
 src/tool_getparam.c             |  5 +++++
 src/tool_help.c                 |  1 +
 src/tool_operate.c              |  2 ++
 13 files changed, 61 insertions(+), 7 deletions(-)
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index 9d712a4..fc28d5f 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -885,6 +885,9 @@ resolved synchronously.  Using the if! format is highly recommended when using
 the multi interfaces to avoid allowing the code to block.  If "if!" is
 specified but the parameter does not match an existing interface,
 CURLE_INTERFACE_FAILED is returned.
+.IP CURLOPT_LOCALADDR
+Pass a char * as parameter. This sets the local IP address to use as outgoing
+network interface. The value should be in dot notation.
 .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
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 5b39a24..e574e1a 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1536,6 +1536,9 @@ typedef enum {
   /* set the SMTP auth originator */
   CINIT(MAIL_AUTH, OBJECTPOINT, 217),
 
+  /* Set the IP-Address string to use as outgoing IP Addr */
+  CINIT(LOCALADDR, OBJECTPOINT, 218),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h
index f8917e8..259b594 100644
--- a/include/curl/typecheck-gcc.h
+++ b/include/curl/typecheck-gcc.h
@@ -221,6 +221,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
   ((option) == CURLOPT_URL ||                                                 \
    (option) == CURLOPT_PROXY ||                                               \
    (option) == CURLOPT_INTERFACE ||                                           \
+   (option) == CURLOPT_LOCALADDR ||                                           \
    (option) == CURLOPT_NETRC_FILE ||                                          \
    (option) == CURLOPT_USERPWD ||                                             \
    (option) == CURLOPT_USERNAME ||                                            \
diff --git a/lib/connect.c b/lib/connect.c
index acaf3b4..e7a5f8f 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -269,6 +269,7 @@ static CURLcode bindlocal(struct connectdata *conn,
   /* how many port numbers to try to bind to, increasing one at a time */
   int portnum = data->set.localportrange;
   const char *dev = data->set.str[STRING_DEVICE];
+  const char *addr = data->set.str[STRING_LOCALADDR];
   int error;
   char myhost[256] = "";
   int done = 0; /* -1 for error, 1 for address found */
@@ -298,8 +299,14 @@ static CURLcode bindlocal(struct connectdata *conn,
 
     /* interface */
     if(!is_host && (is_interface || Curl_if_is_interface_name(dev))) {
-      if(Curl_if2ip(af, dev, myhost, sizeof(myhost)) == NULL)
-        return CURLE_INTERFACE_FAILED;
+      if(addr && dev) {
+        strncpy(myhost, addr, sizeof(myhost));
+        myhost[sizeof(myhost)-1] = 0;
+      }
+      else {
+        if(Curl_if2ip(af, dev, myhost, sizeof(myhost)) == NULL)
+          return CURLE_INTERFACE_FAILED;
+      }
 
       /*
        * We now have the numerical IP address in the 'myhost' buffer
diff --git a/lib/url.c b/lib/url.c
index c107d2c..d17e89e 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1832,6 +1832,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     result = setstropt(&data->set.str[STRING_DEVICE],
                        va_arg(param, char *));
     break;
+  case CURLOPT_LOCALADDR:
+    /*
+     * Set what local address to bind the socket to when
+     * performing an operation and thus what from-IP your connection will use.
+     */
+    result = setstropt(&data->set.str[STRING_LOCALADDR],
+                       va_arg(param, char *));
+    break;
   case CURLOPT_LOCALPORT:
     /*
      * Set what local port to bind the socket to when performing an operation.
@@ -2496,6 +2504,7 @@ static void conn_free(struct connectdata *conn)
   conn->done_pipe = NULL;
 
   Curl_safefree(conn->localdev);
+  Curl_safefree(conn->localaddr);
   Curl_free_ssl_config(&conn->ssl_config);
 
   free(conn); /* free all the connection oriented data */
@@ -2875,7 +2884,7 @@ ConnectionExists(struct SessionHandle *data,
            already in use so we skip it */
         continue;
 
-      if(needle->localdev || needle->localport) {
+      if(needle->localdev || needle->localport || needle->localaddr) {
         /* If we are bound to a specific local end (IP+port), we must not
            re-use a random other one, although if we didn't ask for a
            particular one we can reuse one that was bound.
@@ -2888,10 +2897,19 @@ ConnectionExists(struct SessionHandle *data,
            missing out a few edge cases shouldn't hurt anyone very much.
         */
         if((check->localport != needle->localport) ||
-           (check->localportrange != needle->localportrange) ||
-           !check->localdev ||
-           !needle->localdev ||
-           strcmp(check->localdev, needle->localdev))
+           (check->localportrange != needle->localportrange))
+          continue;
+
+        if((check->localdev && !needle->localdev) ||
+           (needle->localdev && !needle->localdev) ||
+           (needle->localdev && check->localdev &&
+            (strcmp(check->localdev, needle->localdev) != 0)))
+          continue;
+
+        if((check->localaddr && !needle->localaddr) ||
+           (needle->localaddr && !needle->localaddr) ||
+           (needle->localaddr && check->localaddr &&
+            (strcmp(check->localaddr, needle->localaddr) != 0)))
           continue;
       }
 
@@ -3506,6 +3524,11 @@ static struct connectdata *allocate_conn(struct SessionHandle *data)
     if(!conn->localdev)
       goto error;
   }
+  if(data->set.str[STRING_LOCALADDR]) {
+    conn->localaddr = strdup(data->set.str[STRING_LOCALADDR]);
+    if(!conn->localaddr)
+      goto error;
+  }
   conn->localportrange = data->set.localportrange;
   conn->localport = data->set.localport;
 
@@ -3529,6 +3552,7 @@ static struct connectdata *allocate_conn(struct SessionHandle *data)
 
   Curl_safefree(conn->master_buffer);
   Curl_safefree(conn->localdev);
+  Curl_safefree(conn->localaddr);
   Curl_safefree(conn);
   return NULL;
 }
@@ -4624,6 +4648,7 @@ static void reuse_conn(struct connectdata *old_conn,
   Curl_safefree(old_conn->proxyuser);
   Curl_safefree(old_conn->proxypasswd);
   Curl_safefree(old_conn->localdev);
+  Curl_safefree(old_conn->localaddr);
 
   Curl_llist_destroy(old_conn->send_pipe, NULL);
   Curl_llist_destroy(old_conn->recv_pipe, NULL);
diff --git a/lib/urldata.h b/lib/urldata.h
index 576872d..e32f5ce 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1000,6 +1000,7 @@ struct connectdata {
      that subsequent bound-requested connections aren't accidentally re-using
      wrong connections. */
   char *localdev;
+  char *localaddr; /* Local addr in dot notation */
   unsigned short localport;
   int localportrange;
 
@@ -1375,6 +1376,7 @@ enum dupstring {
   STRING_TLSAUTH_USERNAME,     /* TLS auth <username> */
   STRING_TLSAUTH_PASSWORD,     /* TLS auth <password> */
 #endif
+  STRING_LOCALADDR,            /* Local IP Addr to use. */
 
   /* -- end of strings -- */
   STRING_LAST /* not used, just an end-of-list marker */
diff --git a/packages/OS400/ccsidcurl.c b/packages/OS400/ccsidcurl.c
index 1baff39..903172c 100644
--- a/packages/OS400/ccsidcurl.c
+++ b/packages/OS400/ccsidcurl.c
@@ -1058,6 +1058,7 @@ curl_easy_setopt_ccsid(CURL * curl, CURLoption tag, ...)
   case CURLOPT_FTP_ALTERNATIVE_TO_USER:
   case CURLOPT_FTPPORT:
   case CURLOPT_INTERFACE:
+  case CURLOPT_LOCALADDR:
   case CURLOPT_ISSUERCERT:
   case CURLOPT_KEYPASSWD:
   case CURLOPT_KRBLEVEL:
diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in
index 33ca12a..f611a19 100644
--- a/packages/OS400/curl.inc.in
+++ b/packages/OS400/curl.inc.in
@@ -1152,6 +1152,8 @@
      d                 c                   00216
      d  CURLOPT_MAIL_AUTH...
      d                 c                   10217
+     d  CURLOPT_LOCALADDR...
+     d                 c                   10218
       *
       /if not defined(CURL_NO_OLDIES)
      d  CURLOPT_SSLKEYPASSWD...
diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c
index da11f4a..b7dd196 100644
--- a/src/tool_cfgable.c
+++ b/src/tool_cfgable.c
@@ -47,6 +47,7 @@ void free_config_fields(struct Configurable *config)
   Curl_safefree(config->headerfile);
   Curl_safefree(config->ftpport);
   Curl_safefree(config->iface);
+  Curl_safefree(config->localaddr);
 
   Curl_safefree(config->range);
 
diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h
index 1f6f948..a4a7c7c 100644
--- a/src/tool_cfgable.h
+++ b/src/tool_cfgable.h
@@ -60,6 +60,7 @@ struct Configurable {
   char *headerfile;
   char *ftpport;
   char *iface;
+  char *localaddr; /* local IP address */
   int localport;
   int localportrange;
   unsigned short porttouse;
diff --git a/src/tool_getparam.c b/src/tool_getparam.c
index 297b986..9e9dced 100644
--- a/src/tool_getparam.c
+++ b/src/tool_getparam.c
@@ -105,6 +105,7 @@ static const struct LongShort aliases[]= {
   {"*u", "crlf",                     FALSE},
   {"*v", "stderr",                   TRUE},
   {"*w", "interface",                TRUE},
+  {"*W", "localaddr",                TRUE},
   {"*x", "krb" ,                     TRUE},
   {"*x", "krb4" ,                    TRUE},
          /* 'krb4' is the previous name */
@@ -583,6 +584,10 @@ ParameterError getparameter(char *flag,    /* f or -long-flag */
         /* interface */
         GetStr(&config->iface, nextarg);
         break;
+      case 'W': /* --localaddr */
+        /* addr in dot notation */
+        GetStr(&config->localaddr, nextarg);
+        break;
       case 'x': /* --krb */
         /* kerberos level string */
         if(curlinfo->features & (CURL_VERSION_KERBEROS4 |
diff --git a/src/tool_help.c b/src/tool_help.c
index 124f640..e6eaa18 100644
--- a/src/tool_help.c
+++ b/src/tool_help.c
@@ -103,6 +103,7 @@ static const char *const helptext[] = {
   " -i, --include       Include protocol headers in the output (H/F)",
   " -k, --insecure      Allow connections to SSL sites without certs (H)",
   "     --interface INTERFACE  Specify network interface/address to use",
+  "     --localaddr IPADDR  Specify local IP-address to use",
   " -4, --ipv4          Resolve name to IPv4 address",
   " -6, --ipv6          Resolve name to IPv6 address",
   " -j, --junk-session-cookies Ignore session cookies read from file (H)",
diff --git a/src/tool_operate.c b/src/tool_operate.c
index bcbce20..0ed4de1 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -1118,6 +1118,8 @@ int operate(struct Configurable *config, int argc, argv_item_t argv[])
         my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
         my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel);
 
+        my_setopt_str(curl, CURLOPT_LOCALADDR, config->localaddr);
+
         progressbarinit(&progressbar, config);
         if((config->progressmode == CURL_PROGRESS_BAR) &&
            !config->noprogress && !config->mute) {
-- 
1.7.11.7
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html
Received on 2013-01-16