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,33 +111,28 @@ 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 ;
114+ MMDB_s * mmdb = (( Reader_obj * ) self ) -> mmdb ;
110115
111- Reader_obj * mmdb_obj = (Reader_obj * )self ;
112- if (!PyArg_ParseTuple (args , "s" , & ip_address )) {
116+ if (NULL == mmdb ) {
117+ PyErr_SetString (PyExc_ValueError ,
118+ "Attempt to read from a closed MaxMind DB." );
113119 return NULL ;
114120 }
115121
116- MMDB_s * mmdb = mmdb_obj -> mmdb ;
122+ struct sockaddr * ip_address = NULL ;
123+ if (!PyArg_ParseTuple (args , "O&" , ip_converter , & ip_address )) {
124+ return NULL ;
125+ }
117126
118- if (NULL == mmdb ) {
127+ if (ip_address == NULL ) {
119128 PyErr_SetString (PyExc_ValueError ,
120- "Attempt to read from a closed MaxMind DB. " );
129+ "Error parsing argument " );
121130 return NULL ;
122131 }
123132
124- int gai_error = 0 ;
125133 int mmdb_error = MMDB_SUCCESS ;
126134 MMDB_lookup_result_s result =
127- MMDB_lookup_string (mmdb , ip_address , & gai_error ,
128- & mmdb_error );
129-
130- if (0 != gai_error ) {
131- PyErr_Format (PyExc_ValueError ,
132- "'%s' does not appear to be an IPv4 or IPv6 address." ,
133- ip_address );
134- return NULL ;
135- }
135+ MMDB_lookup_sockaddr (mmdb , ip_address , & mmdb_error );
136136
137137 if (MMDB_SUCCESS != mmdb_error ) {
138138 PyObject * exception ;
@@ -141,31 +141,136 @@ static PyObject *Reader_get(PyObject *self, PyObject *args)
141141 } else {
142142 exception = MaxMindDB_error ;
143143 }
144+ char * ipstr = format_sockaddr (ip_address );
144145 PyErr_Format (exception , "Error looking up %s. %s" ,
145- ip_address , MMDB_strerror (mmdb_error ));
146+ ipstr , MMDB_strerror (mmdb_error ));
147+ free (ipstr );
148+ free (ip_address );
146149 return NULL ;
147150 }
148151
149152 if (!result .found_entry ) {
153+ free (ip_address );
150154 Py_RETURN_NONE ;
151155 }
152156
153157 MMDB_entry_data_list_s * entry_data_list = NULL ;
154158 int status = MMDB_get_entry_data_list (& result .entry , & entry_data_list );
155159 if (MMDB_SUCCESS != status ) {
160+ char * ipstr = format_sockaddr (ip_address );
156161 PyErr_Format (MaxMindDB_error ,
157162 "Error while looking up data for %s. %s" ,
158- ip_address , MMDB_strerror (status ));
163+ ipstr , MMDB_strerror (status ));
164+ free (ipstr );
165+ free (ip_address );
159166 MMDB_free_entry_data_list (entry_data_list );
160167 return NULL ;
161168 }
162169
163170 MMDB_entry_data_list_s * original_entry_data_list = entry_data_list ;
164171 PyObject * py_obj = from_entry_data_list (& entry_data_list );
165172 MMDB_free_entry_data_list (original_entry_data_list );
173+ free (ip_address );
166174 return py_obj ;
167175}
168176
177+ static int ip_converter (PyObject * obj , struct sockaddr * * ip_address )
178+ {
179+ #if PY_MAJOR_VERSION >= 3
180+ if (PyUnicode_Check (obj )) {
181+ Py_ssize_t len ;
182+ const char * ipstr = PyUnicode_AsUTF8AndSize (obj , & len );
183+ # else
184+ if (PyUnicode_Check (obj ) || PyString_Check (obj )) {
185+ // Although this should work on Python 3, we will hopefully delete
186+ // this soon and the Python 3 version is cleaner.
187+ const char * ipstr = PyString_AsString (obj );
188+ Py_ssize_t len = strlen (ipstr );
189+ #endif
190+ if (!ipstr ) {
191+ PyErr_SetString (PyExc_ValueError , "invalid string" );
192+ return 0 ;
193+ }
194+ if (strlen (ipstr ) != (size_t )len ) {
195+ PyErr_SetString (PyExc_ValueError , "embedded null character" );
196+ return 0 ;
197+ }
198+
199+ struct addrinfo hints = {
200+ .ai_family = AF_UNSPEC ,
201+ .ai_flags = AI_NUMERICHOST ,
202+ // We set ai_socktype so that we only get one result back
203+ .ai_socktype = SOCK_STREAM
204+ };
205+
206+ struct addrinfo * addresses = NULL ;
207+ int gai_status = getaddrinfo (ipstr , NULL , & hints , & addresses );
208+ if (gai_status ) {
209+ PyErr_Format (PyExc_ValueError ,
210+ "'%s' does not appear to be an IPv4 or IPv6 address." ,
211+ ipstr );
212+ return 0 ;
213+ }
214+ * ip_address = calloc (1 , sizeof (struct sockaddr_storage ));
215+ memcpy (* ip_address , addresses -> ai_addr , addresses -> ai_addrlen );
216+ freeaddrinfo (addresses );
217+ return 1 ;
218+ }
219+ PyObject * packed = PyObject_GetAttrString (obj , "packed" );
220+ if (!packed ) {
221+ PyErr_SetString (PyExc_ValueError , "error about object type" );
222+ }
223+ Py_ssize_t len ;
224+ char * bytes ;
225+ int status = PyBytes_AsStringAndSize (packed , & bytes , & len );
226+ if (status == -1 ) {
227+ PyErr_SetString (PyExc_ValueError , "cannot get bytes" );
228+ Py_DECREF (packed );
229+ return 0 ;
230+ }
231+
232+ * ip_address = calloc (1 , sizeof (struct sockaddr_storage ));
233+
234+ switch (len ) {
235+ case 16 : {
236+ (* ip_address )-> sa_family = AF_INET6 ;
237+ struct sockaddr_in6 * sin = (struct sockaddr_in6 * )* ip_address ;
238+ memcpy (sin -> sin6_addr .s6_addr , bytes , len );
239+ Py_DECREF (packed );
240+ return 1 ;
241+ }
242+ case 4 : {
243+ (* ip_address )-> sa_family = AF_INET ;
244+ struct sockaddr_in * sin = (struct sockaddr_in * )* ip_address ;
245+ memcpy (& (sin -> sin_addr .s_addr ), bytes , len );
246+ Py_DECREF (packed );
247+ return 1 ;
248+ }
249+ default :
250+ PyErr_SetString (PyExc_ValueError , "unexpected packed length" );
251+ Py_DECREF (packed );
252+ return 0 ;
253+ }
254+ }
255+
256+ static char * format_sockaddr (struct sockaddr * sa )
257+ {
258+ char * ip = calloc (INET6_ADDRSTRLEN , sizeof (char ));
259+
260+ char * addr ;
261+ if (sa -> sa_family == AF_INET ) {
262+ struct sockaddr_in * sin = (struct sockaddr_in * )sa ;
263+ addr = (char * )& sin -> sin_addr ;
264+ } else {
265+ struct sockaddr_in6 * sin = (struct sockaddr_in6 * )sa ;
266+ addr = (char * )& sin -> sin6_addr ;
267+ }
268+
269+ inet_ntop (sa -> sa_family , addr , ip , INET6_ADDRSTRLEN );
270+ return ip ;
271+ }
272+
273+
169274static PyObject * Reader_metadata (PyObject * self , PyObject * UNUSED (args ))
170275{
171276 Reader_obj * mmdb_obj = (Reader_obj * )self ;
0 commit comments