xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/isc/sockaddr.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
1 /*	$NetBSD: sockaddr.c,v 1.1 2024/02/18 20:57:50 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*! \file */
17 
18 #include <stdbool.h>
19 #include <stdio.h>
20 #if defined(WIN32) || defined(WIN64)
21 #include <malloc.h>
22 #endif /* if defined(WIN32) || defined(WIN64) */
23 
24 #include <isc/buffer.h>
25 #include <isc/hash.h>
26 #include <isc/netaddr.h>
27 #include <isc/print.h>
28 #include <isc/region.h>
29 #include <isc/sockaddr.h>
30 #include <isc/string.h>
31 #include <isc/util.h>
32 
33 bool
isc_sockaddr_equal(const isc_sockaddr_t * a,const isc_sockaddr_t * b)34 isc_sockaddr_equal(const isc_sockaddr_t *a, const isc_sockaddr_t *b) {
35 	return (isc_sockaddr_compare(a, b,
36 				     ISC_SOCKADDR_CMPADDR |
37 					     ISC_SOCKADDR_CMPPORT |
38 					     ISC_SOCKADDR_CMPSCOPE));
39 }
40 
41 bool
isc_sockaddr_eqaddr(const isc_sockaddr_t * a,const isc_sockaddr_t * b)42 isc_sockaddr_eqaddr(const isc_sockaddr_t *a, const isc_sockaddr_t *b) {
43 	return (isc_sockaddr_compare(
44 		a, b, ISC_SOCKADDR_CMPADDR | ISC_SOCKADDR_CMPSCOPE));
45 }
46 
47 bool
isc_sockaddr_compare(const isc_sockaddr_t * a,const isc_sockaddr_t * b,unsigned int flags)48 isc_sockaddr_compare(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
49 		     unsigned int flags) {
50 	REQUIRE(a != NULL && b != NULL);
51 
52 	if (a->length != b->length) {
53 		return (false);
54 	}
55 
56 	/*
57 	 * We don't just memcmp because the sin_zero field isn't always
58 	 * zero.
59 	 */
60 
61 	if (a->type.sa.sa_family != b->type.sa.sa_family) {
62 		return (false);
63 	}
64 	switch (a->type.sa.sa_family) {
65 	case AF_INET:
66 		if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
67 		    memcmp(&a->type.sin.sin_addr, &b->type.sin.sin_addr,
68 			   sizeof(a->type.sin.sin_addr)) != 0)
69 		{
70 			return (false);
71 		}
72 		if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
73 		    a->type.sin.sin_port != b->type.sin.sin_port)
74 		{
75 			return (false);
76 		}
77 		break;
78 	case AF_INET6:
79 		if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
80 		    memcmp(&a->type.sin6.sin6_addr, &b->type.sin6.sin6_addr,
81 			   sizeof(a->type.sin6.sin6_addr)) != 0)
82 		{
83 			return (false);
84 		}
85 		/*
86 		 * If ISC_SOCKADDR_CMPSCOPEZERO is set then don't return
87 		 * false if one of the scopes in zero.
88 		 */
89 		if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 &&
90 		    a->type.sin6.sin6_scope_id != b->type.sin6.sin6_scope_id &&
91 		    ((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 ||
92 		     (a->type.sin6.sin6_scope_id != 0 &&
93 		      b->type.sin6.sin6_scope_id != 0)))
94 		{
95 			return (false);
96 		}
97 		if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
98 		    a->type.sin6.sin6_port != b->type.sin6.sin6_port)
99 		{
100 			return (false);
101 		}
102 		break;
103 	default:
104 		if (memcmp(&a->type, &b->type, a->length) != 0) {
105 			return (false);
106 		}
107 	}
108 	return (true);
109 }
110 
111 bool
isc_sockaddr_eqaddrprefix(const isc_sockaddr_t * a,const isc_sockaddr_t * b,unsigned int prefixlen)112 isc_sockaddr_eqaddrprefix(const isc_sockaddr_t *a, const isc_sockaddr_t *b,
113 			  unsigned int prefixlen) {
114 	isc_netaddr_t na, nb;
115 	isc_netaddr_fromsockaddr(&na, a);
116 	isc_netaddr_fromsockaddr(&nb, b);
117 	return (isc_netaddr_eqprefix(&na, &nb, prefixlen));
118 }
119 
120 isc_result_t
isc_sockaddr_totext(const isc_sockaddr_t * sockaddr,isc_buffer_t * target)121 isc_sockaddr_totext(const isc_sockaddr_t *sockaddr, isc_buffer_t *target) {
122 	isc_result_t result;
123 	isc_netaddr_t netaddr;
124 	char pbuf[sizeof("65000")];
125 	unsigned int plen;
126 	isc_region_t avail;
127 
128 	REQUIRE(sockaddr != NULL);
129 
130 	/*
131 	 * Do the port first, giving us the opportunity to check for
132 	 * unsupported address families before calling
133 	 * isc_netaddr_fromsockaddr().
134 	 */
135 	switch (sockaddr->type.sa.sa_family) {
136 	case AF_INET:
137 		snprintf(pbuf, sizeof(pbuf), "%u",
138 			 ntohs(sockaddr->type.sin.sin_port));
139 		break;
140 	case AF_INET6:
141 		snprintf(pbuf, sizeof(pbuf), "%u",
142 			 ntohs(sockaddr->type.sin6.sin6_port));
143 		break;
144 #ifdef ISC_PLATFORM_HAVESYSUNH
145 	case AF_UNIX:
146 		plen = strlen(sockaddr->type.sunix.sun_path);
147 		if (plen >= isc_buffer_availablelength(target)) {
148 			return (ISC_R_NOSPACE);
149 		}
150 
151 		isc_buffer_putmem(
152 			target,
153 			(const unsigned char *)sockaddr->type.sunix.sun_path,
154 			plen);
155 
156 		/*
157 		 * Null terminate after used region.
158 		 */
159 		isc_buffer_availableregion(target, &avail);
160 		INSIST(avail.length >= 1);
161 		avail.base[0] = '\0';
162 
163 		return (ISC_R_SUCCESS);
164 #endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
165 	default:
166 		return (ISC_R_FAILURE);
167 	}
168 
169 	plen = strlen(pbuf);
170 	INSIST(plen < sizeof(pbuf));
171 
172 	isc_netaddr_fromsockaddr(&netaddr, sockaddr);
173 	result = isc_netaddr_totext(&netaddr, target);
174 	if (result != ISC_R_SUCCESS) {
175 		return (result);
176 	}
177 
178 	if (1 + plen + 1 > isc_buffer_availablelength(target)) {
179 		return (ISC_R_NOSPACE);
180 	}
181 
182 	isc_buffer_putmem(target, (const unsigned char *)"#", 1);
183 	isc_buffer_putmem(target, (const unsigned char *)pbuf, plen);
184 
185 	/*
186 	 * Null terminate after used region.
187 	 */
188 	isc_buffer_availableregion(target, &avail);
189 	INSIST(avail.length >= 1);
190 	avail.base[0] = '\0';
191 
192 	return (ISC_R_SUCCESS);
193 }
194 
195 void
isc_sockaddr_format(const isc_sockaddr_t * sa,char * array,unsigned int size)196 isc_sockaddr_format(const isc_sockaddr_t *sa, char *array, unsigned int size) {
197 	isc_result_t result;
198 	isc_buffer_t buf;
199 
200 	if (size == 0U) {
201 		return;
202 	}
203 
204 	isc_buffer_init(&buf, array, size);
205 	result = isc_sockaddr_totext(sa, &buf);
206 	if (result != ISC_R_SUCCESS) {
207 		/*
208 		 * The message is the same as in netaddr.c.
209 		 */
210 		snprintf(array, size, "<unknown address, family %u>",
211 			 sa->type.sa.sa_family);
212 		array[size - 1] = '\0';
213 	}
214 }
215 
216 unsigned int
isc_sockaddr_hash(const isc_sockaddr_t * sockaddr,bool address_only)217 isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, bool address_only) {
218 	unsigned int length = 0;
219 	const unsigned char *s = NULL;
220 	unsigned int h = 0;
221 	unsigned int p = 0;
222 	const struct in6_addr *in6;
223 
224 	REQUIRE(sockaddr != NULL);
225 
226 	switch (sockaddr->type.sa.sa_family) {
227 	case AF_INET:
228 		s = (const unsigned char *)&sockaddr->type.sin.sin_addr;
229 		p = ntohs(sockaddr->type.sin.sin_port);
230 		length = sizeof(sockaddr->type.sin.sin_addr.s_addr);
231 		break;
232 	case AF_INET6:
233 		in6 = &sockaddr->type.sin6.sin6_addr;
234 		s = (const unsigned char *)in6;
235 		if (IN6_IS_ADDR_V4MAPPED(in6)) {
236 			s += 12;
237 			length = sizeof(sockaddr->type.sin.sin_addr.s_addr);
238 		} else {
239 			length = sizeof(sockaddr->type.sin6.sin6_addr);
240 		}
241 		p = ntohs(sockaddr->type.sin6.sin6_port);
242 		break;
243 	default:
244 		UNEXPECTED_ERROR(__FILE__, __LINE__,
245 				 "unknown address family: %d",
246 				 (int)sockaddr->type.sa.sa_family);
247 		s = (const unsigned char *)&sockaddr->type;
248 		length = sockaddr->length;
249 		p = 0;
250 	}
251 
252 	uint8_t buf[sizeof(struct sockaddr_storage) + sizeof(p)];
253 	memmove(buf, s, length);
254 	if (!address_only) {
255 		memmove(buf + length, &p, sizeof(p));
256 		h = isc_hash_function(buf, length + sizeof(p), true);
257 	} else {
258 		h = isc_hash_function(buf, length, true);
259 	}
260 
261 	return (h);
262 }
263 
264 void
isc_sockaddr_any(isc_sockaddr_t * sockaddr)265 isc_sockaddr_any(isc_sockaddr_t *sockaddr) {
266 	memset(sockaddr, 0, sizeof(*sockaddr));
267 	sockaddr->type.sin.sin_family = AF_INET;
268 	sockaddr->type.sin.sin_addr.s_addr = INADDR_ANY;
269 	sockaddr->type.sin.sin_port = 0;
270 	sockaddr->length = sizeof(sockaddr->type.sin);
271 	ISC_LINK_INIT(sockaddr, link);
272 }
273 
274 void
isc_sockaddr_any6(isc_sockaddr_t * sockaddr)275 isc_sockaddr_any6(isc_sockaddr_t *sockaddr) {
276 	memset(sockaddr, 0, sizeof(*sockaddr));
277 	sockaddr->type.sin6.sin6_family = AF_INET6;
278 	sockaddr->type.sin6.sin6_addr = in6addr_any;
279 	sockaddr->type.sin6.sin6_port = 0;
280 	sockaddr->length = sizeof(sockaddr->type.sin6);
281 	ISC_LINK_INIT(sockaddr, link);
282 }
283 
284 void
isc_sockaddr_fromin(isc_sockaddr_t * sockaddr,const struct in_addr * ina,in_port_t port)285 isc_sockaddr_fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
286 		    in_port_t port) {
287 	memset(sockaddr, 0, sizeof(*sockaddr));
288 	sockaddr->type.sin.sin_family = AF_INET;
289 	sockaddr->type.sin.sin_addr = *ina;
290 	sockaddr->type.sin.sin_port = htons(port);
291 	sockaddr->length = sizeof(sockaddr->type.sin);
292 	ISC_LINK_INIT(sockaddr, link);
293 }
294 
295 void
isc_sockaddr_anyofpf(isc_sockaddr_t * sockaddr,int pf)296 isc_sockaddr_anyofpf(isc_sockaddr_t *sockaddr, int pf) {
297 	switch (pf) {
298 	case AF_INET:
299 		isc_sockaddr_any(sockaddr);
300 		break;
301 	case AF_INET6:
302 		isc_sockaddr_any6(sockaddr);
303 		break;
304 	default:
305 		UNREACHABLE();
306 	}
307 }
308 
309 void
isc_sockaddr_fromin6(isc_sockaddr_t * sockaddr,const struct in6_addr * ina6,in_port_t port)310 isc_sockaddr_fromin6(isc_sockaddr_t *sockaddr, const struct in6_addr *ina6,
311 		     in_port_t port) {
312 	memset(sockaddr, 0, sizeof(*sockaddr));
313 	sockaddr->type.sin6.sin6_family = AF_INET6;
314 	sockaddr->type.sin6.sin6_addr = *ina6;
315 	sockaddr->type.sin6.sin6_port = htons(port);
316 	sockaddr->length = sizeof(sockaddr->type.sin6);
317 	ISC_LINK_INIT(sockaddr, link);
318 }
319 
320 void
isc_sockaddr_v6fromin(isc_sockaddr_t * sockaddr,const struct in_addr * ina,in_port_t port)321 isc_sockaddr_v6fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina,
322 		      in_port_t port) {
323 	memset(sockaddr, 0, sizeof(*sockaddr));
324 	sockaddr->type.sin6.sin6_family = AF_INET6;
325 	sockaddr->type.sin6.sin6_addr.s6_addr[10] = 0xff;
326 	sockaddr->type.sin6.sin6_addr.s6_addr[11] = 0xff;
327 	memmove(&sockaddr->type.sin6.sin6_addr.s6_addr[12], ina, 4);
328 	sockaddr->type.sin6.sin6_port = htons(port);
329 	sockaddr->length = sizeof(sockaddr->type.sin6);
330 	ISC_LINK_INIT(sockaddr, link);
331 }
332 
333 int
isc_sockaddr_pf(const isc_sockaddr_t * sockaddr)334 isc_sockaddr_pf(const isc_sockaddr_t *sockaddr) {
335 	/*
336 	 * Get the protocol family of 'sockaddr'.
337 	 */
338 
339 #if (AF_INET == PF_INET && AF_INET6 == PF_INET6)
340 	/*
341 	 * Assume that PF_xxx == AF_xxx for all AF and PF.
342 	 */
343 	return (sockaddr->type.sa.sa_family);
344 #else  /* if (AF_INET == PF_INET && AF_INET6 == PF_INET6) */
345 	switch (sockaddr->type.sa.sa_family) {
346 	case AF_INET:
347 		return (PF_INET);
348 	case AF_INET6:
349 		return (PF_INET6);
350 	default:
351 		FATAL_ERROR(__FILE__, __LINE__, "unknown address family: %d",
352 			    (int)sockaddr->type.sa.sa_family);
353 	}
354 #endif /* if (AF_INET == PF_INET && AF_INET6 == PF_INET6) */
355 }
356 
357 void
isc_sockaddr_fromnetaddr(isc_sockaddr_t * sockaddr,const isc_netaddr_t * na,in_port_t port)358 isc_sockaddr_fromnetaddr(isc_sockaddr_t *sockaddr, const isc_netaddr_t *na,
359 			 in_port_t port) {
360 	memset(sockaddr, 0, sizeof(*sockaddr));
361 	sockaddr->type.sin.sin_family = na->family;
362 	switch (na->family) {
363 	case AF_INET:
364 		sockaddr->length = sizeof(sockaddr->type.sin);
365 		sockaddr->type.sin.sin_addr = na->type.in;
366 		sockaddr->type.sin.sin_port = htons(port);
367 		break;
368 	case AF_INET6:
369 		sockaddr->length = sizeof(sockaddr->type.sin6);
370 		memmove(&sockaddr->type.sin6.sin6_addr, &na->type.in6, 16);
371 		sockaddr->type.sin6.sin6_scope_id = isc_netaddr_getzone(na);
372 		sockaddr->type.sin6.sin6_port = htons(port);
373 		break;
374 	default:
375 		UNREACHABLE();
376 	}
377 	ISC_LINK_INIT(sockaddr, link);
378 }
379 
380 void
isc_sockaddr_setport(isc_sockaddr_t * sockaddr,in_port_t port)381 isc_sockaddr_setport(isc_sockaddr_t *sockaddr, in_port_t port) {
382 	switch (sockaddr->type.sa.sa_family) {
383 	case AF_INET:
384 		sockaddr->type.sin.sin_port = htons(port);
385 		break;
386 	case AF_INET6:
387 		sockaddr->type.sin6.sin6_port = htons(port);
388 		break;
389 	default:
390 		FATAL_ERROR(__FILE__, __LINE__, "unknown address family: %d",
391 			    (int)sockaddr->type.sa.sa_family);
392 	}
393 }
394 
395 in_port_t
isc_sockaddr_getport(const isc_sockaddr_t * sockaddr)396 isc_sockaddr_getport(const isc_sockaddr_t *sockaddr) {
397 	in_port_t port = 0;
398 
399 	switch (sockaddr->type.sa.sa_family) {
400 	case AF_INET:
401 		port = ntohs(sockaddr->type.sin.sin_port);
402 		break;
403 	case AF_INET6:
404 		port = ntohs(sockaddr->type.sin6.sin6_port);
405 		break;
406 	default:
407 		FATAL_ERROR(__FILE__, __LINE__, "unknown address family: %d",
408 			    (int)sockaddr->type.sa.sa_family);
409 	}
410 
411 	return (port);
412 }
413 
414 bool
isc_sockaddr_ismulticast(const isc_sockaddr_t * sockaddr)415 isc_sockaddr_ismulticast(const isc_sockaddr_t *sockaddr) {
416 	isc_netaddr_t netaddr;
417 
418 	if (sockaddr->type.sa.sa_family == AF_INET ||
419 	    sockaddr->type.sa.sa_family == AF_INET6)
420 	{
421 		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
422 		return (isc_netaddr_ismulticast(&netaddr));
423 	}
424 	return (false);
425 }
426 
427 bool
isc_sockaddr_isexperimental(const isc_sockaddr_t * sockaddr)428 isc_sockaddr_isexperimental(const isc_sockaddr_t *sockaddr) {
429 	isc_netaddr_t netaddr;
430 
431 	if (sockaddr->type.sa.sa_family == AF_INET) {
432 		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
433 		return (isc_netaddr_isexperimental(&netaddr));
434 	}
435 	return (false);
436 }
437 
438 bool
isc_sockaddr_issitelocal(const isc_sockaddr_t * sockaddr)439 isc_sockaddr_issitelocal(const isc_sockaddr_t *sockaddr) {
440 	isc_netaddr_t netaddr;
441 
442 	if (sockaddr->type.sa.sa_family == AF_INET6) {
443 		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
444 		return (isc_netaddr_issitelocal(&netaddr));
445 	}
446 	return (false);
447 }
448 
449 bool
isc_sockaddr_islinklocal(const isc_sockaddr_t * sockaddr)450 isc_sockaddr_islinklocal(const isc_sockaddr_t *sockaddr) {
451 	isc_netaddr_t netaddr;
452 
453 	if (sockaddr->type.sa.sa_family == AF_INET6) {
454 		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
455 		return (isc_netaddr_islinklocal(&netaddr));
456 	}
457 	return (false);
458 }
459 
460 bool
isc_sockaddr_isnetzero(const isc_sockaddr_t * sockaddr)461 isc_sockaddr_isnetzero(const isc_sockaddr_t *sockaddr) {
462 	isc_netaddr_t netaddr;
463 
464 	if (sockaddr->type.sa.sa_family == AF_INET) {
465 		isc_netaddr_fromsockaddr(&netaddr, sockaddr);
466 		return (isc_netaddr_isnetzero(&netaddr));
467 	}
468 	return (false);
469 }
470 
471 isc_result_t
isc_sockaddr_frompath(isc_sockaddr_t * sockaddr,const char * path)472 isc_sockaddr_frompath(isc_sockaddr_t *sockaddr, const char *path) {
473 #ifdef ISC_PLATFORM_HAVESYSUNH
474 	if (strlen(path) >= sizeof(sockaddr->type.sunix.sun_path)) {
475 		return (ISC_R_NOSPACE);
476 	}
477 	memset(sockaddr, 0, sizeof(*sockaddr));
478 	sockaddr->length = sizeof(sockaddr->type.sunix);
479 	sockaddr->type.sunix.sun_family = AF_UNIX;
480 	strlcpy(sockaddr->type.sunix.sun_path, path,
481 		sizeof(sockaddr->type.sunix.sun_path));
482 	return (ISC_R_SUCCESS);
483 #else  /* ifdef ISC_PLATFORM_HAVESYSUNH */
484 	UNUSED(sockaddr);
485 	UNUSED(path);
486 	return (ISC_R_NOTIMPLEMENTED);
487 #endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
488 }
489 
490 isc_result_t
isc_sockaddr_fromsockaddr(isc_sockaddr_t * isa,const struct sockaddr * sa)491 isc_sockaddr_fromsockaddr(isc_sockaddr_t *isa, const struct sockaddr *sa) {
492 	unsigned int length = 0;
493 
494 	switch (sa->sa_family) {
495 	case AF_INET:
496 		length = sizeof(isa->type.sin);
497 		break;
498 	case AF_INET6:
499 		length = sizeof(isa->type.sin6);
500 		break;
501 #ifdef ISC_PLATFORM_HAVESYSUNH
502 	case AF_UNIX:
503 		length = sizeof(isa->type.sunix);
504 		break;
505 #endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
506 	default:
507 		return (ISC_R_NOTIMPLEMENTED);
508 	}
509 
510 	memset(isa, 0, sizeof(isc_sockaddr_t));
511 	memmove(isa, sa, length);
512 	isa->length = length;
513 
514 	return (ISC_R_SUCCESS);
515 }
516