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