1/*	$NetBSD: netaddr.c,v 1.1 2024/02/18 20:57:49 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 <inttypes.h>
19#include <stdbool.h>
20#include <stdio.h>
21
22#include <isc/buffer.h>
23#include <isc/net.h>
24#include <isc/netaddr.h>
25#include <isc/print.h>
26#include <isc/sockaddr.h>
27#include <isc/string.h>
28#include <isc/util.h>
29
30bool
31isc_netaddr_equal(const isc_netaddr_t *a, const isc_netaddr_t *b) {
32	REQUIRE(a != NULL && b != NULL);
33
34	if (a->family != b->family) {
35		return (false);
36	}
37
38	if (a->zone != b->zone) {
39		return (false);
40	}
41
42	switch (a->family) {
43	case AF_INET:
44		if (a->type.in.s_addr != b->type.in.s_addr) {
45			return (false);
46		}
47		break;
48	case AF_INET6:
49		if (memcmp(&a->type.in6, &b->type.in6, sizeof(a->type.in6)) !=
50			    0 ||
51		    a->zone != b->zone)
52		{
53			return (false);
54		}
55		break;
56#ifdef ISC_PLATFORM_HAVESYSUNH
57	case AF_UNIX:
58		if (strcmp(a->type.un, b->type.un) != 0) {
59			return (false);
60		}
61		break;
62#endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
63	default:
64		return (false);
65	}
66	return (true);
67}
68
69bool
70isc_netaddr_eqprefix(const isc_netaddr_t *a, const isc_netaddr_t *b,
71		     unsigned int prefixlen) {
72	const unsigned char *pa = NULL, *pb = NULL;
73	unsigned int ipabytes = 0; /* Length of whole IP address in bytes */
74	unsigned int nbytes;	   /* Number of significant whole bytes */
75	unsigned int nbits;	   /* Number of significant leftover bits */
76
77	REQUIRE(a != NULL && b != NULL);
78
79	if (a->family != b->family) {
80		return (false);
81	}
82
83	if (a->zone != b->zone && b->zone != 0) {
84		return (false);
85	}
86
87	switch (a->family) {
88	case AF_INET:
89		pa = (const unsigned char *)&a->type.in;
90		pb = (const unsigned char *)&b->type.in;
91		ipabytes = 4;
92		break;
93	case AF_INET6:
94		pa = (const unsigned char *)&a->type.in6;
95		pb = (const unsigned char *)&b->type.in6;
96		ipabytes = 16;
97		break;
98	default:
99		return (false);
100	}
101
102	/*
103	 * Don't crash if we get a pattern like 10.0.0.1/9999999.
104	 */
105	if (prefixlen > ipabytes * 8) {
106		prefixlen = ipabytes * 8;
107	}
108
109	nbytes = prefixlen / 8;
110	nbits = prefixlen % 8;
111
112	if (nbytes > 0) {
113		if (memcmp(pa, pb, nbytes) != 0) {
114			return (false);
115		}
116	}
117	if (nbits > 0) {
118		unsigned int bytea, byteb, mask;
119		INSIST(nbytes < ipabytes);
120		INSIST(nbits < 8);
121		bytea = pa[nbytes];
122		byteb = pb[nbytes];
123		mask = (0xFF << (8 - nbits)) & 0xFF;
124		if ((bytea & mask) != (byteb & mask)) {
125			return (false);
126		}
127	}
128	return (true);
129}
130
131isc_result_t
132isc_netaddr_totext(const isc_netaddr_t *netaddr, isc_buffer_t *target) {
133	char abuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
134	char zbuf[sizeof("%4294967295")];
135	unsigned int alen;
136	int zlen;
137	const char *r;
138	const void *type;
139
140	REQUIRE(netaddr != NULL);
141
142	switch (netaddr->family) {
143	case AF_INET:
144		type = &netaddr->type.in;
145		break;
146	case AF_INET6:
147		type = &netaddr->type.in6;
148		break;
149#ifdef ISC_PLATFORM_HAVESYSUNH
150	case AF_UNIX:
151		alen = strlen(netaddr->type.un);
152		if (alen > isc_buffer_availablelength(target)) {
153			return (ISC_R_NOSPACE);
154		}
155		isc_buffer_putmem(target,
156				  (const unsigned char *)(netaddr->type.un),
157				  alen);
158		return (ISC_R_SUCCESS);
159#endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
160	default:
161		return (ISC_R_FAILURE);
162	}
163	r = inet_ntop(netaddr->family, type, abuf, sizeof(abuf));
164	if (r == NULL) {
165		return (ISC_R_FAILURE);
166	}
167
168	alen = strlen(abuf);
169	INSIST(alen < sizeof(abuf));
170
171	zlen = 0;
172	if (netaddr->family == AF_INET6 && netaddr->zone != 0) {
173		zlen = snprintf(zbuf, sizeof(zbuf), "%%%u", netaddr->zone);
174		if (zlen < 0) {
175			return (ISC_R_FAILURE);
176		}
177		INSIST((unsigned int)zlen < sizeof(zbuf));
178	}
179
180	if (alen + zlen > isc_buffer_availablelength(target)) {
181		return (ISC_R_NOSPACE);
182	}
183
184	isc_buffer_putmem(target, (unsigned char *)abuf, alen);
185	isc_buffer_putmem(target, (unsigned char *)zbuf, (unsigned int)zlen);
186
187	return (ISC_R_SUCCESS);
188}
189
190void
191isc_netaddr_format(const isc_netaddr_t *na, char *array, unsigned int size) {
192	isc_result_t result;
193	isc_buffer_t buf;
194
195	isc_buffer_init(&buf, array, size);
196	result = isc_netaddr_totext(na, &buf);
197
198	if (size == 0) {
199		return;
200	}
201
202	/*
203	 * Null terminate.
204	 */
205	if (result == ISC_R_SUCCESS) {
206		if (isc_buffer_availablelength(&buf) >= 1) {
207			isc_buffer_putuint8(&buf, 0);
208		} else {
209			result = ISC_R_NOSPACE;
210		}
211	}
212
213	if (result != ISC_R_SUCCESS) {
214		snprintf(array, size, "<unknown address, family %u>",
215			 na->family);
216		array[size - 1] = '\0';
217	}
218}
219
220isc_result_t
221isc_netaddr_prefixok(const isc_netaddr_t *na, unsigned int prefixlen) {
222	static const unsigned char zeros[16];
223	unsigned int nbits, nbytes, ipbytes = 0;
224	const unsigned char *p;
225
226	switch (na->family) {
227	case AF_INET:
228		p = (const unsigned char *)&na->type.in;
229		ipbytes = 4;
230		if (prefixlen > 32) {
231			return (ISC_R_RANGE);
232		}
233		break;
234	case AF_INET6:
235		p = (const unsigned char *)&na->type.in6;
236		ipbytes = 16;
237		if (prefixlen > 128) {
238			return (ISC_R_RANGE);
239		}
240		break;
241	default:
242		return (ISC_R_NOTIMPLEMENTED);
243	}
244	nbytes = prefixlen / 8;
245	nbits = prefixlen % 8;
246	if (nbits != 0) {
247		INSIST(nbytes < ipbytes);
248		if ((p[nbytes] & (0xff >> nbits)) != 0U) {
249			return (ISC_R_FAILURE);
250		}
251		nbytes++;
252	}
253	if (nbytes < ipbytes &&
254	    memcmp(p + nbytes, zeros, ipbytes - nbytes) != 0)
255	{
256		return (ISC_R_FAILURE);
257	}
258	return (ISC_R_SUCCESS);
259}
260
261isc_result_t
262isc_netaddr_masktoprefixlen(const isc_netaddr_t *s, unsigned int *lenp) {
263	unsigned int nbits = 0, nbytes = 0, ipbytes = 0, i;
264	const unsigned char *p;
265
266	switch (s->family) {
267	case AF_INET:
268		p = (const unsigned char *)&s->type.in;
269		ipbytes = 4;
270		break;
271	case AF_INET6:
272		p = (const unsigned char *)&s->type.in6;
273		ipbytes = 16;
274		break;
275	default:
276		return (ISC_R_NOTIMPLEMENTED);
277	}
278	for (i = 0; i < ipbytes; i++) {
279		if (p[i] != 0xFF) {
280			break;
281		}
282	}
283	nbytes = i;
284	if (i < ipbytes) {
285		unsigned int c = p[nbytes];
286		while ((c & 0x80) != 0 && nbits < 8) {
287			c <<= 1;
288			nbits++;
289		}
290		if ((c & 0xFF) != 0) {
291			return (ISC_R_MASKNONCONTIG);
292		}
293		i++;
294	}
295	for (; i < ipbytes; i++) {
296		if (p[i] != 0) {
297			return (ISC_R_MASKNONCONTIG);
298		}
299	}
300	*lenp = nbytes * 8 + nbits;
301	return (ISC_R_SUCCESS);
302}
303
304void
305isc_netaddr_fromin(isc_netaddr_t *netaddr, const struct in_addr *ina) {
306	memset(netaddr, 0, sizeof(*netaddr));
307	netaddr->family = AF_INET;
308	netaddr->type.in = *ina;
309}
310
311void
312isc_netaddr_fromin6(isc_netaddr_t *netaddr, const struct in6_addr *ina6) {
313	memset(netaddr, 0, sizeof(*netaddr));
314	netaddr->family = AF_INET6;
315	netaddr->type.in6 = *ina6;
316}
317
318isc_result_t
319isc_netaddr_frompath(isc_netaddr_t *netaddr, const char *path) {
320#ifdef ISC_PLATFORM_HAVESYSUNH
321	if (strlen(path) > sizeof(netaddr->type.un) - 1) {
322		return (ISC_R_NOSPACE);
323	}
324
325	memset(netaddr, 0, sizeof(*netaddr));
326	netaddr->family = AF_UNIX;
327	strlcpy(netaddr->type.un, path, sizeof(netaddr->type.un));
328	netaddr->zone = 0;
329	return (ISC_R_SUCCESS);
330#else  /* ifdef ISC_PLATFORM_HAVESYSUNH */
331	UNUSED(netaddr);
332	UNUSED(path);
333	return (ISC_R_NOTIMPLEMENTED);
334#endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
335}
336
337void
338isc_netaddr_setzone(isc_netaddr_t *netaddr, uint32_t zone) {
339	/* we currently only support AF_INET6. */
340	REQUIRE(netaddr->family == AF_INET6);
341
342	netaddr->zone = zone;
343}
344
345uint32_t
346isc_netaddr_getzone(const isc_netaddr_t *netaddr) {
347	return (netaddr->zone);
348}
349
350void
351isc_netaddr_fromsockaddr(isc_netaddr_t *t, const isc_sockaddr_t *s) {
352	int family = s->type.sa.sa_family;
353	t->family = family;
354	switch (family) {
355	case AF_INET:
356		t->type.in = s->type.sin.sin_addr;
357		t->zone = 0;
358		break;
359	case AF_INET6:
360		memmove(&t->type.in6, &s->type.sin6.sin6_addr, 16);
361		t->zone = s->type.sin6.sin6_scope_id;
362		break;
363#ifdef ISC_PLATFORM_HAVESYSUNH
364	case AF_UNIX:
365		memmove(t->type.un, s->type.sunix.sun_path, sizeof(t->type.un));
366		t->zone = 0;
367		break;
368#endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
369	default:
370		UNREACHABLE();
371	}
372}
373
374void
375isc_netaddr_any(isc_netaddr_t *netaddr) {
376	memset(netaddr, 0, sizeof(*netaddr));
377	netaddr->family = AF_INET;
378	netaddr->type.in.s_addr = INADDR_ANY;
379}
380
381void
382isc_netaddr_any6(isc_netaddr_t *netaddr) {
383	memset(netaddr, 0, sizeof(*netaddr));
384	netaddr->family = AF_INET6;
385	netaddr->type.in6 = in6addr_any;
386}
387
388void
389isc_netaddr_unspec(isc_netaddr_t *netaddr) {
390	memset(netaddr, 0, sizeof(*netaddr));
391	netaddr->family = AF_UNSPEC;
392}
393
394bool
395isc_netaddr_ismulticast(const isc_netaddr_t *na) {
396	switch (na->family) {
397	case AF_INET:
398		return (ISC_IPADDR_ISMULTICAST(na->type.in.s_addr));
399	case AF_INET6:
400		return (IN6_IS_ADDR_MULTICAST(&na->type.in6));
401	default:
402		return (false); /* XXXMLG ? */
403	}
404}
405
406bool
407isc_netaddr_isexperimental(const isc_netaddr_t *na) {
408	switch (na->family) {
409	case AF_INET:
410		return (ISC_IPADDR_ISEXPERIMENTAL(na->type.in.s_addr));
411	default:
412		return (false); /* XXXMLG ? */
413	}
414}
415
416bool
417isc_netaddr_islinklocal(const isc_netaddr_t *na) {
418	switch (na->family) {
419	case AF_INET:
420		return (false);
421	case AF_INET6:
422		return (IN6_IS_ADDR_LINKLOCAL(&na->type.in6));
423	default:
424		return (false);
425	}
426}
427
428bool
429isc_netaddr_issitelocal(const isc_netaddr_t *na) {
430	switch (na->family) {
431	case AF_INET:
432		return (false);
433	case AF_INET6:
434		return (IN6_IS_ADDR_SITELOCAL(&na->type.in6));
435	default:
436		return (false);
437	}
438}
439
440#define ISC_IPADDR_ISNETZERO(i) \
441	(((uint32_t)(i)&ISC__IPADDR(0xff000000)) == ISC__IPADDR(0x00000000))
442
443bool
444isc_netaddr_isnetzero(const isc_netaddr_t *na) {
445	switch (na->family) {
446	case AF_INET:
447		return (ISC_IPADDR_ISNETZERO(na->type.in.s_addr));
448	case AF_INET6:
449		return (false);
450	default:
451		return (false);
452	}
453}
454
455void
456isc_netaddr_fromv4mapped(isc_netaddr_t *t, const isc_netaddr_t *s) {
457	isc_netaddr_t *src;
458
459	DE_CONST(s, src); /* Must come before IN6_IS_ADDR_V4MAPPED. */
460
461	REQUIRE(s->family == AF_INET6);
462	REQUIRE(IN6_IS_ADDR_V4MAPPED(&src->type.in6));
463
464	memset(t, 0, sizeof(*t));
465	t->family = AF_INET;
466	memmove(&t->type.in, (char *)&src->type.in6 + 12, 4);
467	return;
468}
469
470bool
471isc_netaddr_isloopback(const isc_netaddr_t *na) {
472	switch (na->family) {
473	case AF_INET:
474		return (((ntohl(na->type.in.s_addr) & 0xff000000U) ==
475			 0x7f000000U));
476	case AF_INET6:
477		return (IN6_IS_ADDR_LOOPBACK(&na->type.in6));
478	default:
479		return (false);
480	}
481}
482