xref: /openbsd/usr.bin/dig/lib/isc/sockaddr.c (revision e26546db)
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 
32 int
isc_sockaddr_equal(const struct sockaddr_storage * a,const struct sockaddr_storage * b)33 isc_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 
39 int
isc_sockaddr_eqaddr(const struct sockaddr_storage * a,const struct sockaddr_storage * b)40 isc_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 
45 int
isc_sockaddr_compare(const struct sockaddr_storage * a,const struct sockaddr_storage * b,unsigned int flags)46 isc_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 
105 isc_result_t
isc_sockaddr_totext(const struct sockaddr_storage * sockaddr,isc_buffer_t * target)106 isc_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 
159 void
isc_sockaddr_format(const struct sockaddr_storage * sa,char * array,unsigned int size)160 isc_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 
176 void
isc_sockaddr_any(struct sockaddr_storage * sockaddr)177 isc_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 
187 void
isc_sockaddr_any6(struct sockaddr_storage * sockaddr)188 isc_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 
198 void
isc_sockaddr_anyofpf(struct sockaddr_storage * sockaddr,int pf)199 isc_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 
212 int
isc_sockaddr_pf(const struct sockaddr_storage * sockaddr)213 isc_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 
222 in_port_t
isc_sockaddr_getport(const struct sockaddr_storage * sockaddr)223 isc_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 
243 int
isc_sockaddr_ismulticast(const struct sockaddr_storage * sockaddr)244 isc_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 
260 int
isc_sockaddr_issitelocal(const struct sockaddr_storage * sockaddr)261 isc_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 
270 int
isc_sockaddr_islinklocal(const struct sockaddr_storage * sockaddr)271 isc_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