This patch adds support to bind Wget's client socket to a user specified port.


-- 
Thanking You,
Darshit Shah
PGP Fingerprint: 7845 120B 07CB D8D6 ECE5 FF2B 2A17 43ED A91A 35B6
From 033a14cc952bd93618fa9b52f26beb4783a55d5f Mon Sep 17 00:00:00 2001
From: Darshit Shah <dar...@gnu.org>
Date: Thu, 3 May 2018 18:26:38 +0200
Subject: [PATCH] Add support for binding to local port

* src/options.h: Intriduce bind_port variable
* src/init.c: initialize bind_port to -1 to indicate no user value
* src/main.c: Add bind_port to command line options. Also ensure that
port is only specified along with bind_address
* src/connect.c: Use the specified bind_port when binding to a specific
address.
Also set the SO_REUSEADDR option on the socket when binding to a local
address
* doc/wget.texi: Add documentation for new option
---
 doc/wget.texi | 11 +++++++++++
 src/connect.c | 17 +++++++++++++++--
 src/init.c    |  2 ++
 src/main.c    | 12 ++++++++++++
 src/options.h |  1 +
 5 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/doc/wget.texi b/doc/wget.texi
index 5fd11137..9b2f1a0a 100644
--- a/doc/wget.texi
+++ b/doc/wget.texi
@@ -591,6 +591,14 @@ the local machine.  @var{ADDRESS} may be specified as a 
hostname or IP
 address.  This option can be useful if your machine is bound to multiple
 IPs.
 
+@cindex bind port
+@cindex client port number
+@cindex IP address, client, port
+@item --bind-port=@var{PORT}
+When making client TCP/IP connections using @samp{--bind-address}, additionally
+bind to a specific @var{PORT} on the client machine.  If a negative value is
+passed as the parameter, then the default vallue of 0 will be used.
+
 @cindex bind DNS address
 @cindex client DNS address
 @cindex DNS IP address, client, DNS
@@ -3243,6 +3251,9 @@ as being relative to @var{string}---the same as 
@samp{--base=@var{string}}.
 @item bind_address = @var{address}
 Bind to @var{address}, like the @samp{--bind-address=@var{address}}.
 
+@item bind_port = @var{port}
+In addition to @samp{bind_address}, bind to specific @var{port}.
+
 @item ca_certificate = @var{file}
 Set the certificate authority bundle file to @var{file}.  The same
 as @samp{--ca-certificate=@var{file}}.
diff --git a/src/connect.c b/src/connect.c
index 37dae215..37a30879 100644
--- a/src/connect.c
+++ b/src/connect.c
@@ -187,7 +187,7 @@ resolve_bind_address (struct sockaddr *sa)
   if (called)
     {
       if (should_bind)
-        sockaddr_set_data (sa, &ip, 0);
+        sockaddr_set_data (sa, &ip, opt.bind_port);
       return should_bind;
     }
   called = true;
@@ -209,7 +209,7 @@ resolve_bind_address (struct sockaddr *sa)
   ip = *address_list_address_at (al, 0);
   address_list_release (al);
 
-  sockaddr_set_data (sa, &ip, 0);
+  sockaddr_set_data (sa, &ip, opt.bind_port);
   should_bind = true;
   return true;
 }
@@ -340,6 +340,19 @@ connect_to_ip (const ip_address *ip, int port, const char 
*print)
       struct sockaddr *bind_sa = (struct sockaddr *)&bind_ss;
       if (resolve_bind_address (bind_sa))
         {
+
+           // Set the SO_REUSEADDR socket option if it is available. It is
+                  // useful when explicitly binding to a given address
+#ifdef SO_REUSEADDR
+          /* For setting options with setsockopt. */
+          int setopt_val = 1;
+          void *setopt_ptr = (void *)&setopt_val;
+          socklen_t setopt_size = sizeof (setopt_val);
+
+          if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, setopt_ptr, 
setopt_size))
+            logprintf (LOG_NOTQUIET, _("setsockopt SO_REUSEADDR failed: %s\n"),
+                       strerror (errno));
+#endif
           if (bind (sock, bind_sa, sockaddr_size (bind_sa)) < 0)
             goto err;
         }
diff --git a/src/init.c b/src/init.c
index e4186abe..98b6ac45 100644
--- a/src/init.c
+++ b/src/init.c
@@ -150,6 +150,7 @@ static const struct {
 #ifdef HAVE_LIBCARES
   { "binddnsaddress",   &opt.bind_dns_address,  cmd_string },
 #endif
+  { "bindport",                        &opt.bind_port,                 
cmd_number },
   { "bodydata",         &opt.body_data,         cmd_string },
   { "bodyfile",         &opt.body_file,         cmd_string },
 #ifdef HAVE_SSL
@@ -396,6 +397,7 @@ defaults (void)
   opt.metalink_index = -1;
 #endif
 
+  opt.bind_port = -1;
   opt.cookies = true;
   opt.verbose = -1;
   opt.ntry = 20;
diff --git a/src/main.c b/src/main.c
index 46824efd..c6e560bd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -275,6 +275,7 @@ static struct cmdline_option option_data[] =
 #ifdef HAVE_LIBCARES
     { "bind-dns-address", 0, OPT_VALUE, "binddnsaddress", -1 },
 #endif
+    { "bind-port", 0, OPT_VALUE, "bindport", -1 },
     { "body-data", 0, OPT_VALUE, "bodydata", -1 },
     { "body-file", 0, OPT_VALUE, "bodyfile", -1 },
     { IF_SSL ("ca-certificate"), 0, OPT_VALUE, "cacertificate", -1 },
@@ -692,6 +693,8 @@ Download:\n"),
   -Q,  --quota=NUMBER              set retrieval quota to NUMBER\n"),
     N_("\
        --bind-address=ADDRESS      bind to ADDRESS (hostname or IP) on local 
host\n"),
+    N_("\
+       --bind-port=PORT            bind to PORT on local host\n"),
     N_("\
        --limit-rate=RATE           limit download rate to RATE\n"),
     N_("\
@@ -1749,6 +1752,15 @@ for details.\n\n"));
       exit (WGET_EXIT_GENERIC_ERROR);
     }
 
+  if (opt.bind_port != -1 && !opt.bind_address) {
+    fprintf (stderr, _("bind-port requires bind-address to also be 
specified.\n"));
+    exit (WGET_EXIT_GENERIC_ERROR);
+  } else {
+    // We explicitly set the port to 0 if nothing (or a negative value) is
+    // specified
+    opt.bind_port = MAX(opt.bind_port, 0);
+  }
+
   /* Compile the regular expressions.  */
   switch (opt.regex_type)
     {
diff --git a/src/options.h b/src/options.h
index 30845a1b..777affad 100644
--- a/src/options.h
+++ b/src/options.h
@@ -219,6 +219,7 @@ struct options
   bool page_requisites;         /* Whether we need to download all files
                                    necessary to display a page properly. */
   char *bind_address;           /* What local IP address to bind to. */
+  int bind_port;                               /* What local port to bind to. 
*/
 
 #ifdef HAVE_SSL
   enum {
-- 
2.17.0

Attachment: signature.asc
Description: PGP signature

Reply via email to