From: Gerrit Renker Date: Mon, 17 Dec 2007 12:12:25 +0000 (+0100) Subject: 02_HTTP_access-list.diff X-Git-Tag: v0.3.0~47 X-Git-Url: http://git.tue.mpg.de/?a=commitdiff_plain;h=78200714a07173565ecff4adc8d12b87895017f3;p=paraslash.git 02_HTTP_access-list.diff After adding IPv6 support the access list matching routine was left in a broken state. This patch fixes the problem by continuing the support for IPv4 address/netmask access lists. The following problems are fixed: * support for a server listening to both IPv6 (AF_INET6) and IPv4 (AF_INET) address (since one can not assume that the server's address has AF_INET); * explicit support for addresses in network-byte-order (without having to reverse the netmask); * a routine to extract v6-mapped IPv4 addresses ("::FFFF:x.x.x.x") for IPv4 clients connecting to a (dual-stack) IPv6 server. When the access list is in use, the connection mode is automatically switched to IPv4, i.e. IPv6 clients connecting to the server will be treated as if they were in the deny-list. --- diff --git a/http_send.c b/http_send.c index 33d5ed93..28e245ea 100644 --- a/http_send.c +++ b/http_send.c @@ -202,24 +202,37 @@ static void http_send( long unsigned current_chunk, } } +/** + * Return true if addr_1 matches addr_2 in the first `netmask' bits. + */ +static int v4_addr_match(uint32_t addr_1, uint32_t addr_2, uint8_t netmask) +{ + uint32_t mask = ~0U; + + if (netmask < 32) + mask <<= (32 - netmask); + return (htonl(addr_1) & mask) == (htonl(addr_2) & mask); +} + static int host_in_access_perm_list(struct http_client *hc) { - struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); + struct access_info *ai, *tmp; + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + struct in_addr v4_addr; if (getpeername(hc->fd, (struct sockaddr *)&ss, &sslen) < 0) { - PARA_ERROR_LOG("can not determine address family: %s\n", strerror(errno)); - } else if (ss.ss_family == AF_INET) { - /* FIXME: access restriction is (currently) only supported for IPv4 */ - struct access_info *ai, *tmp; - struct in_addr client_addr = ((struct sockaddr_in *)&ss)->sin_addr; - - list_for_each_entry_safe(ai, tmp, &access_perm_list, node) { - unsigned mask = ((~0U) >> ai->netmask); - if ((client_addr.s_addr & mask) == (ai->addr.s_addr & mask)) - return 1; - } + PARA_ERROR_LOG("Can not determine peer address: %s\n", strerror(errno)); + goto no_match; } + v4_addr = extract_v4_addr(&ss); + if (!v4_addr.s_addr) + goto no_match; + + list_for_each_entry_safe(ai, tmp, &access_perm_list, node) + if (v4_addr_match(v4_addr.s_addr, ai->addr.s_addr, ai->netmask)) + return 1; +no_match: return 0; } diff --git a/net.c b/net.c index 5d7c3fef..7f8d596d 100644 --- a/net.c +++ b/net.c @@ -322,6 +322,28 @@ char *remote_name(int sockfd) return __get_sock_name(sockfd, getpeername); } +/** + * Extract IPv4 or IPv6-mapped-IPv4 address from sockaddr_storage. + * \param ss Container of IPv4/6 address + * \return Extracted IPv4 address (different from 0) or 0 if unsuccessful. + * + * \sa RFC 3493 + */ +struct in_addr extract_v4_addr(const struct sockaddr_storage *ss) +{ + struct in_addr ia = { 0 }; + + if (ss->ss_family == AF_INET) + ia.s_addr = ((struct sockaddr_in *)ss)->sin_addr.s_addr; + if (ss->ss_family == AF_INET6) { + const struct in6_addr v6_addr = ((struct sockaddr_in6 *)ss)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(&v6_addr)) + memcpy(&ia.s_addr, &(v6_addr.s6_addr[12]), 4); + } + return ia; +} + /* * Send out a buffer, resend on short writes. * diff --git a/net.h b/net.h index d73ffccb..9b0c83bd 100644 --- a/net.h +++ b/net.h @@ -29,6 +29,7 @@ */ extern int makesock(unsigned l3type, unsigned l4type, int passive, const char *host, unsigned short port_number); +extern struct in_addr extract_v4_addr(const struct sockaddr_storage *ss); /** * Functions to support listening sockets.