]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network.c
Fall back to getenv() when secure_getenv() is not available
[people/ms/libloc.git] / src / network.c
CommitLineData
3b5f4af2
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
17#include <arpa/inet.h>
18#include <assert.h>
f3e02bc5 19#include <endian.h>
3b5f4af2 20#include <errno.h>
71ff3e69 21#include <stdio.h>
3b5f4af2
MT
22#include <stdlib.h>
23#include <string.h>
24
25#include <loc/libloc.h>
57146963 26#include <loc/country.h>
3b5f4af2 27#include <loc/network.h>
9fc7f001 28#include <loc/private.h>
3b5f4af2
MT
29
30struct loc_network {
31 struct loc_ctx* ctx;
32 int refcount;
33
34 struct in6_addr start_address;
35 unsigned int prefix;
36
37 char country_code[3];
71ff3e69 38 uint32_t asn;
a293f829 39 enum loc_network_flags flags;
3b5f4af2
MT
40};
41
6183c0f2
MT
42static int valid_prefix(struct in6_addr* address, unsigned int prefix) {
43 // The prefix cannot be larger than 128 bits
44 if (prefix > 128)
45 return 1;
46
47 // And the prefix cannot be zero
48 if (prefix == 0)
49 return 1;
50
aa37232a
MT
51 // For IPv4-mapped addresses the prefix has to be 96 or lager
52 if (IN6_IS_ADDR_V4MAPPED(address) && prefix <= 96)
53 return 1;
54
6183c0f2
MT
55 return 0;
56}
57
a82ce8bc
MT
58static struct in6_addr prefix_to_bitmask(unsigned int prefix) {
59 struct in6_addr bitmask;
60
61 for (unsigned int i = 0; i < 16; i++)
62 bitmask.s6_addr[i] = 0;
63
364a2a37 64 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
a82ce8bc
MT
65 if (i >= 8)
66 bitmask.s6_addr[j] = 0xff;
67 else
68 bitmask.s6_addr[j] = 0xff << (8 - i);
69 }
70
71 return bitmask;
72}
73
2a30e4de 74static struct in6_addr make_start_address(const struct in6_addr* address, unsigned int prefix) {
a82ce8bc
MT
75 struct in6_addr a;
76 struct in6_addr bitmask = prefix_to_bitmask(prefix);
77
78 // Perform bitwise AND
79 for (unsigned int i = 0; i < 4; i++)
80 a.s6_addr32[i] = address->s6_addr32[i] & bitmask.s6_addr32[i];
81
82 return a;
83}
84
2a30e4de
MT
85static struct in6_addr make_last_address(const struct in6_addr* address, unsigned int prefix) {
86 struct in6_addr a;
87 struct in6_addr bitmask = prefix_to_bitmask(prefix);
88
89 // Perform bitwise OR
90 for (unsigned int i = 0; i < 4; i++)
c7096aef 91 a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask.s6_addr32[i];
2a30e4de
MT
92
93 return a;
94}
95
3b5f4af2 96LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
10778041 97 struct in6_addr* address, unsigned int prefix) {
3b5f4af2 98 // Address cannot be unspecified
bca87342 99 if (IN6_IS_ADDR_UNSPECIFIED(address)) {
3b5f4af2
MT
100 DEBUG(ctx, "Start address is unspecified\n");
101 return -EINVAL;
102 }
103
104 // Address cannot be loopback
bca87342 105 if (IN6_IS_ADDR_LOOPBACK(address)) {
3b5f4af2
MT
106 DEBUG(ctx, "Start address is loopback address\n");
107 return -EINVAL;
108 }
109
110 // Address cannot be link-local
bca87342 111 if (IN6_IS_ADDR_LINKLOCAL(address)) {
3b5f4af2
MT
112 DEBUG(ctx, "Start address cannot be link-local\n");
113 return -EINVAL;
114 }
115
116 // Address cannot be site-local
bca87342 117 if (IN6_IS_ADDR_SITELOCAL(address)) {
3b5f4af2
MT
118 DEBUG(ctx, "Start address cannot be site-local\n");
119 return -EINVAL;
120 }
121
6183c0f2 122 // Validate the prefix
10778041 123 if (valid_prefix(address, prefix) != 0) {
6183c0f2
MT
124 DEBUG(ctx, "Invalid prefix: %u\n", prefix);
125 return -EINVAL;
126 }
127
3b5f4af2
MT
128 struct loc_network* n = calloc(1, sizeof(*n));
129 if (!n)
130 return -ENOMEM;
131
132 n->ctx = loc_ref(ctx);
133 n->refcount = 1;
134
a82ce8bc 135 // Store the first address in the network
10778041 136 n->start_address = make_start_address(address, prefix);
3b5f4af2
MT
137 n->prefix = prefix;
138
139 DEBUG(n->ctx, "Network allocated at %p\n", n);
140 *network = n;
141 return 0;
142}
143
d3409788
MT
144static int loc_network_address_family(struct loc_network* network) {
145 if (IN6_IS_ADDR_V4MAPPED(&network->start_address))
146 return AF_INET;
147
148 return AF_INET6;
149}
150
3b5f4af2
MT
151LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network,
152 const char* address_string) {
153 struct in6_addr start_address;
b61f14fc 154 unsigned int prefix = 0;
3b5f4af2 155 char* prefix_string;
b61f14fc 156 int r = 1;
3b5f4af2
MT
157
158 // Make a copy of the string to work on it
159 char* buffer = strdup(address_string);
160 address_string = prefix_string = buffer;
161
162 // Split address and prefix
163 address_string = strsep(&prefix_string, "/");
164
b61f14fc
MT
165 // Did we find a prefix?
166 if (prefix_string) {
167 // Convert prefix to integer
168 prefix = strtol(prefix_string, NULL, 10);
3b5f4af2 169
b61f14fc
MT
170 if (prefix) {
171 // Parse the address
2a30e4de 172 r = loc_parse_address(ctx, address_string, &start_address);
aa37232a
MT
173
174 // Map the prefix to IPv6 if needed
175 if (IN6_IS_ADDR_V4MAPPED(&start_address))
176 prefix += 96;
b61f14fc
MT
177 }
178 }
3b5f4af2
MT
179
180 // Free temporary buffer
181 free(buffer);
182
d3409788 183 if (r == 0) {
10778041 184 r = loc_network_new(ctx, network, &start_address, prefix);
3b5f4af2
MT
185 }
186
187 return r;
188}
189
190LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
191 network->refcount++;
192
193 return network;
194}
195
196static void loc_network_free(struct loc_network* network) {
197 DEBUG(network->ctx, "Releasing network at %p\n", network);
198
3b5f4af2
MT
199 loc_unref(network->ctx);
200 free(network);
201}
202
203LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
2a30e4de
MT
204 if (!network)
205 return NULL;
206
3b5f4af2
MT
207 if (--network->refcount > 0)
208 return network;
209
210 loc_network_free(network);
211 return NULL;
212}
213
8fb874e9
MT
214static int format_ipv6_address(const struct in6_addr* address, char* string, size_t length) {
215 const char* ret = inet_ntop(AF_INET6, address, string, length);
d3409788
MT
216 if (!ret)
217 return -1;
218
219 return 0;
220}
221
8fb874e9 222static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) {
d3409788 223 struct in_addr ipv4_address;
8fb874e9 224 ipv4_address.s_addr = address->s6_addr32[3];
d3409788
MT
225
226 const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length);
227 if (!ret)
228 return -1;
229
230 return 0;
231}
232
3b5f4af2 233LOC_EXPORT char* loc_network_str(struct loc_network* network) {
d3409788
MT
234 int r;
235 const size_t length = INET6_ADDRSTRLEN + 4;
3b5f4af2 236
d3409788 237 char* string = malloc(length);
3b5f4af2
MT
238 if (!string)
239 return NULL;
240
aa37232a
MT
241 unsigned int prefix = network->prefix;
242
d3409788
MT
243 int family = loc_network_address_family(network);
244 switch (family) {
245 case AF_INET6:
8fb874e9 246 r = format_ipv6_address(&network->start_address, string, length);
d3409788 247 break;
3b5f4af2 248
d3409788 249 case AF_INET:
8fb874e9 250 r = format_ipv4_address(&network->start_address, string, length);
aa37232a 251 prefix -= 96;
d3409788
MT
252 break;
253
254 default:
255 r = -1;
256 break;
257 }
258
259 if (r) {
260 ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno));
3b5f4af2 261 free(string);
d3409788 262
3b5f4af2
MT
263 return NULL;
264 }
265
266 // Append prefix
aa37232a 267 sprintf(string + strlen(string), "/%u", prefix);
3b5f4af2
MT
268
269 return string;
270}
271
2a30e4de 272LOC_EXPORT int loc_network_match_address(struct loc_network* network, const struct in6_addr* address) {
f50adb09 273 // Address must be larger than the start address
2a30e4de
MT
274 if (in6_addr_cmp(&network->start_address, address) > 0)
275 return 1;
276
277 // Determine the last address in this network
278 struct in6_addr last_address = make_last_address(&network->start_address, network->prefix);
279
280 // Address must be smaller than the last address
0d33b671 281 if (in6_addr_cmp(address, &last_address) > 0)
2a30e4de
MT
282 return 1;
283
284 // The address is inside this network
285 return 0;
286}
287
3b5f4af2
MT
288LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
289 return network->country_code;
290}
291
292LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
901ef882
MT
293 // Set empty country code
294 if (!country_code || !*country_code) {
295 *network->country_code = '\0';
296 return 0;
297 }
298
57146963
MT
299 // Check country code
300 if (!loc_country_code_is_valid(country_code))
3b5f4af2
MT
301 return -EINVAL;
302
a7a3d958 303 loc_country_code_copy(network->country_code, country_code);
3b5f4af2
MT
304
305 return 0;
306}
307
e3f696c1 308LOC_EXPORT int loc_network_match_country_code(struct loc_network* network, const char* country_code) {
57146963
MT
309 // Check country code
310 if (!loc_country_code_is_valid(country_code))
e3f696c1
MT
311 return -EINVAL;
312
313 return (network->country_code[0] == country_code[0])
314 && (network->country_code[1] == country_code[1]);
315}
316
71ff3e69
MT
317LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
318 return network->asn;
319}
320
321LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
322 network->asn = asn;
323
324 return 0;
325}
326
82910b95
MT
327LOC_EXPORT int loc_network_match_asn(struct loc_network* network, uint32_t asn) {
328 return network->asn == asn;
329}
330
a99e7c2b
MT
331LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
332 return network->flags & flag;
333}
334
335LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
336 network->flags |= flag;
337
338 return 0;
339}
340
341LOC_EXPORT int loc_network_match_flag(struct loc_network* network, uint32_t flag) {
342 return loc_network_has_flag(network, flag);
343}
344
f3e02bc5 345LOC_EXPORT int loc_network_to_database_v0(struct loc_network* network, struct loc_database_network_v0* dbobj) {
f3e02bc5 346 // Add country code
a7a3d958 347 loc_country_code_copy(dbobj->country_code, network->country_code);
f3e02bc5
MT
348
349 // Add ASN
71ff3e69 350 dbobj->asn = htobe32(network->asn);
f3e02bc5 351
a99e7c2b 352 // Flags
eee6346d 353 dbobj->flags = htobe16(network->flags);
a99e7c2b 354
f3e02bc5
MT
355 return 0;
356}
357
10778041 358LOC_EXPORT int loc_network_new_from_database_v0(struct loc_ctx* ctx, struct loc_network** network,
39a55353 359 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v0* dbobj) {
a7a3d958
MT
360 char country_code[3];
361
39a55353 362 int r = loc_network_new(ctx, network, address, prefix);
10778041
MT
363 if (r)
364 return r;
365
366 // Import country code
a7a3d958 367 loc_country_code_copy(country_code, dbobj->country_code);
10778041
MT
368
369 r = loc_network_set_country_code(*network, country_code);
370 if (r)
371 return r;
372
373 // Import ASN
374 r = loc_network_set_asn(*network, be32toh(dbobj->asn));
375 if (r)
376 return r;
377
a99e7c2b 378 // Import flags
eee6346d 379 r = loc_network_set_flag(*network, be16toh(dbobj->flags));
a99e7c2b
MT
380 if (r)
381 return r;
382
10778041
MT
383 return 0;
384}
385
3b5f4af2
MT
386struct loc_network_tree {
387 struct loc_ctx* ctx;
388 int refcount;
389
390 struct loc_network_tree_node* root;
391};
392
393struct loc_network_tree_node {
438db08c
MT
394 struct loc_ctx* ctx;
395 int refcount;
396
3b5f4af2
MT
397 struct loc_network_tree_node* zero;
398 struct loc_network_tree_node* one;
399
400 struct loc_network* network;
401};
402
403LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
404 struct loc_network_tree* t = calloc(1, sizeof(*t));
405 if (!t)
406 return -ENOMEM;
407
408 t->ctx = loc_ref(ctx);
409 t->refcount = 1;
410
411 // Create the root node
438db08c
MT
412 int r = loc_network_tree_node_new(ctx, &t->root);
413 if (r) {
414 loc_network_tree_unref(t);
415 return r;
416 }
3b5f4af2
MT
417
418 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
419 *tree = t;
420 return 0;
421}
422
438db08c
MT
423LOC_EXPORT struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
424 return loc_network_tree_node_ref(tree->root);
3b5f4af2
MT
425}
426
427static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
428 struct loc_network_tree_node** n;
429
2a30e4de 430 if (path == 0)
3b5f4af2 431 n = &node->zero;
3eda9cab
MT
432 else
433 n = &node->one;
3b5f4af2
MT
434
435 // If the desired node doesn't exist, yet, we will create it
436 if (*n == NULL) {
438db08c 437 int r = loc_network_tree_node_new(node->ctx, n);
3b5f4af2
MT
438 if (r)
439 return NULL;
440 }
441
442 return *n;
443}
444
39a55353 445static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
3b5f4af2
MT
446 struct loc_network_tree_node* node = tree->root;
447
39a55353 448 for (unsigned int i = 0; i < prefix; i++) {
3b5f4af2 449 // Check if the ith bit is one or zero
2a30e4de 450 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
3b5f4af2
MT
451 }
452
453 return node;
454}
455
456static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
457 int(*filter_callback)(struct loc_network* network, void* data),
458 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
459 int r;
460
461 // Finding a network ends the walk here
462 if (node->network) {
463 if (filter_callback) {
f3e02bc5 464 int f = filter_callback(node->network, data);
3b5f4af2
MT
465 if (f < 0)
466 return f;
467
468 // Skip network if filter function returns value greater than zero
469 if (f > 0)
470 return 0;
471 }
472
f3e02bc5 473 r = callback(node->network, data);
3b5f4af2
MT
474 if (r)
475 return r;
476 }
477
478 // Walk down on the left side of the tree first
479 if (node->zero) {
f3e02bc5 480 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
481 if (r)
482 return r;
483 }
484
485 // Then walk on the other side
486 if (node->one) {
f3e02bc5 487 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
488 if (r)
489 return r;
490 }
491
492 return 0;
493}
494
f3e02bc5
MT
495LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree,
496 int(*filter_callback)(struct loc_network* network, void* data),
497 int(*callback)(struct loc_network* network, void* data), void* data) {
498 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
499}
500
3b5f4af2
MT
501static void loc_network_tree_free(struct loc_network_tree* tree) {
502 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
503
438db08c 504 loc_network_tree_node_unref(tree->root);
3b5f4af2
MT
505
506 loc_unref(tree->ctx);
507 free(tree);
508}
509
510LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
511 if (--tree->refcount > 0)
512 return tree;
513
514 loc_network_tree_free(tree);
515 return NULL;
516}
517
e9b4e2a8 518static int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
519 DEBUG(network->ctx, "Dumping network at %p\n", network);
520
521 char* s = loc_network_str(network);
522 if (!s)
523 return 1;
524
525 INFO(network->ctx, "%s\n", s);
526 free(s);
527
528 return 0;
529}
530
531LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
532 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
533
f3e02bc5 534 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
535}
536
537LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
538 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
539
39a55353
MT
540 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
541 &network->start_address, network->prefix);
3b5f4af2
MT
542 if (!node) {
543 ERROR(tree->ctx, "Could not find a node\n");
544 return -ENOMEM;
545 }
546
547 // Check if node has not been set before
548 if (node->network) {
549 DEBUG(tree->ctx, "There is already a network at this path\n");
c04005bb 550 return -EBUSY;
3b5f4af2
MT
551 }
552
553 // Point node to the network
554 node->network = loc_network_ref(network);
555
556 return 0;
557}
f3e02bc5
MT
558
559static int __loc_network_tree_count(struct loc_network* network, void* data) {
560 size_t* counter = (size_t*)data;
561
562 // Increase the counter for each network
563 counter++;
564
565 return 0;
566}
567
568LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
569 size_t counter = 0;
570
571 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
572 if (r)
573 return r;
574
575 return counter;
576}
940f9c2b
MT
577
578static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
579 size_t counter = 1;
580
581 if (node->zero)
582 counter += __loc_network_tree_count_nodes(node->zero);
583
584 if (node->one)
585 counter += __loc_network_tree_count_nodes(node->one);
586
587 return counter;
588}
589
590LOC_EXPORT size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
591 return __loc_network_tree_count_nodes(tree->root);
592}
438db08c
MT
593
594LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
595 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
596 if (!n)
597 return -ENOMEM;
598
599 n->ctx = loc_ref(ctx);
600 n->refcount = 1;
601
602 n->zero = n->one = NULL;
603
604 DEBUG(n->ctx, "Network node allocated at %p\n", n);
605 *node = n;
606 return 0;
607}
608
609LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
610 if (node)
611 node->refcount++;
612
613 return node;
614}
615
616static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
617 DEBUG(node->ctx, "Releasing network node at %p\n", node);
618
619 if (node->network)
620 loc_network_unref(node->network);
621
622 if (node->zero)
623 loc_network_tree_node_unref(node->zero);
624
625 if (node->one)
626 loc_network_tree_node_unref(node->one);
627
628 loc_unref(node->ctx);
629 free(node);
630}
631
632LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
633 if (!node)
634 return NULL;
635
636 if (--node->refcount > 0)
637 return node;
638
639 loc_network_tree_node_free(node);
640 return NULL;
641}
642
643LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
644 if (index == 0)
645 node = node->zero;
646 else
647 node = node->one;
648
649 if (!node)
650 return NULL;
651
652 return loc_network_tree_node_ref(node);
653}
654
655LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
656 return (!!node->network);
657}
658
659LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
660 return loc_network_ref(node->network);
661}