1 /* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include <assert.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <netdb.h>
22
23 #include "libknot/errcode.h"
24 #include "contrib/sockaddr.h"
25 #include "contrib/openbsd/strlcpy.h"
26 #include "contrib/macros.h"
27
sockaddr_len(const struct sockaddr_storage * ss)28 int sockaddr_len(const struct sockaddr_storage *ss)
29 {
30 if (ss == NULL) {
31 return 0;
32 }
33
34 switch(ss->ss_family) {
35 case AF_INET:
36 return sizeof(struct sockaddr_in);
37 case AF_INET6:
38 return sizeof(struct sockaddr_in6);
39 case AF_UNIX:
40 return sizeof(struct sockaddr_un);
41 default:
42 return 0;
43 }
44 }
45
cmp_ipv4(const struct sockaddr_in * a,const struct sockaddr_in * b,bool ignore_port)46 static int cmp_ipv4(const struct sockaddr_in *a, const struct sockaddr_in *b,
47 bool ignore_port)
48 {
49 if (a->sin_addr.s_addr < b->sin_addr.s_addr) {
50 return -1;
51 } else if (a->sin_addr.s_addr > b->sin_addr.s_addr) {
52 return 1;
53 } else {
54 return ignore_port ? 0 : a->sin_port - b->sin_port;
55 }
56 }
57
cmp_ipv6(const struct sockaddr_in6 * a,const struct sockaddr_in6 * b,bool ignore_port)58 static int cmp_ipv6(const struct sockaddr_in6 *a, const struct sockaddr_in6 *b,
59 bool ignore_port)
60 {
61 int ret = memcmp(&a->sin6_addr, &b->sin6_addr, sizeof(struct in6_addr));
62 if (ret == 0) {
63 ret = ignore_port ? 0 : a->sin6_port - b->sin6_port;
64 }
65
66 return ret;
67 }
68
cmp_unix(const struct sockaddr_un * a,const struct sockaddr_un * b)69 static int cmp_unix(const struct sockaddr_un *a, const struct sockaddr_un *b)
70 {
71 int len_a = strnlen(a->sun_path, sizeof(a->sun_path));
72 int len_b = strnlen(b->sun_path, sizeof(b->sun_path));
73 int len_min = len_a <= len_b ? len_a : len_b;
74
75 int ret = strncmp(a->sun_path, b->sun_path, len_min);
76 if (ret == 0) {
77 ret = len_a - len_b;
78 }
79
80 return ret;
81 }
82
sockaddr_cmp(const struct sockaddr_storage * a,const struct sockaddr_storage * b,bool ignore_port)83 int sockaddr_cmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b,
84 bool ignore_port)
85 {
86 assert(a);
87 assert(b);
88 if (a->ss_family != b->ss_family) {
89 return (int)a->ss_family - (int)b->ss_family;
90 }
91
92 switch (a->ss_family) {
93 case AF_UNSPEC:
94 return 0;
95 case AF_INET:
96 return cmp_ipv4((struct sockaddr_in *)a, (struct sockaddr_in *)b,
97 ignore_port);
98 case AF_INET6:
99 return cmp_ipv6((struct sockaddr_in6 *)a, (struct sockaddr_in6 *)b,
100 ignore_port);
101 case AF_UNIX:
102 return cmp_unix((struct sockaddr_un *)a, (struct sockaddr_un *)b);
103 default:
104 return 1;
105 }
106 }
107
sockaddr_set(struct sockaddr_storage * ss,int family,const char * straddr,int port)108 int sockaddr_set(struct sockaddr_storage *ss, int family, const char *straddr, int port)
109 {
110 if (ss == NULL || straddr == NULL) {
111 return KNOT_EINVAL;
112 }
113
114 /* Set family and port. */
115 memset(ss, 0, sizeof(*ss));
116 ss->ss_family = family;
117 sockaddr_port_set(ss, port);
118
119 /* Initialize address depending on address family. */
120 if (family == AF_INET6) {
121 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss;
122 if (inet_pton(family, straddr, &ipv6->sin6_addr) < 1) {
123 return KNOT_ERROR;
124 }
125 return KNOT_EOK;
126 } else if (family == AF_INET) {
127 struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss;
128 if (inet_pton(family, straddr, &ipv4->sin_addr) < 1) {
129 return KNOT_ERROR;
130 }
131 return KNOT_EOK;
132 } else if (family == AF_UNIX) {
133 struct sockaddr_un *un = (struct sockaddr_un *)ss;
134 size_t ret = strlcpy(un->sun_path, straddr, sizeof(un->sun_path));
135 if (ret >= sizeof(un->sun_path)) {
136 return KNOT_ESPACE;
137 }
138 return KNOT_EOK;
139 }
140
141 return KNOT_EINVAL;
142 }
143
sockaddr_raw(const struct sockaddr_storage * ss,size_t * addr_size)144 void *sockaddr_raw(const struct sockaddr_storage *ss, size_t *addr_size)
145 {
146 if (ss == NULL || addr_size == NULL) {
147 return NULL;
148 }
149
150 if (ss->ss_family == AF_INET) {
151 struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss;
152 *addr_size = sizeof(ipv4->sin_addr);
153 return &ipv4->sin_addr;
154 } else if (ss->ss_family == AF_INET6) {
155 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss;
156 *addr_size = sizeof(ipv6->sin6_addr);
157 return &ipv6->sin6_addr;
158 } else {
159 return NULL;
160 }
161 }
162
sockaddr_set_raw(struct sockaddr_storage * ss,int family,const uint8_t * raw_addr,size_t raw_addr_size)163 int sockaddr_set_raw(struct sockaddr_storage *ss, int family,
164 const uint8_t *raw_addr, size_t raw_addr_size)
165 {
166 if (ss == NULL || raw_addr == NULL) {
167 return KNOT_EINVAL;
168 }
169
170 memset(ss, 0, sizeof(*ss));
171 ss->ss_family = family;
172
173 size_t ss_size = 0;
174 void *ss_data = sockaddr_raw(ss, &ss_size);
175 if (ss_data == NULL || ss_size != raw_addr_size) {
176 return KNOT_EINVAL;
177 }
178
179 memcpy(ss_data, raw_addr, ss_size);
180
181 return KNOT_EOK;
182 }
183
sockaddr_tostr(char * buf,size_t maxlen,const struct sockaddr_storage * ss)184 int sockaddr_tostr(char *buf, size_t maxlen, const struct sockaddr_storage *ss)
185 {
186 if (ss == NULL || buf == NULL) {
187 return KNOT_EINVAL;
188 }
189
190 const char *out = NULL;
191
192 /* Convert network address string. */
193 if (ss->ss_family == AF_INET6) {
194 const struct sockaddr_in6 *s = (const struct sockaddr_in6 *)ss;
195 out = inet_ntop(ss->ss_family, &s->sin6_addr, buf, maxlen);
196 } else if (ss->ss_family == AF_INET) {
197 const struct sockaddr_in *s = (const struct sockaddr_in *)ss;
198 out = inet_ntop(ss->ss_family, &s->sin_addr, buf, maxlen);
199 } else if (ss->ss_family == AF_UNIX) {
200 const struct sockaddr_un *s = (const struct sockaddr_un *)ss;
201 size_t ret = strlcpy(buf, s->sun_path, maxlen);
202 out = (ret < maxlen) ? buf : NULL;
203 } else {
204 return KNOT_EINVAL;
205 }
206
207 if (out == NULL) {
208 *buf = '\0';
209 return KNOT_ESPACE;
210 }
211
212 /* Write separator and port. */
213 int written = strlen(buf);
214 int port = sockaddr_port(ss);
215 if (port > 0) {
216 int ret = snprintf(&buf[written], maxlen - written, "@%d", port);
217 if (ret <= 0 || (size_t)ret >= maxlen - written) {
218 *buf = '\0';
219 return KNOT_ESPACE;
220 }
221
222 written += ret;
223 }
224
225 return written;
226 }
227
sockaddr_port(const struct sockaddr_storage * ss)228 int sockaddr_port(const struct sockaddr_storage *ss)
229 {
230 if (ss == NULL) {
231 return KNOT_EINVAL;
232 }
233
234 if (ss->ss_family == AF_INET6) {
235 return ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
236 } else if (ss->ss_family == AF_INET) {
237 return ntohs(((struct sockaddr_in *)ss)->sin_port);
238 } else {
239 return KNOT_EINVAL;
240 }
241 }
242
sockaddr_port_set(struct sockaddr_storage * ss,uint16_t port)243 void sockaddr_port_set(struct sockaddr_storage *ss, uint16_t port)
244 {
245 if (ss == NULL) {
246 return;
247 }
248
249 if (ss->ss_family == AF_INET6) {
250 ((struct sockaddr_in6 *)ss)->sin6_port = htons(port);
251 } else if (ss->ss_family == AF_INET) {
252 ((struct sockaddr_in *)ss)->sin_port = htons(port);
253 }
254 }
255
sockaddr_hostname(void)256 char *sockaddr_hostname(void)
257 {
258 /* Fetch hostname. */
259 char host[256] = "";
260 if (gethostname(host, sizeof(host)) != 0) {
261 return NULL;
262 }
263 /* Just to be sure. */
264 host[sizeof(host) - 1] = '\0';
265
266 /* Fetch canonical name for this address/DNS. */
267 struct addrinfo hints, *info = NULL;
268 memset(&hints, 0, sizeof hints);
269 hints.ai_family = AF_UNSPEC;
270 hints.ai_socktype = SOCK_DGRAM;
271 hints.ai_flags = AI_CANONNAME;
272 if (getaddrinfo(host, "domain", &hints, &info) != 0) {
273 return NULL;
274 }
275
276 /* Fetch first valid hostname. */
277 char *hname = NULL;
278 struct addrinfo *p = NULL;
279 for (p = info; p != NULL; p = p->ai_next) {
280 if (p->ai_canonname) {
281 hname = strdup(p->ai_canonname);
282 break;
283 }
284 }
285
286 /* No valid hostname found, resort to gethostname() result */
287 if (hname == NULL) {
288 hname = strdup(host);
289 }
290
291 freeaddrinfo(info);
292 return hname;
293 }
294
sockaddr_is_any(const struct sockaddr_storage * ss)295 bool sockaddr_is_any(const struct sockaddr_storage *ss)
296 {
297 if (ss == NULL) {
298 return false;
299 }
300
301 if (ss->ss_family == AF_INET) {
302 const struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss;
303 return ipv4->sin_addr.s_addr == INADDR_ANY;
304 }
305
306 if (ss->ss_family == AF_INET6) {
307 const struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss;
308 return memcmp(&ipv6->sin6_addr, &in6addr_any, sizeof(ipv6->sin6_addr)) == 0;
309 }
310
311 return false;
312 }
313
sockaddr_net_match(const struct sockaddr_storage * ss1,const struct sockaddr_storage * ss2,unsigned prefix)314 bool sockaddr_net_match(const struct sockaddr_storage *ss1,
315 const struct sockaddr_storage *ss2,
316 unsigned prefix)
317 {
318 if (ss1 == NULL || ss2 == NULL) {
319 return false;
320 }
321
322 if (ss1->ss_family != ss2->ss_family) {
323 return false;
324 }
325
326 size_t raw_len = 0;
327 const uint8_t *raw_1 = sockaddr_raw(ss1, &raw_len);
328 const uint8_t *raw_2 = sockaddr_raw(ss2, &raw_len);
329
330 prefix = MIN(prefix, raw_len * 8);
331 unsigned bytes = prefix / 8;
332 unsigned bits = prefix % 8;
333
334 /* Compare full bytes. */
335 if (memcmp(raw_1, raw_2, bytes) != 0) {
336 return false;
337 }
338
339 /* Compare last partial byte. */
340 return bits == 0 ||
341 (raw_1[bytes] >> (8 - bits) == raw_2[bytes] >> (8 - bits));
342 }
343
sockaddr_range_match(const struct sockaddr_storage * ss,const struct sockaddr_storage * ss_min,const struct sockaddr_storage * ss_max)344 bool sockaddr_range_match(const struct sockaddr_storage *ss,
345 const struct sockaddr_storage *ss_min,
346 const struct sockaddr_storage *ss_max)
347 {
348 if (ss == NULL || ss_min == NULL || ss_max == NULL) {
349 return false;
350 }
351
352 if (ss_min->ss_family != ss_max->ss_family ||
353 ss_min->ss_family != ss->ss_family) {
354 return false;
355 }
356
357 return sockaddr_cmp(ss, ss_min, true) >= 0 &&
358 sockaddr_cmp(ss, ss_max, true) <= 0;
359 }
360