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