]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network.c
Prevent segmentation fault when no prefix is present
[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>
26#include <loc/network.h>
9fc7f001 27#include <loc/private.h>
3b5f4af2
MT
28
29struct loc_network {
30 struct loc_ctx* ctx;
31 int refcount;
32
33 struct in6_addr start_address;
34 unsigned int prefix;
35
36 char country_code[3];
71ff3e69 37 uint32_t asn;
3b5f4af2
MT
38};
39
40LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
41 struct in6_addr start_address, unsigned int prefix) {
42 // Address cannot be unspecified
43 if (IN6_IS_ADDR_UNSPECIFIED(&start_address)) {
44 DEBUG(ctx, "Start address is unspecified\n");
45 return -EINVAL;
46 }
47
48 // Address cannot be loopback
49 if (IN6_IS_ADDR_LOOPBACK(&start_address)) {
50 DEBUG(ctx, "Start address is loopback address\n");
51 return -EINVAL;
52 }
53
54 // Address cannot be link-local
55 if (IN6_IS_ADDR_LINKLOCAL(&start_address)) {
56 DEBUG(ctx, "Start address cannot be link-local\n");
57 return -EINVAL;
58 }
59
60 // Address cannot be site-local
61 if (IN6_IS_ADDR_SITELOCAL(&start_address)) {
62 DEBUG(ctx, "Start address cannot be site-local\n");
63 return -EINVAL;
64 }
65
66 struct loc_network* n = calloc(1, sizeof(*n));
67 if (!n)
68 return -ENOMEM;
69
70 n->ctx = loc_ref(ctx);
71 n->refcount = 1;
72
73 n->start_address = start_address;
74 n->prefix = prefix;
75
76 DEBUG(n->ctx, "Network allocated at %p\n", n);
77 *network = n;
78 return 0;
79}
80
d3409788
MT
81static int loc_network_address_family(struct loc_network* network) {
82 if (IN6_IS_ADDR_V4MAPPED(&network->start_address))
83 return AF_INET;
84
85 return AF_INET6;
86}
87
88static int parse_address(struct loc_ctx* ctx, const char* string, struct in6_addr* address) {
89 DEBUG(ctx, "Paring IP address %s\n", string);
90
91 // Try parsing this as an IPv6 address
92 int r = inet_pton(AF_INET6, string, address);
93
94 // If inet_pton returns one it has been successful
95 if (r == 1) {
96 DEBUG(ctx, "%s is an IPv6 address\n", string);
97 return 0;
98 }
99
100 // Try parsing this as an IPv4 address
101 struct in_addr ipv4_address;
102 r = inet_pton(AF_INET, string, &ipv4_address);
103 if (r == 1) {
104 DEBUG(ctx, "%s is an IPv4 address\n", string);
105
106 // Convert to IPv6-mapped address
107 address->s6_addr32[0] = htonl(0x0000);
108 address->s6_addr32[1] = htonl(0x0000);
109 address->s6_addr32[2] = htonl(0xffff);
110 address->s6_addr32[3] = ipv4_address.s_addr;
111
112 return 0;
113 }
114
115 DEBUG(ctx, "%s is not an valid IP address\n", string);
116 return 1;
117}
118
3b5f4af2
MT
119LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network,
120 const char* address_string) {
121 struct in6_addr start_address;
b61f14fc 122 unsigned int prefix = 0;
3b5f4af2 123 char* prefix_string;
b61f14fc 124 int r = 1;
3b5f4af2
MT
125
126 // Make a copy of the string to work on it
127 char* buffer = strdup(address_string);
128 address_string = prefix_string = buffer;
129
130 // Split address and prefix
131 address_string = strsep(&prefix_string, "/");
132
b61f14fc
MT
133 // Did we find a prefix?
134 if (prefix_string) {
135 // Convert prefix to integer
136 prefix = strtol(prefix_string, NULL, 10);
3b5f4af2 137
b61f14fc
MT
138 if (prefix) {
139 // Parse the address
140 r = parse_address(ctx, address_string, &start_address);
141 }
142 }
3b5f4af2
MT
143
144 // Free temporary buffer
145 free(buffer);
146
d3409788 147 if (r == 0) {
3b5f4af2
MT
148 r = loc_network_new(ctx, network, start_address, prefix);
149 }
150
151 return r;
152}
153
154LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
155 network->refcount++;
156
157 return network;
158}
159
160static void loc_network_free(struct loc_network* network) {
161 DEBUG(network->ctx, "Releasing network at %p\n", network);
162
3b5f4af2
MT
163 loc_unref(network->ctx);
164 free(network);
165}
166
167LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
168 if (--network->refcount > 0)
169 return network;
170
171 loc_network_free(network);
172 return NULL;
173}
174
d3409788
MT
175static int format_ipv6_address(struct loc_network* network, char* string, size_t length) {
176 const char* ret = inet_ntop(AF_INET6, &network->start_address, string, length);
177 if (!ret)
178 return -1;
179
180 return 0;
181}
182
183static int format_ipv4_address(struct loc_network* network, char* string, size_t length) {
184 struct in_addr ipv4_address;
185 ipv4_address.s_addr = network->start_address.s6_addr32[3];
186
187 const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length);
188 if (!ret)
189 return -1;
190
191 return 0;
192}
193
3b5f4af2 194LOC_EXPORT char* loc_network_str(struct loc_network* network) {
d3409788
MT
195 int r;
196 const size_t length = INET6_ADDRSTRLEN + 4;
3b5f4af2 197
d3409788 198 char* string = malloc(length);
3b5f4af2
MT
199 if (!string)
200 return NULL;
201
d3409788
MT
202 int family = loc_network_address_family(network);
203 switch (family) {
204 case AF_INET6:
205 r = format_ipv6_address(network, string, length);
206 break;
3b5f4af2 207
d3409788
MT
208 case AF_INET:
209 r = format_ipv4_address(network, string, length);
210 break;
211
212 default:
213 r = -1;
214 break;
215 }
216
217 if (r) {
218 ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno));
3b5f4af2 219 free(string);
d3409788 220
3b5f4af2
MT
221 return NULL;
222 }
223
224 // Append prefix
225 sprintf(string + strlen(string), "/%u", network->prefix);
226
227 return string;
228}
229
230LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
231 return network->country_code;
232}
233
234LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
235 // Country codes must be two characters
236 if (strlen(country_code) != 2)
237 return -EINVAL;
238
239 for (unsigned int i = 0; i < 3; i++) {
240 network->country_code[i] = country_code[i];
241 }
242
243 return 0;
244}
245
71ff3e69
MT
246LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
247 return network->asn;
248}
249
250LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
251 network->asn = asn;
252
253 return 0;
254}
255
f3e02bc5
MT
256LOC_EXPORT int loc_network_to_database_v0(struct loc_network* network, struct loc_database_network_v0* dbobj) {
257 dbobj->prefix = htobe16(network->prefix);
258
259 // Add country code
260 for (unsigned int i = 0; i < 2; i++) {
261 dbobj->country_code[i] = network->country_code ? network->country_code[i] : '\0';
262 }
263
264 // Add ASN
71ff3e69 265 dbobj->asn = htobe32(network->asn);
f3e02bc5
MT
266
267 return 0;
268}
269
3b5f4af2
MT
270struct loc_network_tree {
271 struct loc_ctx* ctx;
272 int refcount;
273
274 struct loc_network_tree_node* root;
275};
276
277struct loc_network_tree_node {
278 struct loc_network_tree_node* zero;
279 struct loc_network_tree_node* one;
280
281 struct loc_network* network;
282};
283
284LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
285 struct loc_network_tree* t = calloc(1, sizeof(*t));
286 if (!t)
287 return -ENOMEM;
288
289 t->ctx = loc_ref(ctx);
290 t->refcount = 1;
291
292 // Create the root node
293 t->root = calloc(1, sizeof(*t->root));
294
295 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
296 *tree = t;
297 return 0;
298}
299
300static int loc_network_tree_node_new(struct loc_network_tree_node** node) {
301 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
302 if (!n)
303 return -ENOMEM;
304
305 n->zero = n->one = NULL;
306
307 *node = n;
308 return 0;
309}
310
311static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
312 struct loc_network_tree_node** n;
313
314 if (path)
315 n = &node->one;
316 else
317 n = &node->zero;
318
319 // If the desired node doesn't exist, yet, we will create it
320 if (*n == NULL) {
321 int r = loc_network_tree_node_new(n);
322 if (r)
323 return NULL;
324 }
325
326 return *n;
327}
328
329static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address) {
330 struct loc_network_tree_node* node = tree->root;
331
332 for (unsigned int i = 127; i > 0; i--) {
333 // Check if the ith bit is one or zero
334 node = loc_network_tree_get_node(node, ((address->s6_addr32[i / 32] & (1 << (i % 32))) == 0));
335 }
336
337 return node;
338}
339
340static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
341 int(*filter_callback)(struct loc_network* network, void* data),
342 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
343 int r;
344
345 // Finding a network ends the walk here
346 if (node->network) {
347 if (filter_callback) {
f3e02bc5 348 int f = filter_callback(node->network, data);
3b5f4af2
MT
349 if (f < 0)
350 return f;
351
352 // Skip network if filter function returns value greater than zero
353 if (f > 0)
354 return 0;
355 }
356
f3e02bc5 357 r = callback(node->network, data);
3b5f4af2
MT
358 if (r)
359 return r;
360 }
361
362 // Walk down on the left side of the tree first
363 if (node->zero) {
f3e02bc5 364 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
365 if (r)
366 return r;
367 }
368
369 // Then walk on the other side
370 if (node->one) {
f3e02bc5 371 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
372 if (r)
373 return r;
374 }
375
376 return 0;
377}
378
f3e02bc5
MT
379LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree,
380 int(*filter_callback)(struct loc_network* network, void* data),
381 int(*callback)(struct loc_network* network, void* data), void* data) {
382 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
383}
384
3b5f4af2
MT
385static void loc_network_tree_free_subtree(struct loc_network_tree_node* node) {
386 if (node->network)
387 loc_network_unref(node->network);
388
389 if (node->zero)
390 loc_network_tree_free_subtree(node->zero);
391
392 if (node->one)
393 loc_network_tree_free_subtree(node->one);
394
395 free(node);
396}
397
398static void loc_network_tree_free(struct loc_network_tree* tree) {
399 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
400
401 loc_network_tree_free_subtree(tree->root);
402
403 loc_unref(tree->ctx);
404 free(tree);
405}
406
407LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
408 if (--tree->refcount > 0)
409 return tree;
410
411 loc_network_tree_free(tree);
412 return NULL;
413}
414
f3e02bc5 415int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
416 DEBUG(network->ctx, "Dumping network at %p\n", network);
417
418 char* s = loc_network_str(network);
419 if (!s)
420 return 1;
421
422 INFO(network->ctx, "%s\n", s);
423 free(s);
424
425 return 0;
426}
427
428LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
429 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
430
f3e02bc5 431 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
432}
433
434LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
435 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
436
437 struct loc_network_tree_node* node = loc_network_tree_get_path(tree, &network->start_address);
438 if (!node) {
439 ERROR(tree->ctx, "Could not find a node\n");
440 return -ENOMEM;
441 }
442
443 // Check if node has not been set before
444 if (node->network) {
445 DEBUG(tree->ctx, "There is already a network at this path\n");
446 return 1;
447 }
448
449 // Point node to the network
450 node->network = loc_network_ref(network);
451
452 return 0;
453}
f3e02bc5
MT
454
455static int __loc_network_tree_count(struct loc_network* network, void* data) {
456 size_t* counter = (size_t*)data;
457
458 // Increase the counter for each network
459 counter++;
460
461 return 0;
462}
463
464LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
465 size_t counter = 0;
466
467 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
468 if (r)
469 return r;
470
471 return counter;
472}