diff --git a/configure.in b/configure.in index 6c6ce39..713c3f0 100644 --- a/configure.in +++ b/configure.in @@ -1239,6 +1239,14 @@ 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 poll putenv random regcomp select setlocale snprintf socket strchr strdup strerror strftime strlcat strlcpy strptime strstr strtoul timegm tzset]) +dnl Checks for RFC 2553 resolver and socket functions +AC_CHECK_FUNC([getaddrinfo], + [AC_CHECK_FUNC([getnameinfo], + [AC_DEFINE([HAVE_RFC2553], [1], + [Define if RFC 2553 resolver functions like getaddrinfo(3) and + getnameinfo(3) present]) + ]) + ]) dnl ================================================================= dnl Checks for libraries. diff --git a/doc/source/p-config.sgml b/doc/source/p-config.sgml index 3b59fbf..2addcb3 100644 --- a/doc/source/p-config.sgml +++ b/doc/source/p-config.sgml @@ -1213,9 +1213,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. @@ -1231,6 +1231,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 + + @@ -1611,23 +1623,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 3493, 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). + @@ -1677,6 +1707,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:0:0/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). @@ -1717,6 +1754,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 + + @@ -1838,7 +1893,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. @@ -1865,6 +1920,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 3493 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. @@ -1891,6 +1956,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]:*> . + + @@ -1963,6 +2046,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 3493 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 2999e3e..ac7ad28 100644 --- a/doc/source/user-manual.sgml +++ b/doc/source/user-manual.sgml @@ -2147,12 +2147,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 @@ -2161,6 +2161,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 + (<, >). + @@ -2211,6 +2217,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 8fa1d4e..1701bb2 100644 --- a/filters.c +++ b/filters.c @@ -665,6 +665,11 @@ const char filters_rcs[] = "$Id: filters.c,v 1.113 2009/03/08 14:19:23 fabiankei #include #include +#ifdef HAVE_RFC2553 +#include +#include +#endif /* def HAVE_RFC2553 */ + #ifndef _WIN32 #ifndef __OS2__ #include @@ -712,6 +717,151 @@ 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_RFC2553 +/********************************************************************* + * + * 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 = ( (struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; + } + 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_RFC2553 */ + + /********************************************************************* * * Function : block_acl @@ -741,7 +891,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_RFC2553 + 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) { @@ -751,8 +907,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_RFC2553 + /* 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) { @@ -788,12 +959,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_RFC2553 + struct addrinfo hints, *result; + uint8_t *mask_data; + in_port_t *mask_port; + unsigned int addr_len; +#else long port; +#endif /* def HAVE_RFC2553 */ char *p; char *acl_spec = NULL; +#ifdef HAVE_RFC2553 + /* FIXME: Depend on ai_family */ + masklength = 128; +#else masklength = 32; port = 0; +#endif /* * Use a temporary acl spec copy so we can log @@ -817,13 +1000,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_RFC2553 + (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_RFC2553 + 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, result->ai_addrlen); + freeaddrinfo(result); +#else + if (p != NULL) { char *endptr; @@ -847,8 +1070,51 @@ int acl_addr(const char *aspec, struct access_control_addr *aca) /* XXX: This will be logged as parse error. */ return(-1); } +#endif /* def HAVE_RFC2553 */ /* build the netmask */ +#ifdef HAVE_RFC2553 + /* 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. */ + /* XXX: Cast to uint8_t is for quieting compiler. The right side is + * always between 1 and 127 inclusive. */ + mask_data[i] = (uint8_t) ~((1U << (8 - masklength)) - 1); + masklength = 0; + } + } + +#else aca->mask = 0; for (i=1; i <= masklength ; i++) { @@ -859,6 +1125,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_RFC2553 */ return(0); @@ -2706,4 +2973,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 4548ff2..d553e63 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -355,6 +355,177 @@ const char jbsockets_h_rcs[] = JBSOCKETS_H_VERSION; * file descriptor. * *********************************************************************/ +#ifdef HAVE_RFC2553 +/* Getaddrinfo implementation */ +jb_socket connect_to(const char *host, int portnum, struct client_state *csp) +{ + struct addrinfo hints, *result, *rp; + char service[6]; + int retval; + 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 */ + + 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) + { + +#ifdef FEATURE_ACL + memcpy(&dst->addr, rp->ai_addr, rp->ai_addrlen); + + if (block_acl(dst, csp)) + { +#ifdef __OS2__ + errno = SOCEPERM; +#else + errno = EPERM; +#endif + continue; + } +#endif /* def FEATURE_ACL */ + + 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; + } + +#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; + } + +#ifdef TCP_NODELAY + { /* turn off TCP coalescence */ + int mi = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int)); + } +#endif /* def TCP_NODELAY */ + +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + if ((flags = fcntl(fd, F_GETFL, 0)) != -1) + { + flags |= O_NDELAY; + fcntl(fd, F_SETFL, flags); + } +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ + + connect_failed = 0; + while (connect(fd, rp->ai_addr, rp->ai_addrlen) == JB_INVALID_SOCKET) + { +#ifdef _WIN32 + if (errno == WSAEINPROGRESS) +#elif __OS2__ + if (sock_errno() == EINPROGRESS) +#else /* ifndef _WIN32 */ + if (errno == EINPROGRESS) +#endif /* ndef _WIN32 || __OS2__ */ + { + break; + } + +#ifdef __OS2__ + if (sock_errno() != EINTR) +#else + if (errno != EINTR) +#endif /* __OS2__ */ + { + close_socket(fd); + connect_failed = 1; + break; + } + } + if (connect_failed) + { + continue; + } + +#if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) + if (flags != -1) + { + flags &= ~O_NDELAY; + fcntl(fd, F_SETFL, flags); + } +#endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ + + /* wait for connection to complete */ + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + tv->tv_sec = 30; + tv->tv_usec = 0; + + /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Wierd! */ + if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) + { + close_socket(fd); + continue; + } + + break; /* for; Connection established; don't try other addresses */ + } + + 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);*/ + + return(fd); + +} + +# else /* ndef HAVE_RFC2553 */ +/* Pre-getaddrinfo implementation */ + jb_socket connect_to(const char *host, int portnum, struct client_state *csp) { struct sockaddr_in inaddr; @@ -482,6 +653,7 @@ jb_socket connect_to(const char *host, int portnum, struct client_state *csp) return(fd); } +#endif /* ndef HAVE_RFC2553 */ /********************************************************************* @@ -677,7 +849,16 @@ void close_socket(jb_socket fd) *********************************************************************/ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) { +#ifdef HAVE_RFC2553 + 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_RFC2553 */ jb_socket fd; #ifndef _WIN32 int one = 1; @@ -685,6 +866,32 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) *pfd = JB_INVALID_SOCKET; +#ifdef HAVE_RFC2553 + 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; @@ -707,8 +914,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_RFC2553 */ +#ifdef HAVE_RFC2553 + 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_RFC2553 */ #ifdef _WIN32 if (fd == JB_INVALID_SOCKET) @@ -716,7 +930,11 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) if (fd < 0) #endif { +#ifdef HAVE_RFC2553 + continue; +#else return(-1); +#endif } #ifndef _WIN32 @@ -735,7 +953,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_RFC2553 + 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(); @@ -744,15 +966,36 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) if (errno == EADDRINUSE) #endif { +#ifdef HAVE_RFC2553 + freeaddrinfo(result); +#endif close_socket(fd); return(-3); } else { close_socket(fd); +#ifndef HAVE_RFC2553 return(-1); } } +#else /* def HAVE_RFC2553 */ + } + } + 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 /* def HAVE_RFC2553 */ while (listen(fd, MAX_LISTEN_BACKLOG) == -1) { @@ -792,14 +1035,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_RFC2553 + struct sockaddr_storage server; + int retval; +#else struct sockaddr_in server; struct hostent *host = NULL; +#endif /* HAVE_RFC2553 */ #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_RFC2553 #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) @@ -809,7 +1058,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_RFC2553 */ + s_length = s_length_provided = sizeof(server); if (NULL != hostname) { @@ -819,8 +1069,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_RFC2553 + *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_RFC2553 */ if (NULL == hostname) { /* @@ -829,6 +1094,16 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) */ return; } + +#ifdef HAVE_RFC2553 + *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, @@ -866,6 +1141,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **hostname) { *hostname = strdup(host->h_name); } +#endif /* else def HAVE_RFC2553 */ } return; @@ -890,7 +1166,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_RFC2553 + /* 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. */ @@ -919,8 +1201,21 @@ int accept_connection(struct client_state * csp, jb_socket fd) #endif csp->cfd = afd; +#ifdef HAVE_RFC2553 + 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_RFC2553 */ return 1; @@ -1051,4 +1346,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 1007128..6273bd4 100644 --- a/jcc.c +++ b/jcc.c @@ -2710,7 +2710,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 @@ -4483,4 +4483,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 c2e3bd1..1cd8cec 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -835,7 +835,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(""); @@ -1044,6 +1043,12 @@ struct configuration_spec * load_config(void) break; } } +#ifdef HAVE_RFC2553 + else + { + cur_acl->wildcard_dst = 1; + } +#endif /* def HAVE_RFC2553 */ /* * Add it to the list. Note we reverse the list to get the @@ -1193,7 +1198,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); @@ -1257,11 +1273,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; @@ -1275,7 +1303,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); @@ -1345,11 +1384,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; @@ -1362,7 +1413,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); @@ -1512,6 +1574,12 @@ struct configuration_spec * load_config(void) break; } } +#ifdef HAVE_RFC2553 + else + { + cur_acl->wildcard_dst = 1; + } +#endif /* def HAVE_RFC2553 */ /* * Add it to the list. Note we reverse the list to get the @@ -1851,18 +1919,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 */ } @@ -2038,4 +2108,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 300d9d6..b5d54c3 100644 --- a/project.h +++ b/project.h @@ -710,6 +710,12 @@ /* Needed for pcre choice */ #include "config.h" +#ifdef HAVE_RFC2553 +/* Need for struct sockaddr_storage */ +#include +#endif + + /* * Include appropriate regular expression libraries. * Note that pcrs and pcre (native) are needed for cgi @@ -861,16 +867,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 */ @@ -1424,9 +1424,15 @@ struct client_state /** Client PC's IP address, as reported by the accept() function. As a string. */ char *ip_addr_str; +#ifdef HAVE_RFC2553 + /** 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. */ unsigned long ip_addr_long; +#endif /* def HAVE_RFC2553 */ /** The URL that was requested */ struct http_request http[1]; @@ -1660,9 +1666,14 @@ struct re_filterfile_spec */ struct access_control_addr { +#ifdef HAVE_RFC2553 + 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'; @@ -1449,4 +1505,6 @@ int match_portlist(const char *portlist, int port) Local Variables: tab-width: 3 end: + + vim:softtabstop=3 shiftwidth=3 */