From 033a14cc952bd93618fa9b52f26beb4783a55d5f Mon Sep 17 00:00:00 2001 From: Darshit Shah 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. address@hidden bind port address@hidden client port number address@hidden IP address, client, port address@hidden address@hidden +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 @address@hidden @item bind_address = @var{address} Bind to @var{address}, like the @address@hidden address@hidden 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 @address@hidden 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