]> git.ipfire.org Git - people/ms/libloc.git/blob - src/network-list.c
lua: Fix calling methods that belong to an object
[people/ms/libloc.git] / src / network-list.c
1 /*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2020 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 <errno.h>
18 #include <stdlib.h>
19 #include <time.h>
20
21 #include <libloc/address.h>
22 #include <libloc/libloc.h>
23 #include <libloc/network.h>
24 #include <libloc/private.h>
25
26 struct loc_network_list {
27 struct loc_ctx* ctx;
28 int refcount;
29
30 struct loc_network** elements;
31 size_t elements_size;
32
33 size_t size;
34 };
35
36 static int loc_network_list_grow(struct loc_network_list* list) {
37 size_t size = list->elements_size * 2;
38 if (size < 1024)
39 size = 1024;
40
41 DEBUG(list->ctx, "Growing network list %p by %zu to %zu\n",
42 list, size, list->elements_size + size);
43
44 struct loc_network** elements = reallocarray(list->elements,
45 list->elements_size + size, sizeof(*list->elements));
46 if (!elements)
47 return 1;
48
49 list->elements = elements;
50 list->elements_size += size;
51
52 return 0;
53 }
54
55 LOC_EXPORT int loc_network_list_new(struct loc_ctx* ctx,
56 struct loc_network_list** list) {
57 struct loc_network_list* l = calloc(1, sizeof(*l));
58 if (!l)
59 return -ENOMEM;
60
61 l->ctx = loc_ref(ctx);
62 l->refcount = 1;
63
64 DEBUG(l->ctx, "Network list allocated at %p\n", l);
65 *list = l;
66 return 0;
67 }
68
69 LOC_EXPORT struct loc_network_list* loc_network_list_ref(struct loc_network_list* list) {
70 list->refcount++;
71
72 return list;
73 }
74
75 static void loc_network_list_free(struct loc_network_list* list) {
76 DEBUG(list->ctx, "Releasing network list at %p\n", list);
77
78 // Remove all content
79 loc_network_list_clear(list);
80
81 loc_unref(list->ctx);
82 free(list);
83 }
84
85 LOC_EXPORT struct loc_network_list* loc_network_list_unref(struct loc_network_list* list) {
86 if (--list->refcount > 0)
87 return list;
88
89 loc_network_list_free(list);
90 return NULL;
91 }
92
93 LOC_EXPORT size_t loc_network_list_size(struct loc_network_list* list) {
94 return list->size;
95 }
96
97 LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
98 return list->size == 0;
99 }
100
101 LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
102 if (!list->elements)
103 return;
104
105 for (unsigned int i = 0; i < list->size; i++)
106 loc_network_unref(list->elements[i]);
107
108 free(list->elements);
109 list->elements = NULL;
110 list->elements_size = 0;
111
112 list->size = 0;
113 }
114
115 LOC_EXPORT void loc_network_list_dump(struct loc_network_list* list) {
116 struct loc_network* network;
117
118 for (unsigned int i = 0; i < list->size; i++) {
119 network = list->elements[i];
120
121 INFO(list->ctx, "%4d: %s\n",
122 i, loc_network_str(network));
123 }
124 }
125
126 LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index) {
127 // Check index
128 if (index >= list->size)
129 return NULL;
130
131 return loc_network_ref(list->elements[index]);
132 }
133
134 static off_t loc_network_list_find(struct loc_network_list* list,
135 struct loc_network* network, int* found) {
136 // Insert at the beginning for an empty list
137 if (loc_network_list_empty(list))
138 return 0;
139
140 off_t lo = 0;
141 off_t hi = list->size - 1;
142 int result;
143
144 // Since we are working on an ordered list, there is often a good chance that
145 // the network we are looking for is at the end or has to go to the end.
146 if (hi >= 0) {
147 result = loc_network_cmp(network, list->elements[hi]);
148
149 // Match, so we are done
150 if (result == 0) {
151 *found = 1;
152
153 return hi;
154
155 // This needs to be added after the last one
156 } else if (result > 0) {
157 *found = 0;
158
159 return hi + 1;
160 }
161 }
162
163 #ifdef ENABLE_DEBUG
164 // Save start time
165 clock_t start = clock();
166 #endif
167
168 off_t i = 0;
169
170 while (lo <= hi) {
171 i = (lo + hi) / 2;
172
173 // Check if this is a match
174 result = loc_network_cmp(network, list->elements[i]);
175
176 if (result == 0) {
177 *found = 1;
178
179 #ifdef ENABLE_DEBUG
180 clock_t end = clock();
181
182 // Log how fast this has been
183 DEBUG(list->ctx, "Found network in %.4fms at %jd\n",
184 (double)(end - start) / CLOCKS_PER_SEC * 1000, (intmax_t)i);
185 #endif
186
187 return i;
188 }
189
190 if (result > 0) {
191 lo = i + 1;
192 i++;
193 } else {
194 hi = i - 1;
195 }
196 }
197
198 *found = 0;
199
200 #ifdef ENABLE_DEBUG
201 clock_t end = clock();
202
203 // Log how fast this has been
204 DEBUG(list->ctx, "Did not find network in %.4fms (last i = %jd)\n",
205 (double)(end - start) / CLOCKS_PER_SEC * 1000, (intmax_t)i);
206 #endif
207
208 return i;
209 }
210
211 LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
212 int found = 0;
213
214 off_t index = loc_network_list_find(list, network, &found);
215
216 // The network has been found on the list. Nothing to do.
217 if (found)
218 return 0;
219
220 DEBUG(list->ctx, "%p: Inserting network %p at index %jd\n",
221 list, network, (intmax_t)index);
222
223 // Check if we have space left
224 if (list->size >= list->elements_size) {
225 int r = loc_network_list_grow(list);
226 if (r)
227 return r;
228 }
229
230 // The list is now larger
231 list->size++;
232
233 // Move all elements out of the way
234 for (unsigned int i = list->size - 1; i > index; i--)
235 list->elements[i] = list->elements[i - 1];
236
237 // Add the new element at the right place
238 list->elements[index] = loc_network_ref(network);
239
240 return 0;
241 }
242
243 LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* list) {
244 // Return nothing when empty
245 if (loc_network_list_empty(list)) {
246 DEBUG(list->ctx, "%p: Popped empty stack\n", list);
247 return NULL;
248 }
249
250 struct loc_network* network = list->elements[--list->size];
251
252 DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
253
254 return network;
255 }
256
257 LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_list* list) {
258 // Return nothing when empty
259 if (loc_network_list_empty(list)) {
260 DEBUG(list->ctx, "%p: Popped empty stack\n", list);
261 return NULL;
262 }
263
264 struct loc_network* network = list->elements[0];
265
266 // Move all elements to the top of the stack
267 for (unsigned int i = 0; i < list->size - 1; i++) {
268 list->elements[i] = list->elements[i+1];
269 }
270
271 // The list is shorter now
272 --list->size;
273
274 DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
275
276 return network;
277 }
278
279 int loc_network_list_remove(struct loc_network_list* list, struct loc_network* network) {
280 int found = 0;
281
282 // Find the network on the list
283 off_t index = loc_network_list_find(list, network, &found);
284
285 // Nothing to do if the network wasn't found
286 if (!found)
287 return 0;
288
289 // Dereference the network at the position
290 loc_network_unref(list->elements[index]);
291
292 // Move all other elements back
293 for (unsigned int i = index; i < list->size - 1; i++)
294 list->elements[i] = list->elements[i+1];
295
296 // The list is shorter now
297 --list->size;
298
299 return 0;
300 }
301
302 LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
303 int found = 0;
304
305 loc_network_list_find(list, network, &found);
306
307 return found;
308 }
309
310 LOC_EXPORT int loc_network_list_merge(
311 struct loc_network_list* self, struct loc_network_list* other) {
312 int r;
313
314 for (unsigned int i = 0; i < other->size; i++) {
315 r = loc_network_list_push(self, other->elements[i]);
316 if (r)
317 return r;
318 }
319
320 return 0;
321 }
322
323 int loc_network_list_summarize(struct loc_ctx* ctx,
324 const struct in6_addr* first, const struct in6_addr* last, struct loc_network_list** list) {
325 int r;
326
327 if (!list) {
328 errno = EINVAL;
329 return 1;
330 }
331
332 DEBUG(ctx, "Summarizing %s - %s\n", loc_address_str(first), loc_address_str(last));
333
334 const int family1 = loc_address_family(first);
335 const int family2 = loc_address_family(last);
336
337 // Check if address families match
338 if (family1 != family2) {
339 ERROR(ctx, "Address families do not match\n");
340 errno = EINVAL;
341 return 1;
342 }
343
344 // Check if the last address is larger than the first address
345 if (loc_address_cmp(first, last) >= 0) {
346 ERROR(ctx, "The first address must be smaller than the last address\n");
347 errno = EINVAL;
348 return 1;
349 }
350
351 struct loc_network* network = NULL;
352 struct in6_addr start = *first;
353
354 const int family_bit_length = loc_address_family_bit_length(family1);
355
356 while (loc_address_cmp(&start, last) <= 0) {
357 struct in6_addr num;
358 int bits1;
359
360 // Find the number of trailing zeroes of the start address
361 if (loc_address_all_zeroes(&start))
362 bits1 = family_bit_length;
363 else {
364 bits1 = loc_address_count_trailing_zero_bits(&start);
365 if (bits1 > family_bit_length)
366 bits1 = family_bit_length;
367 }
368
369 // Subtract the start address from the last address and add one
370 // (i.e. how many addresses are in this network?)
371 r = loc_address_sub(&num, last, &start);
372 if (r)
373 return r;
374
375 loc_address_increment(&num);
376
377 // How many bits do we need to represent this address?
378 int bits2 = loc_address_bit_length(&num) - 1;
379
380 // Select the smaller one
381 int bits = (bits1 > bits2) ? bits2 : bits1;
382
383 // Create a network
384 r = loc_network_new(ctx, &network, &start, family_bit_length - bits);
385 if (r)
386 return r;
387
388 DEBUG(ctx, "Found network %s\n", loc_network_str(network));
389
390 // Push network on the list
391 r = loc_network_list_push(*list, network);
392 if (r) {
393 loc_network_unref(network);
394 return r;
395 }
396
397 // The next network starts right after this one
398 start = *loc_network_get_last_address(network);
399
400 // If we have reached the end of possible IP addresses, we stop
401 if (loc_address_all_ones(&start))
402 break;
403
404 loc_address_increment(&start);
405 }
406
407 return 0;
408 }
409
410 void loc_network_list_remove_with_prefix_smaller_than(
411 struct loc_network_list* list, const unsigned int prefix) {
412 unsigned int p = 0;
413
414 // Count how many networks were removed
415 unsigned int removed = 0;
416
417 for (unsigned int i = 0; i < list->size; i++) {
418 // Fetch the prefix
419 p = loc_network_prefix(list->elements[i]);
420
421 if (p > prefix) {
422 // Drop this network
423 loc_network_unref(list->elements[i]);
424
425 // Increment counter
426 removed++;
427
428 continue;
429 }
430
431 // Move pointers backwards to keep the list filled
432 list->elements[i - removed] = list->elements[i];
433 }
434
435 // Adjust size
436 list->size -= removed;
437
438 return;
439 }