]> git.ipfire.org Git - people/ms/libloc.git/blob - src/network.c
bf11f2a68b46c360ed1ed5caf4404309c086f20c
[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 <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include <loc/libloc.h>
24 #include <loc/network.h>
25
26 #include "libloc-private.h"
27 #include "as.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 struct loc_network_tree {
172 struct loc_ctx* ctx;
173 int refcount;
174
175 struct loc_network_tree_node* root;
176 };
177
178 struct loc_network_tree_node {
179 struct loc_network_tree_node* zero;
180 struct loc_network_tree_node* one;
181
182 struct loc_network* network;
183 };
184
185 LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
186 struct loc_network_tree* t = calloc(1, sizeof(*t));
187 if (!t)
188 return -ENOMEM;
189
190 t->ctx = loc_ref(ctx);
191 t->refcount = 1;
192
193 // Create the root node
194 t->root = calloc(1, sizeof(*t->root));
195
196 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
197 *tree = t;
198 return 0;
199 }
200
201 static int loc_network_tree_node_new(struct loc_network_tree_node** node) {
202 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
203 if (!n)
204 return -ENOMEM;
205
206 n->zero = n->one = NULL;
207
208 *node = n;
209 return 0;
210 }
211
212 static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
213 struct loc_network_tree_node** n;
214
215 if (path)
216 n = &node->one;
217 else
218 n = &node->zero;
219
220 // If the desired node doesn't exist, yet, we will create it
221 if (*n == NULL) {
222 int r = loc_network_tree_node_new(n);
223 if (r)
224 return NULL;
225 }
226
227 return *n;
228 }
229
230 static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address) {
231 struct loc_network_tree_node* node = tree->root;
232
233 for (unsigned int i = 127; i > 0; i--) {
234 // Check if the ith bit is one or zero
235 node = loc_network_tree_get_node(node, ((address->s6_addr32[i / 32] & (1 << (i % 32))) == 0));
236 }
237
238 return node;
239 }
240
241 static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
242 int(*filter_callback)(struct loc_network* network), int(*callback)(struct loc_network* network)) {
243 int r;
244
245 // Finding a network ends the walk here
246 if (node->network) {
247 if (filter_callback) {
248 int f = filter_callback(node->network);
249 if (f < 0)
250 return f;
251
252 // Skip network if filter function returns value greater than zero
253 if (f > 0)
254 return 0;
255 }
256
257 r = callback(node->network);
258 if (r)
259 return r;
260 }
261
262 // Walk down on the left side of the tree first
263 if (node->zero) {
264 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback);
265 if (r)
266 return r;
267 }
268
269 // Then walk on the other side
270 if (node->one) {
271 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback);
272 if (r)
273 return r;
274 }
275
276 return 0;
277 }
278
279 static void loc_network_tree_free_subtree(struct loc_network_tree_node* node) {
280 if (node->network)
281 loc_network_unref(node->network);
282
283 if (node->zero)
284 loc_network_tree_free_subtree(node->zero);
285
286 if (node->one)
287 loc_network_tree_free_subtree(node->one);
288
289 free(node);
290 }
291
292 static void loc_network_tree_free(struct loc_network_tree* tree) {
293 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
294
295 loc_network_tree_free_subtree(tree->root);
296
297 loc_unref(tree->ctx);
298 free(tree);
299 }
300
301 LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
302 if (--tree->refcount > 0)
303 return tree;
304
305 loc_network_tree_free(tree);
306 return NULL;
307 }
308
309 int __loc_network_tree_dump(struct loc_network* network) {
310 DEBUG(network->ctx, "Dumping network at %p\n", network);
311
312 char* s = loc_network_str(network);
313 if (!s)
314 return 1;
315
316 INFO(network->ctx, "%s\n", s);
317 free(s);
318
319 return 0;
320 }
321
322 LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
323 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
324
325 return __loc_network_tree_walk(tree->ctx, tree->root, NULL, __loc_network_tree_dump);
326 }
327
328 LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
329 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
330
331 struct loc_network_tree_node* node = loc_network_tree_get_path(tree, &network->start_address);
332 if (!node) {
333 ERROR(tree->ctx, "Could not find a node\n");
334 return -ENOMEM;
335 }
336
337 // Check if node has not been set before
338 if (node->network) {
339 DEBUG(tree->ctx, "There is already a network at this path\n");
340 return 1;
341 }
342
343 // Point node to the network
344 node->network = loc_network_ref(network);
345
346 return 0;
347 }