15185a700Sflorian /*
25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian *
45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian * copyright notice and this permission notice appear in all copies.
75185a700Sflorian *
85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian */
165185a700Sflorian
17*1f327cd4Sflorian /* $Id: sockaddr.c,v 1.17 2024/07/08 13:46:33 florian Exp $ */
185185a700Sflorian
195185a700Sflorian /*! \file */
207240555eSflorian #include <sys/types.h>
217240555eSflorian #include <sys/socket.h>
227240555eSflorian #include <netdb.h>
235185a700Sflorian #include <stdio.h>
245185a700Sflorian
255185a700Sflorian #include <isc/buffer.h>
265185a700Sflorian
275185a700Sflorian #include <isc/region.h>
285185a700Sflorian #include <isc/sockaddr.h>
295185a700Sflorian #include <string.h>
305185a700Sflorian #include <isc/util.h>
315185a700Sflorian
321fb015a8Sflorian int
isc_sockaddr_equal(const struct sockaddr_storage * a,const struct sockaddr_storage * b)33b1a294b5Sflorian isc_sockaddr_equal(const struct sockaddr_storage *a, const struct sockaddr_storage *b) {
345185a700Sflorian return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
355185a700Sflorian ISC_SOCKADDR_CMPPORT|
365185a700Sflorian ISC_SOCKADDR_CMPSCOPE));
375185a700Sflorian }
385185a700Sflorian
391fb015a8Sflorian int
isc_sockaddr_eqaddr(const struct sockaddr_storage * a,const struct sockaddr_storage * b)40b1a294b5Sflorian isc_sockaddr_eqaddr(const struct sockaddr_storage *a, const struct sockaddr_storage *b) {
415185a700Sflorian return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
425185a700Sflorian ISC_SOCKADDR_CMPSCOPE));
435185a700Sflorian }
445185a700Sflorian
451fb015a8Sflorian int
isc_sockaddr_compare(const struct sockaddr_storage * a,const struct sockaddr_storage * b,unsigned int flags)46b1a294b5Sflorian isc_sockaddr_compare(const struct sockaddr_storage *a, const struct sockaddr_storage *b,
475185a700Sflorian unsigned int flags)
485185a700Sflorian {
49b1a294b5Sflorian struct sockaddr_in *sin_a, *sin_b;
50b1a294b5Sflorian struct sockaddr_in6 *sin6_a, *sin6_b;
51b1a294b5Sflorian
525185a700Sflorian REQUIRE(a != NULL && b != NULL);
535185a700Sflorian
54b1a294b5Sflorian if (a->ss_len != b->ss_len)
551fb015a8Sflorian return (0);
565185a700Sflorian
575185a700Sflorian /*
585185a700Sflorian * We don't just memcmp because the sin_zero field isn't always
595185a700Sflorian * zero.
605185a700Sflorian */
615185a700Sflorian
62b1a294b5Sflorian if (a->ss_family != b->ss_family)
631fb015a8Sflorian return (0);
64b1a294b5Sflorian switch (a->ss_family) {
655185a700Sflorian case AF_INET:
66b1a294b5Sflorian sin_a = (struct sockaddr_in *) a;
67b1a294b5Sflorian sin_b = (struct sockaddr_in *) b;
685185a700Sflorian if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
69b1a294b5Sflorian memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
70b1a294b5Sflorian sizeof(sin_a->sin_addr)) != 0)
711fb015a8Sflorian return (0);
725185a700Sflorian if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
73b1a294b5Sflorian sin_a->sin_port != sin_b->sin_port)
741fb015a8Sflorian return (0);
755185a700Sflorian break;
765185a700Sflorian case AF_INET6:
77b1a294b5Sflorian sin6_a = (struct sockaddr_in6 *) a;
78b1a294b5Sflorian sin6_b = (struct sockaddr_in6 *) b;
79b1a294b5Sflorian
805185a700Sflorian if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
81b1a294b5Sflorian memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
82b1a294b5Sflorian sizeof(sin6_a->sin6_addr)) != 0)
831fb015a8Sflorian return (0);
845185a700Sflorian /*
855185a700Sflorian * If ISC_SOCKADDR_CMPSCOPEZERO is set then don't return
861fb015a8Sflorian * 0 if one of the scopes in zero.
875185a700Sflorian */
885185a700Sflorian if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 &&
89b1a294b5Sflorian sin6_a->sin6_scope_id != sin6_b->sin6_scope_id &&
905185a700Sflorian ((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 ||
91b1a294b5Sflorian (sin6_a->sin6_scope_id != 0 &&
92b1a294b5Sflorian sin6_b->sin6_scope_id != 0)))
931fb015a8Sflorian return (0);
945185a700Sflorian if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
95b1a294b5Sflorian sin6_a->sin6_port != sin6_b->sin6_port)
961fb015a8Sflorian return (0);
975185a700Sflorian break;
985185a700Sflorian default:
99b1a294b5Sflorian if (memcmp(a, b, a->ss_len) != 0)
1001fb015a8Sflorian return (0);
1015185a700Sflorian }
1021fb015a8Sflorian return (1);
1035185a700Sflorian }
1045185a700Sflorian
1055185a700Sflorian isc_result_t
isc_sockaddr_totext(const struct sockaddr_storage * sockaddr,isc_buffer_t * target)106b1a294b5Sflorian isc_sockaddr_totext(const struct sockaddr_storage *sockaddr, isc_buffer_t *target) {
107b1a294b5Sflorian struct sockaddr_in *sin;
108b1a294b5Sflorian struct sockaddr_in6 *sin6;
1095185a700Sflorian char pbuf[sizeof("65000")];
1105185a700Sflorian unsigned int plen;
1115185a700Sflorian isc_region_t avail;
1127240555eSflorian char tmp[NI_MAXHOST];
1135185a700Sflorian
1145185a700Sflorian REQUIRE(sockaddr != NULL);
1155185a700Sflorian
1165185a700Sflorian /*
1175185a700Sflorian * Do the port first, giving us the opportunity to check for
1187240555eSflorian * unsupported address families.
1195185a700Sflorian */
120b1a294b5Sflorian switch (sockaddr->ss_family) {
1215185a700Sflorian case AF_INET:
122b1a294b5Sflorian sin = (struct sockaddr_in *)sockaddr;
123b1a294b5Sflorian snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin->sin_port));
1245185a700Sflorian break;
1255185a700Sflorian case AF_INET6:
126b1a294b5Sflorian sin6 = (struct sockaddr_in6 *)sockaddr;
127b1a294b5Sflorian snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin6->sin6_port));
1285185a700Sflorian break;
1295185a700Sflorian default:
1305185a700Sflorian return (ISC_R_FAILURE);
1315185a700Sflorian }
1325185a700Sflorian
1335185a700Sflorian plen = strlen(pbuf);
1345185a700Sflorian INSIST(plen < sizeof(pbuf));
1355185a700Sflorian
136e26546dbSnaddy if (getnameinfo((struct sockaddr *)sockaddr, sockaddr->ss_len,
137e26546dbSnaddy tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0)
138e26546dbSnaddy return (ISC_R_FAILURE);
1397240555eSflorian if (strlen(tmp) > isc_buffer_availablelength(target))
1407240555eSflorian return (ISC_R_NOSPACE);
1417240555eSflorian isc_buffer_putmem(target, tmp, strlen(tmp));
1425185a700Sflorian
1435185a700Sflorian if (1 + plen + 1 > isc_buffer_availablelength(target))
1445185a700Sflorian return (ISC_R_NOSPACE);
1455185a700Sflorian
1465185a700Sflorian isc_buffer_putmem(target, (const unsigned char *)"#", 1);
1475185a700Sflorian isc_buffer_putmem(target, (const unsigned char *)pbuf, plen);
1485185a700Sflorian
1495185a700Sflorian /*
1505185a700Sflorian * Null terminate after used region.
1515185a700Sflorian */
1525185a700Sflorian isc_buffer_availableregion(target, &avail);
1535185a700Sflorian INSIST(avail.length >= 1);
1545185a700Sflorian avail.base[0] = '\0';
1555185a700Sflorian
1565185a700Sflorian return (ISC_R_SUCCESS);
1575185a700Sflorian }
1585185a700Sflorian
1595185a700Sflorian void
isc_sockaddr_format(const struct sockaddr_storage * sa,char * array,unsigned int size)160b1a294b5Sflorian isc_sockaddr_format(const struct sockaddr_storage *sa, char *array, unsigned int size) {
1615185a700Sflorian isc_result_t result;
1625185a700Sflorian isc_buffer_t buf;
1635185a700Sflorian
1645185a700Sflorian if (size == 0U)
1655185a700Sflorian return;
1665185a700Sflorian
1675185a700Sflorian isc_buffer_init(&buf, array, size);
1685185a700Sflorian result = isc_sockaddr_totext(sa, &buf);
1695185a700Sflorian if (result != ISC_R_SUCCESS) {
1705185a700Sflorian snprintf(array, size, "<unknown address, family %u>",
171b1a294b5Sflorian sa->ss_family);
1725185a700Sflorian array[size - 1] = '\0';
1735185a700Sflorian }
1745185a700Sflorian }
1755185a700Sflorian
1765185a700Sflorian void
isc_sockaddr_any(struct sockaddr_storage * sockaddr)177b1a294b5Sflorian isc_sockaddr_any(struct sockaddr_storage *sockaddr)
1785185a700Sflorian {
179b1a294b5Sflorian struct sockaddr_in *sin = (struct sockaddr_in *) sockaddr;
1805185a700Sflorian memset(sockaddr, 0, sizeof(*sockaddr));
181b1a294b5Sflorian sin->sin_family = AF_INET;
182b1a294b5Sflorian sin->sin_len = sizeof(*sin);
183b1a294b5Sflorian sin->sin_addr.s_addr = INADDR_ANY;
184b1a294b5Sflorian sin->sin_port = 0;
1855185a700Sflorian }
1865185a700Sflorian
1875185a700Sflorian void
isc_sockaddr_any6(struct sockaddr_storage * sockaddr)188b1a294b5Sflorian isc_sockaddr_any6(struct sockaddr_storage *sockaddr)
1895185a700Sflorian {
190b1a294b5Sflorian struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sockaddr;
1915185a700Sflorian memset(sockaddr, 0, sizeof(*sockaddr));
192b1a294b5Sflorian sin6->sin6_family = AF_INET6;
193b1a294b5Sflorian sin6->sin6_len = sizeof(*sin6);
194b1a294b5Sflorian sin6->sin6_addr = in6addr_any;
195b1a294b5Sflorian sin6->sin6_port = 0;
1965185a700Sflorian }
1975185a700Sflorian
1985185a700Sflorian void
isc_sockaddr_anyofpf(struct sockaddr_storage * sockaddr,int pf)199b1a294b5Sflorian isc_sockaddr_anyofpf(struct sockaddr_storage *sockaddr, int pf) {
2005185a700Sflorian switch (pf) {
2015185a700Sflorian case AF_INET:
2025185a700Sflorian isc_sockaddr_any(sockaddr);
2035185a700Sflorian break;
2045185a700Sflorian case AF_INET6:
2055185a700Sflorian isc_sockaddr_any6(sockaddr);
2065185a700Sflorian break;
2075185a700Sflorian default:
2085185a700Sflorian INSIST(0);
2095185a700Sflorian }
2105185a700Sflorian }
2115185a700Sflorian
2125185a700Sflorian int
isc_sockaddr_pf(const struct sockaddr_storage * sockaddr)213b1a294b5Sflorian isc_sockaddr_pf(const struct sockaddr_storage *sockaddr) {
2145185a700Sflorian
2155185a700Sflorian /*
2165185a700Sflorian * Get the protocol family of 'sockaddr'.
2175185a700Sflorian */
2185185a700Sflorian
219b1a294b5Sflorian return (sockaddr->ss_family);
2205185a700Sflorian }
2215185a700Sflorian
2225185a700Sflorian in_port_t
isc_sockaddr_getport(const struct sockaddr_storage * sockaddr)223b1a294b5Sflorian isc_sockaddr_getport(const struct sockaddr_storage *sockaddr) {
224b1a294b5Sflorian struct sockaddr_in *sin;
225b1a294b5Sflorian struct sockaddr_in6 *sin6;
2265185a700Sflorian
227b1a294b5Sflorian switch (sockaddr->ss_family) {
2285185a700Sflorian case AF_INET:
229b1a294b5Sflorian sin = (struct sockaddr_in *)sockaddr;
230b1a294b5Sflorian return (ntohs(sin->sin_port));
2315185a700Sflorian break;
2325185a700Sflorian case AF_INET6:
233b1a294b5Sflorian sin6 = (struct sockaddr_in6 *)sockaddr;
234b1a294b5Sflorian return (ntohs(sin6->sin6_port));
2355185a700Sflorian break;
2365185a700Sflorian default:
2375185a700Sflorian FATAL_ERROR(__FILE__, __LINE__,
2385185a700Sflorian "unknown address family: %d",
239b1a294b5Sflorian (int)sockaddr->ss_family);
2405185a700Sflorian }
2415185a700Sflorian }
2425185a700Sflorian
2431fb015a8Sflorian int
isc_sockaddr_ismulticast(const struct sockaddr_storage * sockaddr)244b1a294b5Sflorian isc_sockaddr_ismulticast(const struct sockaddr_storage *sockaddr) {
245b1a294b5Sflorian struct sockaddr_in *sin;
246b1a294b5Sflorian struct sockaddr_in6 *sin6;
247b1a294b5Sflorian
248b1a294b5Sflorian switch (sockaddr->ss_family) {
249f3f77cb3Sflorian case AF_INET:
250b1a294b5Sflorian sin = (struct sockaddr_in *)sockaddr;
251*1f327cd4Sflorian return (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)));
252f3f77cb3Sflorian case AF_INET6:
253b1a294b5Sflorian sin6 = (struct sockaddr_in6 *)sockaddr;
254b1a294b5Sflorian return (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr));
255f3f77cb3Sflorian default:
2561fb015a8Sflorian return (0);
2575185a700Sflorian }
258f3f77cb3Sflorian }
2595185a700Sflorian
2601fb015a8Sflorian int
isc_sockaddr_issitelocal(const struct sockaddr_storage * sockaddr)261b1a294b5Sflorian isc_sockaddr_issitelocal(const struct sockaddr_storage *sockaddr) {
262b1a294b5Sflorian struct sockaddr_in6 *sin6;
263b1a294b5Sflorian if (sockaddr->ss_family == AF_INET6) {
264b1a294b5Sflorian sin6 = (struct sockaddr_in6 *)sockaddr;
265b1a294b5Sflorian return (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr));
266b1a294b5Sflorian }
2671fb015a8Sflorian return (0);
2685185a700Sflorian }
2695185a700Sflorian
2701fb015a8Sflorian int
isc_sockaddr_islinklocal(const struct sockaddr_storage * sockaddr)271b1a294b5Sflorian isc_sockaddr_islinklocal(const struct sockaddr_storage *sockaddr) {
272b1a294b5Sflorian struct sockaddr_in6 *sin6;
273b1a294b5Sflorian if (sockaddr->ss_family == AF_INET6) {
274b1a294b5Sflorian sin6 = (struct sockaddr_in6 *)sockaddr;
275b1a294b5Sflorian return (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr));
276b1a294b5Sflorian }
2771fb015a8Sflorian return (0);
2785185a700Sflorian }
279