]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network.c
python: Remove local stringpool and ctx from AS class
[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
MT
20#include <errno.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include <loc/libloc.h>
9fc7f001 25#include <loc/as.h>
3b5f4af2 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];
37
38 struct loc_as* as;
39};
40
41LOC_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
82LOC_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
110LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
111 network->refcount++;
112
113 return network;
114}
115
116static 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
126LOC_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
134LOC_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
155LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
156 return network->country_code;
157}
158
159LOC_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
f3e02bc5
MT
171LOC_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
3b5f4af2
MT
189struct loc_network_tree {
190 struct loc_ctx* ctx;
191 int refcount;
192
193 struct loc_network_tree_node* root;
194};
195
196struct 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
203LOC_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
219static 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
230static 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
248static 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
259static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
260 int(*filter_callback)(struct loc_network* network, void* data),
261 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
262 int r;
263
264 // Finding a network ends the walk here
265 if (node->network) {
266 if (filter_callback) {
f3e02bc5 267 int f = filter_callback(node->network, data);
3b5f4af2
MT
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
f3e02bc5 276 r = callback(node->network, data);
3b5f4af2
MT
277 if (r)
278 return r;
279 }
280
281 // Walk down on the left side of the tree first
282 if (node->zero) {
f3e02bc5 283 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
284 if (r)
285 return r;
286 }
287
288 // Then walk on the other side
289 if (node->one) {
f3e02bc5 290 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
291 if (r)
292 return r;
293 }
294
295 return 0;
296}
297
f3e02bc5
MT
298LOC_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
3b5f4af2
MT
304static 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
317static 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
326LOC_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
f3e02bc5 334int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
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
347LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
348 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
349
f3e02bc5 350 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
351}
352
353LOC_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}
f3e02bc5
MT
373
374static 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
383LOC_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}