Index: modules/proxy/proxy_util.c =================================================================== --- modules/proxy/proxy_util.c (revision 677580) +++ modules/proxy/proxy_util.c (working copy) @@ -2267,6 +2267,31 @@ } #endif /* USE_ALTERNATE_IS_CONNECTED */ +apr_status_t bind_to_addr(apr_socket_t *newsock, apr_sockaddr_t *laddr, + const char *proxy_function, proxy_bind_addr *bind, server_rec *s) +{ + int i; + const int idx = bind->idx; + const int range = bind->range; + const int start = bind->port; + for(i = 0; i < range; ++i) { /* loop until we can bind correctly*/ + int port = start + ((idx + i) % range); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "proxy: %s: trying to bind to %s:%u", + proxy_function, laddr,port, NULL); + + laddr->port = port; + laddr->sa.sin.sin_port = htons(port); + if (apr_socket_bind(newsock, laddr) == APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "proxy: %s: bound to %s:%u", + proxy_function, laddr, laddr->port, NULL); + bind->idx = (port - start + 1) % range; + return APR_SUCCESS; + } + } + return APR_EINVAL; +} + + PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, @@ -2349,6 +2374,24 @@ "proxy: %s: fam %d socket created to connect to %s", proxy_function, backend_addr->family, worker->hostname); + proxy_bind_addr* bind = (worker->bind ? worker->bind : conf->bind); + if (bind) { + rv = APR_EINVAL; + apr_sockaddr_t *addr; + for(addr = bind->addr; addr; addr = addr->next) { + if (addr->family != backend_addr->family) continue; + rv = bind_to_addr(newsock, addr, proxy_function, bind, s); + if (rv == APR_SUCCESS) break; + } + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "proxy: %s: can not bind to %s:%u+%u", + proxy_function,bind->addr, bind->port, bind->range - 1, NULL); + return DECLINED; + } + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "proxy: Not using bind ",proxy_function); + } + /* make the connection out of the socket */ rv = apr_socket_connect(newsock, backend_addr); Index: modules/proxy/mod_proxy.c =================================================================== --- modules/proxy/mod_proxy.c (revision 677580) +++ modules/proxy/mod_proxy.c (working copy) @@ -35,6 +35,12 @@ #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #endif +/* Value from /usr/sys/port_kernel.h is it exposed in any portable header? */ +#define PORT_MAX_PORTS 0x10000 + +/* a sane value for RANGE of bind */ +#define MIN_RANGE_HINT 8 + /* return the sizeof of one lb_worker in scoreboard. */ static int ap_proxy_lb_worker_size(void) { @@ -70,6 +76,12 @@ (w)->io_buffer_size_set = (c)->io_buffer_size_set; \ } while (0) +static int parse_bind_address(char* val, + apr_sockaddr_t **bind_addr, + apr_port_t *bind_port, + apr_port_t *range, + apr_pool_t *p); + static const char *set_worker_param(apr_pool_t *p, proxy_worker *worker, const char *key, @@ -281,12 +293,59 @@ return "lbset must be between 0 and 99"; worker->lbset = ival; } + else if (!strcasecmp(key, "bind")) { + proxy_bind_addr* bind = + (proxy_bind_addr*) apr_pcalloc(p, sizeof(proxy_bind_addr)); + switch (parse_bind_address((char*)val, &(bind->addr), &(bind->port), &(bind->range), p)) { + case -1: + return "ProxySet bind: Invalid address -" + " format is [:+]"; + case -2: + return "ProxySet bind: Hostname did not resolve."; + } + bind->idx = 0; + worker->bind = bind; + } else { return "unknown Worker parameter"; } return NULL; } +static int parse_bind_address(char* val, + apr_sockaddr_t **bind_addr, + apr_port_t *port, + apr_port_t *r, apr_pool_t *p) { + char* range, *host, *scope_id; + range = ap_strrchr((char*)val,'+'); + if (range) { + *range++ = 0; + *r = atoi(range) + 1; + if (*r < MIN_RANGE_HINT) + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, + "range %u is too low, consider increasing the bind range.", (*r) - 1, NULL); + } else *r = 1; /* No range => "+0" -- internally 1 */ + /* We dont accept a single port with out a range as it would be same + * as a single threaded single process http-client + * + * We do not allow ip:0+0 or ip:0 as it can be specified more clearly + * as just ip. + * + * we dont accept a range with out a port either. + */ + if((apr_parse_addr_port(&host, &scope_id, port, val, p) != APR_SUCCESS) + || scope_id /* we dont know how to use scope_id */ + || (!*port && range) /* only a combo [port+range] is valid */ + || (*port && (!range || *r <= 0)) /* Let admin specify r=0 if he wants to */ + || ((*port + *r) > PORT_MAX_PORTS) + ) + return -1; + /* Preparse the address */ + if((apr_sockaddr_info_get(bind_addr, host, APR_UNSPEC, *port, 0, p) + != APR_SUCCESS) || !*bind_addr) + return -2; +} + static const char *set_balancer_param(proxy_server_conf *conf, apr_pool_t *p, proxy_balancer *balancer, @@ -1066,6 +1125,7 @@ ps->timeout_set = 0; ps->badopt = bad_error; ps->badopt_set = 0; + ps->bind = NULL; ps->pool = p; return ps; @@ -1109,6 +1169,7 @@ ps->badopt_set = overrides->badopt_set || base->badopt_set; ps->proxy_status = (overrides->proxy_status_set == 0) ? base->proxy_status : overrides->proxy_status; ps->proxy_status_set = overrides->proxy_status_set || base->proxy_status_set; + ps->bind = (overrides->bind ? overrides->bind : base->bind); ps->pool = p; return ps; } @@ -1706,6 +1767,26 @@ return NULL; } +static const char* + set_proxy_bindaddr(cmd_parms *parms, void *dummy, const char *addr) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + proxy_bind_addr* bind = + (proxy_bind_addr*) apr_pcalloc(parms->pool, sizeof(proxy_bind_addr)); + switch (parse_bind_address((char*)addr, &(bind->addr), &(bind->port), &(bind->range), parms->pool)) { + case -1: + return "ProxyBindAddress: Invalid address -" + " format is [:+]"; + case -2: + return "ProxyBindAddress: Hostname did not resolve."; + } + bind->idx = 0; + psf->bind = bind; + return NULL; +} + static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; @@ -2088,6 +2169,8 @@ "A balancer or worker name with list of params"), AP_INIT_TAKE1("ProxyFtpDirCharset", set_ftp_directory_charset, NULL, RSRC_CONF|ACCESS_CONF, "Define the character set for proxied FTP listings"), + AP_INIT_TAKE1("ProxyBindAddress", set_proxy_bindaddr, NULL, RSRC_CONF, + "Set the source address for a proxied connection."), {NULL} }; Index: modules/proxy/mod_proxy.h =================================================================== --- modules/proxy/mod_proxy.h (revision 677580) +++ modules/proxy/mod_proxy.h (working copy) @@ -133,7 +133,15 @@ typedef struct proxy_conn_pool proxy_conn_pool; typedef struct proxy_balancer_method proxy_balancer_method; +/*bind to local ports*/ typedef struct { + apr_sockaddr_t *addr; + apr_port_t port; + apr_port_t range; /* restrict the source ports used by mod_proxy */ + int idx; +} proxy_bind_addr; + +typedef struct { apr_array_header_t *proxies; apr_array_header_t *sec_proxy; apr_array_header_t *aliases; @@ -190,6 +198,10 @@ status_full } proxy_status; /* Status display options */ char proxy_status_set; + + /*bind to local ports*/ + proxy_bind_addr* bind; + apr_pool_t *pool; /* Pool used for allocating this struct */ } proxy_server_conf; @@ -352,6 +364,10 @@ char retry_set; char disablereuse; char disablereuse_set; + + /*bind to local ports*/ + proxy_bind_addr* bind; + }; /*