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