[Privoxy-devel] Feature Enhancement: Support for outgoing address (TODO#77)

Christian Kröger ck at tfmt.de
Thu Feb 15 09:35:55 UTC 2018


> On 15. Feb 2018, at 09:54, Fabian Keil <fk at fabiankeil.de> wrote:
> 
> Christian Kröger <ck at tfmt.de> wrote:
> 
>> i’ve been asked to add support for specifying the IP address for
>> outgoing connections made by Privoxy (#77 from the TODO list as found in
>> VCS). The patch adds a new config option called “outgoing-address”,
>> which can be (optionally) specified multiple times. Supports dual-stack
>> (IPv4+IPv6).
> 
> Great.
> 
>> The behaviour of that option is described below:
>> 
>> 1. If the option is not set, it will work like it did before to ensure
>> full backwards-compatibility. 2. If the option is set once, its IP
>> address will be used for outgoing connections. 3. If the option is set
>> multiple times, the address is chosen randomly for outgoing connections.
>> 
>> Since I couldn’t find any official Git repository for Privoxy, I’ve made
>> the changes against 3.0.26-stable and pushed it to a Git(hub) repository
>> which can be found here:
>> 
>> https://github.com/commx/privoxy/pull/1
> 
> Please use "git format-patch" and send the patch to this thread.


---
 config      | 27 +++++++++++++++++
 jbsockets.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 jbsockets.h |  1 +
 loadcfg.c   | 37 +++++++++++++++++++++++
 project.h   |  9 ++++++
 5 files changed, 172 insertions(+)

diff --git a/config b/config
index 924df90..d84d04a 100644
--- a/config
+++ b/config
@@ -1167,6 +1167,33 @@ buffer-limit 4096
 #
 enable-proxy-authentication-forwarding 0
 #
+#  4.10. outgoing-address
+#  =======================
+#
+#  Specifies:
+#
+#      IP address to use for outgoing connections.
+#
+#  Type of value:
+#
+#      IP-Address
+#      Hostname
+#
+#  Default value:
+#
+#      unspecified
+#
+#  Effect if unset:
+#
+#      Outgoing connections are using the primary IP address of matching
+#      Route, or the default gateway.
+#
+#  Notes:
+#
+#      This option can be used to override the IP address used for
+#      outgoing connections.
+#
+#
 #  5. FORWARDING
 #  ==============
 #
diff --git a/jbsockets.c b/jbsockets.c
index 9db4270..734d15b 100644
--- a/jbsockets.c
+++ b/jbsockets.c
@@ -150,6 +150,98 @@ static void set_no_delay_flag(int fd)
 #endif /* def TCP_NODELAY */
 }

+/*********************************************************************
+ *
+ * Function    :  simple_addrinfo
+ *
+ * Description :  Resolve host addresses (or IP addresses) in their
+ *                binary representation, which can be used for
+ *                socket operations directly.
+ *
+ *                In case the return value is non-NULL, it need to
+ *                be free'd using freeaddrinfo() afterwards.
+ *
+ * Parameters  :
+ *          1  :  host = Host name or IP address to look up
+ *
+ * Returns     :  struct addrinfo * or NULL on error
+ *
+ *********************************************************************/
+struct addrinfo *simple_addrinfo(const char *host)
+{
+   struct addrinfo hints, *result;
+   int rc;
+
+   memset(&hints, 0, sizeof(struct sockaddr));
+   hints.ai_family = AF_UNSPEC;
+   hints.ai_socktype = SOCK_STREAM;
+
+   rc = getaddrinfo(host, NULL, &hints, &result);
+   if (rc != 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to get sockaddr for '%s'", host);
+      return NULL;
+   }
+
+   return result;
+}
+
+/*********************************************************************
+ *
+ * Function    :  set_outgoing_address
+ *
+ * Description :  Set outgoing address for socket connections.
+ *
+ * Parameters  :
+ *          1  :  fd = The file descriptor to operate on
+ *          2  :  af = Address family (AF_INET or AF_INET6) of the socket
+ *          3  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  void
+ *
+ *********************************************************************/
+static void set_outgoing_address(int fd, int af, struct client_state *csp)
+{
+   int i, j, rc, enable = 1;
+   const struct addrinfo *addrinfo[MAX_OUTGOING_ADDRESSES];
+   const struct addrinfo *rp;
+
+   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) != 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to set SO_REUSEADDR on socket %d", fd);
+      return;
+   }
+
+   // filter matching address family
+   for (i = 0, j = 0; i < MAX_OUTGOING_ADDRESSES; i++)
+   {
+      rp = csp->config->outgoing_addresses[i];
+      if (rp != NULL && rp->ai_family == af)
+         addrinfo[j++] = rp;
+   }
+
+   if (j == 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "No outgoing address for %s found",
+         (af == AF_INET) ? "AF_INET" : "AF_INET6");
+      return;
+   }
+
+   // pick address randomly in case there are more than one
+   i = (j > 1) ? rand() % j : 0;
+   rp = addrinfo[i];
+
+   if ((rc = bind(fd, rp->ai_addr, rp->ai_addrlen)) != 0)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Setting outgoing address on socket %d failed: %s",
+         fd, gai_strerror(rc));
+   }
+}
+
 /*********************************************************************
  *
  * Function    :  connect_to
@@ -317,6 +409,9 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client

       set_no_delay_flag(fd);

+      if (csp->config->outgoing_addresses[0] != NULL)
+         set_outgoing_address(fd, rp->ai_family, csp);
+
 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
       if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
       {
@@ -505,6 +600,9 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli

    set_no_delay_flag(fd);

+   if (csp->config->outgoing_addresses[0] != NULL)
+      set_outgoing_address(fd, rp->ai_family, csp);
+
 #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
    if ((flags = fcntl(fd, F_GETFL, 0)) != -1)
    {
diff --git a/jbsockets.h b/jbsockets.h
index 3eb6748..efc1871 100644
--- a/jbsockets.h
+++ b/jbsockets.h
@@ -42,6 +42,7 @@

 struct client_state;

+extern struct addrinfo *simple_addrinfo(const char *host);
 extern jb_socket connect_to(const char *host, int portnum, struct client_state *csp);
 #ifdef AMIGA
 extern int write_socket(jb_socket fd, const char *buf, ssize_t n);
diff --git a/loadcfg.c b/loadcfg.c
index 0f5fe07..954f915 100644
--- a/loadcfg.c
+++ b/loadcfg.c
@@ -88,6 +88,7 @@ const char loadcfg_rcs[] = "$Id: loadcfg.c,v 1.153 2016/05/22 12:43:07 fabiankei
 #ifdef FEATURE_CLIENT_TAGS
 #include "client-tags.h"
 #endif
+#include "jbsockets.h"

 const char loadcfg_h_rcs[] = LOADCFG_H_VERSION;

@@ -179,6 +180,7 @@ static struct file_list *current_configfile = NULL;
 #define hash_log_max_lines               2868344173U /* "log-max-lines" */
 #define hash_log_messages                2291744899U /* "log-messages" */
 #define hash_show_on_task_bar             215410365U /* "show-on-task-bar" */
+#define hash_outgoing_address            1523901695U /* "outgoing-address" */


 static void savearg(char *command, char *argument, struct configuration_spec * config);
@@ -240,6 +242,14 @@ static void unload_configfile (void * data)
    {
       freez(config->haddr[i]);
    }
+   for (i = 0; i < MAX_OUTGOING_ADDRESSES; i++)
+   {
+      if (config->outgoing_addresses[i] != NULL)
+      {
+         freeaddrinfo(config->outgoing_addresses[i]);
+         config->outgoing_addresses[i] = NULL;
+      }
+   }
    freez(config->logfile);

    for (i = 0; i < MAX_AF_FILES; i++)
@@ -1417,6 +1427,33 @@ struct configuration_spec * load_config(void)
             break;
          }

+/* *************************************************************************
+ * outgoing-address ip-or-hostname
+ * *************************************************************************/
+         case hash_outgoing_address:
+            for (i = 0; i < MAX_OUTGOING_ADDRESSES &&
+               config->outgoing_addresses[i] != NULL; i++);
+
+            if (i >= MAX_OUTGOING_ADDRESSES)
+            {
+               log_error(LOG_LEVEL_FATAL,
+                  "Too many 'outgoing-address' directives in config file - "
+                  "limit is %d.\n"
+                  "(You can increase this limit by changing "
+                  "MAX_OUTGOING_ADDRESSES in project.h and recompiling).",
+                  MAX_OUTGOING_ADDRESSES);
+            }
+
+            config->outgoing_addresses[i] = simple_addrinfo(arg);
+
+            if (config->outgoing_addresses[i] == NULL)
+            {
+               log_error(LOG_LEVEL_FATAL,
+                  "Invalid host or address for outgoing-address '%s'", arg);
+            }
+
+            break;
+
 /* *************************************************************************
  * permit-access source-ip[/significant-bits] [dest-ip[/significant-bits]]
  * *************************************************************************/
diff --git a/project.h b/project.h
index 914b52b..66830de 100644
--- a/project.h
+++ b/project.h
@@ -891,6 +891,12 @@ struct reusable_connection
  */
 #define MAX_LISTENING_SOCKETS 10

+/**
+ * Maximum number of outgoing addresses.  This limit is arbitrary - it's just used
+ * to size an array.
+ */
+#define MAX_OUTGOING_ADDRESSES 10
+
 /**
  * The state of a Privoxy processing thread.
  */
@@ -1338,6 +1344,9 @@ struct configuration_spec
    /** Size limit for IOB */
    size_t buffer_limit;

+   /** Socket addresses used for outgoing connections. */
+   struct addrinfo *outgoing_addresses[MAX_OUTGOING_ADDRESSES];
+
 #ifdef FEATURE_TRUST

    /** The file name of the trust file. */
--
2.16.1

> 
>> Please let me know if this could be merged into upstream.
> 
> I'm currently working on setting up the git repository.
> Once we have a working repository again I'll look into
> applying the patch.

Alright, thank you.

> 
> Fabian
> _______________________________________________
> Privoxy-devel mailing list
> Privoxy-devel at lists.privoxy.org
> https://lists.privoxy.org/mailman/listinfo/privoxy-devel

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: Message signed with OpenPGP
URL: <https://lists.privoxy.org/pipermail/privoxy-devel/attachments/20180215/f32e8175/attachment-0001.bin>


More information about the Privoxy-devel mailing list