]> git.ipfire.org Git - people/ms/libloc.git/blame - src/python/database.c
python: Move tree flattening into C
[people/ms/libloc.git] / src / python / database.c
CommitLineData
9cdf6c53
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 <Python.h>
18
9fc7f001
MT
19#include <loc/libloc.h>
20#include <loc/database.h>
21
1da9cd39 22#include "locationmodule.h"
86ca7ef7 23#include "as.h"
7c922e9c 24#include "country.h"
9cdf6c53 25#include "database.h"
31edab76 26#include "network.h"
9cdf6c53
MT
27
28static PyObject* Database_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
9cdf6c53 29 DatabaseObject* self = (DatabaseObject*)type->tp_alloc(type, 0);
9cdf6c53
MT
30
31 return (PyObject*)self;
32}
33
34static void Database_dealloc(DatabaseObject* self) {
35 if (self->db)
36 loc_database_unref(self->db);
37
6fd96715
MT
38 if (self->path)
39 free(self->path);
40
9cdf6c53
MT
41 Py_TYPE(self)->tp_free((PyObject* )self);
42}
43
44static int Database_init(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
45 const char* path = NULL;
46
47 if (!PyArg_ParseTuple(args, "s", &path))
48 return -1;
49
6fd96715
MT
50 self->path = strdup(path);
51
9cdf6c53 52 // Open the file for reading
6fd96715 53 FILE* f = fopen(self->path, "r");
774eea26
MT
54 if (!f) {
55 PyErr_SetFromErrno(PyExc_IOError);
9cdf6c53 56 return -1;
774eea26 57 }
9cdf6c53
MT
58
59 // Load the database
38e07ee0 60 int r = loc_database_new(loc_ctx, &self->db, f);
9cdf6c53
MT
61 fclose(f);
62
63 // Return on any errors
64 if (r)
65 return -1;
66
67 return 0;
68}
69
6fd96715
MT
70static PyObject* Database_repr(DatabaseObject* self) {
71 return PyUnicode_FromFormat("<Database %s>", self->path);
72}
73
726f9984
MT
74static PyObject* Database_verify(DatabaseObject* self, PyObject* args) {
75 PyObject* public_key = NULL;
76 FILE* f = NULL;
77
78 // Parse arguments
79 if (!PyArg_ParseTuple(args, "O", &public_key))
80 return NULL;
81
82 // Convert into FILE*
83 int fd = PyObject_AsFileDescriptor(public_key);
84 if (fd < 0)
85 return NULL;
86
87 // Re-open file descriptor
88 f = fdopen(fd, "r");
89 if (!f) {
90 PyErr_SetFromErrno(PyExc_IOError);
91 return NULL;
92 }
93
94 int r = loc_database_verify(self->db, f);
b1720435
MT
95
96 if (r == 0)
97 Py_RETURN_TRUE;
98
99 Py_RETURN_FALSE;
100}
101
d99b0256
MT
102static PyObject* Database_get_description(DatabaseObject* self) {
103 const char* description = loc_database_get_description(self->db);
104
105 return PyUnicode_FromString(description);
106}
107
108static PyObject* Database_get_vendor(DatabaseObject* self) {
109 const char* vendor = loc_database_get_vendor(self->db);
110
111 return PyUnicode_FromString(vendor);
112}
113
4bf49d00
MT
114static PyObject* Database_get_license(DatabaseObject* self) {
115 const char* license = loc_database_get_license(self->db);
116
117 return PyUnicode_FromString(license);
118}
119
53524b2d
MT
120static PyObject* Database_get_created_at(DatabaseObject* self) {
121 time_t created_at = loc_database_created_at(self->db);
122
123 return PyLong_FromLong(created_at);
124}
125
86ca7ef7
MT
126static PyObject* Database_get_as(DatabaseObject* self, PyObject* args) {
127 struct loc_as* as = NULL;
128 uint32_t number = 0;
129
130 if (!PyArg_ParseTuple(args, "i", &number))
131 return NULL;
132
133 // Try to retrieve the AS
134 int r = loc_database_get_as(self->db, &as, number);
86ca7ef7 135
4a0a0f7e
MT
136 // We got an AS
137 if (r == 0) {
86ca7ef7
MT
138 PyObject* obj = new_as(&ASType, as);
139 loc_as_unref(as);
140
141 return obj;
86ca7ef7
MT
142
143 // Nothing found
4a0a0f7e
MT
144 } else if (r == 1) {
145 Py_RETURN_NONE;
146 }
147
148 // Unexpected error
149 return NULL;
86ca7ef7
MT
150}
151
7c922e9c
MT
152static PyObject* Database_get_country(DatabaseObject* self, PyObject* args) {
153 const char* country_code = NULL;
154
155 if (!PyArg_ParseTuple(args, "s", &country_code))
156 return NULL;
157
158 struct loc_country* country;
159 int r = loc_database_get_country(self->db, &country, country_code);
160 if (r) {
161 Py_RETURN_NONE;
162 }
163
164 PyObject* obj = new_country(&CountryType, country);
165 loc_country_unref(country);
166
167 return obj;
168}
169
31edab76
MT
170static PyObject* Database_lookup(DatabaseObject* self, PyObject* args) {
171 struct loc_network* network = NULL;
172 const char* address = NULL;
173
174 if (!PyArg_ParseTuple(args, "s", &address))
175 return NULL;
176
177 // Try to retrieve a matching network
178 int r = loc_database_lookup_from_string(self->db, address, &network);
179
180 // We got a network
181 if (r == 0) {
182 PyObject* obj = new_network(&NetworkType, network);
183 loc_network_unref(network);
184
185 return obj;
186
187 // Nothing found
188 } else if (r == 1) {
189 Py_RETURN_NONE;
927e82f2
MT
190
191 // Invalid input
192 } else if (r == -EINVAL) {
193 PyErr_Format(PyExc_ValueError, "Invalid IP address: %s", address);
194 return NULL;
31edab76
MT
195 }
196
197 // Unexpected error
198 return NULL;
199}
200
afb426df
MT
201static PyObject* new_database_enumerator(PyTypeObject* type, struct loc_database_enumerator* enumerator) {
202 DatabaseEnumeratorObject* self = (DatabaseEnumeratorObject*)type->tp_alloc(type, 0);
203 if (self) {
204 self->enumerator = loc_database_enumerator_ref(enumerator);
205 }
206
207 return (PyObject*)self;
208}
209
681ff05c 210static PyObject* Database_iterate_all(DatabaseObject* self, enum loc_database_enumerator_mode what, int flags) {
a68a46f5
MT
211 struct loc_database_enumerator* enumerator;
212
681ff05c 213 int r = loc_database_enumerator_new(&enumerator, self->db, what, flags);
a68a46f5
MT
214 if (r) {
215 PyErr_SetFromErrno(PyExc_SystemError);
216 return NULL;
217 }
218
219 PyObject* obj = new_database_enumerator(&DatabaseEnumeratorType, enumerator);
220 loc_database_enumerator_unref(enumerator);
221
222 return obj;
223}
224
225static PyObject* Database_ases(DatabaseObject* self) {
681ff05c 226 return Database_iterate_all(self, LOC_DB_ENUMERATE_ASES, 0);
a68a46f5
MT
227}
228
afb426df
MT
229static PyObject* Database_search_as(DatabaseObject* self, PyObject* args) {
230 const char* string = NULL;
231
232 if (!PyArg_ParseTuple(args, "s", &string))
233 return NULL;
234
235 struct loc_database_enumerator* enumerator;
236
681ff05c 237 int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_ASES, 0);
afb426df
MT
238 if (r) {
239 PyErr_SetFromErrno(PyExc_SystemError);
240 return NULL;
241 }
242
243 // Search string we are searching for
244 loc_database_enumerator_set_string(enumerator, string);
245
246 PyObject* obj = new_database_enumerator(&DatabaseEnumeratorType, enumerator);
247 loc_database_enumerator_unref(enumerator);
248
249 return obj;
250}
251
a68a46f5 252static PyObject* Database_networks(DatabaseObject* self) {
681ff05c
MT
253 return Database_iterate_all(self, LOC_DB_ENUMERATE_NETWORKS, 0);
254}
255
256static PyObject* Database_networks_flattened(DatabaseObject *self) {
257 return Database_iterate_all(self, LOC_DB_ENUMERATE_NETWORKS, LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
a68a46f5
MT
258}
259
ccc7ab4e 260static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
c242f732 261 char* kwlist[] = { "country_code", "asn", "flags", "family", "flatten", NULL };
ccc7ab4e
MT
262 const char* country_code = NULL;
263 unsigned int asn = 0;
bbdb2e0a 264 int flags = 0;
44e5ef71 265 int family = 0;
c242f732 266 int flatten = 0;
ccc7ab4e 267
c242f732 268 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiip", kwlist, &country_code, &asn, &flags, &family, &flatten))
ccc7ab4e
MT
269 return NULL;
270
271 struct loc_database_enumerator* enumerator;
c242f732
MT
272 int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_NETWORKS,
273 (flatten) ? LOC_DB_ENUMERATOR_FLAGS_FLATTEN : 0);
ccc7ab4e
MT
274 if (r) {
275 PyErr_SetFromErrno(PyExc_SystemError);
276 return NULL;
277 }
278
279 // Set country code we are searching for
280 if (country_code) {
281 r = loc_database_enumerator_set_country_code(enumerator, country_code);
282
283 if (r) {
284 PyErr_SetFromErrno(PyExc_SystemError);
285 return NULL;
286 }
287 }
288
289 // Set the ASN we are searching for
290 if (asn) {
291 r = loc_database_enumerator_set_asn(enumerator, asn);
292
293 if (r) {
294 PyErr_SetFromErrno(PyExc_SystemError);
295 return NULL;
296 }
297 }
298
bbdb2e0a
MT
299 // Set the flags we are searching for
300 if (flags) {
301 r = loc_database_enumerator_set_flag(enumerator, flags);
302
303 if (r) {
304 PyErr_SetFromErrno(PyExc_SystemError);
305 return NULL;
306 }
307 }
308
44e5ef71
MT
309 // Set the family we are searching for
310 if (family) {
311 r = loc_database_enumerator_set_family(enumerator, family);
312
313 if (r) {
314 PyErr_SetFromErrno(PyExc_SystemError);
315 return NULL;
316 }
317 }
318
ccc7ab4e
MT
319 PyObject* obj = new_database_enumerator(&DatabaseEnumeratorType, enumerator);
320 loc_database_enumerator_unref(enumerator);
321
322 return obj;
323}
324
fa9a3663 325static PyObject* Database_countries(DatabaseObject* self) {
681ff05c 326 return Database_iterate_all(self, LOC_DB_ENUMERATE_COUNTRIES, 0);
fa9a3663
MT
327}
328
9cdf6c53 329static struct PyMethodDef Database_methods[] = {
86ca7ef7
MT
330 {
331 "get_as",
332 (PyCFunction)Database_get_as,
333 METH_VARARGS,
334 NULL,
335 },
7c922e9c
MT
336 {
337 "get_country",
338 (PyCFunction)Database_get_country,
339 METH_VARARGS,
340 NULL,
341 },
31edab76
MT
342 {
343 "lookup",
344 (PyCFunction)Database_lookup,
345 METH_VARARGS,
346 NULL,
347 },
afb426df
MT
348 {
349 "search_as",
350 (PyCFunction)Database_search_as,
351 METH_VARARGS,
352 NULL,
353 },
ccc7ab4e
MT
354 {
355 "search_networks",
356 (PyCFunction)Database_search_networks,
357 METH_VARARGS|METH_KEYWORDS,
358 NULL,
359 },
b1720435
MT
360 {
361 "verify",
362 (PyCFunction)Database_verify,
726f9984 363 METH_VARARGS,
b1720435
MT
364 NULL,
365 },
9cdf6c53
MT
366 { NULL },
367};
368
d99b0256 369static struct PyGetSetDef Database_getsetters[] = {
a68a46f5
MT
370 {
371 "ases",
372 (getter)Database_ases,
373 NULL,
374 NULL,
375 NULL,
376 },
fa9a3663
MT
377 {
378 "countries",
379 (getter)Database_countries,
380 NULL,
381 NULL,
382 NULL,
383 },
53524b2d
MT
384 {
385 "created_at",
386 (getter)Database_get_created_at,
387 NULL,
388 NULL,
389 NULL,
390 },
d99b0256
MT
391 {
392 "description",
393 (getter)Database_get_description,
394 NULL,
395 NULL,
396 NULL,
397 },
4bf49d00
MT
398 {
399 "license",
400 (getter)Database_get_license,
401 NULL,
402 NULL,
403 NULL,
404 },
a68a46f5
MT
405 {
406 "networks",
407 (getter)Database_networks,
408 NULL,
409 NULL,
410 NULL,
411 },
681ff05c
MT
412 {
413 "networks_flattened",
414 (getter)Database_networks_flattened,
415 NULL,
416 NULL,
417 NULL,
418 },
d99b0256
MT
419 {
420 "vendor",
421 (getter)Database_get_vendor,
422 NULL,
423 NULL,
53524b2d 424 NULL,
d99b0256
MT
425 },
426 { NULL },
427};
428
9cdf6c53
MT
429PyTypeObject DatabaseType = {
430 PyVarObject_HEAD_INIT(NULL, 0)
d42e1dcd
MT
431 .tp_name = "location.Database",
432 .tp_basicsize = sizeof(DatabaseObject),
433 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
434 .tp_new = Database_new,
435 .tp_dealloc = (destructor)Database_dealloc,
436 .tp_init = (initproc)Database_init,
437 .tp_doc = "Database object",
438 .tp_methods = Database_methods,
439 .tp_getset = Database_getsetters,
440 .tp_repr = (reprfunc)Database_repr,
9cdf6c53 441};
afb426df
MT
442
443static PyObject* DatabaseEnumerator_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
444 DatabaseEnumeratorObject* self = (DatabaseEnumeratorObject*)type->tp_alloc(type, 0);
445
446 return (PyObject*)self;
447}
448
449static void DatabaseEnumerator_dealloc(DatabaseEnumeratorObject* self) {
450 loc_database_enumerator_unref(self->enumerator);
451
452 Py_TYPE(self)->tp_free((PyObject* )self);
453}
454
455static PyObject* DatabaseEnumerator_next(DatabaseEnumeratorObject* self) {
15f79e2d
MT
456 struct loc_network* network = NULL;
457
ccc7ab4e 458 // Enumerate all networks
15f79e2d
MT
459 int r = loc_database_enumerator_next_network(self->enumerator, &network);
460 if (r) {
20bb6d0c 461 PyErr_SetFromErrno(PyExc_ValueError);
15f79e2d
MT
462 return NULL;
463 }
464
465 // A network was found
ccc7ab4e
MT
466 if (network) {
467 PyObject* obj = new_network(&NetworkType, network);
468 loc_network_unref(network);
469
470 return obj;
471 }
472
473 // Enumerate all ASes
15f79e2d
MT
474 struct loc_as* as = NULL;
475
476 r = loc_database_enumerator_next_as(self->enumerator, &as);
477 if (r) {
20bb6d0c 478 PyErr_SetFromErrno(PyExc_ValueError);
15f79e2d
MT
479 return NULL;
480 }
481
afb426df
MT
482 if (as) {
483 PyObject* obj = new_as(&ASType, as);
484 loc_as_unref(as);
485
486 return obj;
487 }
488
fa9a3663
MT
489 // Enumerate all countries
490 struct loc_country* country = NULL;
491
492 r = loc_database_enumerator_next_country(self->enumerator, &country);
493 if (r) {
494 PyErr_SetFromErrno(PyExc_ValueError);
495 return NULL;
496 }
497
498 if (country) {
499 PyObject* obj = new_country(&CountryType, country);
500 loc_country_unref(country);
501
502 return obj;
503 }
504
afb426df
MT
505 // Nothing found, that means the end
506 PyErr_SetNone(PyExc_StopIteration);
507 return NULL;
508}
509
510PyTypeObject DatabaseEnumeratorType = {
511 PyVarObject_HEAD_INIT(NULL, 0)
d42e1dcd
MT
512 .tp_name = "location.DatabaseEnumerator",
513 .tp_basicsize = sizeof(DatabaseEnumeratorObject),
514 .tp_flags = Py_TPFLAGS_DEFAULT,
515 .tp_alloc = PyType_GenericAlloc,
516 .tp_new = DatabaseEnumerator_new,
517 .tp_dealloc = (destructor)DatabaseEnumerator_dealloc,
518 .tp_iter = PyObject_SelfIter,
519 .tp_iternext = (iternextfunc)DatabaseEnumerator_next,
afb426df 520};