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.17 2024/07/08 13:46:33 florian 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(ntohl(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