11#include <Python.h>
22#include <maxminddb.h>
3+ #include <arpa/inet.h>
4+ #include <netinet/in.h>
5+ #include <sys/socket.h>
36#include "structmember.h"
47
58#define __STDC_FORMAT_MACROS
@@ -28,10 +31,12 @@ typedef struct {
2831 PyObject * record_size ;
2932} Metadata_obj ;
3033
34+ static char * format_sockaddr (struct sockaddr * addr );
3135static PyObject * from_entry_data_list (MMDB_entry_data_list_s * * entry_data_list );
3236static PyObject * from_map (MMDB_entry_data_list_s * * entry_data_list );
3337static PyObject * from_array (MMDB_entry_data_list_s * * entry_data_list );
3438static PyObject * from_uint128 (const MMDB_entry_data_list_s * entry_data_list );
39+ static int ip_converter (PyObject * obj , struct sockaddr * * ip_address );
3540
3641#if PY_MAJOR_VERSION >= 3
3742 #define MOD_INIT (name ) PyMODINIT_FUNC PyInit_ ## name(void)
@@ -106,65 +111,17 @@ static int Reader_init(PyObject *self, PyObject *args, PyObject *kwds)
106111
107112static PyObject * Reader_get (PyObject * self , PyObject * args )
108113{
109- char * ip_address = NULL ;
110- PyObject * object = NULL ;
111- #if PY_MAJOR_VERSION >= 3
112- PyObject * bytes = NULL ;
113- #endif
114+ struct sockaddr * ip_address = NULL ;
114115
115116 Reader_obj * mmdb_obj = (Reader_obj * )self ;
116117
117- if (PyArg_ParseTuple (args , "s" , & ip_address )) {
118- // pass
119- } else if (PyErr_Clear (), PyArg_ParseTuple (args , "O" , & object )) {
120- PyObject * module = PyImport_ImportModule ("ipaddress" );
121- if (module == NULL ) {
122- return NULL ;
123- }
124-
125- PyObject * module_dict = PyModule_GetDict (module );
126- if (module_dict == NULL ) {
127- return NULL ;
128- }
129-
130- PyObject * ipaddress_IPv4Address = PyDict_GetItemString (
131- module_dict , "IPv4Address" );
132- int is_ipaddress_object = 0 ;
133-
134- if (ipaddress_IPv4Address != NULL ) {
135- is_ipaddress_object = PyObject_IsInstance (ipaddress_IPv4Address ,
136- object );
137- }
138-
139- PyObject * ipaddress_IPv6Address ;
140- if (!is_ipaddress_object &&
141- (ipaddress_IPv6Address = PyDict_GetItemString (module_dict ,
142- "IPv6Address" )) != NULL ) {
143- is_ipaddress_object = PyObject_IsInstance (ipaddress_IPv6Address ,
144- object );
145- }
146-
147- PyErr_Clear ();
148- PyObject * str ;
149-
150- if (!is_ipaddress_object ) {
151- PyErr_SetString (PyExc_TypeError , "IP address must be a string,"
152- " ipaddress.IPv4Address or ipaddress.IPv6Address object" );
153- } else if ((str = PyObject_Str (object )) != NULL ) {
154- #if PY_MAJOR_VERSION >= 3
155- bytes = PyUnicode_AsEncodedString (str , "UTF-8" , "strict" );
156- ip_address = PyBytes_AS_STRING (bytes );
157- #else
158- ip_address = PyString_AsString (str );
159- #endif
160- }
161-
162- Py_DECREF (module );
163- } else {
118+ if (!PyArg_ParseTuple (args , "O&" , ip_converter , & ip_address )) {
164119 return NULL ;
165120 }
166121
167122 if (ip_address == NULL ) {
123+ PyErr_SetString (PyExc_ValueError ,
124+ "Error parsing argument" );
168125 return NULL ;
169126 }
170127
@@ -176,22 +133,9 @@ static PyObject *Reader_get(PyObject *self, PyObject *args)
176133 return NULL ;
177134 }
178135
179- int gai_error = 0 ;
180136 int mmdb_error = MMDB_SUCCESS ;
181137 MMDB_lookup_result_s result =
182- MMDB_lookup_string (mmdb , ip_address , & gai_error ,
183- & mmdb_error );
184-
185- if (0 != gai_error ) {
186- PyErr_Format (PyExc_ValueError ,
187- "'%s' does not appear to be an IPv4 or IPv6 address." ,
188- ip_address );
189-
190- #if PY_MAJOR_VERSION >= 3
191- Py_XDECREF (bytes );
192- #endif
193- return NULL ;
194- }
138+ MMDB_lookup_sockaddr (mmdb , ip_address , & mmdb_error );
195139
196140 if (MMDB_SUCCESS != mmdb_error ) {
197141 PyObject * exception ;
@@ -200,11 +144,10 @@ static PyObject *Reader_get(PyObject *self, PyObject *args)
200144 } else {
201145 exception = MaxMindDB_error ;
202146 }
147+ char * ipstr = format_sockaddr (ip_address );
203148 PyErr_Format (exception , "Error looking up %s. %s" ,
204- ip_address , MMDB_strerror (mmdb_error ));
205- #if PY_MAJOR_VERSION >= 3
206- Py_XDECREF (bytes );
207- #endif
149+ ipstr , MMDB_strerror (mmdb_error ));
150+ free (ipstr );
208151 return NULL ;
209152 }
210153
@@ -215,26 +158,106 @@ static PyObject *Reader_get(PyObject *self, PyObject *args)
215158 MMDB_entry_data_list_s * entry_data_list = NULL ;
216159 int status = MMDB_get_entry_data_list (& result .entry , & entry_data_list );
217160 if (MMDB_SUCCESS != status ) {
161+ char * ipstr = format_sockaddr (ip_address );
218162 PyErr_Format (MaxMindDB_error ,
219163 "Error while looking up data for %s. %s" ,
220- ip_address , MMDB_strerror (status ));
164+ ipstr , MMDB_strerror (status ));
165+ free (ipstr );
221166 MMDB_free_entry_data_list (entry_data_list );
222- #if PY_MAJOR_VERSION >= 3
223- Py_XDECREF (bytes );
224- #endif
225167 return NULL ;
226168 }
227169
228- #if PY_MAJOR_VERSION >= 3
229- Py_XDECREF (bytes );
230- #endif
231-
232170 MMDB_entry_data_list_s * original_entry_data_list = entry_data_list ;
233171 PyObject * py_obj = from_entry_data_list (& entry_data_list );
234172 MMDB_free_entry_data_list (original_entry_data_list );
235173 return py_obj ;
236174}
237175
176+ static int ip_converter (PyObject * obj , struct sockaddr * * ip_address )
177+ {
178+ if (PyUnicode_Check (obj )) {
179+ Py_ssize_t len ;
180+ const char * ipstr = PyUnicode_AsUTF8AndSize (obj , & len );
181+ if (!ipstr ) {
182+ PyErr_SetString (PyExc_ValueError , "invalid string" );
183+ return 0 ;
184+ }
185+ if (strlen (ipstr ) != (size_t )len ) {
186+ PyErr_SetString (PyExc_ValueError , "embedded null character" );
187+ return 0 ;
188+ }
189+
190+ struct addrinfo hints = {
191+ .ai_family = AF_UNSPEC ,
192+ .ai_flags = AI_NUMERICHOST ,
193+ // We set ai_socktype so that we only get one result back
194+ .ai_socktype = SOCK_STREAM
195+ };
196+
197+ struct addrinfo * addresses = NULL ;
198+ int gai_status = getaddrinfo (ipstr , NULL , & hints , & addresses );
199+ if (gai_status ) {
200+ PyErr_Format (PyExc_ValueError ,
201+ "'%s' does not appear to be an IPv4 or IPv6 address." ,
202+ ipstr );
203+ return 0 ;
204+ }
205+ * ip_address = addresses -> ai_addr ;
206+ return 1 ;
207+ }
208+ PyObject * packed = PyObject_GetAttrString (obj , "packed" );
209+ if (!packed ) {
210+ PyErr_SetString (PyExc_ValueError , "error about object type" );
211+ }
212+ Py_ssize_t len ;
213+ char * bytes ;
214+ int status = PyBytes_AsStringAndSize (packed , & bytes , & len );
215+ if (status == -1 ) {
216+ PyErr_SetString (PyExc_ValueError , "cannot get bytes" );
217+ return 0 ;
218+ }
219+
220+ * ip_address = calloc (1 , sizeof (struct sockaddr_storage ));
221+
222+ switch (len ) {
223+ case 16 : {
224+ (* ip_address )-> sa_family = AF_INET6 ;
225+ struct sockaddr_in6 * sin = (struct sockaddr_in6 * )* ip_address ;
226+ memcpy (sin -> sin6_addr .s6_addr , bytes , len );
227+ Py_XDECREF (bytes );
228+ return 1 ;
229+ }
230+ case 4 : {
231+ (* ip_address )-> sa_family = AF_INET ;
232+ struct sockaddr_in * sin = (struct sockaddr_in * )* ip_address ;
233+ memcpy (& (sin -> sin_addr .s_addr ), bytes , len );
234+ Py_XDECREF (bytes );
235+ return 1 ;
236+ }
237+ default :
238+ PyErr_SetString (PyExc_ValueError , "unexpected packed length" );
239+ return 0 ;
240+ }
241+ }
242+
243+ static char * format_sockaddr (struct sockaddr * sa )
244+ {
245+ char * ip = calloc (INET6_ADDRSTRLEN , sizeof (char ));
246+
247+ char * addr ;
248+ if (sa -> sa_family == AF_INET ) {
249+ struct sockaddr_in * sin = (struct sockaddr_in * )sa ;
250+ addr = (char * )& sin -> sin_addr ;
251+ } else {
252+ struct sockaddr_in6 * sin = (struct sockaddr_in6 * )sa ;
253+ addr = (char * )& sin -> sin6_addr ;
254+ }
255+
256+ inet_ntop (sa -> sa_family , addr , ip , INET6_ADDRSTRLEN );
257+ return ip ;
258+ }
259+
260+
238261static PyObject * Reader_metadata (PyObject * self , PyObject * UNUSED (args ))
239262{
240263 Reader_obj * mmdb_obj = (Reader_obj * )self ;
0 commit comments