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