ok
Direktori : /opt/alt/python35/lib64/python3.5/site-packages/maxminddb/extension/ |
Current File : //opt/alt/python35/lib64/python3.5/site-packages/maxminddb/extension/maxminddb.c |
#include <Python.h> #include <maxminddb.h> #include "structmember.h" #define __STDC_FORMAT_MACROS #include <inttypes.h> static PyTypeObject Reader_Type; static PyTypeObject Metadata_Type; static PyObject *MaxMindDB_error; typedef struct { PyObject_HEAD /* no semicolon */ MMDB_s *mmdb; } Reader_obj; typedef struct { PyObject_HEAD /* no semicolon */ PyObject *binary_format_major_version; PyObject *binary_format_minor_version; PyObject *build_epoch; PyObject *database_type; PyObject *description; PyObject *ip_version; PyObject *languages; PyObject *node_count; PyObject *record_size; } Metadata_obj; static PyObject *from_entry_data_list(MMDB_entry_data_list_s **entry_data_list); static PyObject *from_map(MMDB_entry_data_list_s **entry_data_list); static PyObject *from_array(MMDB_entry_data_list_s **entry_data_list); static PyObject *from_uint128(const MMDB_entry_data_list_s *entry_data_list); #if PY_MAJOR_VERSION >= 3 #define MOD_INIT(name) PyMODINIT_FUNC PyInit_ ## name(void) #define RETURN_MOD_INIT(m) return (m) #define FILE_NOT_FOUND_ERROR PyExc_FileNotFoundError #else #define MOD_INIT(name) PyMODINIT_FUNC init ## name(void) #define RETURN_MOD_INIT(m) return #define PyInt_FromLong PyLong_FromLong #define FILE_NOT_FOUND_ERROR PyExc_IOError #endif #ifdef __GNUC__ # define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) #else # define UNUSED(x) UNUSED_ ## x #endif static int Reader_init(PyObject *self, PyObject *args, PyObject *kwds) { char *filename; int mode = 0; static char *kwlist[] = {"database", "mode", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|i", kwlist, &filename, &mode)) { return -1; } if (mode != 0 && mode != 1) { PyErr_Format(PyExc_ValueError, "Unsupported open mode (%i). Only " "MODE_AUTO and MODE_MMAP_EXT are supported by this extension.", mode); return -1; } if (0 != access(filename, R_OK)) { PyErr_Format(FILE_NOT_FOUND_ERROR, "No such file or directory: '%s'", filename); return -1; } MMDB_s *mmdb = (MMDB_s *)malloc(sizeof(MMDB_s)); if (NULL == mmdb) { PyErr_NoMemory(); return -1; } Reader_obj *mmdb_obj = (Reader_obj *)self; if (!mmdb_obj) { free(mmdb); PyErr_NoMemory(); return -1; } uint16_t status = MMDB_open(filename, MMDB_MODE_MMAP, mmdb); if (MMDB_SUCCESS != status) { free(mmdb); PyErr_Format( MaxMindDB_error, "Error opening database file (%s). Is this a valid MaxMind DB file?", filename ); return -1; } mmdb_obj->mmdb = mmdb; return 0; } static PyObject *Reader_get(PyObject *self, PyObject *args) { char *ip_address = NULL; Reader_obj *mmdb_obj = (Reader_obj *)self; if (!PyArg_ParseTuple(args, "s", &ip_address)) { return NULL; } MMDB_s *mmdb = mmdb_obj->mmdb; if (NULL == mmdb) { PyErr_SetString(PyExc_ValueError, "Attempt to read from a closed MaxMind DB."); return NULL; } int gai_error = 0; int mmdb_error = MMDB_SUCCESS; MMDB_lookup_result_s result = MMDB_lookup_string(mmdb, ip_address, &gai_error, &mmdb_error); if (0 != gai_error) { PyErr_Format(PyExc_ValueError, "'%s' does not appear to be an IPv4 or IPv6 address.", ip_address); return NULL; } if (MMDB_SUCCESS != mmdb_error) { PyObject *exception; if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) { exception = PyExc_ValueError; } else { exception = MaxMindDB_error; } PyErr_Format(exception, "Error looking up %s. %s", ip_address, MMDB_strerror(mmdb_error)); return NULL; } if (!result.found_entry) { Py_RETURN_NONE; } MMDB_entry_data_list_s *entry_data_list = NULL; int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); if (MMDB_SUCCESS != status) { PyErr_Format(MaxMindDB_error, "Error while looking up data for %s. %s", ip_address, MMDB_strerror(status)); MMDB_free_entry_data_list(entry_data_list); return NULL; } MMDB_entry_data_list_s *original_entry_data_list = entry_data_list; PyObject *py_obj = from_entry_data_list(&entry_data_list); MMDB_free_entry_data_list(original_entry_data_list); return py_obj; } static PyObject *Reader_metadata(PyObject *self, PyObject *UNUSED(args)) { Reader_obj *mmdb_obj = (Reader_obj *)self; if (NULL == mmdb_obj->mmdb) { PyErr_SetString(PyExc_IOError, "Attempt to read from a closed MaxMind DB."); return NULL; } MMDB_entry_data_list_s *entry_data_list; MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list); MMDB_entry_data_list_s *original_entry_data_list = entry_data_list; PyObject *metadata_dict = from_entry_data_list(&entry_data_list); MMDB_free_entry_data_list(original_entry_data_list); if (NULL == metadata_dict || !PyDict_Check(metadata_dict)) { PyErr_SetString(MaxMindDB_error, "Error decoding metadata."); return NULL; } PyObject *args = PyTuple_New(0); if (NULL == args) { Py_DECREF(metadata_dict); return NULL; } PyObject *metadata = PyObject_Call((PyObject *)&Metadata_Type, args, metadata_dict); Py_DECREF(metadata_dict); return metadata; } static PyObject *Reader_close(PyObject *self, PyObject *UNUSED(args)) { Reader_obj *mmdb_obj = (Reader_obj *)self; if (NULL != mmdb_obj->mmdb) { MMDB_close(mmdb_obj->mmdb); free(mmdb_obj->mmdb); mmdb_obj->mmdb = NULL; } Py_RETURN_NONE; } static void Reader_dealloc(PyObject *self) { Reader_obj *obj = (Reader_obj *)self; if (NULL != obj->mmdb) { Reader_close(self, NULL); } PyObject_Del(self); } static int Metadata_init(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *binary_format_major_version, *binary_format_minor_version, *build_epoch, *database_type, *description, *ip_version, *languages, *node_count, *record_size; static char *kwlist[] = { "binary_format_major_version", "binary_format_minor_version", "build_epoch", "database_type", "description", "ip_version", "languages", "node_count", "record_size", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOOOO", kwlist, &binary_format_major_version, &binary_format_minor_version, &build_epoch, &database_type, &description, &ip_version, &languages, &node_count, &record_size)) { return -1; } Metadata_obj *obj = (Metadata_obj *)self; obj->binary_format_major_version = binary_format_major_version; obj->binary_format_minor_version = binary_format_minor_version; obj->build_epoch = build_epoch; obj->database_type = database_type; obj->description = description; obj->ip_version = ip_version; obj->languages = languages; obj->node_count = node_count; obj->record_size = record_size; Py_INCREF(obj->binary_format_major_version); Py_INCREF(obj->binary_format_minor_version); Py_INCREF(obj->build_epoch); Py_INCREF(obj->database_type); Py_INCREF(obj->description); Py_INCREF(obj->ip_version); Py_INCREF(obj->languages); Py_INCREF(obj->node_count); Py_INCREF(obj->record_size); return 0; } static void Metadata_dealloc(PyObject *self) { Metadata_obj *obj = (Metadata_obj *)self; Py_DECREF(obj->binary_format_major_version); Py_DECREF(obj->binary_format_minor_version); Py_DECREF(obj->build_epoch); Py_DECREF(obj->database_type); Py_DECREF(obj->description); Py_DECREF(obj->ip_version); Py_DECREF(obj->languages); Py_DECREF(obj->node_count); Py_DECREF(obj->record_size); PyObject_Del(self); } static PyObject *from_entry_data_list(MMDB_entry_data_list_s **entry_data_list) { if (NULL == entry_data_list || NULL == *entry_data_list) { PyErr_SetString( MaxMindDB_error, "Error while looking up data. Your database may be corrupt or you have found a bug in libmaxminddb." ); return NULL; } switch ((*entry_data_list)->entry_data.type) { case MMDB_DATA_TYPE_MAP: return from_map(entry_data_list); case MMDB_DATA_TYPE_ARRAY: return from_array(entry_data_list); case MMDB_DATA_TYPE_UTF8_STRING: return PyUnicode_FromStringAndSize( (*entry_data_list)->entry_data.utf8_string, (*entry_data_list)->entry_data.data_size ); case MMDB_DATA_TYPE_BYTES: return PyByteArray_FromStringAndSize( (const char *)(*entry_data_list)->entry_data.bytes, (Py_ssize_t)(*entry_data_list)->entry_data.data_size); case MMDB_DATA_TYPE_DOUBLE: return PyFloat_FromDouble((*entry_data_list)->entry_data.double_value); case MMDB_DATA_TYPE_FLOAT: return PyFloat_FromDouble((*entry_data_list)->entry_data.float_value); case MMDB_DATA_TYPE_UINT16: return PyLong_FromLong( (*entry_data_list)->entry_data.uint16); case MMDB_DATA_TYPE_UINT32: return PyLong_FromLong((*entry_data_list)->entry_data.uint32); case MMDB_DATA_TYPE_BOOLEAN: return PyBool_FromLong((*entry_data_list)->entry_data.boolean); case MMDB_DATA_TYPE_UINT64: return PyLong_FromUnsignedLongLong( (*entry_data_list)->entry_data.uint64); case MMDB_DATA_TYPE_UINT128: return from_uint128(*entry_data_list); case MMDB_DATA_TYPE_INT32: return PyLong_FromLong((*entry_data_list)->entry_data.int32); default: PyErr_Format(MaxMindDB_error, "Invalid data type arguments: %d", (*entry_data_list)->entry_data.type); return NULL; } return NULL; } static PyObject *from_map(MMDB_entry_data_list_s **entry_data_list) { PyObject *py_obj = PyDict_New(); if (NULL == py_obj) { PyErr_NoMemory(); return NULL; } const uint32_t map_size = (*entry_data_list)->entry_data.data_size; uint i; // entry_data_list cannot start out NULL (see from_entry_data_list). We // check it in the loop because it may become NULL. // coverity[check_after_deref] for (i = 0; i < map_size && entry_data_list; i++) { *entry_data_list = (*entry_data_list)->next; PyObject *key = PyUnicode_FromStringAndSize( (char *)(*entry_data_list)->entry_data.utf8_string, (*entry_data_list)->entry_data.data_size ); *entry_data_list = (*entry_data_list)->next; PyObject *value = from_entry_data_list(entry_data_list); if (NULL == value) { Py_DECREF(key); Py_DECREF(py_obj); return NULL; } PyDict_SetItem(py_obj, key, value); Py_DECREF(value); Py_DECREF(key); } return py_obj; } static PyObject *from_array(MMDB_entry_data_list_s **entry_data_list) { const uint32_t size = (*entry_data_list)->entry_data.data_size; PyObject *py_obj = PyList_New(size); if (NULL == py_obj) { PyErr_NoMemory(); return NULL; } uint i; // entry_data_list cannot start out NULL (see from_entry_data_list). We // check it in the loop because it may become NULL. // coverity[check_after_deref] for (i = 0; i < size && entry_data_list; i++) { *entry_data_list = (*entry_data_list)->next; PyObject *value = from_entry_data_list(entry_data_list); if (NULL == value) { Py_DECREF(py_obj); return NULL; } // PyList_SetItem 'steals' the reference PyList_SetItem(py_obj, i, value); } return py_obj; } static PyObject *from_uint128(const MMDB_entry_data_list_s *entry_data_list) { uint64_t high = 0; uint64_t low = 0; #if MMDB_UINT128_IS_BYTE_ARRAY int i; for (i = 0; i < 8; i++) { high = (high << 8) | entry_data_list->entry_data.uint128[i]; } for (i = 8; i < 16; i++) { low = (low << 8) | entry_data_list->entry_data.uint128[i]; } #else high = entry_data_list->entry_data.uint128 >> 64; low = (uint64_t)entry_data_list->entry_data.uint128; #endif char *num_str = malloc(33); if (NULL == num_str) { PyErr_NoMemory(); return NULL; } snprintf(num_str, 33, "%016" PRIX64 "%016" PRIX64, high, low); PyObject *py_obj = PyLong_FromString(num_str, NULL, 16); free(num_str); return py_obj; } static PyMethodDef Reader_methods[] = { { "get", Reader_get, METH_VARARGS, "Get record for IP address" }, { "metadata", Reader_metadata, METH_NOARGS, "Returns metadata object for database" }, { "close", Reader_close, METH_NOARGS, "Closes database"}, { NULL, NULL, 0, NULL } }; static PyTypeObject Reader_Type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_basicsize = sizeof(Reader_obj), .tp_dealloc = Reader_dealloc, .tp_doc = "Reader object", .tp_flags = Py_TPFLAGS_DEFAULT, .tp_methods = Reader_methods, .tp_name = "Reader", .tp_init = Reader_init, }; static PyMethodDef Metadata_methods[] = { { NULL, NULL, 0, NULL } }; /* *INDENT-OFF* */ static PyMemberDef Metadata_members[] = { { "binary_format_major_version", T_OBJECT, offsetof( Metadata_obj, binary_format_major_version), READONLY, NULL }, { "binary_format_minor_version", T_OBJECT, offsetof( Metadata_obj, binary_format_minor_version), READONLY, NULL }, { "build_epoch", T_OBJECT, offsetof(Metadata_obj, build_epoch), READONLY, NULL }, { "database_type", T_OBJECT, offsetof(Metadata_obj, database_type), READONLY, NULL }, { "description", T_OBJECT, offsetof(Metadata_obj, description), READONLY, NULL }, { "ip_version", T_OBJECT, offsetof(Metadata_obj, ip_version), READONLY, NULL }, { "languages", T_OBJECT, offsetof(Metadata_obj, languages), READONLY, NULL }, { "node_count", T_OBJECT, offsetof(Metadata_obj, node_count), READONLY, NULL }, { "record_size", T_OBJECT, offsetof(Metadata_obj, record_size), READONLY, NULL }, { NULL, 0, 0, 0, NULL } }; /* *INDENT-ON* */ static PyTypeObject Metadata_Type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_basicsize = sizeof(Metadata_obj), .tp_dealloc = Metadata_dealloc, .tp_doc = "Metadata object", .tp_flags = Py_TPFLAGS_DEFAULT, .tp_members = Metadata_members, .tp_methods = Metadata_methods, .tp_name = "Metadata", .tp_init = Metadata_init }; static PyMethodDef MaxMindDB_methods[] = { { NULL, NULL, 0, NULL } }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef MaxMindDB_module = { PyModuleDef_HEAD_INIT, .m_name = "extension", .m_doc = "This is a C extension to read MaxMind DB file format", .m_methods = MaxMindDB_methods, }; #endif MOD_INIT(extension){ PyObject *m; #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&MaxMindDB_module); #else m = Py_InitModule("extension", MaxMindDB_methods); #endif if (!m) { RETURN_MOD_INIT(NULL); } Reader_Type.tp_new = PyType_GenericNew; if (PyType_Ready(&Reader_Type)) { RETURN_MOD_INIT(NULL); } Py_INCREF(&Reader_Type); PyModule_AddObject(m, "Reader", (PyObject *)&Reader_Type); Metadata_Type.tp_new = PyType_GenericNew; if (PyType_Ready(&Metadata_Type)) { RETURN_MOD_INIT(NULL); } PyModule_AddObject(m, "extension", (PyObject *)&Metadata_Type); PyObject* error_mod = PyImport_ImportModule("maxminddb.errors"); if (error_mod == NULL) { RETURN_MOD_INIT(NULL); } MaxMindDB_error = PyObject_GetAttrString(error_mod, "InvalidDatabaseError"); Py_DECREF(error_mod); if (MaxMindDB_error == NULL) { RETURN_MOD_INIT(NULL); } Py_INCREF(MaxMindDB_error); /* We primarily add it to the module for backwards compatibility */ PyModule_AddObject(m, "InvalidDatabaseError", MaxMindDB_error); RETURN_MOD_INIT(m); }