xref: /openbsd-src/usr.bin/dig/lib/isc/sockaddr.c (revision 1f327cd496aa9b34d83aff9c40ae414d53b3c813)
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