From 915f888f3e78be3a42e66a627bcc6ce1d679d5d3 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 5 Oct 2020 08:21:28 +0100 Subject: [PATCH] sa46: network check This adds a helper to test if an sa46 is on an sa46-based subnet. The compare helper is adapted to say that non INET/INET6 addresses with the same AF match. --- include/libwebsockets/lws-network-helper.h | 19 ++++- lib/core-net/adopt.c | 12 ++-- lib/core-net/client/connect3.c | 5 +- lib/core-net/network.c | 80 +++++++++++++++++++++- lib/core-net/private-lib-core-net.h | 3 + 5 files changed, 110 insertions(+), 9 deletions(-) diff --git a/include/libwebsockets/lws-network-helper.h b/include/libwebsockets/lws-network-helper.h index 3ba324a76..2e686e790 100644 --- a/include/libwebsockets/lws-network-helper.h +++ b/include/libwebsockets/lws-network-helper.h @@ -124,11 +124,28 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, * \param sa46a: first * \param sa46b: second * - * Returns 0 if the address family and address are the same, otherwise nonzero. + * Returns 0 if the address family is INET or INET6 and the address is the same, + * or if the AF is the same but not INET or INET6, otherwise nonzero. */ LWS_VISIBLE LWS_EXTERN int lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b); +/** + * lws_sa46_on_net() - checks if an sa46 is on the subnet represented by another + * + * \param sa46a: first + * \param sa46_net: network + * \param net_len: length of network non-mask + * + * Returns 0 if sa46a belongs on network sa46_net/net_len + * + * If there is an ipv4 / v6 mismatch between the ip and the net, the ipv4 + * address is promoted to ::ffff:x.x.x.x before the comparison. + */ +LWS_VISIBLE LWS_EXTERN int +lws_sa46_on_net(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46_net, + int net_len); + /* * lws_parse_numeric_address() - converts numeric ipv4 or ipv6 to byte address * diff --git a/lib/core-net/adopt.c b/lib/core-net/adopt.c index 1458c8a01..a8f61eb1e 100644 --- a/lib/core-net/adopt.c +++ b/lib/core-net/adopt.c @@ -466,14 +466,9 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, struct lws * lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info) { - socklen_t slen = sizeof(sa46); + socklen_t slen = sizeof(lws_sockaddr46); struct lws *new_wsi; - if (info->type & LWS_ADOPT_SOCKET && - getpeername(info->fd.sockfd, (struct sockaddr *)&wsi->sa46_peer, - &slen) < 0) - lwsl_info("%s: getpeername failed\n", __func__); - #if defined(LWS_WITH_PEER_LIMITS) struct lws_peer *peer = NULL; @@ -507,6 +502,11 @@ lws_adopt_descriptor_vhost_via_info(const lws_adopt_desc_t *info) return NULL; } + if (info->type & LWS_ADOPT_SOCKET && + getpeername(info->fd.sockfd, (struct sockaddr *)&new_wsi->sa46_peer, + &slen) < 0) + lwsl_info("%s: getpeername failed\n", __func__); + #if defined(LWS_WITH_PEER_LIMITS) if (peer) lws_peer_add_wsi(info->vh->context, peer, new_wsi); diff --git a/lib/core-net/client/connect3.c b/lib/core-net/client/connect3.c index 275f70987..582c5739f 100644 --- a/lib/core-net/client/connect3.c +++ b/lib/core-net/client/connect3.c @@ -404,7 +404,10 @@ ads_known: #endif /* grab a copy for peer tracking */ - wsi->sa46_peer = *psa; +#if defined(LWS_WITH_UNIX_SOCK) + if (!wsi->unix_skt) +#endif + memcpy(&wsi->sa46_peer, psa, n); m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa, n); if (m == -1) { diff --git a/lib/core-net/network.c b/lib/core-net/network.c index 7ad0399b1..bdbde227c 100644 --- a/lib/core-net/network.c +++ b/lib/core-net/network.c @@ -870,7 +870,85 @@ lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b) return memcmp(&sa46a->sa6.sin6_addr, &sa46b->sa6.sin6_addr, 16); #endif - return sa46a->sa4.sin_addr.s_addr != sa46b->sa4.sin_addr.s_addr; + if (sa46a->sa4.sin_family == AF_INET) + return sa46a->sa4.sin_addr.s_addr != sa46b->sa4.sin_addr.s_addr; + + return 0; +} + +int +lws_sa46_on_net(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46_net, + int net_len) +{ + uint8_t mask = 0xff, norm[16]; + const uint8_t *p1, *p2; + + if (sa46a->sa4.sin_family == AF_INET) { + p1 = (uint8_t *)&sa46a->sa4.sin_addr; + if (sa46_net->sa4.sin_family == AF_INET6) { + /* ip is v4, net is v6, promote ip to v6 */ + memset(norm, 0, 10); + norm[10] = norm[11] = 0xff; + norm[12] = p1[0]; + norm[13] = p1[1]; + norm[14] = p1[2]; + norm[15] = p1[3]; + p1 = norm; + } +#if defined(LWS_WITH_IPV6) + } else + if (sa46a->sa4.sin_family == AF_INET6) { + p1 = (uint8_t *)&sa46a->sa6.sin6_addr; +#endif + } else + return 1; + + if (sa46_net->sa4.sin_family == AF_INET) { + p2 = (uint8_t *)&sa46_net->sa4.sin_addr; + if (sa46a->sa4.sin_family == AF_INET6) { + /* ip is v6, net is v4, promote net to v6 */ + memset(norm, 0, 10); + norm[10] = norm[11] = 0xff; + norm[12] = p2[0]; + norm[13] = p2[1]; + norm[14] = p2[2]; + norm[15] = p2[3]; + p2 = norm; + /* because the mask length is for net v4 address */ + net_len += 12 * 8; + } +#if defined(LWS_WITH_IPV6) + } else + if (sa46a->sa4.sin_family == AF_INET6) { + p2 = (uint8_t *)&sa46_net->sa6.sin6_addr; +#endif + } else + return 1; + + while (net_len > 0) { + if (net_len < 8) + mask <<= 8 - net_len; + + if (((*p1++) & mask) != ((*p2++) & mask)) + return 1; + + net_len -= 8; + } + + return 0; +} + +void +lws_sa46_copy_address(lws_sockaddr46 *sa46a, const void *in, int af) +{ + sa46a->sa4.sin_family = af; + + if (af == AF_INET) + memcpy(&sa46a->sa4.sin_addr, in, 4); +#if defined(LWS_WITH_IPV6) + else if (af == AF_INET6) + memcpy(&sa46a->sa6.sin6_addr, in, sizeof(sa46a->sa6.sin6_addr)); +#endif } #if defined(LWS_WITH_SYS_STATE) diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 7889a2189..9063bf37a 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -1131,6 +1131,9 @@ lws_parse(struct lws *wsi, unsigned char *buf, int *len); int LWS_WARN_UNUSED_RESULT lws_parse_urldecode(struct lws *wsi, uint8_t *_c); +void +lws_sa46_copy_address(lws_sockaddr46 *sa46a, const void *in, int af); + int LWS_WARN_UNUSED_RESULT lws_http_action(struct lws *wsi);