]> git.ipfire.org Git - location/libloc.git/blame - src/network.c
network: Make loc_network_match_country_code match special countries
[location/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>
19#include <errno.h>
71ff3e69 20#include <stdio.h>
3b5f4af2
MT
21#include <stdlib.h>
22#include <string.h>
23
42f3ccd7
MT
24#ifdef HAVE_ENDIAN_H
25# include <endian.h>
26#endif
27
c12a1385
MT
28#include <libloc/libloc.h>
29#include <libloc/compat.h>
30#include <libloc/country.h>
31#include <libloc/network.h>
32#include <libloc/network-list.h>
33#include <libloc/private.h>
3b5f4af2
MT
34
35struct loc_network {
36 struct loc_ctx* ctx;
37 int refcount;
38
c3068be1 39 int family;
ce4f5752 40 struct in6_addr first_address;
b43edb61 41 struct in6_addr last_address;
3b5f4af2
MT
42 unsigned int prefix;
43
44 char country_code[3];
71ff3e69 45 uint32_t asn;
a293f829 46 enum loc_network_flags flags;
3b5f4af2
MT
47};
48
6183c0f2
MT
49static int valid_prefix(struct in6_addr* address, unsigned int prefix) {
50 // The prefix cannot be larger than 128 bits
51 if (prefix > 128)
52 return 1;
53
54 // And the prefix cannot be zero
55 if (prefix == 0)
56 return 1;
57
aa37232a
MT
58 // For IPv4-mapped addresses the prefix has to be 96 or lager
59 if (IN6_IS_ADDR_V4MAPPED(address) && prefix <= 96)
60 return 1;
61
6183c0f2
MT
62 return 0;
63}
64
a82ce8bc
MT
65static struct in6_addr prefix_to_bitmask(unsigned int prefix) {
66 struct in6_addr bitmask;
67
68 for (unsigned int i = 0; i < 16; i++)
69 bitmask.s6_addr[i] = 0;
70
364a2a37 71 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
a82ce8bc
MT
72 if (i >= 8)
73 bitmask.s6_addr[j] = 0xff;
74 else
75 bitmask.s6_addr[j] = 0xff << (8 - i);
76 }
77
78 return bitmask;
79}
80
b43edb61 81static struct in6_addr make_first_address(const struct in6_addr* address, const struct in6_addr* bitmask) {
a82ce8bc 82 struct in6_addr a;
a82ce8bc
MT
83
84 // Perform bitwise AND
85 for (unsigned int i = 0; i < 4; i++)
b43edb61 86 a.s6_addr32[i] = address->s6_addr32[i] & bitmask->s6_addr32[i];
a82ce8bc
MT
87
88 return a;
89}
90
b43edb61 91static struct in6_addr make_last_address(const struct in6_addr* address, const struct in6_addr* bitmask) {
2a30e4de 92 struct in6_addr a;
2a30e4de
MT
93
94 // Perform bitwise OR
95 for (unsigned int i = 0; i < 4; i++)
b43edb61 96 a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask->s6_addr32[i];
2a30e4de
MT
97
98 return a;
99}
100
3b5f4af2 101LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
10778041 102 struct in6_addr* address, unsigned int prefix) {
3b5f4af2 103 // Address cannot be unspecified
bca87342 104 if (IN6_IS_ADDR_UNSPECIFIED(address)) {
3b5f4af2
MT
105 DEBUG(ctx, "Start address is unspecified\n");
106 return -EINVAL;
107 }
108
109 // Address cannot be loopback
bca87342 110 if (IN6_IS_ADDR_LOOPBACK(address)) {
3b5f4af2
MT
111 DEBUG(ctx, "Start address is loopback address\n");
112 return -EINVAL;
113 }
114
115 // Address cannot be link-local
bca87342 116 if (IN6_IS_ADDR_LINKLOCAL(address)) {
3b5f4af2
MT
117 DEBUG(ctx, "Start address cannot be link-local\n");
118 return -EINVAL;
119 }
120
121 // Address cannot be site-local
bca87342 122 if (IN6_IS_ADDR_SITELOCAL(address)) {
3b5f4af2
MT
123 DEBUG(ctx, "Start address cannot be site-local\n");
124 return -EINVAL;
125 }
126
6183c0f2 127 // Validate the prefix
10778041 128 if (valid_prefix(address, prefix) != 0) {
6183c0f2
MT
129 DEBUG(ctx, "Invalid prefix: %u\n", prefix);
130 return -EINVAL;
131 }
132
3b5f4af2
MT
133 struct loc_network* n = calloc(1, sizeof(*n));
134 if (!n)
135 return -ENOMEM;
136
137 n->ctx = loc_ref(ctx);
138 n->refcount = 1;
139
b43edb61 140 // Store the prefix
3b5f4af2
MT
141 n->prefix = prefix;
142
b43edb61
MT
143 // Convert the prefix into a bitmask
144 struct in6_addr bitmask = prefix_to_bitmask(n->prefix);
145
146 // Store the first and last address in the network
147 n->first_address = make_first_address(address, &bitmask);
148 n->last_address = make_last_address(&n->first_address, &bitmask);
149
c3068be1 150 // Set family
71877f5f 151 n->family = loc_address_family(&n->first_address);
c3068be1 152
3b5f4af2
MT
153 DEBUG(n->ctx, "Network allocated at %p\n", n);
154 *network = n;
155 return 0;
156}
157
158LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network,
159 const char* address_string) {
ce4f5752 160 struct in6_addr first_address;
3b5f4af2 161 char* prefix_string;
26ab419b 162 unsigned int prefix = 128;
13ad6e69
MT
163 int r = -EINVAL;
164
165 DEBUG(ctx, "Attempting to parse network %s\n", address_string);
3b5f4af2
MT
166
167 // Make a copy of the string to work on it
168 char* buffer = strdup(address_string);
169 address_string = prefix_string = buffer;
170
171 // Split address and prefix
172 address_string = strsep(&prefix_string, "/");
173
13ad6e69
MT
174 DEBUG(ctx, " Split into address = %s, prefix = %s\n", address_string, prefix_string);
175
13ad6e69
MT
176 // Parse the address
177 r = loc_parse_address(ctx, address_string, &first_address);
178 if (r) {
179 DEBUG(ctx, "The address could not be parsed\n");
180 goto FAIL;
b61f14fc 181 }
3b5f4af2 182
26ab419b
MT
183 // If a prefix was given, we will try to parse it
184 if (prefix_string) {
185 // Convert prefix to integer
186 prefix = strtol(prefix_string, NULL, 10);
187
188 if (!prefix) {
189 DEBUG(ctx, "The prefix was not parsable: %s\n", prefix_string);
190 goto FAIL;
191 }
192
193 // Map the prefix to IPv6 if needed
194 if (IN6_IS_ADDR_V4MAPPED(&first_address))
195 prefix += 96;
196 }
13ad6e69
MT
197
198FAIL:
3b5f4af2
MT
199 // Free temporary buffer
200 free(buffer);
201
13ad6e69
MT
202 // Exit if the parsing was unsuccessful
203 if (r)
204 return r;
205
13ad6e69
MT
206 // Create a new network
207 return loc_network_new(ctx, network, &first_address, prefix);
3b5f4af2
MT
208}
209
210LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
211 network->refcount++;
212
213 return network;
214}
215
216static void loc_network_free(struct loc_network* network) {
217 DEBUG(network->ctx, "Releasing network at %p\n", network);
218
3b5f4af2
MT
219 loc_unref(network->ctx);
220 free(network);
221}
222
223LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
2a30e4de
MT
224 if (!network)
225 return NULL;
226
3b5f4af2
MT
227 if (--network->refcount > 0)
228 return network;
229
230 loc_network_free(network);
231 return NULL;
232}
233
8fb874e9
MT
234static int format_ipv6_address(const struct in6_addr* address, char* string, size_t length) {
235 const char* ret = inet_ntop(AF_INET6, address, string, length);
d3409788
MT
236 if (!ret)
237 return -1;
238
239 return 0;
240}
241
8fb874e9 242static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) {
d3409788 243 struct in_addr ipv4_address;
8fb874e9 244 ipv4_address.s_addr = address->s6_addr32[3];
d3409788
MT
245
246 const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length);
247 if (!ret)
248 return -1;
249
250 return 0;
251}
252
3b5f4af2 253LOC_EXPORT char* loc_network_str(struct loc_network* network) {
d3409788
MT
254 int r;
255 const size_t length = INET6_ADDRSTRLEN + 4;
3b5f4af2 256
d3409788 257 char* string = malloc(length);
3b5f4af2
MT
258 if (!string)
259 return NULL;
260
aa37232a
MT
261 unsigned int prefix = network->prefix;
262
c3068be1 263 switch (network->family) {
d3409788 264 case AF_INET6:
ce4f5752 265 r = format_ipv6_address(&network->first_address, string, length);
d3409788 266 break;
3b5f4af2 267
d3409788 268 case AF_INET:
ce4f5752 269 r = format_ipv4_address(&network->first_address, string, length);
aa37232a 270 prefix -= 96;
d3409788
MT
271 break;
272
273 default:
274 r = -1;
275 break;
276 }
277
278 if (r) {
279 ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno));
3b5f4af2 280 free(string);
d3409788 281
3b5f4af2
MT
282 return NULL;
283 }
284
285 // Append prefix
aa37232a 286 sprintf(string + strlen(string), "/%u", prefix);
3b5f4af2
MT
287
288 return string;
289}
290
44e5ef71 291LOC_EXPORT int loc_network_address_family(struct loc_network* network) {
c3068be1 292 return network->family;
44e5ef71
MT
293}
294
7fe6a218
MT
295LOC_EXPORT unsigned int loc_network_prefix(struct loc_network* network) {
296 switch (network->family) {
297 case AF_INET6:
298 return network->prefix;
299
300 case AF_INET:
301 return network->prefix - 96;
302 }
303
304 return 0;
305}
306
2b9338ea
MT
307static char* loc_network_format_address(struct loc_network* network, const struct in6_addr* address) {
308 const size_t length = INET6_ADDRSTRLEN;
309
310 char* string = malloc(length);
311 if (!string)
312 return NULL;
313
314 int r = 0;
315
c3068be1 316 switch (network->family) {
2b9338ea
MT
317 case AF_INET6:
318 r = format_ipv6_address(address, string, length);
319 break;
320
321 case AF_INET:
322 r = format_ipv4_address(address, string, length);
323 break;
324
325 default:
326 r = -1;
327 break;
328 }
329
330 if (r) {
331 ERROR(network->ctx, "Could not format IP address to string: %s\n", strerror(errno));
332 free(string);
333
334 return NULL;
335 }
336
337 return string;
338}
339
a1a00053
MT
340LOC_EXPORT const struct in6_addr* loc_network_get_first_address(struct loc_network* network) {
341 return &network->first_address;
342}
343
2b9338ea
MT
344LOC_EXPORT char* loc_network_format_first_address(struct loc_network* network) {
345 return loc_network_format_address(network, &network->first_address);
346}
347
a1a00053
MT
348LOC_EXPORT const struct in6_addr* loc_network_get_last_address(struct loc_network* network) {
349 return &network->last_address;
350}
351
2b9338ea
MT
352LOC_EXPORT char* loc_network_format_last_address(struct loc_network* network) {
353 return loc_network_format_address(network, &network->last_address);
354}
355
2a30e4de 356LOC_EXPORT int loc_network_match_address(struct loc_network* network, const struct in6_addr* address) {
f50adb09 357 // Address must be larger than the start address
ce4f5752 358 if (in6_addr_cmp(&network->first_address, address) > 0)
fc692a58 359 return 0;
2a30e4de 360
2a30e4de 361 // Address must be smaller than the last address
b43edb61 362 if (in6_addr_cmp(&network->last_address, address) < 0)
fc692a58 363 return 0;
2a30e4de
MT
364
365 // The address is inside this network
fc692a58 366 return 1;
2a30e4de
MT
367}
368
3b5f4af2
MT
369LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
370 return network->country_code;
371}
372
373LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
901ef882
MT
374 // Set empty country code
375 if (!country_code || !*country_code) {
376 *network->country_code = '\0';
377 return 0;
378 }
379
57146963
MT
380 // Check country code
381 if (!loc_country_code_is_valid(country_code))
3b5f4af2
MT
382 return -EINVAL;
383
a7a3d958 384 loc_country_code_copy(network->country_code, country_code);
3b5f4af2
MT
385
386 return 0;
387}
388
e3f696c1 389LOC_EXPORT int loc_network_match_country_code(struct loc_network* network, const char* country_code) {
0b27a567
MT
390 // Search for any special flags
391 const int flag = loc_country_special_code_to_flag(country_code);
392
393 // If we found a flag, we will return whether it is set or not
394 if (flag)
395 return loc_network_has_flag(network, flag);
396
57146963
MT
397 // Check country code
398 if (!loc_country_code_is_valid(country_code))
e3f696c1
MT
399 return -EINVAL;
400
0b27a567 401 // Check for an exact match
e3f696c1
MT
402 return (network->country_code[0] == country_code[0])
403 && (network->country_code[1] == country_code[1]);
404}
405
71ff3e69
MT
406LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
407 return network->asn;
408}
409
410LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
411 network->asn = asn;
412
413 return 0;
414}
415
a99e7c2b
MT
416LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
417 return network->flags & flag;
418}
419
420LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
421 network->flags |= flag;
422
423 return 0;
424}
425
af4689bf 426LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* other) {
af4689bf
MT
427 // Compare address
428 int r = in6_addr_cmp(&self->first_address, &other->first_address);
429 if (r)
430 return r;
431
432 // Compare prefix
433 if (self->prefix > other->prefix)
434 return 1;
435 else if (self->prefix < other->prefix)
436 return -1;
437
438 // Both networks are equal
439 return 0;
440}
441
6159d384 442LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
fc692a58
MT
443 // Either of the start addresses must be in the other subnet
444 if (loc_network_match_address(self, &other->first_address))
6159d384
MT
445 return 1;
446
fc692a58 447 if (loc_network_match_address(other, &self->first_address))
6159d384
MT
448 return 1;
449
fc692a58
MT
450 // Or either of the end addresses is in the other subnet
451 if (loc_network_match_address(self, &other->last_address))
6159d384
MT
452 return 1;
453
fc692a58 454 if (loc_network_match_address(other, &self->last_address))
6159d384
MT
455 return 1;
456
457 return 0;
458}
459
33a051e0 460LOC_EXPORT int loc_network_is_subnet(struct loc_network* self, struct loc_network* other) {
cf8d3c64
MT
461 // The prefix must be smaller (this avoids the more complex comparisons later)
462 if (self->prefix > other->prefix)
463 return 0;
464
33a051e0
MT
465 // If the start address of the other network is smaller than this network,
466 // it cannot be a subnet.
1209ff0c 467 if (in6_addr_cmp(&self->first_address, &other->first_address) > 0)
33a051e0
MT
468 return 0;
469
470 // If the end address of the other network is greater than this network,
471 // it cannot be a subnet.
1209ff0c 472 if (in6_addr_cmp(&self->last_address, &other->last_address) < 0)
33a051e0
MT
473 return 0;
474
475 return 1;
476}
477
a5967330
MT
478LOC_EXPORT int loc_network_subnets(struct loc_network* network,
479 struct loc_network** subnet1, struct loc_network** subnet2) {
480 int r;
481 *subnet1 = NULL;
482 *subnet2 = NULL;
850e7516
MT
483
484 // New prefix length
485 unsigned int prefix = network->prefix + 1;
486
487 // Check if the new prefix is valid
488 if (valid_prefix(&network->first_address, prefix))
a5967330 489 return -1;
850e7516
MT
490
491 // Create the first half of the network
a5967330 492 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
850e7516 493 if (r)
a5967330 494 return r;
850e7516
MT
495
496 // The next subnet starts after the first one
a5967330 497 struct in6_addr first_address = address_increment(&(*subnet1)->last_address);
850e7516
MT
498
499 // Create the second half of the network
a5967330 500 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
850e7516 501 if (r)
a5967330 502 return r;
850e7516 503
594ca328
MT
504 // Copy country code
505 const char* country_code = loc_network_get_country_code(network);
506 if (country_code) {
a5967330
MT
507 loc_network_set_country_code(*subnet1, country_code);
508 loc_network_set_country_code(*subnet2, country_code);
594ca328
MT
509 }
510
511 // Copy ASN
512 uint32_t asn = loc_network_get_asn(network);
513 if (asn) {
a5967330
MT
514 loc_network_set_asn(*subnet1, asn);
515 loc_network_set_asn(*subnet2, asn);
594ca328
MT
516 }
517
3e8d86ab
MT
518 // Copy flags
519 loc_network_set_flag(*subnet1, network->flags);
520 loc_network_set_flag(*subnet2, network->flags);
521
a5967330
MT
522 return 0;
523}
850e7516 524
a5967330
MT
525static int __loc_network_exclude(struct loc_network* network,
526 struct loc_network* other, struct loc_network_list* list) {
527 struct loc_network* subnet1 = NULL;
528 struct loc_network* subnet2 = NULL;
529
530 int r = loc_network_subnets(network, &subnet1, &subnet2);
531 if (r)
532 goto ERROR;
533
da101d55 534 if (loc_network_cmp(other, subnet1) == 0) {
a5967330
MT
535 r = loc_network_list_push(list, subnet2);
536 if (r)
537 goto ERROR;
538
da101d55 539 } else if (loc_network_cmp(other, subnet2) == 0) {
a5967330
MT
540 r = loc_network_list_push(list, subnet1);
541 if (r)
542 goto ERROR;
543
d6a5092f 544 } else if (loc_network_is_subnet(subnet1, other)) {
a5967330
MT
545 r = loc_network_list_push(list, subnet2);
546 if (r)
547 goto ERROR;
548
549 r = __loc_network_exclude(subnet1, other, list);
550 if (r)
551 goto ERROR;
552
d6a5092f 553 } else if (loc_network_is_subnet(subnet2, other)) {
a5967330
MT
554 r = loc_network_list_push(list, subnet1);
555 if (r)
556 goto ERROR;
557
558 r = __loc_network_exclude(subnet2, other, list);
559 if (r)
560 goto ERROR;
561
562 } else {
563 ERROR(network->ctx, "We should never get here\n");
564 r = 1;
565 goto ERROR;
566 }
850e7516
MT
567
568ERROR:
569 if (subnet1)
570 loc_network_unref(subnet1);
571
572 if (subnet2)
573 loc_network_unref(subnet2);
574
a5967330 575 return r;
850e7516
MT
576}
577
c650008e
MT
578static int __loc_network_exclude_to_list(struct loc_network* self,
579 struct loc_network* other, struct loc_network_list* list) {
850e7516 580 // Other must be a subnet of self
d6a5092f 581 if (!loc_network_is_subnet(self, other)) {
850e7516
MT
582 DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
583
abf55926
MT
584 // Exit silently
585 return 0;
850e7516
MT
586 }
587
588 // We cannot perform this operation if both networks equal
da101d55 589 if (loc_network_cmp(self, other) == 0) {
850e7516
MT
590 DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
591
abf55926
MT
592 // Exit silently
593 return 0;
850e7516
MT
594 }
595
c650008e
MT
596 return __loc_network_exclude(self, other, list);
597}
598
599LOC_EXPORT struct loc_network_list* loc_network_exclude(
600 struct loc_network* self, struct loc_network* other) {
601 struct loc_network_list* list;
602
603#ifdef ENABLE_DEBUG
604 char* n1 = loc_network_str(self);
605 char* n2 = loc_network_str(other);
606
607 DEBUG(self->ctx, "Returning %s excluding %s...\n", n1, n2);
608
609 free(n1);
610 free(n2);
611#endif
612
850e7516
MT
613 // Create a new list with the result
614 int r = loc_network_list_new(self->ctx, &list);
615 if (r) {
616 ERROR(self->ctx, "Could not create network list: %d\n", r);
c650008e 617
850e7516
MT
618 return NULL;
619 }
620
c650008e 621 r = __loc_network_exclude_to_list(self, other, list);
a5967330
MT
622 if (r) {
623 loc_network_list_unref(list);
850e7516 624
a5967330 625 return NULL;
850e7516
MT
626 }
627
850e7516
MT
628 // Return the result
629 return list;
850e7516
MT
630}
631
add5bb65
MT
632LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
633 struct loc_network* network, struct loc_network_list* list) {
634 struct loc_network_list* to_check;
635
636 // Create a new list with all networks to look at
637 int r = loc_network_list_new(network->ctx, &to_check);
638 if (r)
639 return NULL;
640
641 struct loc_network* subnet = NULL;
642 struct loc_network_list* subnets = NULL;
643
644 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
645 subnet = loc_network_list_get(list, i);
646
647 // Find all excluded networks
c650008e
MT
648 if (!loc_network_list_contains(to_check, subnet)) {
649 r = __loc_network_exclude_to_list(network, subnet, to_check);
650 if (r) {
651 loc_network_list_unref(to_check);
652 loc_network_unref(subnet);
653
654 return NULL;
655 }
add5bb65
MT
656 }
657
658 // Cleanup
659 loc_network_unref(subnet);
660 }
661
662 r = loc_network_list_new(network->ctx, &subnets);
663 if (r) {
664 loc_network_list_unref(to_check);
665 return NULL;
666 }
667
058e7800
MT
668 off_t smallest_subnet = 0;
669
add5bb65 670 while (!loc_network_list_empty(to_check)) {
058e7800 671 struct loc_network* subnet_to_check = loc_network_list_pop_first(to_check);
add5bb65 672
06177d8c
MT
673 // Check whether the subnet to check is part of the input list
674 if (loc_network_list_contains(list, subnet_to_check)) {
675 loc_network_unref(subnet_to_check);
676 continue;
677 }
678
add5bb65
MT
679 // Marks whether this subnet passed all checks
680 int passed = 1;
681
058e7800 682 for (unsigned int i = smallest_subnet; i < loc_network_list_size(list); i++) {
add5bb65
MT
683 subnet = loc_network_list_get(list, i);
684
add5bb65 685 // Drop this subnet if is a subnet of another subnet
4de8ff8e 686 if (loc_network_is_subnet(subnet, subnet_to_check)) {
add5bb65
MT
687 passed = 0;
688 loc_network_unref(subnet);
689 break;
690 }
691
692 // Break it down if it overlaps
4de8ff8e 693 if (loc_network_overlaps(subnet, subnet_to_check)) {
add5bb65
MT
694 passed = 0;
695
4fc034e2 696 __loc_network_exclude_to_list(subnet_to_check, subnet, to_check);
add5bb65
MT
697
698 loc_network_unref(subnet);
699 break;
700 }
701
058e7800
MT
702 // If the subnet is strictly greater, we do not need to continue the search
703 r = loc_network_cmp(subnet, subnet_to_check);
704 if (r > 0) {
705 loc_network_unref(subnet);
706 break;
707
708 // If it is strictly smaller, we can continue the search from here next
709 // time because all networks that are to be checked can only be larger
710 // than this one.
711 } else if (r < 0) {
712 smallest_subnet = i;
713 }
714
add5bb65
MT
715 loc_network_unref(subnet);
716 }
717
718 if (passed) {
719 r = loc_network_list_push(subnets, subnet_to_check);
720 }
721
722 loc_network_unref(subnet_to_check);
723 }
724
725 loc_network_list_unref(to_check);
726
727 return subnets;
728}
729
8298728e 730int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
f3e02bc5 731 // Add country code
a7a3d958 732 loc_country_code_copy(dbobj->country_code, network->country_code);
f3e02bc5
MT
733
734 // Add ASN
71ff3e69 735 dbobj->asn = htobe32(network->asn);
f3e02bc5 736
a99e7c2b 737 // Flags
eee6346d 738 dbobj->flags = htobe16(network->flags);
a99e7c2b 739
f3e02bc5
MT
740 return 0;
741}
742
8298728e 743int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
b904896a 744 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
3dc8adfb 745 char country_code[3] = "\0\0";
a7a3d958 746
39a55353 747 int r = loc_network_new(ctx, network, address, prefix);
94a3f329
MT
748 if (r) {
749 ERROR(ctx, "Could not allocate a new network: %s", strerror(-r));
10778041 750 return r;
94a3f329 751 }
10778041
MT
752
753 // Import country code
a7a3d958 754 loc_country_code_copy(country_code, dbobj->country_code);
10778041
MT
755
756 r = loc_network_set_country_code(*network, country_code);
94a3f329
MT
757 if (r) {
758 ERROR(ctx, "Could not set country code: %s\n", country_code);
10778041 759 return r;
94a3f329 760 }
10778041
MT
761
762 // Import ASN
94a3f329
MT
763 uint32_t asn = be32toh(dbobj->asn);
764 r = loc_network_set_asn(*network, asn);
765 if (r) {
766 ERROR(ctx, "Could not set ASN: %d\n", asn);
10778041 767 return r;
94a3f329 768 }
10778041 769
a99e7c2b 770 // Import flags
94a3f329
MT
771 int flags = be16toh(dbobj->flags);
772 r = loc_network_set_flag(*network, flags);
773 if (r) {
774 ERROR(ctx, "Could not set flags: %d\n", flags);
a99e7c2b 775 return r;
94a3f329 776 }
a99e7c2b 777
10778041
MT
778 return 0;
779}
780
3b5f4af2
MT
781struct loc_network_tree {
782 struct loc_ctx* ctx;
783 int refcount;
784
785 struct loc_network_tree_node* root;
786};
787
788struct loc_network_tree_node {
438db08c
MT
789 struct loc_ctx* ctx;
790 int refcount;
791
3b5f4af2
MT
792 struct loc_network_tree_node* zero;
793 struct loc_network_tree_node* one;
794
795 struct loc_network* network;
796};
797
7933f5bf 798int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
3b5f4af2
MT
799 struct loc_network_tree* t = calloc(1, sizeof(*t));
800 if (!t)
801 return -ENOMEM;
802
803 t->ctx = loc_ref(ctx);
804 t->refcount = 1;
805
806 // Create the root node
438db08c
MT
807 int r = loc_network_tree_node_new(ctx, &t->root);
808 if (r) {
809 loc_network_tree_unref(t);
810 return r;
811 }
3b5f4af2
MT
812
813 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
814 *tree = t;
815 return 0;
816}
817
7933f5bf 818struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
438db08c 819 return loc_network_tree_node_ref(tree->root);
3b5f4af2
MT
820}
821
822static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
823 struct loc_network_tree_node** n;
824
2a30e4de 825 if (path == 0)
3b5f4af2 826 n = &node->zero;
3eda9cab
MT
827 else
828 n = &node->one;
3b5f4af2
MT
829
830 // If the desired node doesn't exist, yet, we will create it
831 if (*n == NULL) {
438db08c 832 int r = loc_network_tree_node_new(node->ctx, n);
3b5f4af2
MT
833 if (r)
834 return NULL;
835 }
836
837 return *n;
838}
839
39a55353 840static 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
841 struct loc_network_tree_node* node = tree->root;
842
39a55353 843 for (unsigned int i = 0; i < prefix; i++) {
3b5f4af2 844 // Check if the ith bit is one or zero
2a30e4de 845 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
3b5f4af2
MT
846 }
847
848 return node;
849}
850
851static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
852 int(*filter_callback)(struct loc_network* network, void* data),
853 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
854 int r;
855
856 // Finding a network ends the walk here
857 if (node->network) {
858 if (filter_callback) {
f3e02bc5 859 int f = filter_callback(node->network, data);
3b5f4af2
MT
860 if (f < 0)
861 return f;
862
863 // Skip network if filter function returns value greater than zero
864 if (f > 0)
865 return 0;
866 }
867
f3e02bc5 868 r = callback(node->network, data);
3b5f4af2
MT
869 if (r)
870 return r;
871 }
872
873 // Walk down on the left side of the tree first
874 if (node->zero) {
f3e02bc5 875 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
876 if (r)
877 return r;
878 }
879
880 // Then walk on the other side
881 if (node->one) {
f3e02bc5 882 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
883 if (r)
884 return r;
885 }
886
887 return 0;
888}
889
7933f5bf 890int loc_network_tree_walk(struct loc_network_tree* tree,
f3e02bc5
MT
891 int(*filter_callback)(struct loc_network* network, void* data),
892 int(*callback)(struct loc_network* network, void* data), void* data) {
893 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
894}
895
3b5f4af2
MT
896static void loc_network_tree_free(struct loc_network_tree* tree) {
897 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
898
438db08c 899 loc_network_tree_node_unref(tree->root);
3b5f4af2
MT
900
901 loc_unref(tree->ctx);
902 free(tree);
903}
904
7933f5bf 905struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
3b5f4af2
MT
906 if (--tree->refcount > 0)
907 return tree;
908
909 loc_network_tree_free(tree);
910 return NULL;
911}
912
e9b4e2a8 913static int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
914 DEBUG(network->ctx, "Dumping network at %p\n", network);
915
916 char* s = loc_network_str(network);
917 if (!s)
918 return 1;
919
920 INFO(network->ctx, "%s\n", s);
921 free(s);
922
923 return 0;
924}
925
7933f5bf 926int loc_network_tree_dump(struct loc_network_tree* tree) {
3b5f4af2
MT
927 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
928
f3e02bc5 929 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
930}
931
7933f5bf 932int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
3b5f4af2
MT
933 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
934
39a55353 935 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
ce4f5752 936 &network->first_address, network->prefix);
3b5f4af2
MT
937 if (!node) {
938 ERROR(tree->ctx, "Could not find a node\n");
939 return -ENOMEM;
940 }
941
942 // Check if node has not been set before
943 if (node->network) {
944 DEBUG(tree->ctx, "There is already a network at this path\n");
c04005bb 945 return -EBUSY;
3b5f4af2
MT
946 }
947
948 // Point node to the network
949 node->network = loc_network_ref(network);
950
951 return 0;
952}
f3e02bc5
MT
953
954static int __loc_network_tree_count(struct loc_network* network, void* data) {
955 size_t* counter = (size_t*)data;
956
957 // Increase the counter for each network
958 counter++;
959
960 return 0;
961}
962
7933f5bf 963size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
f3e02bc5
MT
964 size_t counter = 0;
965
966 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
967 if (r)
968 return r;
969
970 return counter;
971}
940f9c2b
MT
972
973static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
974 size_t counter = 1;
975
976 if (node->zero)
977 counter += __loc_network_tree_count_nodes(node->zero);
978
979 if (node->one)
980 counter += __loc_network_tree_count_nodes(node->one);
981
982 return counter;
983}
984
7933f5bf 985size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
940f9c2b
MT
986 return __loc_network_tree_count_nodes(tree->root);
987}
438db08c 988
7933f5bf 989int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
438db08c
MT
990 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
991 if (!n)
992 return -ENOMEM;
993
994 n->ctx = loc_ref(ctx);
995 n->refcount = 1;
996
997 n->zero = n->one = NULL;
998
999 DEBUG(n->ctx, "Network node allocated at %p\n", n);
1000 *node = n;
1001 return 0;
1002}
1003
7933f5bf 1004struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
438db08c
MT
1005 if (node)
1006 node->refcount++;
1007
1008 return node;
1009}
1010
1011static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
1012 DEBUG(node->ctx, "Releasing network node at %p\n", node);
1013
1014 if (node->network)
1015 loc_network_unref(node->network);
1016
1017 if (node->zero)
1018 loc_network_tree_node_unref(node->zero);
1019
1020 if (node->one)
1021 loc_network_tree_node_unref(node->one);
1022
1023 loc_unref(node->ctx);
1024 free(node);
1025}
1026
7933f5bf 1027struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
438db08c
MT
1028 if (!node)
1029 return NULL;
1030
1031 if (--node->refcount > 0)
1032 return node;
1033
1034 loc_network_tree_node_free(node);
1035 return NULL;
1036}
1037
7933f5bf 1038struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
438db08c
MT
1039 if (index == 0)
1040 node = node->zero;
1041 else
1042 node = node->one;
1043
1044 if (!node)
1045 return NULL;
1046
1047 return loc_network_tree_node_ref(node);
1048}
1049
7933f5bf 1050int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
438db08c
MT
1051 return (!!node->network);
1052}
1053
7933f5bf 1054struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
438db08c
MT
1055 return loc_network_ref(node->network);
1056}