]> git.ipfire.org Git - people/ms/libloc.git/blob - src/network.c
dc1775a83da18424b231547a64dabf887703620d
[people/ms/libloc.git] / src / network.c
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 <endian.h>
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <loc/libloc.h>
26 #include <loc/network.h>
27 #include <loc/private.h>
28
29 struct 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];
37 uint32_t asn;
38 };
39
40 LOC_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
81 static 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
88 static 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
119 LOC_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;
122 unsigned int prefix = 0;
123 char* prefix_string;
124 int r = 1;
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
133 // Did we find a prefix?
134 if (prefix_string) {
135 // Convert prefix to integer
136 prefix = strtol(prefix_string, NULL, 10);
137
138 if (prefix) {
139 // Parse the address
140 r = parse_address(ctx, address_string, &start_address);
141 }
142 }
143
144 // Free temporary buffer
145 free(buffer);
146
147 if (r == 0) {
148 r = loc_network_new(ctx, network, start_address, prefix);
149 }
150
151 return r;
152 }
153
154 LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
155 network->refcount++;
156
157 return network;
158 }
159
160 static void loc_network_free(struct loc_network* network) {
161 DEBUG(network->ctx, "Releasing network at %p\n", network);
162
163 loc_unref(network->ctx);
164 free(network);
165 }
166
167 LOC_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
175 static 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
183 static 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
194 LOC_EXPORT char* loc_network_str(struct loc_network* network) {
195 int r;
196 const size_t length = INET6_ADDRSTRLEN + 4;
197
198 char* string = malloc(length);
199 if (!string)
200 return NULL;
201
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;
207
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));
219 free(string);
220
221 return NULL;
222 }
223
224 // Append prefix
225 sprintf(string + strlen(string), "/%u", network->prefix);
226
227 return string;
228 }
229
230 LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
231 return network->country_code;
232 }
233
234 LOC_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
246 LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
247 return network->asn;
248 }
249
250 LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
251 network->asn = asn;
252
253 return 0;
254 }
255
256 LOC_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
265 dbobj->asn = htobe32(network->asn);
266
267 return 0;
268 }
269
270 struct loc_network_tree {
271 struct loc_ctx* ctx;
272 int refcount;
273
274 struct loc_network_tree_node* root;
275 };
276
277 struct 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
284 LOC_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
300 static 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
311 static 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
329 static 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
340 static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
341 int(*filter_callback)(struct loc_network* network, void* data),
342 int(*callback)(struct loc_network* network, void* data), void* data) {
343 int r;
344
345 // Finding a network ends the walk here
346 if (node->network) {
347 if (filter_callback) {
348 int f = filter_callback(node->network, data);
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
357 r = callback(node->network, data);
358 if (r)
359 return r;
360 }
361
362 // Walk down on the left side of the tree first
363 if (node->zero) {
364 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
365 if (r)
366 return r;
367 }
368
369 // Then walk on the other side
370 if (node->one) {
371 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
372 if (r)
373 return r;
374 }
375
376 return 0;
377 }
378
379 LOC_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
385 static 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
398 static 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
407 LOC_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
415 int __loc_network_tree_dump(struct loc_network* network, void* data) {
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
428 LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
429 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
430
431 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
432 }
433
434 LOC_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 }
454
455 static 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
464 LOC_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 }