1/*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: sockaddr.c,v 1.16 2022/01/17 18:19:51 naddy Exp $ */
18
19/*! \file */
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <netdb.h>
23#include <stdio.h>
24
25#include <isc/buffer.h>
26
27#include <isc/region.h>
28#include <isc/sockaddr.h>
29#include <string.h>
30#include <isc/util.h>
31
32int
33isc_sockaddr_equal(const struct sockaddr_storage *a, const struct sockaddr_storage *b) {
34	return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
35					   ISC_SOCKADDR_CMPPORT|
36					   ISC_SOCKADDR_CMPSCOPE));
37}
38
39int
40isc_sockaddr_eqaddr(const struct sockaddr_storage *a, const struct sockaddr_storage *b) {
41	return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR|
42					   ISC_SOCKADDR_CMPSCOPE));
43}
44
45int
46isc_sockaddr_compare(const struct sockaddr_storage *a, const struct sockaddr_storage *b,
47		     unsigned int flags)
48{
49	struct sockaddr_in	*sin_a, *sin_b;
50	struct sockaddr_in6	*sin6_a, *sin6_b;
51
52	REQUIRE(a != NULL && b != NULL);
53
54	if (a->ss_len != b->ss_len)
55		return (0);
56
57	/*
58	 * We don't just memcmp because the sin_zero field isn't always
59	 * zero.
60	 */
61
62	if (a->ss_family != b->ss_family)
63		return (0);
64	switch (a->ss_family) {
65	case AF_INET:
66		sin_a = (struct sockaddr_in *) a;
67		sin_b = (struct sockaddr_in *) b;
68		if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
69		    memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
70			   sizeof(sin_a->sin_addr)) != 0)
71			return (0);
72		if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
73		    sin_a->sin_port != sin_b->sin_port)
74			return (0);
75		break;
76	case AF_INET6:
77		sin6_a = (struct sockaddr_in6 *) a;
78		sin6_b = (struct sockaddr_in6 *) b;
79
80		if ((flags & ISC_SOCKADDR_CMPADDR) != 0 &&
81		    memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
82			   sizeof(sin6_a->sin6_addr)) != 0)
83			return (0);
84		/*
85		 * If ISC_SOCKADDR_CMPSCOPEZERO is set then don't return
86		 * 0 if one of the scopes in zero.
87		 */
88		if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 &&
89		    sin6_a->sin6_scope_id != sin6_b->sin6_scope_id &&
90		    ((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 ||
91		      (sin6_a->sin6_scope_id != 0 &&
92		       sin6_b->sin6_scope_id != 0)))
93			return (0);
94		if ((flags & ISC_SOCKADDR_CMPPORT) != 0 &&
95		    sin6_a->sin6_port != sin6_b->sin6_port)
96			return (0);
97		break;
98	default:
99		if (memcmp(a, b, a->ss_len) != 0)
100			return (0);
101	}
102	return (1);
103}
104
105isc_result_t
106isc_sockaddr_totext(const struct sockaddr_storage *sockaddr, isc_buffer_t *target) {
107	struct sockaddr_in *sin;
108	struct sockaddr_in6 *sin6;
109	char pbuf[sizeof("65000")];
110	unsigned int plen;
111	isc_region_t avail;
112	char tmp[NI_MAXHOST];
113
114	REQUIRE(sockaddr != NULL);
115
116	/*
117	 * Do the port first, giving us the opportunity to check for
118	 * unsupported address families.
119	 */
120	switch (sockaddr->ss_family) {
121	case AF_INET:
122		sin = (struct sockaddr_in *)sockaddr;
123		snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin->sin_port));
124		break;
125	case AF_INET6:
126		sin6 = (struct sockaddr_in6 *)sockaddr;
127		snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sin6->sin6_port));
128		break;
129	default:
130		return (ISC_R_FAILURE);
131	}
132
133	plen = strlen(pbuf);
134	INSIST(plen < sizeof(pbuf));
135
136	if (getnameinfo((struct sockaddr *)sockaddr, sockaddr->ss_len,
137	    tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0)
138		return (ISC_R_FAILURE);
139	if (strlen(tmp) > isc_buffer_availablelength(target))
140		return (ISC_R_NOSPACE);
141	isc_buffer_putmem(target, tmp, strlen(tmp));
142
143	if (1 + plen + 1 > isc_buffer_availablelength(target))
144		return (ISC_R_NOSPACE);
145
146	isc_buffer_putmem(target, (const unsigned char *)"#", 1);
147	isc_buffer_putmem(target, (const unsigned char *)pbuf, plen);
148
149	/*
150	 * Null terminate after used region.
151	 */
152	isc_buffer_availableregion(target, &avail);
153	INSIST(avail.length >= 1);
154	avail.base[0] = '\0';
155
156	return (ISC_R_SUCCESS);
157}
158
159void
160isc_sockaddr_format(const struct sockaddr_storage *sa, char *array, unsigned int size) {
161	isc_result_t result;
162	isc_buffer_t buf;
163
164	if (size == 0U)
165		return;
166
167	isc_buffer_init(&buf, array, size);
168	result = isc_sockaddr_totext(sa, &buf);
169	if (result != ISC_R_SUCCESS) {
170		snprintf(array, size, "<unknown address, family %u>",
171			 sa->ss_family);
172		array[size - 1] = '\0';
173	}
174}
175
176void
177isc_sockaddr_any(struct sockaddr_storage *sockaddr)
178{
179	struct sockaddr_in *sin = (struct sockaddr_in *) sockaddr;
180	memset(sockaddr, 0, sizeof(*sockaddr));
181	sin->sin_family = AF_INET;
182	sin->sin_len = sizeof(*sin);
183	sin->sin_addr.s_addr = INADDR_ANY;
184	sin->sin_port = 0;
185}
186
187void
188isc_sockaddr_any6(struct sockaddr_storage *sockaddr)
189{
190	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sockaddr;
191	memset(sockaddr, 0, sizeof(*sockaddr));
192	sin6->sin6_family = AF_INET6;
193	sin6->sin6_len = sizeof(*sin6);
194	sin6->sin6_addr = in6addr_any;
195	sin6->sin6_port = 0;
196}
197
198void
199isc_sockaddr_anyofpf(struct sockaddr_storage *sockaddr, int pf) {
200     switch (pf) {
201     case AF_INET:
202	     isc_sockaddr_any(sockaddr);
203	     break;
204     case AF_INET6:
205	     isc_sockaddr_any6(sockaddr);
206	     break;
207     default:
208	     INSIST(0);
209     }
210}
211
212int
213isc_sockaddr_pf(const struct sockaddr_storage *sockaddr) {
214
215	/*
216	 * Get the protocol family of 'sockaddr'.
217	 */
218
219	return (sockaddr->ss_family);
220}
221
222in_port_t
223isc_sockaddr_getport(const struct sockaddr_storage *sockaddr) {
224	struct sockaddr_in *sin;
225	struct sockaddr_in6 *sin6;
226
227	switch (sockaddr->ss_family) {
228	case AF_INET:
229		sin = (struct sockaddr_in *)sockaddr;
230		return (ntohs(sin->sin_port));
231		break;
232	case AF_INET6:
233		sin6 = (struct sockaddr_in6 *)sockaddr;
234		return (ntohs(sin6->sin6_port));
235		break;
236	default:
237		FATAL_ERROR(__FILE__, __LINE__,
238			    "unknown address family: %d",
239			    (int)sockaddr->ss_family);
240	}
241}
242
243int
244isc_sockaddr_ismulticast(const struct sockaddr_storage *sockaddr) {
245	struct sockaddr_in *sin;
246	struct sockaddr_in6 *sin6;
247
248	switch (sockaddr->ss_family) {
249	case AF_INET:
250		sin = (struct sockaddr_in *)sockaddr;
251		return (IN_MULTICAST(sin->sin_addr.s_addr));
252	case AF_INET6:
253		sin6 = (struct sockaddr_in6 *)sockaddr;
254		return (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr));
255	default:
256		return (0);
257	}
258}
259
260int
261isc_sockaddr_issitelocal(const struct sockaddr_storage *sockaddr) {
262	struct sockaddr_in6 *sin6;
263	if (sockaddr->ss_family == AF_INET6) {
264		sin6 = (struct sockaddr_in6 *)sockaddr;
265		return (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr));
266	}
267	return (0);
268}
269
270int
271isc_sockaddr_islinklocal(const struct sockaddr_storage *sockaddr) {
272	struct sockaddr_in6 *sin6;
273	if (sockaddr->ss_family == AF_INET6) {
274		sin6 = (struct sockaddr_in6 *)sockaddr;
275		return (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr));
276	}
277	return (0);
278}
279