/src/freeradius-server/src/lib/util/inet.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * This program is is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License, version 2 of the |
4 | | * License as published by the Free Software Foundation. |
5 | | * |
6 | | * This program is distributed in the hope that it will be useful, |
7 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
8 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
9 | | * GNU General Public License for more details. |
10 | | * |
11 | | * You should have received a copy of the GNU General Public License |
12 | | * along with this program; if not, write to the Free Software |
13 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
14 | | */ |
15 | | |
16 | | /** Functions for parsing, printing, masking and retrieving IP addresses |
17 | | * |
18 | | * @file src/lib/util/inet.c |
19 | | * |
20 | | * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
21 | | * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
22 | | */ |
23 | | #include <freeradius-devel/util/inet.h> |
24 | | #include <freeradius-devel/util/misc.h> |
25 | | #include <freeradius-devel/util/strerror.h> |
26 | | #include <freeradius-devel/util/syserror.h> |
27 | | #include <freeradius-devel/util/value.h> |
28 | | |
29 | | #include <stdlib.h> |
30 | | #include <ifaddrs.h> |
31 | | #include <net/if_arp.h> |
32 | | |
33 | | /* |
34 | | * Linux |
35 | | */ |
36 | | #if defined(HAVE_LINUX_IF_PACKET_H) |
37 | | # include <linux/if_packet.h> |
38 | | # include <linux/if_ether.h> |
39 | | /* |
40 | | * Apple, *BSD |
41 | | */ |
42 | | #elif defined(HAVE_NET_IF_DL_H) |
43 | | # include <net/if_dl.h> /* Needed for struct sockaddr_ll def */ |
44 | | /* |
45 | | * emscripten/musl |
46 | | */ |
47 | | #elif defined(HAVE_NETPACKET_PACKET_H) |
48 | | # include <netpacket/packet.h> /* Needed for struct sockaddr_ll def */ |
49 | | #endif |
50 | | |
51 | | bool fr_reverse_lookups = false; //!< IP -> hostname lookups? |
52 | | bool fr_hostname_lookups = true; //!< hostname -> IP lookups? |
53 | | |
54 | | /** Determine if an address is the INADDR_ANY address for its address family |
55 | | * |
56 | | * @param ipaddr to check. |
57 | | * @return |
58 | | * - 0 if it's not. |
59 | | * - 1 if it is. |
60 | | * - -1 on error. |
61 | | */ |
62 | | int fr_ipaddr_is_inaddr_any(fr_ipaddr_t const *ipaddr) |
63 | 0 | { |
64 | |
|
65 | 0 | if (ipaddr->af == AF_INET) { |
66 | 0 | if (ipaddr->addr.v4.s_addr == htonl(INADDR_ANY)) { |
67 | 0 | return 1; |
68 | 0 | } |
69 | |
|
70 | 0 | #ifdef HAVE_STRUCT_SOCKADDR_IN6 |
71 | 0 | } else if (ipaddr->af == AF_INET6) { |
72 | | /* Unconst for emscripten/musl */ |
73 | 0 | if (IN6_IS_ADDR_UNSPECIFIED(UNCONST(struct in6_addr *, &(ipaddr->addr.v6)))) { |
74 | 0 | return 1; |
75 | 0 | } |
76 | 0 | #endif |
77 | |
|
78 | 0 | } else { |
79 | 0 | fr_strerror_const("Unknown address family"); |
80 | 0 | return -1; |
81 | 0 | } |
82 | | |
83 | 0 | return 0; |
84 | 0 | } |
85 | | |
86 | | /** Determine if an address is a multicast address |
87 | | * |
88 | | * @param ipaddr to check. |
89 | | * @return |
90 | | * - 0 if it's not. |
91 | | * - 1 if it is. |
92 | | * - -1 on error. |
93 | | */ |
94 | | int fr_ipaddr_is_multicast(fr_ipaddr_t const *ipaddr) |
95 | 0 | { |
96 | 0 | if (ipaddr->af == AF_INET) { |
97 | | /* |
98 | | * 224.0.0.0 (3758096384) - 239.255.255.255 (4026531839) |
99 | | */ |
100 | 0 | if ((ipaddr->addr.v4.s_addr >= 3758096384) && (ipaddr->addr.v4.s_addr <= 4026531839)) return 1; |
101 | 0 | #ifdef HAVE_STRUCT_SOCKADDR_IN6 |
102 | 0 | } else if (ipaddr->af == AF_INET6) { |
103 | | /* Unconst for emscripten/musl */ |
104 | 0 | if (IN6_IS_ADDR_MULTICAST(UNCONST(struct in6_addr *, &(ipaddr->addr.v6)))) { |
105 | 0 | return 1; |
106 | 0 | } |
107 | 0 | #endif |
108 | |
|
109 | 0 | } else { |
110 | 0 | fr_strerror_const("Unknown address family"); |
111 | 0 | return -1; |
112 | 0 | } |
113 | | |
114 | 0 | return 0; |
115 | 0 | } |
116 | | |
117 | | /** Determine if an address is a prefix |
118 | | * |
119 | | * @param ipaddr to check. |
120 | | * @return |
121 | | * - 0 if it's not. |
122 | | * - 1 if it is. |
123 | | * - -1 on error. |
124 | | */ |
125 | | int fr_ipaddr_is_prefix(fr_ipaddr_t const *ipaddr) |
126 | 0 | { |
127 | 0 | switch (ipaddr->af) { |
128 | 0 | case AF_INET: |
129 | 0 | return (ipaddr->prefix < 32); |
130 | | |
131 | 0 | case AF_INET6: |
132 | 0 | return (ipaddr->prefix < 128); |
133 | | |
134 | 0 | default: |
135 | 0 | fr_strerror_const("Unknown address family"); |
136 | 0 | return -1; |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | /** Mask off a portion of an IPv4 address |
141 | | * |
142 | | * @param ipaddr to mask. |
143 | | * @param prefix Number of contiguous bits to mask. |
144 | | * @return an ipv4 address with the host portion zeroed out. |
145 | | */ |
146 | | static struct in_addr fr_inaddr_mask(struct in_addr const *ipaddr, uint8_t prefix) |
147 | 792 | { |
148 | 792 | uint32_t ret; |
149 | | |
150 | 792 | if (prefix > 32) prefix = 32; |
151 | | |
152 | | /* Short circuit */ |
153 | 792 | if (prefix == 32) return *ipaddr; |
154 | | |
155 | 680 | if (prefix == 0) ret = 0; |
156 | 395 | else ret = htonl(~((0x00000001UL << (32 - prefix)) - 1)) & ipaddr->s_addr; |
157 | | |
158 | 680 | return (*(struct in_addr *)&ret); |
159 | 792 | } |
160 | | |
161 | | /** Mask off a portion of an IPv6 address |
162 | | * |
163 | | * @param ipaddr to mask. |
164 | | * @param prefix Number of contiguous bits to mask. |
165 | | * @return an ipv6 address with the host portion zeroed out. |
166 | | */ |
167 | | static struct in6_addr fr_in6addr_mask(struct in6_addr const *ipaddr, uint8_t prefix) |
168 | 977 | { |
169 | 977 | uint64_t const *p = (uint64_t const *) ipaddr; |
170 | 977 | uint64_t addr; /* Needed for alignment */ |
171 | 977 | uint64_t ret[2], *o = ret; |
172 | | |
173 | 977 | if (prefix > 128) prefix = 128; |
174 | | |
175 | | /* Short circuit */ |
176 | 977 | if (prefix == 128) return *ipaddr; |
177 | | |
178 | 910 | if (prefix >= 64) { |
179 | 257 | prefix -= 64; |
180 | 257 | memcpy(&addr, p, sizeof(addr)); /* Needed for aligned access (ubsan) */ |
181 | 257 | *o++ = 0xffffffffffffffffULL & addr; /* lhs portion masked */ |
182 | 257 | p++; |
183 | 653 | } else { |
184 | 653 | ret[1] = 0; /* rhs portion zeroed */ |
185 | 653 | } |
186 | | |
187 | | /* Max left shift is 63 else we get overflow */ |
188 | 910 | if (prefix > 0) { |
189 | 441 | memcpy(&addr, p, sizeof(addr)); /* Needed for aligned access (ubsan) */ |
190 | 441 | *o = htonll(~((uint64_t)(0x0000000000000001ULL << (64 - prefix)) - 1)) & addr; |
191 | 469 | } else { |
192 | 469 | *o = 0; |
193 | 469 | } |
194 | | |
195 | 910 | return *(struct in6_addr *) &ret; |
196 | 977 | } |
197 | | |
198 | | /** Zeroes out the host portion of an fr_ipaddr_t |
199 | | * |
200 | | * @param[in,out] addr to mask |
201 | | * @param[in] prefix Length of the network portion. |
202 | | */ |
203 | | void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix) |
204 | 1.47k | { |
205 | | |
206 | 1.47k | switch (addr->af) { |
207 | 522 | case AF_INET: |
208 | 522 | addr->addr.v4 = fr_inaddr_mask(&addr->addr.v4, prefix); |
209 | 522 | break; |
210 | | |
211 | 955 | case AF_INET6: |
212 | 955 | addr->addr.v6 = fr_in6addr_mask(&addr->addr.v6, prefix); |
213 | 955 | break; |
214 | | |
215 | 0 | default: |
216 | 0 | return; |
217 | 1.47k | } |
218 | 1.47k | addr->prefix = prefix; |
219 | 1.47k | } |
220 | | |
221 | | /** Wrappers for IPv4/IPv6 host to IP address lookup |
222 | | * |
223 | | * This function returns only one IP address, of the specified address family, |
224 | | * or the first address (of whatever family), if AF_UNSPEC is used. |
225 | | * |
226 | | * If fallback is specified and af is AF_INET, but not AF_INET records were |
227 | | * found and a record for AF_INET6 exists that record will be returned. |
228 | | * |
229 | | * If fallback is specified and af is AF_INET6, and a record with AF_INET4 exists |
230 | | * that record will be returned inseted. |
231 | | * |
232 | | * @param[out] out Where to write result. |
233 | | * @param[in] af To search for in preference. |
234 | | * @param[in] hostname to search for. |
235 | | * @param[in] fallback to the other address family, if no records matching af, found. |
236 | | * @return |
237 | | * - 0 on success. |
238 | | * - -1 on failure. |
239 | | */ |
240 | | int fr_inet_hton(fr_ipaddr_t *out, int af, char const *hostname, bool fallback) |
241 | 0 | { |
242 | 0 | int ret; |
243 | 0 | struct addrinfo hints, *ai = NULL, *alt = NULL, *res = NULL; |
244 | | |
245 | | /* |
246 | | * Avoid alloc for IP addresses. This helps us debug |
247 | | * memory errors when using talloc. |
248 | | */ |
249 | 0 | if (!fr_hostname_lookups) { |
250 | 0 | #ifdef HAVE_STRUCT_SOCKADDR_IN6 |
251 | 0 | if (af == AF_UNSPEC) { |
252 | 0 | char const *p; |
253 | |
|
254 | 0 | for (p = hostname; *p != '\0'; p++) { |
255 | 0 | if ((*p == ':') || |
256 | 0 | (*p == '[') || |
257 | 0 | (*p == ']')) { |
258 | 0 | af = AF_INET6; |
259 | 0 | break; |
260 | 0 | } |
261 | 0 | } |
262 | 0 | } |
263 | 0 | #endif |
264 | |
|
265 | 0 | if (af == AF_UNSPEC) af = AF_INET; |
266 | |
|
267 | 0 | if (inet_pton(af, hostname, &(out->addr)) == 0) { |
268 | 0 | fr_strerror_printf("\"%s\" is not a valid IP address and " |
269 | 0 | "hostname lookups are disabled", hostname); |
270 | 0 | return -1; |
271 | 0 | } |
272 | 0 | out->af = af; |
273 | 0 | out->prefix = 32; |
274 | 0 | out->scope_id = 0; |
275 | |
|
276 | 0 | return 0; |
277 | 0 | } |
278 | | |
279 | 0 | memset(&hints, 0, sizeof(hints)); |
280 | | |
281 | | /* |
282 | | * If we're falling back we need both IPv4 and IPv6 records |
283 | | */ |
284 | 0 | if (fallback) { |
285 | 0 | hints.ai_family = AF_UNSPEC; |
286 | 0 | } else { |
287 | 0 | hints.ai_family = af; |
288 | 0 | } |
289 | |
|
290 | 0 | if ((ret = getaddrinfo(hostname, NULL, &hints, &res)) != 0) { |
291 | 0 | switch (af) { |
292 | 0 | default: |
293 | 0 | case AF_UNSPEC: |
294 | 0 | fr_strerror_printf("Failed resolving \"%s\" to IP address: %s", |
295 | 0 | hostname, gai_strerror(ret)); |
296 | 0 | return -1; |
297 | | |
298 | 0 | case AF_INET: |
299 | 0 | fr_strerror_printf("Failed resolving \"%s\" to IPv4 address: %s", |
300 | 0 | hostname, gai_strerror(ret)); |
301 | 0 | return -1; |
302 | | |
303 | 0 | case AF_INET6: |
304 | 0 | fr_strerror_printf("Failed resolving \"%s\" to IPv6 address: %s", |
305 | 0 | hostname, gai_strerror(ret)); |
306 | 0 | return -1; |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | 0 | for (ai = res; ai; ai = ai->ai_next) { |
311 | 0 | if ((af == ai->ai_family) || (af == AF_UNSPEC)) break; |
312 | 0 | if (!alt && fallback && ((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6))) alt = ai; |
313 | 0 | } |
314 | |
|
315 | 0 | if (!ai) ai = alt; |
316 | 0 | if (!ai) { |
317 | 0 | fr_strerror_printf("Failed resolving \"%s\": No records matching requested address family returned", |
318 | 0 | hostname); |
319 | 0 | freeaddrinfo(res); |
320 | 0 | return -1; |
321 | 0 | } |
322 | | |
323 | 0 | ret = fr_ipaddr_from_sockaddr(out, NULL, (struct sockaddr_storage *)ai->ai_addr, ai->ai_addrlen); |
324 | 0 | freeaddrinfo(res); |
325 | 0 | if (ret < 0) { |
326 | 0 | fr_strerror_const("Failed converting sockaddr to ipaddr"); |
327 | 0 | return -1; |
328 | 0 | } |
329 | | |
330 | 0 | return 0; |
331 | 0 | } |
332 | | |
333 | | /** Perform reverse resolution of an IP address |
334 | | * |
335 | | * Attempt to resolve an IP address to a DNS record (if dns lookups are enabled). |
336 | | * |
337 | | * @param[in] src address to resolve. |
338 | | * @param[out] out Where to write the resulting hostname. |
339 | | * @param[in] outlen length of the output buffer. |
340 | | */ |
341 | | char const *fr_inet_ntoh(fr_ipaddr_t const *src, char *out, size_t outlen) |
342 | 0 | { |
343 | 0 | struct sockaddr_storage ss; |
344 | 0 | int error; |
345 | 0 | socklen_t salen; |
346 | | |
347 | | /* |
348 | | * No DNS lookups |
349 | | */ |
350 | 0 | if (!fr_reverse_lookups) { |
351 | 0 | return inet_ntop(src->af, &(src->addr), out, outlen); |
352 | 0 | } |
353 | | |
354 | 0 | if (fr_ipaddr_to_sockaddr(&ss, &salen, src, 0) < 0) return NULL; |
355 | | |
356 | 0 | if ((error = getnameinfo((struct sockaddr *)&ss, salen, out, outlen, NULL, 0, |
357 | 0 | NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { |
358 | 0 | fr_strerror_printf("fr_inet_ntoh: %s", gai_strerror(error)); |
359 | 0 | return NULL; |
360 | 0 | } |
361 | 0 | return out; |
362 | 0 | } |
363 | | |
364 | | |
365 | | /** Parse a single octet of an IPv4 address string |
366 | | * |
367 | | * @param[out] out Where to write integer. |
368 | | * @param[in] str to parse. |
369 | | * @return |
370 | | * - >= 0 on success (number of bytes parsed of in). |
371 | | * - < 0 on error. |
372 | | */ |
373 | | static int ip_octet_from_str(uint32_t *out, char const *str) |
374 | 2.33k | { |
375 | 2.33k | uint32_t octet; |
376 | 2.33k | char const *p = str; |
377 | | |
378 | 2.33k | if ((*p < '0') || (*p > '9')) return -1; |
379 | | |
380 | 2.03k | octet = 0; |
381 | | |
382 | 4.99k | while ((*p >= '0') && (*p <= '9')) { |
383 | 3.05k | octet *= 10; |
384 | 3.05k | octet += *p - '0'; |
385 | 3.05k | p++; |
386 | | |
387 | 3.05k | if (octet > 255) return -1; |
388 | 3.05k | } |
389 | | |
390 | 1.94k | *out = octet; |
391 | 1.94k | return p - str; |
392 | 2.03k | } |
393 | | |
394 | | /** Parses the network portion of an IPv4 prefix into an in_addr |
395 | | * |
396 | | * @note output is in network order. |
397 | | * |
398 | | * Parses address strings in dotted quad notation. |
399 | | * Unlike inet_pton allows octets to be omitted, in which case their value is considered to be 0. |
400 | | * Unlike inet_aton treats integers as representing the highest octet of an IPv4 address, and |
401 | | * limits them to 255. |
402 | | * |
403 | | * Examples of acceptable strings: |
404 | | * - 192.168.0.0 |
405 | | * - 192.168.0.0/24 |
406 | | * - 192.168/16 |
407 | | * - 192 |
408 | | * - 192/8 |
409 | | * |
410 | | * @param[out] out Where to write parsed address. |
411 | | * @param[in] str to parse. |
412 | | * @return |
413 | | * - >= 0 on success (number of bytes parsed of in). |
414 | | * - < 0 on error. |
415 | | */ |
416 | | static int ip_prefix_addr_from_str(struct in_addr *out, char const *str) |
417 | 1.18k | { |
418 | 1.18k | int shift, length; |
419 | 1.18k | uint32_t octet; |
420 | 1.18k | uint32_t addr; |
421 | 1.18k | char const *p = str; |
422 | | |
423 | 1.18k | addr = 0; |
424 | 1.18k | out->s_addr = 0; |
425 | | |
426 | 2.56k | for (shift = 24; shift >= 0; shift -= 8) { |
427 | 2.33k | length = ip_octet_from_str(&octet, p); |
428 | 2.33k | if (length <= 0) return -1; |
429 | | |
430 | 1.94k | addr |= octet << shift; |
431 | 1.94k | p += length; |
432 | | |
433 | | /* |
434 | | * EOS or / means we're done. |
435 | | */ |
436 | 1.94k | if (!*p || (*p == '/')) break; |
437 | | |
438 | | /* |
439 | | * We require dots between octets. |
440 | | */ |
441 | 1.51k | if (*p != '.') return -1; |
442 | 1.38k | p++; |
443 | 1.38k | } |
444 | | |
445 | 655 | out->s_addr = htonl(addr); |
446 | 655 | return p - str; |
447 | 1.18k | } |
448 | | |
449 | | /** Parse an IPv4 address or IPv4 prefix in presentation format (and others) |
450 | | * |
451 | | * @param[out] out Where to write the ip address value. |
452 | | * @param[in] value to parse, may be: |
453 | | * - dotted quad [+ prefix] |
454 | | * - integer |
455 | | * - octal number |
456 | | * - '*' (INADDR_ANY) |
457 | | * - FQDN if resolve is true. |
458 | | * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1. |
459 | | * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value as a hostname. |
460 | | * @param[in] fallback to IPv6 resolution if no A records can be found. |
461 | | * @param[in] mask_bits If true, set address bits to zero. |
462 | | * @return |
463 | | * - 0 if ip address was parsed successfully. |
464 | | * - -1 on failure. |
465 | | */ |
466 | | int fr_inet_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback, bool mask_bits) |
467 | 3.48k | { |
468 | 3.48k | char *p; |
469 | 3.48k | unsigned int mask; |
470 | 3.48k | char const *end; |
471 | 3.48k | char *eptr; |
472 | 3.48k | char buffer[256]; /* As per RFC1035 */ |
473 | 3.48k | int ret; |
474 | | |
475 | | /* |
476 | | * Zero out output so we don't have invalid fields |
477 | | * like scope_id hanging around with garbage values. |
478 | | */ |
479 | 3.48k | memset(out, 0, sizeof(*out)); |
480 | | |
481 | 3.48k | end = value + inlen; |
482 | 3.48k | while ((value < end) && isspace((uint8_t) *value)) value++; |
483 | 3.48k | if (value == end) { |
484 | 0 | fr_strerror_const("Empty IPv4 address string is invalid"); |
485 | 0 | return -1; |
486 | 0 | } |
487 | 3.48k | inlen = end - value; |
488 | | |
489 | | /* |
490 | | * Copy to intermediary buffer if we were given a length |
491 | | */ |
492 | 3.48k | if (inlen >= 0) { |
493 | 3.48k | if (inlen >= (ssize_t)sizeof(buffer)) { |
494 | 36 | fr_strerror_printf("Invalid IPv4 address string \"%pV\"", fr_box_strvalue_len(value, inlen)); |
495 | 36 | return -1; |
496 | 36 | } |
497 | 3.44k | memcpy(buffer, value, inlen); |
498 | 3.44k | buffer[inlen] = '\0'; |
499 | 3.44k | value = buffer; |
500 | 3.44k | } |
501 | | |
502 | 3.44k | p = strchr(value, '/'); |
503 | | |
504 | | /* |
505 | | * 192.0.2.2 is parsed as if it was /32 |
506 | | */ |
507 | 3.44k | if (!p) { |
508 | 2.22k | out->prefix = 32; |
509 | 2.22k | out->af = AF_INET; |
510 | | |
511 | | /* |
512 | | * Allow '*' as the wildcard address usually 0.0.0.0 |
513 | | */ |
514 | 2.22k | if ((value[0] == '*') && (value[1] == '\0')) { |
515 | 99 | out->addr.v4.s_addr = htonl(INADDR_ANY); |
516 | | |
517 | | /* |
518 | | * Convert things which are obviously integers to IP addresses |
519 | | * |
520 | | * We assume the number is the bigendian representation of the |
521 | | * IP address. |
522 | | */ |
523 | 2.12k | } else if (is_integer(value) || ((value[0] == '0') && (value[1] == 'x'))) { |
524 | 630 | out->addr.v4.s_addr = htonl(strtoul(value, NULL, 0)); |
525 | | |
526 | 1.49k | } else if (!resolve) { |
527 | 1.49k | unsigned int a, b, c, d; |
528 | 1.49k | int num; |
529 | 1.49k | char rest; |
530 | | |
531 | 1.49k | a = b = c = d = 0; |
532 | | |
533 | 1.49k | num = sscanf(value, "%u.%u.%u.%u%c", &a, &b, &c, &d, &rest); |
534 | 1.49k | if ((num == 0) || (num == 5) || |
535 | 1.49k | (a > 255) || (b > 255) || (c > 255) || (d > 255)) { |
536 | 1.02k | fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value); |
537 | 1.02k | return -1; |
538 | 1.02k | } |
539 | | |
540 | 469 | out->addr.v4.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); |
541 | | |
542 | 469 | } else if (fr_inet_hton(out, AF_INET, value, fallback) < 0) return -1; |
543 | | |
544 | 1.19k | return 0; |
545 | 2.22k | } |
546 | | |
547 | | /* |
548 | | * Otherwise parse the prefix |
549 | | */ |
550 | 1.22k | if ((size_t)(p - value) >= INET_ADDRSTRLEN) { |
551 | 42 | fr_strerror_printf("Invalid IPv4 address string \"%s\"", value); |
552 | 42 | return -1; |
553 | 42 | } |
554 | | |
555 | | /* |
556 | | * Copy the IP portion into a temporary buffer if we haven't already. |
557 | | */ |
558 | 1.18k | if (inlen < 0) memcpy(buffer, value, p - value); |
559 | | |
560 | | /* |
561 | | * We need a special function here, as inet_pton doesn't like |
562 | | * address strings with octets omitted, and inet_aton treats |
563 | | * 127 as an integer value, and sets the lowest octet of the |
564 | | * prefix to 127 instead of the highest. |
565 | | * |
566 | | * @todo we should allow hostnames to be parsed as prefixes. |
567 | | */ |
568 | 1.18k | buffer[p - value] = '\0'; |
569 | 1.18k | ret = ip_prefix_addr_from_str(&out->addr.v4, buffer); |
570 | 1.18k | buffer[p - value] = '/'; /* Set back to '/' to produce proper errors */ |
571 | | |
572 | 1.18k | if (ret <= 0) { |
573 | 525 | fr_strerror_printf("Failed to parse IPv4 prefix string \"%s\"", value); |
574 | 525 | return -1; |
575 | 525 | } |
576 | | |
577 | 655 | mask = strtoul(p + 1, &eptr, 10); |
578 | 655 | if (mask > 32) { |
579 | 179 | fr_strerror_printf("Invalid IPv4 mask length \"%s\". Should be between 0-32", p); |
580 | 179 | return -1; |
581 | 179 | } |
582 | | |
583 | 476 | if (eptr[0] != '\0') { |
584 | 106 | fr_strerror_printf("Failed to parse IPv4 prefix string \"%s\", " |
585 | 106 | "got garbage after mask length \"%s\"", value, eptr); |
586 | 106 | return -1; |
587 | 106 | } |
588 | | |
589 | 370 | if (mask_bits && (mask < 32)) { |
590 | 270 | out->addr.v4 = fr_inaddr_mask(&out->addr.v4, mask); |
591 | 270 | } |
592 | | |
593 | 370 | out->prefix = (uint8_t) mask; |
594 | 370 | out->af = AF_INET; |
595 | | |
596 | 370 | return 0; |
597 | 476 | } |
598 | | |
599 | | /** Parse an IPv6 address or IPv6 prefix in presentation format (and others) |
600 | | * |
601 | | * @param[out] out Where to write the ip address value. |
602 | | * @param[in] value to parse, may be: |
603 | | * - IPv6 hexits [+ prefix]. |
604 | | * - '*' wildcard. |
605 | | * - FQDN if resolve is true. |
606 | | * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1. |
607 | | * @param[in] resolve If true and value doesn't look like an IP address, |
608 | | * try and resolve value as a hostname. |
609 | | * @param[in] fallback to IPv4 resolution if no AAAA records can be found. |
610 | | * @param[in] mask If true, set address bits to zero. |
611 | | * @return |
612 | | * - 0 if ip address was parsed successfully. |
613 | | * - -1 on failure. |
614 | | */ |
615 | | int fr_inet_pton6(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback, bool mask) |
616 | 167 | { |
617 | 167 | char *p; |
618 | 167 | char const *end; |
619 | 167 | unsigned int prefix; |
620 | 167 | char *eptr; |
621 | 167 | char buffer[256]; /* As per RFC1035 */ |
622 | 167 | int ret; |
623 | | |
624 | | /* |
625 | | * Zero out output so we don't have fields |
626 | | * like scope_id hanging around with garbage values. |
627 | | */ |
628 | 167 | memset(out, 0, sizeof(*out)); |
629 | | |
630 | 167 | if (inlen < 0) inlen = strlen(value); |
631 | | |
632 | 167 | end = value + inlen; |
633 | 167 | while ((value < end) && isspace((uint8_t) *value)) value++; |
634 | 167 | if (value == end) { |
635 | 0 | fr_strerror_const("Empty IPv6 address string is invalid"); |
636 | 0 | return -1; |
637 | 0 | } |
638 | 167 | inlen = end - value; /* always >0 due to the above check for value==end */ |
639 | | |
640 | | /* |
641 | | * Copy to intermediary buffer. |
642 | | */ |
643 | 167 | if (inlen >= (ssize_t)sizeof(buffer)) { |
644 | 37 | fr_strerror_printf("Invalid IPv6 address string \"%pV\"", fr_box_strvalue_len(value, inlen)); |
645 | 37 | return -1; |
646 | 37 | } |
647 | | |
648 | 130 | memcpy(buffer, value, inlen); |
649 | 130 | buffer[inlen] = '\0'; |
650 | 130 | value = buffer; |
651 | | |
652 | 130 | p = strchr(value, '/'); |
653 | 130 | if (!p) { |
654 | 49 | out->prefix = 128; |
655 | 49 | out->af = AF_INET6; |
656 | | |
657 | | /* |
658 | | * Allow scopes for non-prefix values. |
659 | | */ |
660 | 49 | p = strchr(value, '%'); |
661 | 49 | if (p) *(p++) = '\0'; |
662 | | |
663 | | /* |
664 | | * Allow '*' as the wildcard address |
665 | | */ |
666 | 49 | if ((value[0] == '*') && (value[1] == '\0')) { |
667 | 17 | out->addr.v6 = (struct in6_addr)IN6ADDR_ANY_INIT; |
668 | 32 | } else if (!resolve) { |
669 | 32 | if (inet_pton(AF_INET6, value, out->addr.v6.s6_addr) <= 0) { |
670 | 29 | fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value); |
671 | 29 | return -1; |
672 | 29 | } |
673 | 32 | } else if (fr_inet_hton(out, AF_INET6, value, fallback) < 0) return -1; |
674 | | |
675 | | /* |
676 | | * No scope, or just '%'. That's fine. |
677 | | */ |
678 | 20 | if (!p || !*p) return 0; |
679 | | |
680 | | /* |
681 | | * Parse scope. |
682 | | */ |
683 | 3 | prefix = strtoul(p, &eptr, 10); |
684 | 3 | if (prefix > UINT32_MAX) { |
685 | 0 | fr_strerror_printf("Invalid scope ID \"%s\". Should be between 0-2^32-1", p); |
686 | 0 | return -1; |
687 | 0 | } |
688 | 3 | if (eptr[0] != '\0') { |
689 | 1 | fr_strerror_printf("Failed to parse scope \"%s\", " |
690 | 1 | "got garbage after numerical scope value \"%s\"", p, eptr); |
691 | 1 | return -1; |
692 | 1 | } |
693 | | |
694 | 2 | return 0; |
695 | 3 | } |
696 | | |
697 | 81 | if ((p - value) >= INET6_ADDRSTRLEN) { |
698 | 1 | fr_strerror_printf("Invalid IPv6 address string \"%s\"", value); |
699 | 1 | return -1; |
700 | 1 | } |
701 | | |
702 | | /* |
703 | | * Copy string to temporary buffer if we didn't do it earlier |
704 | | */ |
705 | 80 | if (inlen < 0) memcpy(buffer, value, p - value); |
706 | | |
707 | 80 | if (!resolve) { |
708 | 80 | buffer[p - value] = '\0'; |
709 | 80 | ret = inet_pton(AF_INET6, buffer, out->addr.v6.s6_addr); |
710 | 80 | buffer[p - value] = '/'; |
711 | 80 | if (ret <= 0) { |
712 | 10 | fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value); |
713 | 10 | return -1; |
714 | 10 | } |
715 | 80 | } else { |
716 | 0 | buffer[p - value] = '\0'; |
717 | 0 | ret = fr_inet_hton(out, AF_INET6, buffer, fallback); |
718 | 0 | buffer[p - value] = '/'; |
719 | 0 | if (ret < 0) return -1; |
720 | 0 | } |
721 | | |
722 | 70 | prefix = strtoul(p + 1, &eptr, 10); |
723 | 70 | if (prefix > 128) { |
724 | 43 | fr_strerror_printf("Invalid IPv6 mask length \"%s\". Should be between 0-128", p); |
725 | 43 | return -1; |
726 | 43 | } |
727 | 27 | if (eptr[0] != '\0') { |
728 | 4 | fr_strerror_printf("Failed to parse IPv6 address string \"%s\", " |
729 | 4 | "got garbage after mask length \"%s\"", value, eptr); |
730 | 4 | return -1; |
731 | 4 | } |
732 | | |
733 | 23 | if (mask && (prefix < 128)) { |
734 | 22 | struct in6_addr addr; |
735 | | |
736 | 22 | addr = fr_in6addr_mask(&out->addr.v6, prefix); |
737 | 22 | memcpy(out->addr.v6.s6_addr, addr.s6_addr, sizeof(out->addr.v6.s6_addr)); |
738 | 22 | } |
739 | | |
740 | 23 | out->af = AF_INET6; |
741 | 23 | out->prefix = (uint8_t) prefix; |
742 | | |
743 | 23 | return 0; |
744 | 27 | } |
745 | | |
746 | | /** Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser |
747 | | * |
748 | | * @param[out] out Where to write the ip address value. |
749 | | * @param[in] value to parse. |
750 | | * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1. |
751 | | * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value |
752 | | * as a hostname. |
753 | | * @param[in] af If the address type is not obvious from the format, and resolve is true, |
754 | | * the DNS record (A or AAAA) we require. Also controls which parser we pass |
755 | | * the address to if we have no idea what it is. |
756 | | * - AF_UNSPEC - Use the server default IP family. |
757 | | * - AF_INET - Treat value as an IPv4 address. |
758 | | * - AF_INET6 - Treat value as in IPv6 address. |
759 | | * @param[in] mask If true, set address bits to zero. |
760 | | * @return |
761 | | * - 0 if ip address was parsed successfully. |
762 | | * - -1 on failure. |
763 | | */ |
764 | | int fr_inet_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve, bool mask) |
765 | 156 | { |
766 | 156 | size_t len, i; |
767 | 156 | bool hostname = true; |
768 | 156 | bool ipv4 = true; |
769 | 156 | bool ipv6 = true; |
770 | 156 | char const *end; |
771 | | |
772 | 156 | end = value + inlen; |
773 | 156 | while ((value < end) && isspace((uint8_t) *value)) value++; |
774 | 156 | if (value == end) { |
775 | 0 | fr_strerror_const("Empty IPv4 address string is invalid"); |
776 | 0 | return -1; |
777 | 0 | } |
778 | 156 | inlen = end - value; |
779 | | |
780 | 156 | len = (inlen >= 0) ? (size_t)inlen : strlen(value); |
781 | | |
782 | 2.17k | for (i = 0; i < len; i++) { |
783 | | /* |
784 | | * These are valid for IPv4, IPv6, and host names. |
785 | | */ |
786 | 2.09k | if ((value[i] >= '0') && (value[i] <= '9')) { |
787 | 899 | continue; |
788 | 899 | } |
789 | | |
790 | | /* |
791 | | * These are invalid for IPv4, but OK for IPv6 |
792 | | * and host names. |
793 | | */ |
794 | 1.19k | if ((value[i] >= 'a') && (value[i] <= 'f')) { |
795 | 388 | ipv4 = false; |
796 | 388 | continue; |
797 | 388 | } |
798 | | |
799 | | /* |
800 | | * These are invalid for IPv4, but OK for IPv6 |
801 | | * and host names. |
802 | | */ |
803 | 805 | if ((value[i] >= 'A') && (value[i] <= 'F')) { |
804 | 216 | ipv4 = false; |
805 | 216 | continue; |
806 | 216 | } |
807 | | |
808 | | /* |
809 | | * This is only valid for IPv6 addresses. |
810 | | */ |
811 | 589 | if (value[i] == ':') { |
812 | 247 | ipv4 = false; |
813 | 247 | hostname = false; |
814 | 247 | continue; |
815 | 247 | } |
816 | | |
817 | | /* |
818 | | * Valid for IPv4 and host names, not for IPv6. |
819 | | */ |
820 | 342 | if (value[i] == '.') { |
821 | 219 | ipv6 = false; |
822 | 219 | continue; |
823 | 219 | } |
824 | | |
825 | | /* |
826 | | * Netmasks are allowed by us, and MUST come at |
827 | | * the end of the address. |
828 | | */ |
829 | 123 | if (value[i] == '/') { |
830 | 74 | break; |
831 | 74 | } |
832 | | |
833 | | /* |
834 | | * Any characters other than what are checked for |
835 | | * above can't be IPv4 or IPv6 addresses. |
836 | | */ |
837 | 49 | ipv4 = false; |
838 | 49 | ipv6 = false; |
839 | 49 | } |
840 | | |
841 | | /* |
842 | | * It's not an IPv4 or IPv6 address. It MUST be a host |
843 | | * name. |
844 | | */ |
845 | 156 | if (!ipv4 && !ipv6) { |
846 | | /* |
847 | | * Not an IPv4 or IPv6 address, and we weren't |
848 | | * asked to do DNS resolution, we can't do it. |
849 | | */ |
850 | 20 | if (!resolve) { |
851 | 20 | fr_strerror_const("Not IPv4/6 address, and asked not to resolve"); |
852 | 20 | return -1; |
853 | 20 | } |
854 | | |
855 | | /* |
856 | | * It's not a hostname, either, so bail out |
857 | | * early. |
858 | | */ |
859 | 0 | if (!hostname) { |
860 | 0 | fr_strerror_const("Invalid address"); |
861 | 0 | return -1; |
862 | 0 | } |
863 | 0 | } |
864 | | |
865 | | /* |
866 | | * The name has a ':' in it. Therefore it must be an |
867 | | * IPv6 address. Error out if the caller specified IPv4. |
868 | | * Otherwise, force IPv6. |
869 | | */ |
870 | 136 | if (ipv6 && !hostname) { |
871 | 35 | if (af == AF_INET) { |
872 | 0 | fr_strerror_const("Invalid address"); |
873 | 0 | return -1; |
874 | 0 | } |
875 | | |
876 | 35 | af = AF_INET6; |
877 | 35 | } |
878 | | |
879 | | /* |
880 | | * Use whatever the caller specified, OR what we |
881 | | * insinuated above from looking at the name string. |
882 | | */ |
883 | 136 | switch (af) { |
884 | 101 | case AF_UNSPEC: |
885 | 101 | return fr_inet_pton4(out, value, inlen, resolve, true, mask); |
886 | | |
887 | 0 | case AF_INET: |
888 | 0 | return fr_inet_pton4(out, value, inlen, resolve, false, mask); |
889 | | |
890 | 35 | case AF_INET6: |
891 | 35 | return fr_inet_pton6(out, value, inlen, resolve, false, mask); |
892 | | |
893 | 0 | default: |
894 | 0 | break; |
895 | 136 | } |
896 | | |
897 | | /* |
898 | | * No idea what it is... |
899 | | */ |
900 | 0 | fr_strerror_printf("Invalid address family %i", af); |
901 | 0 | return -1; |
902 | 136 | } |
903 | | |
904 | | /** Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port) |
905 | | * |
906 | | * @param[out] out Where to write the ip address value. |
907 | | * @param[out] port_out Where to write the port (0 if no port found). |
908 | | * @param[in] value to parse. |
909 | | * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1. |
910 | | * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value |
911 | | * as a hostname. |
912 | | * @param[in] af If the address type is not obvious from the format, and resolve is true, |
913 | | * the DNS record (A or AAAA) we require. Also controls which parser we pass |
914 | | * the address to if we have no idea what it is. |
915 | | * - AF_UNSPEC - Use the server default IP family. |
916 | | * - AF_INET - Treat value as an IPv4 address. |
917 | | * - AF_INET6 - Treat value as in IPv6 address. |
918 | | * @param[in] mask If true, set address bits to zero. |
919 | | * @return |
920 | | * - 0 if ip address was parsed successfully. |
921 | | * - -1 on failure. |
922 | | */ |
923 | | int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, |
924 | | ssize_t inlen, int af, bool resolve, bool mask) |
925 | 0 | { |
926 | 0 | char const *p = value, *q; |
927 | 0 | char *end; |
928 | 0 | unsigned long port; |
929 | 0 | char buffer[6]; |
930 | 0 | size_t len; |
931 | |
|
932 | 0 | *port_out = 0; |
933 | |
|
934 | 0 | len = (inlen >= 0) ? (size_t)inlen : strlen(value); |
935 | |
|
936 | 0 | if (*p == '[') { |
937 | 0 | if (!(q = memchr(p + 1, ']', len - 1))) { |
938 | 0 | fr_strerror_const("Missing closing ']' for IPv6 address"); |
939 | 0 | return -1; |
940 | 0 | } |
941 | | |
942 | | /* |
943 | | * inet_pton doesn't like the address being wrapped in [] |
944 | | */ |
945 | 0 | if (fr_inet_pton6(out, p + 1, (q - p) - 1, false, false, mask) < 0) return -1; |
946 | | |
947 | 0 | if (q[1] == ':') { |
948 | 0 | q++; |
949 | 0 | goto do_port; |
950 | 0 | } |
951 | | |
952 | 0 | return 0; |
953 | 0 | } |
954 | | |
955 | | /* |
956 | | * Host, IPv4 or IPv6 with no port |
957 | | */ |
958 | 0 | q = memchr(p, ':', len); |
959 | 0 | if (!q) return fr_inet_pton(out, p, len, af, resolve, mask); |
960 | | |
961 | | /* |
962 | | * IPv4 or host, with port |
963 | | */ |
964 | 0 | if (fr_inet_pton(out, p, (q - p), af, resolve, mask) < 0) return -1; |
965 | 0 | do_port: |
966 | | /* |
967 | | * Valid ports are a maximum of 5 digits, so if the |
968 | | * input length indicates there are more than 5 chars |
969 | | * after the ':' then there's an issue. |
970 | | */ |
971 | 0 | if (len > (size_t) ((q + sizeof(buffer)) - value)) { |
972 | 0 | error: |
973 | 0 | fr_strerror_const("IP string contains trailing garbage after port delimiter"); |
974 | 0 | return -1; |
975 | 0 | } |
976 | | |
977 | 0 | p = q + 1; /* Move to first digit */ |
978 | |
|
979 | 0 | strlcpy(buffer, p, (len - (p - value)) + 1); |
980 | 0 | port = strtoul(buffer, &end, 10); |
981 | 0 | if (*end != '\0') goto error; /* Trailing garbage after integer */ |
982 | | |
983 | 0 | if ((port > UINT16_MAX) || (port == 0)) { |
984 | 0 | fr_strerror_printf("Port %lu outside valid port range 1-" STRINGIFY(UINT16_MAX), port); |
985 | 0 | return -1; |
986 | 0 | } |
987 | 0 | *port_out = port; |
988 | |
|
989 | 0 | return 0; |
990 | 0 | } |
991 | | |
992 | | /** Print the address portion of a #fr_ipaddr_t |
993 | | * |
994 | | * @note Includes the textual scope_id name (eth0, en0 etc...) if supported. |
995 | | * |
996 | | * @param[out] out Where to write the resulting IP string. |
997 | | * Should be at least FR_IPADDR_STRLEN bytes. |
998 | | * @param[in] outlen of output buffer. |
999 | | * @param[in] addr to convert to presentation format. |
1000 | | * @return |
1001 | | * - NULL on error (use fr_syserror(errno)). |
1002 | | * - a pointer to out on success. |
1003 | | */ |
1004 | | char *fr_inet_ntop(char out[static FR_IPADDR_STRLEN], size_t outlen, fr_ipaddr_t const *addr) |
1005 | 1.11k | { |
1006 | 1.11k | char *p; |
1007 | 1.11k | size_t len; |
1008 | | |
1009 | 1.11k | out[0] = '\0'; |
1010 | | |
1011 | 1.11k | if (inet_ntop(addr->af, &addr->addr, out, outlen) == NULL) { |
1012 | 0 | fr_strerror_printf("%s", fr_syserror(errno)); |
1013 | 0 | return NULL; |
1014 | 0 | } |
1015 | | |
1016 | 1.11k | if ((addr->af == AF_INET) || (addr->scope_id == 0)) return out; |
1017 | | |
1018 | 0 | p = out + strlen(out); |
1019 | |
|
1020 | 0 | #ifdef WITH_IFINDEX_NAME_RESOLUTION |
1021 | 0 | { |
1022 | 0 | char buffer[IFNAMSIZ]; |
1023 | 0 | char *ifname; |
1024 | |
|
1025 | 0 | ifname = fr_ifname_from_ifindex(buffer, addr->scope_id); |
1026 | 0 | if (ifname) { |
1027 | 0 | len = snprintf(p, outlen - (p - out), "%%%s", ifname); |
1028 | 0 | if (is_truncated(len + (p - out), outlen)) { |
1029 | 0 | fr_strerror_printf("Address buffer too small, needed %zu bytes, have %zu bytes", |
1030 | 0 | (p - out) + len, outlen); |
1031 | 0 | return NULL; |
1032 | 0 | } |
1033 | 0 | return out; |
1034 | 0 | } |
1035 | |
|
1036 | 0 | } |
1037 | 0 | #endif |
1038 | | |
1039 | 0 | len = snprintf(p, outlen - (p - out), "%%%u", addr->scope_id); |
1040 | 0 | if (is_truncated(len + (p - out), outlen)) { |
1041 | 0 | fr_strerror_printf("Address buffer too small, needed %zu bytes, have %zu bytes", |
1042 | 0 | (p - out) + len, outlen); |
1043 | 0 | return NULL; |
1044 | 0 | } |
1045 | | |
1046 | 0 | return out; |
1047 | 0 | } |
1048 | | |
1049 | | /** Print a #fr_ipaddr_t as a CIDR style network prefix |
1050 | | * |
1051 | | * @param[out] out Where to write the resulting prefix string. |
1052 | | * Should be at least FR_IPADDR_PREFIX_STRLEN bytes. |
1053 | | * @param[in] outlen of output buffer. |
1054 | | * @param[in] addr to convert to presentation format. |
1055 | | * @return |
1056 | | * - NULL on error (use fr_syserror(errno)). |
1057 | | * - a pointer to out on success. |
1058 | | */ |
1059 | | char *fr_inet_ntop_prefix(char out[static FR_IPADDR_PREFIX_STRLEN], size_t outlen, fr_ipaddr_t const *addr) |
1060 | 1.11k | { |
1061 | 1.11k | char *p; |
1062 | 1.11k | size_t len; |
1063 | | |
1064 | 1.11k | if (fr_inet_ntop(out, outlen, addr) == NULL) return NULL; |
1065 | | |
1066 | 1.11k | p = out + strlen(out); |
1067 | | |
1068 | 1.11k | len = snprintf(p, outlen - (p - out), "/%i", addr->prefix); |
1069 | 1.11k | if (is_truncated(len + (p - out), outlen)) { |
1070 | 0 | fr_strerror_printf("Address buffer too small, needed %zu bytes, have %zu bytes", |
1071 | 0 | (p - out) + len, outlen); |
1072 | 0 | return NULL; |
1073 | 0 | } |
1074 | | |
1075 | 1.11k | return out; |
1076 | 1.11k | } |
1077 | | |
1078 | | /** Print an interface-id in standard colon notation |
1079 | | * |
1080 | | * @param[out] out Where to write the resulting interface-id string. |
1081 | | * @param[in] outlen of output buffer. |
1082 | | * @param[in] ifid to print. |
1083 | | * @return a pointer to out. |
1084 | | */ |
1085 | | char *fr_inet_ifid_ntop(char *out, size_t outlen, uint8_t const *ifid) |
1086 | 0 | { |
1087 | 0 | snprintf(out, outlen, "%x:%x:%x:%x", |
1088 | 0 | fr_nbo_to_uint16(ifid), fr_nbo_to_uint16(ifid + 2), |
1089 | 0 | fr_nbo_to_uint16(ifid + 4), fr_nbo_to_uint16(ifid + 6)); |
1090 | 0 | return out; |
1091 | 0 | } |
1092 | | |
1093 | | /** Convert interface-id in colon notation to 8 byte binary form |
1094 | | * |
1095 | | * @param[out] out Where to write the binary interface-id. |
1096 | | * @param[in] ifid_str to parse. |
1097 | | * @return a pointer to out. |
1098 | | */ |
1099 | | uint8_t *fr_inet_ifid_pton(uint8_t out[static 8], char const *ifid_str) |
1100 | 30 | { |
1101 | 30 | static char const xdigits[] = "0123456789abcdef"; |
1102 | 30 | char const *p, *pch; |
1103 | 30 | int num_id = 0, val = 0, idx = 0; |
1104 | | |
1105 | 123 | for (p = ifid_str; ; ++p) { |
1106 | 123 | if (*p == ':' || *p == '\0') { |
1107 | 48 | if (num_id <= 0) |
1108 | 8 | return NULL; |
1109 | | |
1110 | | /* |
1111 | | * Drop 'val' into the array. |
1112 | | */ |
1113 | 40 | out[idx] = (val >> 8) & 0xff; |
1114 | 40 | out[idx + 1] = val & 0xff; |
1115 | 40 | if (*p == '\0') { |
1116 | | /* |
1117 | | * Must have all entries before |
1118 | | * end of the string. |
1119 | | */ |
1120 | 14 | if (idx != 6) |
1121 | 12 | return NULL; |
1122 | 2 | break; |
1123 | 14 | } |
1124 | 26 | val = 0; |
1125 | 26 | num_id = 0; |
1126 | 26 | if ((idx += 2) > 6) |
1127 | 1 | return NULL; |
1128 | 75 | } else if ((pch = strchr(xdigits, tolower((uint8_t) *p))) != NULL) { |
1129 | 69 | if (++num_id > 4) |
1130 | 1 | return NULL; |
1131 | | /* |
1132 | | * Dumb version of 'scanf' |
1133 | | */ |
1134 | 68 | val <<= 4; |
1135 | 68 | val |= (pch - xdigits); |
1136 | 68 | } else |
1137 | 6 | return NULL; |
1138 | 123 | } |
1139 | 2 | return out; |
1140 | 30 | } |
1141 | | |
1142 | | #ifdef SIOCGIFADDR |
1143 | | /** Retrieve the primary IP address associated with an interface |
1144 | | * |
1145 | | * @param[out] out The primary IP address associated with the named interface. |
1146 | | * @param[in] af of IP address to retrieve (AF_INET or AF_INET6). |
1147 | | * @param[in] name of interface. |
1148 | | * @return |
1149 | | * - 0 on success. |
1150 | | * - -1 on failure. |
1151 | | */ |
1152 | | int fr_ipaddr_from_ifname(fr_ipaddr_t *out, int af, char const *name) |
1153 | | { |
1154 | | int fd; |
1155 | | struct ifreq if_req; |
1156 | | fr_ipaddr_t ipaddr; |
1157 | | |
1158 | | memset(&if_req, 0, sizeof(if_req)); |
1159 | | memset(out, 0, sizeof(*out)); |
1160 | | |
1161 | | /* |
1162 | | * Set the interface we're resolving, and the address family. |
1163 | | */ |
1164 | | if_req.ifr_addr.sa_family = af; |
1165 | | strlcpy(if_req.ifr_name, name, sizeof(if_req.ifr_name)); |
1166 | | |
1167 | | fd = socket(AF_INET, SOCK_DGRAM, 0); |
1168 | | if (fd < 0) { |
1169 | | fr_strerror_printf("Failed opening temporary socket for SIOCGIFADDR: %s", fr_syserror(errno)); |
1170 | | error: |
1171 | | close(fd); |
1172 | | return -1; |
1173 | | } |
1174 | | if (ioctl(fd, SIOCGIFADDR, &if_req) < 0) { |
1175 | | fr_strerror_printf("Failed determining address for interface %s: %s", name, fr_syserror(errno)); |
1176 | | goto error; |
1177 | | } |
1178 | | |
1179 | | /* |
1180 | | * There's nothing in the ifreq struct that gives us the length |
1181 | | * of the sockaddr struct, so we just use sizeof here. |
1182 | | * sockaddr2ipaddr uses the address family anyway, so we should |
1183 | | * be OK. |
1184 | | */ |
1185 | | if (fr_ipaddr_from_sockaddr(&ipaddr, NULL, |
1186 | | (struct sockaddr_storage *)&if_req.ifr_addr, |
1187 | | sizeof(if_req.ifr_addr)) < 0) goto error; |
1188 | | *out = ipaddr; |
1189 | | |
1190 | | close(fd); |
1191 | | |
1192 | | return 0; |
1193 | | } |
1194 | | #else |
1195 | | int fr_ipaddr_from_ifname(UNUSED fr_ipaddr_t *out, UNUSED int af, char const *name) |
1196 | 0 | { |
1197 | 0 | fr_strerror_printf("No support for SIOCGIFADDR, can't determine IP address of %s", name); |
1198 | 0 | return -1; |
1199 | 0 | } |
1200 | | #endif |
1201 | | |
1202 | | #ifdef WITH_IFINDEX_NAME_RESOLUTION |
1203 | | /** Resolve ifindex to interface name |
1204 | | * |
1205 | | * @param[out] out Buffer to use to store the name, must be at least IFNAMSIZ bytes. |
1206 | | * @param[in] ifindex to resolve to name. |
1207 | | * @return |
1208 | | * - NULL on error. |
1209 | | * - a pointer to out on success. |
1210 | | */ |
1211 | | char *fr_ifname_from_ifindex(char out[static IFNAMSIZ], int ifindex) |
1212 | 0 | { |
1213 | 0 | #ifdef HAVE_IF_INDEXTONAME |
1214 | 0 | if (!if_indextoname(ifindex, out)) { |
1215 | 0 | fr_strerror_printf("Failed resolving interface index %i to name", ifindex); |
1216 | 0 | return NULL; |
1217 | 0 | } |
1218 | | #else |
1219 | | struct ifreq if_req; |
1220 | | int fd; |
1221 | | |
1222 | | memset(&if_req, 0, sizeof(if_req)); |
1223 | | if_req.ifr_ifindex = ifindex; |
1224 | | |
1225 | | fd = socket(AF_INET, SOCK_DGRAM, 0); |
1226 | | if (fd < 0) { |
1227 | | fr_strerror_printf("Failed opening temporary socket for SIOCGIFADDR: %s", fr_syserror(errno)); |
1228 | | error: |
1229 | | close(fd); |
1230 | | return NULL; |
1231 | | } |
1232 | | |
1233 | | /* |
1234 | | * First we resolve the interface index to the interface name |
1235 | | * Which is pretty inefficient, but it seems the only way to |
1236 | | * identify interfaces for SIOCG* operations is with the interface |
1237 | | * name. |
1238 | | */ |
1239 | | if (ioctl(fd, SIOCGIFNAME, &if_req) < 0) { |
1240 | | fr_strerror_printf("Failed resolving interface index %i to name: %s", ifindex, fr_syserror(errno)); |
1241 | | goto error; |
1242 | | } |
1243 | | strlcpy(out, if_req.ifr_name, IFNAMSIZ); |
1244 | | close(fd); |
1245 | | #endif |
1246 | 0 | return out; |
1247 | 0 | } |
1248 | | #endif |
1249 | | |
1250 | | #ifdef WITH_IFINDEX_IPADDR_RESOLUTION |
1251 | | /** Returns the primary IP address for a given interface index |
1252 | | * |
1253 | | * @note Intended to be used with udpfromto (recvfromto) to retrieve the |
1254 | | * source IP address to use when responding to broadcast packets. |
1255 | | * |
1256 | | * @note Will likely be quite slow due to the number of system calls. |
1257 | | * |
1258 | | * @param[out] out Where to write the primary IP address. |
1259 | | * @param[in] fd File descriptor of any datagram or raw socket. |
1260 | | * @param[in] af to get interface for. |
1261 | | * @param[in] ifindex of interface to get IP address for. |
1262 | | * @return |
1263 | | * - 0 on success. |
1264 | | * - -1 on failure. |
1265 | | */ |
1266 | | int fr_ipaddr_from_ifindex(fr_ipaddr_t *out, int fd, int af, int ifindex) |
1267 | | { |
1268 | | struct ifreq if_req; |
1269 | | fr_ipaddr_t ipaddr; |
1270 | | |
1271 | | memset(&if_req, 0, sizeof(if_req)); |
1272 | | memset(out, 0, sizeof(*out)); |
1273 | | |
1274 | | #ifdef SIOCGIFNAME |
1275 | | if_req.ifr_ifindex = ifindex; |
1276 | | /* |
1277 | | * First we resolve the interface index to the interface name |
1278 | | * Which is pretty inefficient, but it seems the only way to |
1279 | | * identify interfaces for SIOCG* operations is with the interface |
1280 | | * name. |
1281 | | */ |
1282 | | if (ioctl(fd, SIOCGIFNAME, &if_req) < 0) { |
1283 | | fr_strerror_printf("Failed resolving interface index %i to name: %s", ifindex, fr_syserror(errno)); |
1284 | | return -1; |
1285 | | } |
1286 | | #elif defined(HAVE_IF_INDEXTONAME) |
1287 | | if (!if_indextoname(ifindex, if_req.ifr_name)) { |
1288 | | fr_strerror_printf("Failed resolving interface index %i to name", ifindex); |
1289 | | return -1; |
1290 | | } |
1291 | | #else |
1292 | | # error Need SIOCGIFNAME or if_indextoname |
1293 | | #endif |
1294 | | |
1295 | | /* |
1296 | | * Name should now be present in if_req, so we just need to |
1297 | | * set the address family. |
1298 | | */ |
1299 | | if_req.ifr_addr.sa_family = af; |
1300 | | |
1301 | | if (ioctl(fd, SIOCGIFADDR, &if_req) < 0) { |
1302 | | fr_strerror_printf("Failed determining address for interface %s: %s", |
1303 | | if_req.ifr_name, fr_syserror(errno)); |
1304 | | return -1; |
1305 | | } |
1306 | | |
1307 | | /* |
1308 | | * There's nothing in the ifreq struct that gives us the length |
1309 | | * of the sockaddr struct, so we just use sizeof here. |
1310 | | * sockaddr2ipaddr uses the address family anyway, so we should |
1311 | | * be OK. |
1312 | | */ |
1313 | | if (fr_ipaddr_from_sockaddr(&ipaddr, NULL, |
1314 | | (struct sockaddr_storage *)&if_req.ifr_addr, |
1315 | | sizeof(if_req.ifr_addr)) < 0) return -1; |
1316 | | *out = ipaddr; |
1317 | | |
1318 | | return 0; |
1319 | | } |
1320 | | #endif |
1321 | | |
1322 | | /** Compare two ip addresses |
1323 | | * |
1324 | | * @param[in] a First ip to compare. |
1325 | | * @param[in] b Second ip to compare. |
1326 | | * @return |
1327 | | * - 1 if a > b |
1328 | | * - 0 if a == b |
1329 | | * - -1 if a < b |
1330 | | * - -2 on error. |
1331 | | */ |
1332 | | int8_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b) |
1333 | 0 | { |
1334 | 0 | int ret; |
1335 | 0 | size_t len; |
1336 | |
|
1337 | 0 | CMP_RETURN(a, b, af); |
1338 | 0 | CMP_RETURN(a, b, prefix); |
1339 | | |
1340 | | /* |
1341 | | * We only care about prefix bytes. |
1342 | | * |
1343 | | * Host bytes should be masked on ingestion |
1344 | | * for prefix types. |
1345 | | */ |
1346 | 0 | len = ((a->prefix + 7) & -8) >> 3; |
1347 | 0 | switch (a->af) { |
1348 | 0 | case AF_INET: |
1349 | 0 | ret = memcmp(&a->addr.v4, &b->addr.v4, len); |
1350 | 0 | return CMP(ret, 0); |
1351 | | |
1352 | 0 | #ifdef HAVE_STRUCT_SOCKADDR_IN6 |
1353 | 0 | case AF_INET6: |
1354 | 0 | CMP_RETURN(a, b, scope_id); |
1355 | 0 | ret = memcmp(&a->addr.v6, &b->addr.v6, len); |
1356 | 0 | return CMP(ret, 0); |
1357 | 0 | #endif |
1358 | | |
1359 | 0 | default: |
1360 | 0 | fr_strerror_printf("Invalid address family %u", a->af); |
1361 | 0 | return -2; |
1362 | 0 | } |
1363 | 0 | } |
1364 | | |
1365 | | /** Convert our internal ip address representation to a sockaddr |
1366 | | * |
1367 | | * @param[out] sa where to write out the sockaddr, |
1368 | | * must be large enough to hold |
1369 | | * sizeof(s6). |
1370 | | * @param[out] salen Length of the sockaddr struct. |
1371 | | * @param[in] ipaddr IP address to convert. |
1372 | | * @param[in] port Port to convert. |
1373 | | |
1374 | | * @return |
1375 | | * - 0 on success. |
1376 | | * - -1 on failure. |
1377 | | */ |
1378 | | int fr_ipaddr_to_sockaddr(struct sockaddr_storage *sa, socklen_t *salen, |
1379 | | fr_ipaddr_t const *ipaddr, uint16_t port) |
1380 | 0 | { |
1381 | 0 | memset(sa, 0, sizeof(*sa)); |
1382 | |
|
1383 | 0 | if (ipaddr->af == AF_INET) { |
1384 | 0 | struct sockaddr_in s4; |
1385 | |
|
1386 | 0 | *salen = sizeof(s4); |
1387 | |
|
1388 | 0 | memset(&s4, 0, sizeof(s4)); |
1389 | 0 | s4.sin_family = AF_INET; |
1390 | 0 | s4.sin_addr = ipaddr->addr.v4; |
1391 | 0 | s4.sin_port = htons(port); |
1392 | 0 | memset(sa, 0, sizeof(*sa)); |
1393 | 0 | memcpy(sa, &s4, sizeof(s4)); |
1394 | |
|
1395 | 0 | #ifdef HAVE_STRUCT_SOCKADDR_IN6 |
1396 | 0 | } else if (ipaddr->af == AF_INET6) { |
1397 | 0 | struct sockaddr_in6 s6; |
1398 | |
|
1399 | 0 | *salen = sizeof(s6); |
1400 | |
|
1401 | 0 | memset(&s6, 0, sizeof(s6)); |
1402 | 0 | s6.sin6_family = AF_INET6; |
1403 | 0 | s6.sin6_addr = ipaddr->addr.v6; |
1404 | 0 | s6.sin6_port = htons(port); |
1405 | 0 | s6.sin6_scope_id = ipaddr->scope_id; |
1406 | 0 | memset(sa, 0, sizeof(*sa)); |
1407 | 0 | memcpy(sa, &s6, sizeof(s6)); |
1408 | 0 | #endif |
1409 | 0 | } else { |
1410 | 0 | fr_strerror_printf("Unsupported address family %d", ipaddr->af); |
1411 | 0 | return -1; |
1412 | 0 | } |
1413 | | |
1414 | 0 | return 0; |
1415 | 0 | } |
1416 | | |
1417 | | /** Convert sockaddr to our internal ip address representation |
1418 | | * |
1419 | | * @param[out] ipaddr Where to write the ipaddr. |
1420 | | * @param[out] port Where to write the port. |
1421 | | * @param[in] sa struct to convert. |
1422 | | * @param[in] salen Length of the sockaddr struct. |
1423 | | * @return |
1424 | | * - 0 on success. |
1425 | | * - -1 on failure. |
1426 | | */ |
1427 | | int fr_ipaddr_from_sockaddr(fr_ipaddr_t *ipaddr, uint16_t *port, |
1428 | | struct sockaddr_storage const *sa, socklen_t salen) |
1429 | 0 | { |
1430 | 0 | memset(ipaddr, 0, sizeof(*ipaddr)); |
1431 | |
|
1432 | 0 | if (sa->ss_family == AF_INET) { |
1433 | 0 | struct sockaddr_in s4; |
1434 | |
|
1435 | 0 | if (salen < sizeof(s4)) { |
1436 | 0 | fr_strerror_const("IPv4 address is too small"); |
1437 | 0 | return 0; |
1438 | 0 | } |
1439 | | |
1440 | 0 | memcpy(&s4, sa, sizeof(s4)); |
1441 | 0 | ipaddr->af = AF_INET; |
1442 | 0 | ipaddr->prefix = 32; |
1443 | 0 | ipaddr->addr.v4 = s4.sin_addr; |
1444 | 0 | if (port) *port = ntohs(s4.sin_port); |
1445 | 0 | ipaddr->scope_id = 0; |
1446 | |
|
1447 | 0 | #ifdef HAVE_STRUCT_SOCKADDR_IN6 |
1448 | 0 | } else if (sa->ss_family == AF_INET6) { |
1449 | 0 | struct sockaddr_in6 s6; |
1450 | |
|
1451 | 0 | if (salen < sizeof(s6)) { |
1452 | 0 | fr_strerror_const("IPv6 address is too small"); |
1453 | 0 | return 0; |
1454 | 0 | } |
1455 | | |
1456 | 0 | memcpy(&s6, sa, sizeof(s6)); |
1457 | 0 | ipaddr->af = AF_INET6; |
1458 | 0 | ipaddr->prefix = 128; |
1459 | 0 | ipaddr->addr.v6 = s6.sin6_addr; |
1460 | 0 | if (port) *port = ntohs(s6.sin6_port); |
1461 | 0 | ipaddr->scope_id = s6.sin6_scope_id; |
1462 | 0 | #endif |
1463 | |
|
1464 | 0 | } else { |
1465 | 0 | fr_strerror_printf("Unsupported address family %d", sa->ss_family); |
1466 | 0 | return -1; |
1467 | 0 | } |
1468 | | |
1469 | 0 | return 0; |
1470 | 0 | } |
1471 | | |
1472 | | char *fr_ipaddr_to_interface(TALLOC_CTX *ctx, fr_ipaddr_t *ipaddr) |
1473 | 0 | { |
1474 | 0 | struct ifaddrs *list = NULL; |
1475 | 0 | struct ifaddrs *i; |
1476 | 0 | char *interface = NULL; |
1477 | | |
1478 | | /* |
1479 | | * Bind manually to an IP used by the named interface. |
1480 | | */ |
1481 | 0 | if (getifaddrs(&list) < 0) return NULL; |
1482 | | |
1483 | 0 | for (i = list; i != NULL; i = i->ifa_next) { |
1484 | 0 | int scope_id; |
1485 | 0 | fr_ipaddr_t my_ipaddr; |
1486 | |
|
1487 | 0 | if (!i->ifa_addr || !i->ifa_name || (ipaddr->af != i->ifa_addr->sa_family)) continue; |
1488 | | |
1489 | 0 | fr_ipaddr_from_sockaddr(&my_ipaddr, NULL, |
1490 | 0 | (struct sockaddr_storage *)i->ifa_addr, sizeof(struct sockaddr_in6)); |
1491 | | |
1492 | | /* |
1493 | | * my_ipaddr will have a scope_id, but the input |
1494 | | * ipaddr won't have one. We therefore set the |
1495 | | * local one to zero, so that we can do correct |
1496 | | * IP address comparisons. |
1497 | | * |
1498 | | * If the comparison succeeds, then we return |
1499 | | * both the interface name, and we update the |
1500 | | * input ipaddr with the correct scope_id. |
1501 | | */ |
1502 | 0 | scope_id = my_ipaddr.scope_id; |
1503 | 0 | my_ipaddr.scope_id = 0; |
1504 | 0 | if (fr_ipaddr_cmp(ipaddr, &my_ipaddr) == 0) { |
1505 | 0 | interface = talloc_strdup(ctx, i->ifa_name); |
1506 | 0 | ipaddr->scope_id = scope_id; |
1507 | 0 | break; |
1508 | 0 | } |
1509 | 0 | } |
1510 | |
|
1511 | 0 | freeifaddrs(list); |
1512 | 0 | return interface; |
1513 | 0 | } |
1514 | | |
1515 | | int fr_interface_to_ipaddr(char const *interface, fr_ipaddr_t *ipaddr, int af, bool link_local) |
1516 | 0 | { |
1517 | 0 | struct ifaddrs *list = NULL; |
1518 | 0 | struct ifaddrs *i; |
1519 | 0 | int ret = -1; |
1520 | |
|
1521 | 0 | if (getifaddrs(&list) < 0) return -1; |
1522 | | |
1523 | 0 | for (i = list; i != NULL; i = i->ifa_next) { |
1524 | 0 | fr_ipaddr_t my_ipaddr; |
1525 | 0 | struct sockaddr_storage sa; |
1526 | |
|
1527 | 0 | if (!i->ifa_addr || !i->ifa_name || ((af != AF_UNSPEC) && (af != i->ifa_addr->sa_family))) continue; |
1528 | 0 | if (strcmp(i->ifa_name, interface) != 0) continue; |
1529 | | |
1530 | 0 | memcpy(&sa, i->ifa_addr, sizeof(struct sockaddr_in6)); /* ifa->ifa_addr may not be aligned properly */ |
1531 | |
|
1532 | 0 | fr_ipaddr_from_sockaddr(&my_ipaddr, NULL, &sa, sizeof(struct sockaddr_in6)); |
1533 | | |
1534 | | /* |
1535 | | * If they ask for a link local address, then give |
1536 | | * it to them. |
1537 | | */ |
1538 | 0 | if (link_local) { |
1539 | 0 | if (my_ipaddr.af != AF_INET6) continue; |
1540 | 0 | if (!IN6_IS_ADDR_LINKLOCAL(&my_ipaddr.addr.v6)) continue; |
1541 | 0 | } |
1542 | | |
1543 | 0 | *ipaddr = my_ipaddr; |
1544 | 0 | ret = 0; |
1545 | 0 | break; |
1546 | 0 | } |
1547 | |
|
1548 | 0 | freeifaddrs(list); |
1549 | 0 | return ret; |
1550 | 0 | } |
1551 | | |
1552 | | /* |
1553 | | * AF_PACKET on Linux |
1554 | | * AF_LINK on BSD |
1555 | | */ |
1556 | | #ifndef AF_LINK |
1557 | 0 | #define AF_LINK AF_PACKET |
1558 | | #endif |
1559 | | |
1560 | | int fr_interface_to_ethernet(char const *interface, fr_ethernet_t *ethernet) |
1561 | 0 | { |
1562 | 0 | struct ifaddrs *list = NULL; |
1563 | 0 | struct ifaddrs *i; |
1564 | 0 | int ret = -1; |
1565 | |
|
1566 | 0 | if (getifaddrs(&list) < 0) return -1; |
1567 | | |
1568 | 0 | for (i = list; i != NULL; i = i->ifa_next) { |
1569 | 0 | if (!i->ifa_addr || !i->ifa_name || (i->ifa_addr->sa_family != AF_LINK)) continue; |
1570 | 0 | if (strcmp(i->ifa_name, interface) != 0) continue; |
1571 | | |
1572 | 0 | #if defined(__linux__) || defined(__EMSCRIPTEN__) |
1573 | 0 | { |
1574 | 0 | struct sockaddr_ll *ll; |
1575 | |
|
1576 | 0 | ll = (struct sockaddr_ll *) i->ifa_addr; |
1577 | 0 | if ((ll->sll_hatype != 1) || (ll->sll_halen != 6)) continue; |
1578 | | |
1579 | 0 | memcpy(ethernet->addr, ll->sll_addr, 6); |
1580 | 0 | } |
1581 | | #else |
1582 | | { |
1583 | | struct sockaddr_dl *ll; |
1584 | | |
1585 | | ll = (struct sockaddr_dl *) i->ifa_addr; |
1586 | | if (ll->sdl_alen != 6) continue; |
1587 | | |
1588 | | memcpy(ethernet->addr, LLADDR(ll), 6); |
1589 | | } |
1590 | | #endif |
1591 | 0 | ret = 0; |
1592 | 0 | break; |
1593 | 0 | } |
1594 | |
|
1595 | 0 | freeifaddrs(list); |
1596 | 0 | return ret; |
1597 | 0 | } |