]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network.c
importer: Drop EDROP as it has been merged into DROP
[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>
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 28#include <libloc/libloc.h>
0b1fef38 29#include <libloc/address.h>
c12a1385
MT
30#include <libloc/compat.h>
31#include <libloc/country.h>
32#include <libloc/network.h>
33#include <libloc/network-list.h>
34#include <libloc/private.h>
3b5f4af2
MT
35
36struct loc_network {
37 struct loc_ctx* ctx;
38 int refcount;
39
c3068be1 40 int family;
ce4f5752 41 struct in6_addr first_address;
b43edb61 42 struct in6_addr last_address;
3b5f4af2
MT
43 unsigned int prefix;
44
45 char country_code[3];
71ff3e69 46 uint32_t asn;
a293f829 47 enum loc_network_flags flags;
0a0a289a
MT
48
49 char string[INET6_ADDRSTRLEN + 4];
3b5f4af2
MT
50};
51
52LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
10778041 53 struct in6_addr* address, unsigned int prefix) {
b9a1dbaf
MT
54 struct loc_network* n = NULL;
55
6183c0f2 56 // Validate the prefix
1fd09d0b 57 if (!loc_address_valid_prefix(address, prefix)) {
95b6a8e4 58 ERROR(ctx, "Invalid prefix in %s: %u\n", loc_address_str(address), prefix);
5559e086
MT
59 errno = EINVAL;
60 return 1;
6183c0f2
MT
61 }
62
b9a1dbaf
MT
63 // Allocate a new network
64 n = calloc(1, sizeof(*n));
198e382c 65 if (!n)
5559e086 66 return 1;
3b5f4af2
MT
67
68 n->ctx = loc_ref(ctx);
69 n->refcount = 1;
70
b43edb61 71 // Store the prefix
1fd09d0b
MT
72 if (IN6_IS_ADDR_V4MAPPED(address))
73 n->prefix = prefix + 96;
74 else
75 n->prefix = prefix;
3b5f4af2 76
b43edb61 77 // Convert the prefix into a bitmask
b9a1dbaf 78 const struct in6_addr bitmask = loc_prefix_to_bitmask(n->prefix);
b43edb61
MT
79
80 // Store the first and last address in the network
30f0f0ed
MT
81 n->first_address = loc_address_and(address, &bitmask);
82 n->last_address = loc_address_or(&n->first_address, &bitmask);
b43edb61 83
c3068be1 84 // Set family
71877f5f 85 n->family = loc_address_family(&n->first_address);
c3068be1 86
3b5f4af2
MT
87 DEBUG(n->ctx, "Network allocated at %p\n", n);
88 *network = n;
89 return 0;
90}
91
95b6a8e4
MT
92LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx,
93 struct loc_network** network, const char* string) {
94 struct in6_addr address;
95 unsigned int prefix;
13ad6e69 96
95b6a8e4
MT
97 // Parse the input
98 int r = loc_address_parse(&address, &prefix, string);
13ad6e69 99 if (r) {
95b6a8e4 100 ERROR(ctx, "Could not parse network %s: %m\n", string);
13ad6e69 101 return r;
95b6a8e4 102 }
13ad6e69 103
13ad6e69 104 // Create a new network
95b6a8e4 105 return loc_network_new(ctx, network, &address, prefix);
3b5f4af2
MT
106}
107
108LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
109 network->refcount++;
110
111 return network;
112}
113
114static void loc_network_free(struct loc_network* network) {
115 DEBUG(network->ctx, "Releasing network at %p\n", network);
116
3b5f4af2
MT
117 loc_unref(network->ctx);
118 free(network);
119}
120
121LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
122 if (--network->refcount > 0)
123 return network;
124
125 loc_network_free(network);
126 return NULL;
127}
128
0a0a289a
MT
129LOC_EXPORT const char* loc_network_str(struct loc_network* network) {
130 if (!*network->string) {
131 // Format the address
132 const char* address = loc_address_str(&network->first_address);
133 if (!address)
134 return NULL;
d3409788 135
0a0a289a
MT
136 // Fetch the prefix
137 unsigned int prefix = loc_network_prefix(network);
d3409788 138
0a0a289a
MT
139 // Format the string
140 int r = snprintf(network->string, sizeof(network->string) - 1,
141 "%s/%u", address, prefix);
142 if (r < 0) {
143 ERROR(network->ctx, "Could not format network string: %m\n");
144 *network->string = '\0';
145 return NULL;
146 }
3b5f4af2
MT
147 }
148
0a0a289a 149 return network->string;
3b5f4af2
MT
150}
151
44e5ef71 152LOC_EXPORT int loc_network_address_family(struct loc_network* network) {
c3068be1 153 return network->family;
44e5ef71
MT
154}
155
7fe6a218
MT
156LOC_EXPORT unsigned int loc_network_prefix(struct loc_network* network) {
157 switch (network->family) {
158 case AF_INET6:
159 return network->prefix;
160
161 case AF_INET:
162 return network->prefix - 96;
163 }
164
165 return 0;
166}
167
a92139f1
MT
168unsigned int loc_network_raw_prefix(struct loc_network* network) {
169 return network->prefix;
170}
171
a1a00053
MT
172LOC_EXPORT const struct in6_addr* loc_network_get_first_address(struct loc_network* network) {
173 return &network->first_address;
174}
175
0a0a289a
MT
176LOC_EXPORT const char* loc_network_format_first_address(struct loc_network* network) {
177 return loc_address_str(&network->first_address);
2b9338ea
MT
178}
179
a1a00053
MT
180LOC_EXPORT const struct in6_addr* loc_network_get_last_address(struct loc_network* network) {
181 return &network->last_address;
182}
183
0a0a289a
MT
184LOC_EXPORT const char* loc_network_format_last_address(struct loc_network* network) {
185 return loc_address_str(&network->last_address);
2b9338ea
MT
186}
187
0258d3c9 188LOC_EXPORT int loc_network_matches_address(struct loc_network* network, const struct in6_addr* address) {
f50adb09 189 // Address must be larger than the start address
9c1e2943 190 if (loc_address_cmp(&network->first_address, address) > 0)
fc692a58 191 return 0;
2a30e4de 192
2a30e4de 193 // Address must be smaller than the last address
9c1e2943 194 if (loc_address_cmp(&network->last_address, address) < 0)
fc692a58 195 return 0;
2a30e4de
MT
196
197 // The address is inside this network
fc692a58 198 return 1;
2a30e4de
MT
199}
200
3b5f4af2
MT
201LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
202 return network->country_code;
203}
204
205LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
901ef882
MT
206 // Set empty country code
207 if (!country_code || !*country_code) {
208 *network->country_code = '\0';
209 return 0;
210 }
211
57146963
MT
212 // Check country code
213 if (!loc_country_code_is_valid(country_code))
3b5f4af2
MT
214 return -EINVAL;
215
a7a3d958 216 loc_country_code_copy(network->country_code, country_code);
3b5f4af2
MT
217
218 return 0;
219}
220
0258d3c9 221LOC_EXPORT int loc_network_matches_country_code(struct loc_network* network, const char* country_code) {
0b27a567
MT
222 // Search for any special flags
223 const int flag = loc_country_special_code_to_flag(country_code);
224
225 // If we found a flag, we will return whether it is set or not
226 if (flag)
227 return loc_network_has_flag(network, flag);
228
57146963
MT
229 // Check country code
230 if (!loc_country_code_is_valid(country_code))
e3f696c1
MT
231 return -EINVAL;
232
0b27a567 233 // Check for an exact match
e3f696c1
MT
234 return (network->country_code[0] == country_code[0])
235 && (network->country_code[1] == country_code[1]);
236}
237
71ff3e69
MT
238LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
239 return network->asn;
240}
241
242LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
243 network->asn = asn;
244
245 return 0;
246}
247
a99e7c2b
MT
248LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
249 return network->flags & flag;
250}
251
252LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
253 network->flags |= flag;
254
255 return 0;
256}
257
af4689bf 258LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* other) {
af4689bf 259 // Compare address
9c1e2943 260 int r = loc_address_cmp(&self->first_address, &other->first_address);
af4689bf
MT
261 if (r)
262 return r;
263
264 // Compare prefix
265 if (self->prefix > other->prefix)
266 return 1;
267 else if (self->prefix < other->prefix)
268 return -1;
269
270 // Both networks are equal
271 return 0;
272}
273
fe8277ed 274int loc_network_properties_cmp(struct loc_network* self, struct loc_network* other) {
f6c6b8d6
MT
275 int r;
276
277 // Check country code
278 r = loc_country_code_cmp(self->country_code, other->country_code);
279 if (r)
280 return r;
281
282 // Check ASN
283 if (self->asn > other->asn)
284 return 1;
285 else if (self->asn < other->asn)
286 return -1;
287
288 // Check flags
289 if (self->flags > other->flags)
290 return 1;
291 else if (self->flags < other->flags)
292 return -1;
293
294 return 0;
295}
296
6159d384 297LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
fc692a58 298 // Either of the start addresses must be in the other subnet
0258d3c9 299 if (loc_network_matches_address(self, &other->first_address))
6159d384
MT
300 return 1;
301
0258d3c9 302 if (loc_network_matches_address(other, &self->first_address))
6159d384
MT
303 return 1;
304
fc692a58 305 // Or either of the end addresses is in the other subnet
0258d3c9 306 if (loc_network_matches_address(self, &other->last_address))
6159d384
MT
307 return 1;
308
0258d3c9 309 if (loc_network_matches_address(other, &self->last_address))
6159d384
MT
310 return 1;
311
312 return 0;
313}
314
33a051e0 315LOC_EXPORT int loc_network_is_subnet(struct loc_network* self, struct loc_network* other) {
cf8d3c64
MT
316 // The prefix must be smaller (this avoids the more complex comparisons later)
317 if (self->prefix > other->prefix)
318 return 0;
319
33a051e0
MT
320 // If the start address of the other network is smaller than this network,
321 // it cannot be a subnet.
9c1e2943 322 if (loc_address_cmp(&self->first_address, &other->first_address) > 0)
33a051e0
MT
323 return 0;
324
325 // If the end address of the other network is greater than this network,
326 // it cannot be a subnet.
9c1e2943 327 if (loc_address_cmp(&self->last_address, &other->last_address) < 0)
33a051e0
MT
328 return 0;
329
330 return 1;
331}
332
a5967330
MT
333LOC_EXPORT int loc_network_subnets(struct loc_network* network,
334 struct loc_network** subnet1, struct loc_network** subnet2) {
335 int r;
336 *subnet1 = NULL;
337 *subnet2 = NULL;
850e7516
MT
338
339 // New prefix length
47b55e70 340 unsigned int prefix = loc_network_prefix(network) + 1;
850e7516
MT
341
342 // Check if the new prefix is valid
92ba094c
MT
343 if (!loc_address_valid_prefix(&network->first_address, prefix)) {
344 ERROR(network->ctx, "Invalid prefix: %d\n", prefix);
345 errno = EINVAL;
346 return 1;
347 }
850e7516
MT
348
349 // Create the first half of the network
a5967330 350 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
850e7516 351 if (r)
a5967330 352 return r;
850e7516
MT
353
354 // The next subnet starts after the first one
5b72642c
MT
355 struct in6_addr first_address = (*subnet1)->last_address;
356 loc_address_increment(&first_address);
850e7516
MT
357
358 // Create the second half of the network
a5967330 359 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
850e7516 360 if (r)
a5967330 361 return r;
850e7516 362
594ca328
MT
363 // Copy country code
364 const char* country_code = loc_network_get_country_code(network);
365 if (country_code) {
a5967330
MT
366 loc_network_set_country_code(*subnet1, country_code);
367 loc_network_set_country_code(*subnet2, country_code);
594ca328
MT
368 }
369
370 // Copy ASN
371 uint32_t asn = loc_network_get_asn(network);
372 if (asn) {
a5967330
MT
373 loc_network_set_asn(*subnet1, asn);
374 loc_network_set_asn(*subnet2, asn);
594ca328
MT
375 }
376
3e8d86ab
MT
377 // Copy flags
378 loc_network_set_flag(*subnet1, network->flags);
379 loc_network_set_flag(*subnet2, network->flags);
380
a5967330
MT
381 return 0;
382}
850e7516 383
a5967330
MT
384static int __loc_network_exclude(struct loc_network* network,
385 struct loc_network* other, struct loc_network_list* list) {
386 struct loc_network* subnet1 = NULL;
387 struct loc_network* subnet2 = NULL;
388
389 int r = loc_network_subnets(network, &subnet1, &subnet2);
390 if (r)
391 goto ERROR;
392
da101d55 393 if (loc_network_cmp(other, subnet1) == 0) {
a5967330
MT
394 r = loc_network_list_push(list, subnet2);
395 if (r)
396 goto ERROR;
397
da101d55 398 } else if (loc_network_cmp(other, subnet2) == 0) {
a5967330
MT
399 r = loc_network_list_push(list, subnet1);
400 if (r)
401 goto ERROR;
402
d6a5092f 403 } else if (loc_network_is_subnet(subnet1, other)) {
a5967330
MT
404 r = loc_network_list_push(list, subnet2);
405 if (r)
406 goto ERROR;
407
408 r = __loc_network_exclude(subnet1, other, list);
409 if (r)
410 goto ERROR;
411
d6a5092f 412 } else if (loc_network_is_subnet(subnet2, other)) {
a5967330
MT
413 r = loc_network_list_push(list, subnet1);
414 if (r)
415 goto ERROR;
416
417 r = __loc_network_exclude(subnet2, other, list);
418 if (r)
419 goto ERROR;
420
421 } else {
422 ERROR(network->ctx, "We should never get here\n");
423 r = 1;
424 goto ERROR;
425 }
850e7516
MT
426
427ERROR:
428 if (subnet1)
429 loc_network_unref(subnet1);
430
431 if (subnet2)
432 loc_network_unref(subnet2);
433
725718e1
MT
434 if (r)
435 DEBUG(network->ctx, "%s has failed with %d\n", __FUNCTION__, r);
436
a5967330 437 return r;
850e7516
MT
438}
439
c650008e
MT
440static int __loc_network_exclude_to_list(struct loc_network* self,
441 struct loc_network* other, struct loc_network_list* list) {
850e7516 442 // Other must be a subnet of self
d6a5092f 443 if (!loc_network_is_subnet(self, other)) {
850e7516
MT
444 DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
445
abf55926
MT
446 // Exit silently
447 return 0;
850e7516
MT
448 }
449
450 // We cannot perform this operation if both networks equal
da101d55 451 if (loc_network_cmp(self, other) == 0) {
850e7516
MT
452 DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
453
abf55926
MT
454 // Exit silently
455 return 0;
850e7516
MT
456 }
457
c650008e
MT
458 return __loc_network_exclude(self, other, list);
459}
460
461LOC_EXPORT struct loc_network_list* loc_network_exclude(
462 struct loc_network* self, struct loc_network* other) {
463 struct loc_network_list* list;
464
0a0a289a
MT
465 DEBUG(self->ctx, "Returning %s excluding %s...\n",
466 loc_network_str(self), loc_network_str(other));
c650008e 467
850e7516
MT
468 // Create a new list with the result
469 int r = loc_network_list_new(self->ctx, &list);
470 if (r) {
471 ERROR(self->ctx, "Could not create network list: %d\n", r);
c650008e 472
850e7516
MT
473 return NULL;
474 }
475
c650008e 476 r = __loc_network_exclude_to_list(self, other, list);
a5967330
MT
477 if (r) {
478 loc_network_list_unref(list);
850e7516 479
a5967330 480 return NULL;
850e7516
MT
481 }
482
850e7516
MT
483 // Return the result
484 return list;
850e7516
MT
485}
486
add5bb65
MT
487LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
488 struct loc_network* network, struct loc_network_list* list) {
489 struct loc_network_list* to_check;
490
491 // Create a new list with all networks to look at
492 int r = loc_network_list_new(network->ctx, &to_check);
493 if (r)
494 return NULL;
495
496 struct loc_network* subnet = NULL;
497 struct loc_network_list* subnets = NULL;
498
499 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
500 subnet = loc_network_list_get(list, i);
501
502 // Find all excluded networks
c650008e
MT
503 if (!loc_network_list_contains(to_check, subnet)) {
504 r = __loc_network_exclude_to_list(network, subnet, to_check);
505 if (r) {
506 loc_network_list_unref(to_check);
507 loc_network_unref(subnet);
508
509 return NULL;
510 }
add5bb65
MT
511 }
512
513 // Cleanup
514 loc_network_unref(subnet);
515 }
516
517 r = loc_network_list_new(network->ctx, &subnets);
518 if (r) {
519 loc_network_list_unref(to_check);
520 return NULL;
521 }
522
058e7800
MT
523 off_t smallest_subnet = 0;
524
add5bb65 525 while (!loc_network_list_empty(to_check)) {
058e7800 526 struct loc_network* subnet_to_check = loc_network_list_pop_first(to_check);
add5bb65 527
06177d8c
MT
528 // Check whether the subnet to check is part of the input list
529 if (loc_network_list_contains(list, subnet_to_check)) {
530 loc_network_unref(subnet_to_check);
531 continue;
532 }
533
add5bb65
MT
534 // Marks whether this subnet passed all checks
535 int passed = 1;
536
058e7800 537 for (unsigned int i = smallest_subnet; i < loc_network_list_size(list); i++) {
add5bb65
MT
538 subnet = loc_network_list_get(list, i);
539
add5bb65 540 // Drop this subnet if is a subnet of another subnet
4de8ff8e 541 if (loc_network_is_subnet(subnet, subnet_to_check)) {
add5bb65
MT
542 passed = 0;
543 loc_network_unref(subnet);
544 break;
545 }
546
547 // Break it down if it overlaps
4de8ff8e 548 if (loc_network_overlaps(subnet, subnet_to_check)) {
add5bb65
MT
549 passed = 0;
550
4fc034e2 551 __loc_network_exclude_to_list(subnet_to_check, subnet, to_check);
add5bb65
MT
552
553 loc_network_unref(subnet);
554 break;
555 }
556
058e7800
MT
557 // If the subnet is strictly greater, we do not need to continue the search
558 r = loc_network_cmp(subnet, subnet_to_check);
559 if (r > 0) {
560 loc_network_unref(subnet);
561 break;
562
563 // If it is strictly smaller, we can continue the search from here next
564 // time because all networks that are to be checked can only be larger
565 // than this one.
566 } else if (r < 0) {
567 smallest_subnet = i;
568 }
569
add5bb65
MT
570 loc_network_unref(subnet);
571 }
572
573 if (passed) {
574 r = loc_network_list_push(subnets, subnet_to_check);
575 }
576
577 loc_network_unref(subnet_to_check);
578 }
579
580 loc_network_list_unref(to_check);
581
582 return subnets;
583}
584
fe8277ed 585int loc_network_merge(struct loc_network** n,
c1ce4349
MT
586 struct loc_network* n1, struct loc_network* n2) {
587 struct loc_network* network = NULL;
588 struct in6_addr address;
589 int r;
590
591 // Reset pointer
592 *n = NULL;
593
fe8277ed
MT
594 DEBUG(n1->ctx, "Attempting to merge %s and %s\n", loc_network_str(n1), loc_network_str(n2));
595
c1ce4349
MT
596 // Family must match
597 if (n1->family != n2->family)
598 return 0;
599
600 // The prefix must match, too
601 if (n1->prefix != n2->prefix)
602 return 0;
603
604 // Cannot merge ::/0 or 0.0.0.0/0
605 if (!n1->prefix || !n2->prefix)
606 return 0;
607
608 const unsigned int prefix = loc_network_prefix(n1);
609
610 // How many bits do we need to represent this address?
8d7d3d6b 611 const size_t bitlength = loc_address_bit_length(&n1->first_address);
c1ce4349
MT
612
613 // We cannot shorten this any more
8d7d3d6b
MT
614 if (bitlength >= prefix) {
615 DEBUG(n1->ctx, "Cannot shorten this any further because we need at least %jd bits,"
616 " but only have %d\n", bitlength, prefix);
617
c1ce4349 618 return 0;
8d7d3d6b 619 }
c1ce4349
MT
620
621 // Increment the last address of the first network
622 address = n1->last_address;
623 loc_address_increment(&address);
624
625 // If they don't match they are not neighbours
626 if (loc_address_cmp(&address, &n2->first_address) != 0)
627 return 0;
628
629 // All properties must match, too
630 if (loc_network_properties_cmp(n1, n2) != 0)
631 return 0;
632
633 // Create a new network object
634 r = loc_network_new(n1->ctx, &network, &n1->first_address, prefix - 1);
635 if (r)
636 return r;
637
638 // Copy everything else
639 loc_country_code_copy(network->country_code, n1->country_code);
640 network->asn = n1->asn;
641 network->flags = n1->flags;
642
643 // Return pointer
644 *n = network;
645
646 return 0;
647}
648
8298728e 649int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
f3e02bc5 650 // Add country code
a7a3d958 651 loc_country_code_copy(dbobj->country_code, network->country_code);
f3e02bc5
MT
652
653 // Add ASN
71ff3e69 654 dbobj->asn = htobe32(network->asn);
f3e02bc5 655
a99e7c2b 656 // Flags
eee6346d 657 dbobj->flags = htobe16(network->flags);
a99e7c2b 658
f3e02bc5
MT
659 return 0;
660}
661
8298728e 662int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
b904896a 663 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
3dc8adfb 664 char country_code[3] = "\0\0";
a7a3d958 665
1fd09d0b
MT
666 // Adjust prefix for IPv4
667 if (IN6_IS_ADDR_V4MAPPED(address))
668 prefix -= 96;
669
39a55353 670 int r = loc_network_new(ctx, network, address, prefix);
94a3f329 671 if (r) {
198e382c 672 ERROR(ctx, "Could not allocate a new network: %m\n");
10778041 673 return r;
94a3f329 674 }
10778041
MT
675
676 // Import country code
a7a3d958 677 loc_country_code_copy(country_code, dbobj->country_code);
10778041
MT
678
679 r = loc_network_set_country_code(*network, country_code);
94a3f329
MT
680 if (r) {
681 ERROR(ctx, "Could not set country code: %s\n", country_code);
10778041 682 return r;
94a3f329 683 }
10778041
MT
684
685 // Import ASN
94a3f329
MT
686 uint32_t asn = be32toh(dbobj->asn);
687 r = loc_network_set_asn(*network, asn);
688 if (r) {
689 ERROR(ctx, "Could not set ASN: %d\n", asn);
10778041 690 return r;
94a3f329 691 }
10778041 692
a99e7c2b 693 // Import flags
94a3f329
MT
694 int flags = be16toh(dbobj->flags);
695 r = loc_network_set_flag(*network, flags);
696 if (r) {
697 ERROR(ctx, "Could not set flags: %d\n", flags);
a99e7c2b 698 return r;
94a3f329 699 }
a99e7c2b 700
10778041
MT
701 return 0;
702}
8da88dda
MT
703
704static char* loc_network_reverse_pointer6(struct loc_network* network, const char* suffix) {
705 char* buffer = NULL;
706 int r;
707
708 unsigned int prefix = loc_network_prefix(network);
709
710 // Must border on a nibble
711 if (prefix % 4) {
712 errno = ENOTSUP;
713 return NULL;
714 }
715
716 if (!suffix)
717 suffix = "ip6.arpa.";
718
719 // Initialize the buffer
720 r = asprintf(&buffer, "%s", suffix);
721 if (r < 0)
722 goto ERROR;
723
724 for (unsigned int i = 0; i < (prefix / 4); i++) {
725 r = asprintf(&buffer, "%x.%s", loc_address_get_nibble(&network->first_address, i), buffer);
726 if (r < 0)
727 goto ERROR;
728 }
729
730 // Add the asterisk
731 if (prefix < 128) {
732 r = asprintf(&buffer, "*.%s", buffer);
733 if (r < 0)
734 goto ERROR;
735 }
736
737 return buffer;
738
739ERROR:
740 if (buffer)
741 free(buffer);
742
743 return NULL;
744}
745
746static char* loc_network_reverse_pointer4(struct loc_network* network, const char* suffix) {
747 char* buffer = NULL;
748 int r;
749
750 unsigned int prefix = loc_network_prefix(network);
751
752 // Must border on an octet
753 if (prefix % 8) {
754 errno = ENOTSUP;
755 return NULL;
756 }
757
758 if (!suffix)
759 suffix = "in-addr.arpa.";
760
761 switch (prefix) {
762 case 32:
763 r = asprintf(&buffer, "%d.%d.%d.%d.%s",
764 loc_address_get_octet(&network->first_address, 3),
765 loc_address_get_octet(&network->first_address, 2),
766 loc_address_get_octet(&network->first_address, 1),
767 loc_address_get_octet(&network->first_address, 0),
768 suffix);
769 break;
770
771 case 24:
772 r = asprintf(&buffer, "*.%d.%d.%d.%s",
773 loc_address_get_octet(&network->first_address, 2),
774 loc_address_get_octet(&network->first_address, 1),
775 loc_address_get_octet(&network->first_address, 0),
776 suffix);
777 break;
778
779 case 16:
780 r = asprintf(&buffer, "*.%d.%d.%s",
781 loc_address_get_octet(&network->first_address, 1),
782 loc_address_get_octet(&network->first_address, 0),
783 suffix);
784 break;
785
786 case 8:
787 r = asprintf(&buffer, "*.%d.%s",
788 loc_address_get_octet(&network->first_address, 0),
789 suffix);
790 break;
791
792 case 0:
793 r = asprintf(&buffer, "*.%s", suffix);
794 break;
795
796 // To make the compiler happy
797 default:
798 return NULL;
799 }
800
801 if (r < 0)
802 return NULL;
803
804 return buffer;
805}
806
807LOC_EXPORT char* loc_network_reverse_pointer(struct loc_network* network, const char* suffix) {
808 switch (network->family) {
809 case AF_INET6:
810 return loc_network_reverse_pointer6(network, suffix);
811
812 case AF_INET:
813 return loc_network_reverse_pointer4(network, suffix);
814
815 default:
816 break;
817 }
818
819 return NULL;
820}