[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