]> git.ipfire.org Git - people/ms/libloc.git/blame_incremental - src/network-list.c
Replace strerror(errno) with %m in format string throughout
[people/ms/libloc.git] / src / network-list.c
... / ...
CommitLineData
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
26struct 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
36static 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 -errno;
48
49 list->elements = elements;
50 list->elements_size += size;
51
52 return 0;
53}
54
55LOC_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
69LOC_EXPORT struct loc_network_list* loc_network_list_ref(struct loc_network_list* list) {
70 list->refcount++;
71
72 return list;
73}
74
75static 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
85LOC_EXPORT struct loc_network_list* loc_network_list_unref(struct loc_network_list* list) {
86 if (!list)
87 return NULL;
88
89 if (--list->refcount > 0)
90 return list;
91
92 loc_network_list_free(list);
93 return NULL;
94}
95
96LOC_EXPORT size_t loc_network_list_size(struct loc_network_list* list) {
97 return list->size;
98}
99
100LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
101 return list->size == 0;
102}
103
104LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
105 if (!list->elements)
106 return;
107
108 for (unsigned int i = 0; i < list->size; i++)
109 loc_network_unref(list->elements[i]);
110
111 free(list->elements);
112 list->elements = NULL;
113 list->elements_size = 0;
114
115 list->size = 0;
116}
117
118LOC_EXPORT void loc_network_list_dump(struct loc_network_list* list) {
119 struct loc_network* network;
120
121 for (unsigned int i = 0; i < list->size; i++) {
122 network = list->elements[i];
123
124 INFO(list->ctx, "%4d: %s\n",
125 i, loc_network_str(network));
126 }
127}
128
129LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index) {
130 // Check index
131 if (index >= list->size)
132 return NULL;
133
134 return loc_network_ref(list->elements[index]);
135}
136
137static off_t loc_network_list_find(struct loc_network_list* list,
138 struct loc_network* network, int* found) {
139 // Insert at the beginning for an empty list
140 if (loc_network_list_empty(list))
141 return 0;
142
143 off_t lo = 0;
144 off_t hi = list->size - 1;
145 int result;
146
147 // Since we are working on an ordered list, there is often a good chance that
148 // the network we are looking for is at the end or has to go to the end.
149 if (hi >= 0) {
150 result = loc_network_cmp(network, list->elements[hi]);
151
152 // Match, so we are done
153 if (result == 0) {
154 *found = 1;
155
156 return hi;
157
158 // This needs to be added after the last one
159 } else if (result > 0) {
160 *found = 0;
161
162 return hi + 1;
163 }
164 }
165
166#ifdef ENABLE_DEBUG
167 // Save start time
168 clock_t start = clock();
169#endif
170
171 off_t i = 0;
172
173 while (lo <= hi) {
174 i = (lo + hi) / 2;
175
176 // Check if this is a match
177 result = loc_network_cmp(network, list->elements[i]);
178
179 if (result == 0) {
180 *found = 1;
181
182#ifdef ENABLE_DEBUG
183 clock_t end = clock();
184
185 // Log how fast this has been
186 DEBUG(list->ctx, "Found network in %.4fms at %jd\n",
187 (double)(end - start) / CLOCKS_PER_SEC * 1000, (intmax_t)i);
188#endif
189
190 return i;
191 }
192
193 if (result > 0) {
194 lo = i + 1;
195 i++;
196 } else {
197 hi = i - 1;
198 }
199 }
200
201 *found = 0;
202
203#ifdef ENABLE_DEBUG
204 clock_t end = clock();
205
206 // Log how fast this has been
207 DEBUG(list->ctx, "Did not find network in %.4fms (last i = %jd)\n",
208 (double)(end - start) / CLOCKS_PER_SEC * 1000, (intmax_t)i);
209#endif
210
211 return i;
212}
213
214LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
215 int found = 0;
216
217 off_t index = loc_network_list_find(list, network, &found);
218
219 // The network has been found on the list. Nothing to do.
220 if (found)
221 return 0;
222
223 DEBUG(list->ctx, "%p: Inserting network %p at index %jd\n",
224 list, network, (intmax_t)index);
225
226 // Check if we have space left
227 if (list->size >= list->elements_size) {
228 int r = loc_network_list_grow(list);
229 if (r)
230 return r;
231 }
232
233 // The list is now larger
234 list->size++;
235
236 // Move all elements out of the way
237 for (unsigned int i = list->size - 1; i > index; i--)
238 list->elements[i] = list->elements[i - 1];
239
240 // Add the new element at the right place
241 list->elements[index] = loc_network_ref(network);
242
243 return 0;
244}
245
246LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* list) {
247 // Return nothing when empty
248 if (loc_network_list_empty(list)) {
249 DEBUG(list->ctx, "%p: Popped empty stack\n", list);
250 return NULL;
251 }
252
253 struct loc_network* network = list->elements[--list->size];
254
255 DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
256
257 return network;
258}
259
260LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_list* list) {
261 // Return nothing when empty
262 if (loc_network_list_empty(list)) {
263 DEBUG(list->ctx, "%p: Popped empty stack\n", list);
264 return NULL;
265 }
266
267 struct loc_network* network = list->elements[0];
268
269 // Move all elements to the top of the stack
270 for (unsigned int i = 0; i < list->size - 1; i++) {
271 list->elements[i] = list->elements[i+1];
272 }
273
274 // The list is shorter now
275 --list->size;
276
277 DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
278
279 return network;
280}
281
282LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
283 int found = 0;
284
285 loc_network_list_find(list, network, &found);
286
287 return found;
288}
289
290LOC_EXPORT int loc_network_list_merge(
291 struct loc_network_list* self, struct loc_network_list* other) {
292 int r;
293
294 for (unsigned int i = 0; i < other->size; i++) {
295 r = loc_network_list_push(self, other->elements[i]);
296 if (r)
297 return r;
298 }
299
300 return 0;
301}
302
303int loc_network_list_summarize(struct loc_ctx* ctx,
304 const struct in6_addr* first, const struct in6_addr* last, struct loc_network_list** list) {
305 int r;
306
307 if (!list) {
308 errno = EINVAL;
309 return 1;
310 }
311
312 DEBUG(ctx, "Summarizing %s - %s\n", loc_address_str(first), loc_address_str(last));
313
314 const int family1 = loc_address_family(first);
315 const int family2 = loc_address_family(last);
316
317 // Check if address families match
318 if (family1 != family2) {
319 ERROR(ctx, "Address families do not match\n");
320 errno = EINVAL;
321 return 1;
322 }
323
324 // Check if the last address is larger than the first address
325 if (loc_address_cmp(first, last) >= 0) {
326 ERROR(ctx, "The first address must be smaller than the last address\n");
327 errno = EINVAL;
328 return 1;
329 }
330
331 struct loc_network* network = NULL;
332 struct in6_addr start = *first;
333
334 const int family_bit_length = loc_address_family_bit_length(family1);
335
336 while (loc_address_cmp(&start, last) <= 0) {
337 struct in6_addr num;
338 int bits1;
339
340 // Find the number of trailing zeroes of the start address
341 if (loc_address_all_zeroes(&start))
342 bits1 = family_bit_length;
343 else {
344 bits1 = loc_address_count_trailing_zero_bits(&start);
345 if (bits1 > family_bit_length)
346 bits1 = family_bit_length;
347 }
348
349 // Subtract the start address from the last address and add one
350 // (i.e. how many addresses are in this network?)
351 r = loc_address_sub(&num, last, &start);
352 if (r)
353 return r;
354
355 loc_address_increment(&num);
356
357 // How many bits do we need to represent this address?
358 int bits2 = loc_address_bit_length(&num) - 1;
359
360 // Select the smaller one
361 int bits = (bits1 > bits2) ? bits2 : bits1;
362
363 // Create a network
364 r = loc_network_new(ctx, &network, &start, family_bit_length - bits);
365 if (r)
366 return r;
367
368 DEBUG(ctx, "Found network %s\n", loc_network_str(network));
369
370 // Push network on the list
371 r = loc_network_list_push(*list, network);
372 if (r) {
373 loc_network_unref(network);
374 return r;
375 }
376
377 // The next network starts right after this one
378 start = *loc_network_get_last_address(network);
379
380 // If we have reached the end of possible IP addresses, we stop
381 if (loc_address_all_ones(&start))
382 break;
383
384 loc_address_increment(&start);
385 }
386
387 return 0;
388}