diff --git a/configure.in b/configure.in
index 179f944..68d4d31 100644
--- a/configure.in
+++ b/configure.in
@@ -1215,7 +1215,7 @@ AC_FUNC_SETPGRP
AC_TYPE_SIGNAL
dnl uncommenting does not work for swa. suse linux
dnl AC_FUNC_STAT
-AC_CHECK_FUNCS([access atexit getcwd gethostbyaddr gethostbyaddr_r gethostbyname gethostbyname_r gettimeofday inet_ntoa localtime_r memchr memmove memset putenv random regcomp select setlocale snprintf socket strchr strdup strerror strftime strlcat strlcpy strptime strstr strtoul timegm tzset])
+AC_CHECK_FUNCS([access atexit getaddrinfo getcwd gethostbyaddr gethostbyaddr_r gethostbyname gethostbyname_r getnameinfo gettimeofday inet_ntoa localtime_r memchr memmove memset putenv random regcomp select setlocale snprintf socket strchr strdup strerror strftime strlcat strlcpy strptime strstr strtoul timegm tzset])
dnl =================================================================
diff --git a/doc/source/p-config.sgml b/doc/source/p-config.sgml
index 87662e3..92d30e2 100644
--- a/doc/source/p-config.sgml
+++ b/doc/source/p-config.sgml
@@ -1266,9 +1266,9 @@ actionsfile
Effect if unset:
- Bind to 127.0.0.1 (localhost), port 8118. This is suitable and recommended for
- home users who run Privoxy on the same machine as
- their browser.
+ Bind to 127.0.0.1 (IPv4 localhost), port 8118. This is suitable and
+ recommended for home users who run Privoxy on
+ the same machine as their browser.
@@ -1284,6 +1284,9 @@ actionsfile
will need to override the default.
+ IPv6 address containing colons has to be quoted by brackets.
+
+
If you leave out the IP address, Privoxy will
bind to all interfaces (addresses) on your machine and may become reachable
from the Internet. In that case, consider using
+
+ Suppose you are running Privoxy on IPv6 capable
+ machine and you want to listen on IPv6 loopback device:
+
+
+
+ listen-address [::1]:8118
+
+
@@ -1664,23 +1676,41 @@ ACLs: permit-access and deny-access
Type of value:
- src_addr[/src_masklen]
- [dst_addr[/dst_masklen]]
+ src_addr[:port][/src_masklen]
+ [dst_addr[:port][/dst_masklen]]
Where src_addr and
- dst_addr are IP addresses in dotted decimal notation or valid
- DNS names, and src_masklen and
+ dst_addr are IPv4 addresses in dotted decimal notation or valid
+ DNS names, port is port
+ number, and src_masklen and
dst_masklen are subnet masks in CIDR notation, i.e. integer
values from 2 to 30 representing the length (in bits) of the network address. The masks and the whole
destination part are optional.
+
+ If your system implements
+ RFC 2553, then
+ src_addr and dst_addr can be IPv6 addresses delimeted by
+ brackets, port can be number
+ or service name, and
+ src_masklen and
+ dst_masklen can be number
+ from 0 to 128.
+
Default value:
Unset
+
+ No port means match any port
+ and no src_masklen or
+ no src_masklen means exactly
+ given IP address (i.e. 32 for IPv4 and 128 for IPv6).
+
@@ -1730,6 +1760,13 @@ ACLs: permit-access and deny-access
IP addresses, only the first one is used.
+ Some systems allows IPv4 client to connect to IPv6 server socket.
+ Then the client's IPv4 address will be translated by system into
+ IPv6 address space with special prefix ::ffff/96 (so called IPv4
+ mapped IPv6 address). Privoxy can handle it
+ and maps such ACL addresses automatically.
+
+
Denying access to particular sites by ACL may have undesired side effects
if the site in question is hosted on a machine which also hosts other sites
(most sites are).
@@ -1770,6 +1807,24 @@ ACLs: permit-access and deny-access
deny-access 192.168.45.73 www.dirty-stuff.example.com
+
+ Allow access from IPv4 network 192.0.2.0/24 even if listening on
+ IPv6 wild card address (where supported by operating system):
+
+
+
+ permit-access 192.0.2.0/24
+
+
+
+ This is equivalent to the following line even if listening on IPv4
+ address (where supported by operating system):
+
+
+
+ permit-access [::ffff:192.0.2.0]/120
+
+
@@ -1891,7 +1946,7 @@ ACLs: permit-access and deny-access
denote all URLs
.
http_parent[:port]
is the DNS name or IP address of the parent HTTP proxy through which the requests should be forwarded,
- optionally followed by its listening port (default: 8080).
+ optionally followed by its listening port (default: 8000).
Use a single dot (.) to denote no forwarding
.
@@ -1918,6 +1973,16 @@ ACLs: permit-access and deny-access
forwarded to another HTTP proxy but are made directly to the web servers.
+ http_parent can be IPv6
+ numerical address (if
+ RFC 2553 is
+ implemented). However not to clash with port delimiter, quote
+ whole IP address with brackets. On the other hand target_pattern containing IPv6 address
+ must be delimited by angle brackets (normal brackets are reserved for
+ regular expression already).
+
+
Multiple lines are OK, they are checked in sequence, and the last match wins.
@@ -1944,6 +2009,24 @@ ACLs: permit-access and deny-access
forward .isp.example.net .
+
+ Parent proxy specified by IPv6 address:
+
+
+
+ foward / [2001:DB8::1]:8000
+
+
+
+ Suppose your parent proxy doesn't support IPv6:
+
+
+
+ forward / parent-proxy.example.org:8000
+ forward ipv6-server.example.org .
+ forward <[2-3][0-9a-f][0-9a-f][0-9a-f]:*> .
+
+
@@ -2016,6 +2099,18 @@ forward-socks4, forward-socks4a and forward-socks5
With forward-socks5 the DNS resolution will happen on the remote server as well.
+ socks_proxy and
+ http_parent can be IPv6
+ numerical address (if
+ RFC 2553 is
+ implemented). However not to clash with port
+ delimiter, quote whole IP address with brackets. On the other
+ hand target_pattern containing
+ IPv6 address must be delimited by angle brackets (normal brackets are
+ reserved for regular expression already). The only exception is SOCKS 4
+ version where only IPv4 is suppored.
+
+
If http_parent is .
, then requests are not
forwarded to another HTTP proxy but are made (HTTP-wise) directly to the web servers, albeit through
a SOCKS proxy.
diff --git a/doc/source/user-manual.sgml b/doc/source/user-manual.sgml
index 88e9e84..1639acb 100644
--- a/doc/source/user-manual.sgml
+++ b/doc/source/user-manual.sgml
@@ -2271,12 +2271,12 @@ for details.
Generally, an URL pattern has the form
- <domain>/<path>, where both the
- <domain> and <path> are
- optional. (This is why the special / pattern matches all
- URLs). Note that the protocol portion of the URL pattern (e.g.
- http://) should not be included in
- the pattern. This is assumed already!
+ <domain><port>/<path>, where both the
+ <domain> and <port>
+ and <path> are optional. (This is why the special
+ / pattern matches all URLs). Note that the protocol
+ portion of the URL pattern (e.g. http://) should
+ not be included in the pattern. This is assumed already!
The pattern matching syntax is different for the domain and path parts of
@@ -2285,6 +2285,12 @@ for details.
Regular
Expressions
(POSIX 1003.2).
+
+ The port part of pattern is decimal port number preceeded by a colon
+ (:). If domain part contains numeric IPv6 address, you
+ will need to quote the domain part by angle brackets
+ (<, >).
+
@@ -2335,6 +2341,23 @@ for details.
+ :8000/
+
+
+ Matches any URL pointing to TCP port 8000.
+
+
+
+
+ <2001:db8::1>/
+
+
+ Matches any URL having 2001:db8::1 as a domain.
+ (Note that real URL uses plain brackets, not an angle brackets.)
+
+
+
+
index.html
diff --git a/filters.c b/filters.c
index 96e2a55..6b03dbd 100644
--- a/filters.c
+++ b/filters.c
@@ -647,6 +647,11 @@ const char filters_rcs[] = "$Id: filters.c,v 1.108 2008/05/21 15:35:08 fabiankei
#include
#include
+#ifdef HAVE_GETADDRINFO
+#include
+#include
+#endif /* def HAVE_GETADDRINFO */
+
#ifndef _WIN32
#ifndef __OS2__
#include
@@ -694,6 +699,152 @@ static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
static jb_err prepare_for_filtering(struct client_state *csp);
#ifdef FEATURE_ACL
+#ifdef HAVE_GETADDRINFO
+/*********************************************************************
+ *
+ * Function : sockaddr_storage_to_ip
+ *
+ * Description : Access internal structure of sockaddr_storage
+ *
+ * Parameters :
+ * 1 : addr = socket address
+ * 2 : ip = IP address as array of octets in network order
+ * (it points into addr)
+ * 3 : len = length of IP address in octets
+ * 4 : port = port number in network order;
+ *
+ * Returns : 0 = no errror; otherwise
+ *
+ *********************************************************************/
+int sockaddr_storage_to_ip(const struct sockaddr_storage *addr, uint8_t **ip,
+ unsigned int *len, in_port_t **port)
+{
+ if (!addr)
+ {
+ return(-1);
+ }
+
+ switch (addr->ss_family)
+ {
+ case AF_INET:
+ if (len)
+ {
+ *len = 4;
+ }
+ if (ip)
+ {
+ *ip = (uint8_t *)
+ &(( (struct sockaddr_in *) addr)->sin_addr.s_addr);
+ }
+ if (port)
+ {
+ *port = &((struct sockaddr_in *) addr)->sin_port;
+ }
+ break;
+
+ case AF_INET6:
+ if (len)
+ {
+ *len = 16;
+ }
+ if (ip)
+ {
+ *ip = (uint8_t *)
+ &(( (struct sockaddr_in6 *) addr)->sin6_addr.in6_u);
+ }
+ if (port)
+ {
+ *port = &((struct sockaddr_in6 *) addr)->sin6_port;
+ }
+ break;
+
+ default:
+ /* Unsupported address family */
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+/*********************************************************************
+ *
+ * Function : match_sockaddr
+ *
+ * Description : Check whether address matches network (IP address and port)
+ *
+ * Parameters :
+ * 1 : network = socket address of subnework
+ * 3 : netmask = network mask as socket address
+ * 2 : address = checked socket address against given network
+ *
+ * Returns : 0 = doesn't match; 1 = does match
+ *
+ *********************************************************************/
+int match_sockaddr(const struct sockaddr_storage *network,
+ const struct sockaddr_storage *netmask,
+ const struct sockaddr_storage *address)
+{
+ uint8_t *network_addr, *netmask_addr, *address_addr;
+ unsigned int addr_len;
+ in_port_t *network_port, *netmask_port, *address_port;
+ int i;
+
+ if (network->ss_family != netmask->ss_family)
+ {
+ /* This should never happen */
+ log_error(LOG_LEVEL_ERROR,
+ "Internal error at %s:%llu: network and netmask differ in family",
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ sockaddr_storage_to_ip(network, &network_addr, &addr_len, &network_port);
+ sockaddr_storage_to_ip(netmask, &netmask_addr, NULL, &netmask_port);
+ sockaddr_storage_to_ip(address, &address_addr, NULL, &address_port);
+
+ /* Check for family */
+ if (network->ss_family == AF_INET && address->ss_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(address_addr))
+ {
+ /* Map AF_INET6 V4MAPPED address into AF_INET */
+ address_addr += 12;
+ addr_len = 4;
+ }
+ else if (network->ss_family == AF_INET6 && address->ss_family == AF_INET &&
+ IN6_IS_ADDR_V4MAPPED(network_addr))
+ {
+ /* Map AF_INET6 V4MAPPED network into AF_INET */
+ network_addr += 12;
+ netmask_addr += 12;
+ addr_len = 4;
+ }
+ else if (network->ss_family != address->ss_family)
+ {
+ return 0;
+ }
+
+ /* XXX: Port check is signaled in netmask */
+ if (*netmask_port && *network_port != *address_port)
+ {
+ return 0;
+ }
+
+ /* TODO: Optimize by checking by words insted of octets */
+ for (i=0; i < addr_len && netmask_addr[i]; i++)
+ {
+ if ( (network_addr[i] & netmask_addr[i]) !=
+ (address_addr[i] & netmask_addr[i]) )
+ {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+#endif /* def HAVE_GETADDRINFO */
+
+
/*********************************************************************
*
* Function : block_acl
@@ -723,7 +874,13 @@ int block_acl(const struct access_control_addr *dst, const struct client_state *
/* search the list */
while (acl != NULL)
{
- if ((csp->ip_addr_long & acl->src->mask) == acl->src->addr)
+ if (
+#ifdef HAVE_GETADDRINFO
+ match_sockaddr(&acl->src->addr, &acl->src->mask, &csp->tcp_addr)
+#else
+ (csp->ip_addr_long & acl->src->mask) == acl->src->addr
+#endif
+ )
{
if (dst == NULL)
{
@@ -733,8 +890,23 @@ int block_acl(const struct access_control_addr *dst, const struct client_state *
return(0);
}
}
- else if ( ((dst->addr & acl->dst->mask) == acl->dst->addr)
- && ((dst->port == acl->dst->port) || (acl->dst->port == 0)))
+ else if (
+#ifdef HAVE_GETADDRINFO
+ /* XXX: Undefined acl->dst is full of zeros and should be
+ * considered as wildcard address.
+ * sockaddr_storage_to_ip() failes on such dst because of
+ * uknown sa_familly on glibc. However this test is not
+ * portable.
+ *
+ * So, we signal the acl->dst is wildcard in wildcard_dst.
+ */
+ acl->wildcard_dst ||
+ match_sockaddr(&acl->dst->addr, &acl->dst->mask, &dst->addr)
+#else
+ ((dst->addr & acl->dst->mask) == acl->dst->addr)
+ && ((dst->port == acl->dst->port) || (acl->dst->port == 0))
+#endif
+ )
{
if (acl->action == ACL_PERMIT)
{
@@ -770,12 +942,24 @@ int block_acl(const struct access_control_addr *dst, const struct client_state *
int acl_addr(const char *aspec, struct access_control_addr *aca)
{
int i, masklength;
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, *result;
+ uint8_t *mask_data;
+ in_port_t *mask_port;
+ unsigned int addr_len;
+#else
long port;
+#endif /* def HAVE_GETADDRINFO */
char *p;
char *acl_spec = NULL;
+#ifdef HAVE_GETADDRINFO
+ /* FIXME: Depend on ai_family */
+ masklength = 128;
+#else
masklength = 32;
port = 0;
+#endif
/*
* Use a temporary acl spec copy so we can log
@@ -799,13 +983,53 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
masklength = atoi(p);
}
- if ((masklength < 0) || (masklength > 32))
+ if ((masklength < 0) ||
+#ifdef HAVE_GETADDRINFO
+ (masklength > 128)
+#else
+ (masklength > 32)
+#endif
+ )
{
freez(acl_spec);
return(-1);
}
- if ((p = strchr(acl_spec, ':')) != NULL)
+ if (*acl_spec == '[' && NULL != (p = strchr(acl_spec, ']')))
+ {
+ *p = '\0';
+ memmove(acl_spec, acl_spec + 1, (size_t) (p - acl_spec));
+
+ if (*++p != ':')
+ {
+ p = NULL;
+ }
+ }
+ else
+ {
+ p = strchr(acl_spec, ':');
+ }
+
+#ifdef HAVE_GETADDRINFO
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ i = getaddrinfo(acl_spec, (p) ? ++p : NULL, &hints, &result);
+ freez(acl_spec);
+
+ if (i != 0)
+ {
+ log_error(LOG_LEVEL_ERROR, "Can not resolve [%s]:%s: %s", acl_spec, p,
+ gai_strerror(i));
+ return(-1);
+ }
+
+ /* TODO: Allow multihomed hostnames */
+ memcpy(&(aca->addr), result->ai_addr, sizeof(aca->addr));
+ freeaddrinfo(result);
+#else
+ if (p != NULL)
{
char *endptr;
@@ -829,8 +1053,49 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
/* XXX: This will be logged as parse error. */
return(-1);
}
+#endif /* def HAVE_GETADDRINFO */
/* build the netmask */
+#ifdef HAVE_GETADDRINFO
+ /* Clip masklength according current family */
+ if (aca->addr.ss_family == AF_INET && masklength > 32)
+ {
+ masklength = 32;
+ }
+
+ aca->mask.ss_family = aca->addr.ss_family;
+ if (sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port))
+ {
+ return(-1);
+ }
+
+ if (p)
+ {
+ /* Port number in ACL has been specified, check ports in future */
+ *mask_port = 1;
+ }
+
+ /* XXX: This could be optimized to operate on whole words instead of octets
+ * (128-bit CPU could do it in one iteration). */
+ /* Octets after prefix can be ommitted because of previous initialization
+ * to zeros. */
+ for (i=0; i < addr_len && masklength; i++)
+ {
+ if (masklength >= 8)
+ {
+ mask_data[i] = 0xFF;
+ masklength -= 8;
+ }
+ else
+ {
+ /* XXX: This assumes MSB of octet is on the left site. This should be
+ * true for all architectures or solved on link layer of OSI model. */
+ mask_data[i] = ~((1 << (8 - masklength)) - 1);
+ masklength = 0;
+ }
+ }
+
+#else
aca->mask = 0;
for (i=1; i <= masklength ; i++)
{
@@ -841,6 +1106,7 @@ int acl_addr(const char *aspec, struct access_control_addr *aca)
* (i.e. save on the network portion of the address).
*/
aca->addr = aca->addr & aca->mask;
+#endif /* def HAVE_GETADDRINFO */
return(0);
@@ -2685,4 +2951,6 @@ int content_filters_enabled(const struct current_action_spec *action)
Local Variables:
tab-width: 3
end:
+
+ vim:softtabstop=3 shiftwidth=3
*/
diff --git a/jbsockets.c b/jbsockets.c
index 91832f8..9467c3f 100644
--- a/jbsockets.c
+++ b/jbsockets.c
@@ -345,19 +345,52 @@ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION;
*********************************************************************/
jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
{
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, *result, *rp;
+ char service[6];
+ int retval;
+#else
struct sockaddr_in inaddr;
- jb_socket fd;
int addr;
+#endif /* def HAVE_GETADDRINFO */
+ jb_socket fd;
fd_set wfds;
struct timeval tv[1];
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA)
int flags;
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) */
+ int connect_failed;
#ifdef FEATURE_ACL
struct access_control_addr dst[1];
#endif /* def FEATURE_ACL */
+#ifdef HAVE_GETADDRINFO
+ retval = snprintf(service, sizeof(service), "%d", portnum);
+ if (-1 == retval || sizeof(service) <= retval)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes",
+ portnum);
+ csp->http->host_ip_addr_str = strdup("unknown");
+ return(JB_INVALID_SOCKET);
+ }
+
+ memset((char *)&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; /* avoid service look-up */
+ if ((retval = getaddrinfo(host, service, &hints, &result)))
+ {
+ log_error(LOG_LEVEL_INFO,
+ "Can not resolve %s: %s", host, gai_strerror(retval));
+ csp->http->host_ip_addr_str = strdup("unknown");
+ return(JB_INVALID_SOCKET);
+ }
+
+ for (rp = result; rp != NULL; rp = rp->ai_next)
+ {
+#else
memset((char *)&inaddr, 0, sizeof inaddr);
if ((addr = resolve_hostname_to_ip(host)) == INADDR_NONE)
@@ -365,10 +398,15 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
csp->http->host_ip_addr_str = strdup("unknown");
return(JB_INVALID_SOCKET);
}
+#endif /* def HAVE_GETADDRINFO */
#ifdef FEATURE_ACL
+#ifdef HAVE_GETADDRINFO
+ memcpy(&dst->addr, rp->ai_addr, sizeof(dst->addr));
+#else
dst->addr = ntohl((unsigned long) addr);
dst->port = portnum;
+#endif /* def HAVE_GETADDRINFO */
if (block_acl(dst, csp))
{
@@ -377,14 +415,43 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
#else
errno = EPERM;
#endif
+#ifdef HAVE_GETADDRINFO
+ continue;
+#else
return(JB_INVALID_SOCKET);
+#endif /* def HAVE_GETADDRINFO */
}
#endif /* def FEATURE_ACL */
+#ifdef HAVE_GETNAMEINFO
+ csp->http->host_ip_addr_str = malloc(NI_MAXHOST);
+ retval = getnameinfo(rp->ai_addr, rp->ai_addrlen,
+ csp->http->host_ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+ if (!csp->http->host_ip_addr_str || retval)
+ {
+ log_error(LOG_LEVEL_ERROR, "Can not save csp->http->host_ip_addr_str: %s",
+ (csp->http->host_ip_addr_str) ? gai_strerror(retval) :
+ "Insufficient memory");
+ freez(csp->http->host_ip_addr_str);
+ continue;
+ }
+#else
inaddr.sin_addr.s_addr = addr;
inaddr.sin_family = AF_INET;
csp->http->host_ip_addr_str = strdup(inet_ntoa(inaddr.sin_addr));
+#endif /* def HAVE_GETNAMERINFO */
+#ifdef HAVE_GETADDRINFO
+#ifdef _WIN32
+ if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) ==
+ JB_INVALID_SOCKET)
+#else
+ if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
+#endif
+ {
+ continue;
+ }
+#else
#ifndef _WIN32
if (sizeof(inaddr.sin_port) == sizeof(short))
#endif /* ndef _WIN32 */
@@ -406,6 +473,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
{
return(JB_INVALID_SOCKET);
}
+#endif /* HAVE_GETADDRINFO */
#ifdef TCP_NODELAY
{ /* turn off TCP coalescence */
@@ -422,7 +490,12 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
}
#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
+ connect_failed = 0;
+#ifdef HAVE_GETADDRINFO
+ while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET)
+#else
while (connect(fd, (struct sockaddr *) & inaddr, sizeof inaddr) == JB_INVALID_SOCKET)
+#endif /* HAVE_GETADDRINFO */
{
#ifdef _WIN32
if (errno == WSAEINPROGRESS)
@@ -442,9 +515,18 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
#endif /* __OS2__ */
{
close_socket(fd);
- return(JB_INVALID_SOCKET);
+ connect_failed = 1;
+ break;
}
}
+ if (connect_failed)
+ {
+#ifdef HAVE_GETADDRINFO
+ continue;
+#else
+ return(JB_INVALID_SOCKET);
+#endif
+ }
#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__)
if (flags != -1)
@@ -465,8 +547,32 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp)
if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0)
{
close_socket(fd);
+#ifdef HAVE_GETADDRINFO
+ continue;
+#else
+ return(JB_INVALID_SOCKET);
+#endif
+ }
+
+#ifdef HAVE_GETADDRINFO
+ break; /* for */
+ }
+
+ freeaddrinfo(result);
+ if (!rp)
+ {
+ log_error(LOG_LEVEL_INFO, "Could not connect to TCP/[%s]:%s", host, service);
return(JB_INVALID_SOCKET);
}
+ /* XXX: Current connection verification (EINPROGRESS && select() for
+ * writing) is not sufficient. E.g. on my Linux-2.6.27 with glibc-2.6
+ * select returns socket ready for writing, however subsequential write(2)
+ * fails with ENOCONNECT. Read Linux connect(2) man page about non-blocking
+ * sockets.
+ * Thus we can not log here the socket is connected. */
+ /*log_error(LOG_LEVEL_INFO, "Connected to TCP/[%s]:%s", host, service);*/
+#endif
+
return(fd);
}
@@ -625,7 +731,16 @@ void close_socket(jb_socket fd)
*********************************************************************/
int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
{
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints;
+ struct addrinfo *result, *rp;
+ /* TODO: portnum shuld be string to allow symbolic service names in
+ * configuration and to avoid following int2string */
+ char servnam[6];
+ int retval;
+#else
struct sockaddr_in inaddr;
+#endif /* def HAVE_GETADDRINFO */
jb_socket fd;
#ifndef _WIN32
int one = 1;
@@ -633,6 +748,32 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
*pfd = JB_INVALID_SOCKET;
+#ifdef HAVE_GETADDRINFO
+ retval = snprintf(servnam, sizeof(servnam), "%d", portnum);
+ if (-1 == retval || sizeof(servnam) <= retval)
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Port number (%d) ASCII decimal representation doesn't fit into 6 bytes",
+ portnum);
+ return -1;
+ }
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family=AF_UNSPEC;
+ hints.ai_socktype=SOCK_STREAM;
+ hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG;
+ hints.ai_protocol=0; /* Realy any stream protocol or TCP only */
+ hints.ai_canonname=NULL;
+ hints.ai_addr=NULL;
+ hints.ai_next=NULL;
+
+ if ((retval = getaddrinfo(hostnam, servnam, &hints, &result)))
+ {
+ log_error(LOG_LEVEL_ERROR,
+ "Can not resolve %s: %s", hostnam, gai_strerror(retval));
+ return -2;
+ }
+#else
memset((char *)&inaddr, '\0', sizeof inaddr);
inaddr.sin_family = AF_INET;
@@ -655,8 +796,15 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
inaddr.sin_port = htonl((unsigned long) portnum);
}
#endif /* ndef _WIN32 */
+#endif /* def HAVE_GETADDRINFO */
+#ifdef HAVE_GETADDRINFO
+ for (rp = result; rp != NULL; rp = rp->ai_next)
+ {
+ fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+#else
fd = socket(AF_INET, SOCK_STREAM, 0);
+#endif /* def HAVE_GETADDRINFO */
#ifdef _WIN32
if (fd == JB_INVALID_SOCKET)
@@ -664,7 +812,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
if (fd < 0)
#endif
{
+#ifdef HAVE_GETADDRINFO
+ continue;
+#else
return(-1);
+#endif
}
#ifndef _WIN32
@@ -683,7 +835,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
#endif /* ndef _WIN32 */
+#ifdef HAVE_GETADDRINFO
+ if (bind(fd, rp->ai_addr, rp->ai_addrlen) < 0)
+#else
if (bind(fd, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0)
+#endif
{
#ifdef _WIN32
errno = WSAGetLastError();
@@ -692,15 +848,36 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
if (errno == EADDRINUSE)
#endif
{
+#ifdef HAVE_GETADDRINFO
+ freeaddrinfo(result);
+#endif
close_socket(fd);
return(-3);
}
else
{
close_socket(fd);
+#ifndef HAVE_GETADDRINFO
return(-1);
}
}
+#else
+ }
+ }
+ else
+ /* bind() succeeded, escape from for-loop */
+ /* TODO: Support multiple listening sockets (e.g. localhost resolves to
+ * AF_INET and AF_INET6, but only fist address is used */
+ break;
+ }
+
+ freeaddrinfo(result);
+ if (rp == NULL)
+ {
+ /* All bind()s failed */
+ return(-1);
+ }
+#endif /* ndef HAVE_GETADDRINFO */
while (listen(fd, MAX_LISTEN_BACKLOG) == -1)
{
@@ -740,14 +917,20 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
*********************************************************************/
void get_host_information(jb_socket afd, char **ip_address, char **hostname)
{
+#ifdef HAVE_GETNAMEINFO
+ struct sockaddr_storage server;
+ int retval;
+#else
struct sockaddr_in server;
struct hostent *host = NULL;
+#endif /* HAVE_GETNAMEINFO */
#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA)
/* according to accept_connection() this fixes a warning. */
- int s_length;
+ int s_length, s_length_provided;
#else
- socklen_t s_length;
+ socklen_t s_length, s_length_provided;
#endif
+#ifndef HAVE_GETNAMEINFO
#if defined(HAVE_GETHOSTBYADDR_R_8_ARGS) || defined(HAVE_GETHOSTBYADDR_R_7_ARGS) || defined(HAVE_GETHOSTBYADDR_R_5_ARGS)
struct hostent result;
#if defined(HAVE_GETHOSTBYADDR_R_5_ARGS)
@@ -757,7 +940,8 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname)
int thd_err;
#endif /* def HAVE_GETHOSTBYADDR_R_5_ARGS */
#endif /* def HAVE_GETHOSTBYADDR_R_(8|7|5)_ARGS */
- s_length = sizeof(server);
+#endif /* ifndef HAVE_GETNAMEINFO */
+ s_length = s_length_provided = sizeof(server);
if (NULL != hostname)
{
@@ -767,8 +951,23 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname)
if (!getsockname(afd, (struct sockaddr *) &server, &s_length))
{
+ if (s_length > s_length_provided)
+ {
+ log_error(LOG_LEVEL_ERROR, "getsockname() truncated server address");
+ return;
+ }
+#ifdef HAVE_GETNAMEINFO
+ *ip_address = malloc(NI_MAXHOST);
+ if ((retval = getnameinfo((struct sockaddr *) &server, s_length,
+ *ip_address, NI_MAXHOST, NULL, 0, NI_NUMERICHOST))) {
+ log_error(LOG_LEVEL_ERROR, "Unable to print my own IP address: %s",
+ gai_strerror(retval));
+ freez(*ip_address);
+ return;
+ }
+#else
*ip_address = strdup(inet_ntoa(server.sin_addr));
-
+#endif /* HAVE_GETNAMEINFO */
if (NULL == hostname)
{
/*
@@ -777,6 +976,16 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname)
*/
return;
}
+
+#ifdef HAVE_GETNAMEINFO
+ *hostname = malloc(NI_MAXHOST);
+ if ((retval = getnameinfo((struct sockaddr *) &server, s_length,
+ *hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD))) {
+ log_error(LOG_LEVEL_ERROR, "Unable to resolve my own IP address: %s",
+ gai_strerror(retval));
+ freez(*hostname);
+ }
+#else
#if defined(HAVE_GETHOSTBYADDR_R_8_ARGS)
gethostbyaddr_r((const char *)&server.sin_addr,
sizeof(server.sin_addr), AF_INET,
@@ -814,6 +1023,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname)
{
*hostname = strdup(host->h_name);
}
+#endif /* else def HAVE_GETNAMEINFO */
}
return;
@@ -838,7 +1048,13 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname)
*********************************************************************/
int accept_connection(struct client_state * csp, jb_socket fd)
{
+#ifdef HAVE_GETNAMEINFO
+ /* XXX: client is stored directly into csp->tcp_addr */
+#define client (csp->tcp_addr)
+ int retval;
+#else
struct sockaddr_in client;
+#endif
jb_socket afd;
#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA)
/* Wierdness - fix a warning. */
@@ -867,8 +1083,21 @@ int accept_connection(struct client_state * csp, jb_socket fd)
#endif
csp->cfd = afd;
+#ifdef HAVE_GETNAMEINFO
+ csp->ip_addr_str = malloc(NI_MAXHOST);
+ retval = getnameinfo((struct sockaddr *) &client, c_length,
+ csp->ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
+ if (!csp->ip_addr_str || retval)
+ {
+ log_error(LOG_LEVEL_ERROR, "Can not save csp->ip_addr_str: %s",
+ (csp->ip_addr_str) ? gai_strerror(retval) : "Insuffcient memory");
+ freez(csp->ip_addr_str);
+ }
+#undef client
+#else
csp->ip_addr_str = strdup(inet_ntoa(client.sin_addr));
csp->ip_addr_long = ntohl(client.sin_addr.s_addr);
+#endif /* def HAVE_GETNAMEINFO */
return 1;
@@ -999,4 +1228,6 @@ unsigned long resolve_hostname_to_ip(const char *host)
Local Variables:
tab-width: 3
end:
+
+ vim:softtabstop=3 shiftwidth=3
*/
diff --git a/jcc.c b/jcc.c
index 0fb7cfd..2800869 100644
--- a/jcc.c
+++ b/jcc.c
@@ -2320,7 +2320,7 @@ static void chat(struct client_state *csp)
if (fwd->forward_host)
{
- log_error(LOG_LEVEL_CONNECT, "via %s:%d to: %s",
+ log_error(LOG_LEVEL_CONNECT, "via [%s]:%d to: %s",
fwd->forward_host, fwd->forward_port, http->hostport);
}
else
@@ -3899,4 +3899,6 @@ static void listen_loop(void)
Local Variables:
tab-width: 3
end:
+
+ vim:softtabstop=3 shiftwidth=3
*/
diff --git a/loadcfg.c b/loadcfg.c
index 69cce44..d3f1cfc 100644
--- a/loadcfg.c
+++ b/loadcfg.c
@@ -776,7 +776,6 @@ struct configuration_spec * load_config(void)
* Set to defaults
*/
config->multi_threaded = 1;
- config->hport = HADDR_PORT;
config->buffer_limit = 4096 * 1024;
config->usermanual = strdup(USER_MANUAL_URL);
config->proxy_args = strdup("");
@@ -985,6 +984,12 @@ struct configuration_spec * load_config(void)
continue;
}
}
+#ifdef HAVE_GETADDRINFO
+ else
+ {
+ cur_acl->wildcard_dst = 1;
+ }
+#endif /* def HAVE_GETADDRINFO */
/*
* Add it to the list. Note we reverse the list to get the
@@ -1133,7 +1138,18 @@ struct configuration_spec * load_config(void)
{
cur_fwd->forward_host = strdup(p);
- if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
+ if (*cur_fwd->forward_host == '[' &&
+ NULL != (p = strchr(cur_fwd->forward_host, ']')))
+ {
+ *p++ = '\0';
+ memmove(cur_fwd->forward_host, cur_fwd->forward_host + 1,
+ (size_t) (p - cur_fwd->forward_host));
+ if (*p == ':')
+ {
+ cur_fwd->forward_port = atoi(++p);
+ }
+ }
+ else if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
{
*p++ = '\0';
cur_fwd->forward_port = atoi(p);
@@ -1196,11 +1212,23 @@ struct configuration_spec * load_config(void)
{
cur_fwd->gateway_host = strdup(p);
- if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
+ if (*cur_fwd->gateway_host == '[' &&
+ NULL != (p = strchr(cur_fwd->gateway_host, ']')))
+ {
+ *p++ = '\0';
+ memmove(cur_fwd->gateway_host, cur_fwd->gateway_host + 1,
+ (size_t) (p - cur_fwd->gateway_host));
+ if (*p == ':')
+ {
+ cur_fwd->gateway_port = atoi(++p);
+ }
+ }
+ else if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
{
*p++ = '\0';
cur_fwd->gateway_port = atoi(p);
}
+
if (cur_fwd->gateway_port <= 0)
{
cur_fwd->gateway_port = 1080;
@@ -1214,7 +1242,18 @@ struct configuration_spec * load_config(void)
{
cur_fwd->forward_host = strdup(p);
- if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
+ if (*cur_fwd->forward_host == '[' &&
+ NULL != (p = strchr(cur_fwd->forward_host, ']')))
+ {
+ *p++ = '\0';
+ memmove(cur_fwd->forward_host, cur_fwd->forward_host + 1,
+ (size_t) (p - cur_fwd->forward_host));
+ if (*p == ':')
+ {
+ cur_fwd->forward_port = atoi(++p);
+ }
+ }
+ else if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
{
*p++ = '\0';
cur_fwd->forward_port = atoi(p);
@@ -1283,11 +1322,23 @@ struct configuration_spec * load_config(void)
cur_fwd->gateway_host = strdup(p);
- if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
+ if (*cur_fwd->gateway_host == '[' &&
+ NULL != (p = strchr(cur_fwd->gateway_host, ']')))
+ {
+ *p++ = '\0';
+ memmove(cur_fwd->gateway_host, cur_fwd->gateway_host + 1,
+ (size_t) (p - cur_fwd->gateway_host));
+ if (*p == ':')
+ {
+ cur_fwd->gateway_port = atoi(++p);
+ }
+ }
+ else if (NULL != (p = strchr(cur_fwd->gateway_host, ':')))
{
*p++ = '\0';
cur_fwd->gateway_port = atoi(p);
}
+
if (cur_fwd->gateway_port <= 0)
{
cur_fwd->gateway_port = 1080;
@@ -1300,7 +1351,18 @@ struct configuration_spec * load_config(void)
{
cur_fwd->forward_host = strdup(p);
- if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
+ if (*cur_fwd->forward_host == '[' &&
+ NULL != (p = strchr(cur_fwd->forward_host, ']')))
+ {
+ *p++ = '\0';
+ memmove(cur_fwd->forward_host, cur_fwd->forward_host + 1,
+ (size_t) (p - cur_fwd->forward_host));
+ if (*p == ':')
+ {
+ cur_fwd->forward_port = atoi(++p);
+ }
+ }
+ else if (NULL != (p = strchr(cur_fwd->forward_host, ':')))
{
*p++ = '\0';
cur_fwd->forward_port = atoi(p);
@@ -1439,6 +1501,12 @@ struct configuration_spec * load_config(void)
continue;
}
}
+#ifdef HAVE_GETADDRINFO
+ else
+ {
+ cur_acl->wildcard_dst = 1;
+ }
+#endif /* def HAVE_GETADDRINFO */
/*
* Add it to the list. Note we reverse the list to get the
@@ -1735,18 +1803,20 @@ struct configuration_spec * load_config(void)
if ( NULL != config->haddr )
{
- if (NULL != (p = strchr(config->haddr, ':')))
+ if (*config->haddr == '[' && NULL != (p = strchr(config->haddr, ']')) &&
+ p[1] == ':' && 0 < (config->hport = atoi(p + 2)))
{
- *p++ = '\0';
- if (*p)
- {
- config->hport = atoi(p);
- }
+ *p='\0';
+ memmove((void *) config->haddr, config->haddr + 1,
+ (size_t) (p - config->haddr));
}
-
- if (config->hport <= 0)
+ else if (NULL != (p = strchr(config->haddr, ':')) &&
+ 0 < (config->hport = atoi(p + 1)))
+ {
+ *p = '\0';
+ }
+ else
{
- *--p = ':';
log_error(LOG_LEVEL_FATAL, "invalid bind port spec %s", config->haddr);
/* Never get here - LOG_LEVEL_FATAL causes program exit */
}
@@ -1922,4 +1992,6 @@ static void savearg(char *command, char *argument, struct configuration_spec * c
Local Variables:
tab-width: 3
end:
+
+ vim:softtabstop=3 shiftwidth=3
*/
diff --git a/project.h b/project.h
index 4dbe236..afbd560 100644
--- a/project.h
+++ b/project.h
@@ -657,6 +657,12 @@
/* Needed for pcre choice */
#include "config.h"
+#ifdef HAVE_GETADDRINFO
+/* Need for struct sockaddr_storage */
+#include
+#endif
+
+
/*
* Include appropriate regular expression libraries.
* Note that pcrs and pcre (native) are needed for cgi
@@ -808,16 +814,10 @@ typedef int jb_err;
#define FOREVER 1
/**
- * Default IP address to listen on, as a string.
- * Set to "127.0.0.1".
- */
-#define HADDR_DEFAULT "127.0.0.1"
-
-/**
- * Default port to listen on, as a number.
- * Set to 8118.
+ * Default TCP/IP address to listen on, as a string.
+ * Set to "127.0.0.1:8118".
*/
-#define HADDR_PORT 8118
+#define HADDR_DEFAULT "127.0.0.1:8118"
/* Forward def for struct client_state */
@@ -1349,9 +1349,15 @@ struct client_state
/** Client PC's IP address, as reported by the accept() function.
As a string. */
char *ip_addr_str;
+#ifdef HAVE_GETADDRINFO
+ /** Client PC's TCP address, as reported by the accept() function.
+ As a sockaddr. */
+ struct sockaddr_storage tcp_addr;
+#else
/** Client PC's IP address, as reported by the accept() function.
As a number. */
long ip_addr_long;
+#endif /* def HAVE_GETADDRINFO */
/** The URL that was requested */
struct http_request http[1];
@@ -1577,9 +1583,14 @@ struct re_filterfile_spec
*/
struct access_control_addr
{
+#ifdef HAVE_GETADDRINFO
+ struct sockaddr_storage addr; /* ' instead. */
+ if (buf[0] == '<' && NULL != (p = strchr(buf + 1, '>')))
+ {
+ *p++ = '\0';
+ buf++;
+
+ if (*p == '\0')
+ {
+ /* Only IPv6 address without port number */
+ p = NULL;
+ }
+ else if (*p != ':')
+ {
+ /* Garbage after address delimiter */
+ return JB_ERR_PARSE;
+ }
+ }
+ else
+ {
+ p = strchr(buf, ':');
+ }
+
if (NULL != p)
{
*p++ = '\0';
@@ -1437,4 +1493,6 @@ int match_portlist(const char *portlist, int port)
Local Variables:
tab-width: 3
end:
+
+ vim:softtabstop=3 shiftwidth=3
*/