1 /**
2  * Copyright (c) 2020 Paul-Louis Ageneau
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "addr.h"
20 #include "log.h"
21 
22 #include <stdio.h>
23 #include <string.h>
24 
addr_get_len(const struct sockaddr * sa)25 socklen_t addr_get_len(const struct sockaddr *sa) {
26 	switch (sa->sa_family) {
27 	case AF_INET:
28 		return sizeof(struct sockaddr_in);
29 	case AF_INET6:
30 		return sizeof(struct sockaddr_in6);
31 	default:
32 		JLOG_WARN("Unknown address family %hu", sa->sa_family);
33 		return 0;
34 	}
35 }
36 
addr_get_port(const struct sockaddr * sa)37 uint16_t addr_get_port(const struct sockaddr *sa) {
38 	switch (sa->sa_family) {
39 	case AF_INET:
40 		return ntohs(((struct sockaddr_in *)sa)->sin_port);
41 	case AF_INET6:
42 		return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
43 	default:
44 		JLOG_WARN("Unknown address family %hu", sa->sa_family);
45 		return 0;
46 	}
47 }
48 
addr_set_port(struct sockaddr * sa,uint16_t port)49 int addr_set_port(struct sockaddr *sa, uint16_t port) {
50 	switch (sa->sa_family) {
51 	case AF_INET:
52 		((struct sockaddr_in *)sa)->sin_port = htons(port);
53 		return 0;
54 	case AF_INET6:
55 		((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
56 		return 0;
57 	default:
58 		JLOG_WARN("Unknown address family %hu", sa->sa_family);
59 		return -1;
60 	}
61 }
62 
addr_is_any(struct sockaddr * sa)63 bool addr_is_any(struct sockaddr *sa) {
64 	switch (sa->sa_family) {
65 	case AF_INET: {
66 		const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
67 		const uint8_t *b = (const uint8_t *)&sin->sin_addr;
68 		for (int i = 0; i < 4; ++i)
69 			if (b[i] != 0)
70 				return false;
71 
72 		return true;
73 	}
74 	case AF_INET6: {
75 		const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
76 		const uint8_t *b = (const uint8_t *)&sin6->sin6_addr;
77 		for (int i = 0; i < 16; ++i)
78 			if (b[i] != 0)
79 				return false;
80 
81 		return true;
82 	}
83 	default:
84 		return false;
85 	}
86 }
87 
addr_is_local(struct sockaddr * sa)88 bool addr_is_local(struct sockaddr *sa) {
89 	switch (sa->sa_family) {
90 	case AF_INET: {
91 		const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
92 		const uint8_t *b = (const uint8_t *)&sin->sin_addr;
93 		if (b[0] == 127) // loopback
94 			return true;
95 		if (b[0] == 169 && b[1] == 254) // link-local
96 			return true;
97 		return false;
98 	}
99 	case AF_INET6: {
100 		const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
101 		if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) {
102 			return true;
103 		}
104 		if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
105 			return true;
106 		}
107 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
108 			const uint8_t *b = (const uint8_t *)&sin6->sin6_addr + 12;
109 			if (b[0] == 127) // loopback
110 				return true;
111 			if (b[0] == 169 && b[1] == 254) // link-local
112 				return true;
113 			return false;
114 		}
115 		return false;
116 	}
117 	default:
118 		return false;
119 	}
120 }
121 
addr_is_temp_inet6(struct sockaddr * sa)122 bool addr_is_temp_inet6(struct sockaddr *sa) {
123 	if (sa->sa_family != AF_INET6)
124 		return false;
125 	if (addr_is_local(sa))
126 		return false;
127 	const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
128 	const uint8_t *b = (const uint8_t *)&sin6->sin6_addr;
129 	return (b[8] & 0x02) ? false : true;
130 }
131 
addr_unmap_inet6_v4mapped(struct sockaddr * sa,socklen_t * len)132 bool addr_unmap_inet6_v4mapped(struct sockaddr *sa, socklen_t *len) {
133 	if (sa->sa_family != AF_INET6)
134 		return false;
135 
136 	const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
137 	if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
138 		return false;
139 
140 	struct sockaddr_in6 copy = *sin6;
141 	sin6 = &copy;
142 
143 	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
144 	memset(sin, 0, sizeof(*sin));
145 	sin->sin_family = AF_INET;
146 	sin->sin_port = sin6->sin6_port;
147 	memcpy(&sin->sin_addr, ((const uint8_t *)&sin6->sin6_addr) + 12, 4);
148 	*len = sizeof(*sin);
149 	return true;
150 }
151 
addr_map_inet6_v4mapped(struct sockaddr_storage * ss,socklen_t * len)152 bool addr_map_inet6_v4mapped(struct sockaddr_storage *ss, socklen_t *len) {
153 	if (ss->ss_family != AF_INET)
154 		return false;
155 
156 	const struct sockaddr_in *sin = (const struct sockaddr_in *)ss;
157 	struct sockaddr_in copy = *sin;
158 	sin = &copy;
159 
160 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
161 	memset(sin6, 0, sizeof(*sin6));
162 	sin6->sin6_family = AF_INET6;
163 	sin6->sin6_port = sin->sin_port;
164 	uint8_t *b = (uint8_t *)&sin6->sin6_addr;
165 	memset(b, 0, 10);
166 	memset(b + 10, 0xFF, 2);
167 	memcpy(b + 12, (const uint8_t *)&sin->sin_addr, 4);
168 	*len = sizeof(*sin6);
169 	return true;
170 }
171 
addr_is_equal(const struct sockaddr * a,const struct sockaddr * b,bool compare_ports)172 bool addr_is_equal(const struct sockaddr *a, const struct sockaddr *b, bool compare_ports) {
173 	if (a->sa_family != b->sa_family)
174 		return false;
175 
176 	switch (a->sa_family) {
177 	case AF_INET: {
178 		const struct sockaddr_in *ain = (const struct sockaddr_in *)a;
179 		const struct sockaddr_in *bin = (const struct sockaddr_in *)b;
180 		if (memcmp(&ain->sin_addr, &bin->sin_addr, 4) != 0)
181 			return false;
182 		if (compare_ports && ain->sin_port != bin->sin_port)
183 			return false;
184 		break;
185 	}
186 	case AF_INET6: {
187 		const struct sockaddr_in6 *ain6 = (const struct sockaddr_in6 *)a;
188 		const struct sockaddr_in6 *bin6 = (const struct sockaddr_in6 *)b;
189 		if (memcmp(&ain6->sin6_addr, &bin6->sin6_addr, 16) != 0)
190 			return false;
191 		if (compare_ports && ain6->sin6_port != bin6->sin6_port)
192 			return false;
193 		break;
194 	}
195 	default:
196 		return false;
197 	}
198 
199 	return true;
200 }
201 
addr_to_string(const struct sockaddr * sa,char * buffer,size_t size)202 int addr_to_string(const struct sockaddr *sa, char *buffer, size_t size) {
203 	socklen_t salen = addr_get_len(sa);
204 	if (salen == 0)
205 		goto error;
206 
207 	char host[ADDR_MAX_NUMERICHOST_LEN];
208 	char service[ADDR_MAX_NUMERICSERV_LEN];
209 	if (getnameinfo(sa, salen, host, ADDR_MAX_NUMERICHOST_LEN, service, ADDR_MAX_NUMERICSERV_LEN,
210 	                NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM)) {
211 		JLOG_ERROR("getnameinfo failed, errno=%d", sockerrno);
212 		goto error;
213 	}
214 
215 	int len = snprintf(buffer, size, "%s:%s", host, service);
216 	if (len < 0 || (size_t)len >= size)
217 		goto error;
218 
219 	return len;
220 
221 error:
222 	// Make sure we still write a valid null-terminated string
223 	snprintf(buffer, size, "?");
224 	return -1;
225 }
226 
227 // djb2 hash function
228 #define DJB2_INIT 5381
djb2(unsigned long * hash,int i)229 static void djb2(unsigned long *hash, int i) {
230 	*hash = ((*hash << 5) + *hash) + i; // hash * 33 + i
231 }
232 
addr_hash(const struct sockaddr * sa,bool with_port)233 unsigned long addr_hash(const struct sockaddr *sa, bool with_port) {
234 	unsigned long hash = DJB2_INIT;
235 
236 	djb2(&hash, sa->sa_family);
237 	switch (sa->sa_family) {
238 	case AF_INET: {
239 		const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
240 		const uint8_t *b = (const uint8_t *)&sin->sin_addr;
241 		for (int i = 0; i < 4; ++i)
242 			djb2(&hash, b[i]);
243 		if (with_port) {
244 			djb2(&hash, sin->sin_port >> 8);
245 			djb2(&hash, sin->sin_port & 0xFF);
246 		}
247 		break;
248 	}
249 	case AF_INET6: {
250 		const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
251 		const uint8_t *b = (const uint8_t *)&sin6->sin6_addr;
252 		for (int i = 0; i < 16; ++i)
253 			djb2(&hash, b[i]);
254 		if (with_port) {
255 			djb2(&hash, sin6->sin6_port >> 8);
256 			djb2(&hash, sin6->sin6_port & 0xFF);
257 		}
258 		break;
259 	}
260 	default:
261 		break;
262 	}
263 
264 	return hash;
265 }
266 
addr_resolve(const char * hostname,const char * service,addr_record_t * records,size_t count)267 int addr_resolve(const char *hostname, const char *service, addr_record_t *records, size_t count) {
268 	addr_record_t *end = records + count;
269 
270 	struct addrinfo hints;
271 	memset(&hints, 0, sizeof(hints));
272 	hints.ai_family = AF_UNSPEC;
273 	hints.ai_socktype = SOCK_DGRAM;
274 	hints.ai_protocol = IPPROTO_UDP;
275 	hints.ai_flags = AI_ADDRCONFIG;
276 	struct addrinfo *ai_list = NULL;
277 	if (getaddrinfo(hostname, service, &hints, &ai_list)) {
278 		JLOG_WARN("Address resolution failed for %s:%s", hostname, service);
279 		return -1;
280 	}
281 
282 	int ret = 0;
283 	for (struct addrinfo *ai = ai_list; ai; ai = ai->ai_next) {
284 		if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
285 			++ret;
286 			if (records != end) {
287 				memcpy(&records->addr, ai->ai_addr, ai->ai_addrlen);
288 				records->len = (socklen_t)ai->ai_addrlen;
289 				++records;
290 			}
291 		}
292 	}
293 
294 	freeaddrinfo(ai_list);
295 	return ret;
296 }
297 
addr_record_is_equal(const addr_record_t * a,const addr_record_t * b,bool compare_ports)298 bool addr_record_is_equal(const addr_record_t *a, const addr_record_t *b, bool compare_ports) {
299 	return addr_is_equal((const struct sockaddr *)&a->addr, (const struct sockaddr *)&b->addr,
300 	                     compare_ports);
301 }
302 
addr_record_to_string(const addr_record_t * record,char * buffer,size_t size)303 int addr_record_to_string(const addr_record_t *record, char *buffer, size_t size) {
304 	return addr_to_string((const struct sockaddr *)&record->addr, buffer, size);
305 }
306 
addr_record_hash(const addr_record_t * record,bool with_port)307 unsigned long addr_record_hash(const addr_record_t *record, bool with_port) {
308 	return addr_hash((const struct sockaddr *)&record->addr, with_port);
309 }
310