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
33bool
34isc_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
41bool
42isc_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
47bool
48isc_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
111bool
112isc_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
120isc_result_t
121isc_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
195void
196isc_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
216unsigned int
217isc_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
264void
265isc_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
274void
275isc_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
284void
285isc_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
295void
296isc_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
309void
310isc_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
320void
321isc_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
333int
334isc_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
357void
358isc_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
380void
381isc_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
395in_port_t
396isc_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
414bool
415isc_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
427bool
428isc_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
438bool
439isc_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
449bool
450isc_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
460bool
461isc_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
471isc_result_t
472isc_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
490isc_result_t
491isc_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