]> git.ipfire.org Git - people/ms/libloc.git/blame - src/python/database.c
location-query: Allow filtering networks by family
[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
210static PyObject* Database_search_as(DatabaseObject* self, PyObject* args) {
211 const char* string = NULL;
212
213 if (!PyArg_ParseTuple(args, "s", &string))
214 return NULL;
215
216 struct loc_database_enumerator* enumerator;
217
ccc7ab4e 218 int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_ASES);
afb426df
MT
219 if (r) {
220 PyErr_SetFromErrno(PyExc_SystemError);
221 return NULL;
222 }
223
224 // Search string we are searching for
225 loc_database_enumerator_set_string(enumerator, string);
226
227 PyObject* obj = new_database_enumerator(&DatabaseEnumeratorType, enumerator);
228 loc_database_enumerator_unref(enumerator);
229
230 return obj;
231}
232
ccc7ab4e 233static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
44e5ef71 234 char* kwlist[] = { "country_code", "asn", "flags", "family", NULL };
ccc7ab4e
MT
235 const char* country_code = NULL;
236 unsigned int asn = 0;
bbdb2e0a 237 int flags = 0;
44e5ef71 238 int family = 0;
ccc7ab4e 239
44e5ef71 240 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siii", kwlist, &country_code, &asn, &flags, &family))
ccc7ab4e
MT
241 return NULL;
242
243 struct loc_database_enumerator* enumerator;
244 int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_NETWORKS);
245 if (r) {
246 PyErr_SetFromErrno(PyExc_SystemError);
247 return NULL;
248 }
249
250 // Set country code we are searching for
251 if (country_code) {
252 r = loc_database_enumerator_set_country_code(enumerator, country_code);
253
254 if (r) {
255 PyErr_SetFromErrno(PyExc_SystemError);
256 return NULL;
257 }
258 }
259
260 // Set the ASN we are searching for
261 if (asn) {
262 r = loc_database_enumerator_set_asn(enumerator, asn);
263
264 if (r) {
265 PyErr_SetFromErrno(PyExc_SystemError);
266 return NULL;
267 }
268 }
269
bbdb2e0a
MT
270 // Set the flags we are searching for
271 if (flags) {
272 r = loc_database_enumerator_set_flag(enumerator, flags);
273
274 if (r) {
275 PyErr_SetFromErrno(PyExc_SystemError);
276 return NULL;
277 }
278 }
279
44e5ef71
MT
280 // Set the family we are searching for
281 if (family) {
282 r = loc_database_enumerator_set_family(enumerator, family);
283
284 if (r) {
285 PyErr_SetFromErrno(PyExc_SystemError);
286 return NULL;
287 }
288 }
289
ccc7ab4e
MT
290 PyObject* obj = new_database_enumerator(&DatabaseEnumeratorType, enumerator);
291 loc_database_enumerator_unref(enumerator);
292
293 return obj;
294}
295
9cdf6c53 296static struct PyMethodDef Database_methods[] = {
86ca7ef7
MT
297 {
298 "get_as",
299 (PyCFunction)Database_get_as,
300 METH_VARARGS,
301 NULL,
302 },
7c922e9c
MT
303 {
304 "get_country",
305 (PyCFunction)Database_get_country,
306 METH_VARARGS,
307 NULL,
308 },
31edab76
MT
309 {
310 "lookup",
311 (PyCFunction)Database_lookup,
312 METH_VARARGS,
313 NULL,
314 },
afb426df
MT
315 {
316 "search_as",
317 (PyCFunction)Database_search_as,
318 METH_VARARGS,
319 NULL,
320 },
ccc7ab4e
MT
321 {
322 "search_networks",
323 (PyCFunction)Database_search_networks,
324 METH_VARARGS|METH_KEYWORDS,
325 NULL,
326 },
b1720435
MT
327 {
328 "verify",
329 (PyCFunction)Database_verify,
726f9984 330 METH_VARARGS,
b1720435
MT
331 NULL,
332 },
9cdf6c53
MT
333 { NULL },
334};
335
d99b0256 336static struct PyGetSetDef Database_getsetters[] = {
53524b2d
MT
337 {
338 "created_at",
339 (getter)Database_get_created_at,
340 NULL,
341 NULL,
342 NULL,
343 },
d99b0256
MT
344 {
345 "description",
346 (getter)Database_get_description,
347 NULL,
348 NULL,
349 NULL,
350 },
4bf49d00
MT
351 {
352 "license",
353 (getter)Database_get_license,
354 NULL,
355 NULL,
356 NULL,
357 },
d99b0256
MT
358 {
359 "vendor",
360 (getter)Database_get_vendor,
361 NULL,
362 NULL,
53524b2d 363 NULL,
d99b0256
MT
364 },
365 { NULL },
366};
367
9cdf6c53
MT
368PyTypeObject DatabaseType = {
369 PyVarObject_HEAD_INIT(NULL, 0)
d42e1dcd
MT
370 .tp_name = "location.Database",
371 .tp_basicsize = sizeof(DatabaseObject),
372 .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
373 .tp_new = Database_new,
374 .tp_dealloc = (destructor)Database_dealloc,
375 .tp_init = (initproc)Database_init,
376 .tp_doc = "Database object",
377 .tp_methods = Database_methods,
378 .tp_getset = Database_getsetters,
379 .tp_repr = (reprfunc)Database_repr,
9cdf6c53 380};
afb426df
MT
381
382static PyObject* DatabaseEnumerator_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
383 DatabaseEnumeratorObject* self = (DatabaseEnumeratorObject*)type->tp_alloc(type, 0);
384
385 return (PyObject*)self;
386}
387
388static void DatabaseEnumerator_dealloc(DatabaseEnumeratorObject* self) {
389 loc_database_enumerator_unref(self->enumerator);
390
391 Py_TYPE(self)->tp_free((PyObject* )self);
392}
393
394static PyObject* DatabaseEnumerator_next(DatabaseEnumeratorObject* self) {
15f79e2d
MT
395 struct loc_network* network = NULL;
396
ccc7ab4e 397 // Enumerate all networks
15f79e2d
MT
398 int r = loc_database_enumerator_next_network(self->enumerator, &network);
399 if (r) {
20bb6d0c 400 PyErr_SetFromErrno(PyExc_ValueError);
15f79e2d
MT
401 return NULL;
402 }
403
404 // A network was found
ccc7ab4e
MT
405 if (network) {
406 PyObject* obj = new_network(&NetworkType, network);
407 loc_network_unref(network);
408
409 return obj;
410 }
411
412 // Enumerate all ASes
15f79e2d
MT
413 struct loc_as* as = NULL;
414
415 r = loc_database_enumerator_next_as(self->enumerator, &as);
416 if (r) {
20bb6d0c 417 PyErr_SetFromErrno(PyExc_ValueError);
15f79e2d
MT
418 return NULL;
419 }
420
afb426df
MT
421 if (as) {
422 PyObject* obj = new_as(&ASType, as);
423 loc_as_unref(as);
424
425 return obj;
426 }
427
428 // Nothing found, that means the end
429 PyErr_SetNone(PyExc_StopIteration);
430 return NULL;
431}
432
433PyTypeObject DatabaseEnumeratorType = {
434 PyVarObject_HEAD_INIT(NULL, 0)
d42e1dcd
MT
435 .tp_name = "location.DatabaseEnumerator",
436 .tp_basicsize = sizeof(DatabaseEnumeratorObject),
437 .tp_flags = Py_TPFLAGS_DEFAULT,
438 .tp_alloc = PyType_GenericAlloc,
439 .tp_new = DatabaseEnumerator_new,
440 .tp_dealloc = (destructor)DatabaseEnumerator_dealloc,
441 .tp_iter = PyObject_SelfIter,
442 .tp_iternext = (iternextfunc)DatabaseEnumerator_next,
afb426df 443};