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