Reputation: 760
I'm trying to get my head around ctypes in python, and I'm able to get some simple things working, but when it comes to unpacking c structs I find myself running into some difficulties. I decided that to learn this I should play around a bit, and while I know the socket standard library implements gethostbyname_ex()
, I thought I'd make an attempt at implementing it using ctypes
and libc.gethostbyname()
.
I'm able to execute libc.gethostbyname()
fairly easily:
#!/usr/bin/env python
from ctypes import *
cdll.LoadLibrary('libc.so.6')
libc = CDLL('libc.so.6')
he = libc.gethostbyname("www.google.com")
But that gives me a hostent
data-structure. I thought the best way to unpack this would be to grab the c struct and create a class which inherits from ctypes.Structure
, and so I came up with this (I found the hostent
struct definition in netdb.h
):
class hostent(Structure):
'''
struct hostent
{
char *h_name; /* Official name of host. */
char **h_aliases; /* Alias list. */
int h_addrtype; /* Host address type. */
int h_length; /* Length of address. */
char **h_addr_list; /* List of addresses from name server. */
}
'''
_fields_ = [("h_name", c_char_p), ("h_aliases", POINTER(c_char_p)),
("h_addrtype", c_int), ("h_length", c_int),
("h_addr_list", POINTER(c_char_p))]
Where I'm unclear is whether I've setup the h_aliases
and the h_addr_list
fields correctly, because whenever I attempt to access them as arrays, even at the 0th index after a lookup of something that I know has at least one alias and at least one address, I get a NULL pointer access ValueError
exception:
>>> he = hostent(libc.gethostbyname("www.google.com"))
>>> pprint.pprint(he.h_addr_list)
<__main__.LP_c_char_p object at 0xb75dae84>
>>> print he.h_addr_list[0]
Traceback (most recent call last):
File "/tmp/py2659JxK", line 24, in <module>
print he.h_addr_list[0]
ValueError: NULL pointer access
Any suggestions would be welcome.
Upvotes: 3
Views: 2094
Reputation: 110108
You need to define what the return type of gethostbyname
is:
>>> libc.gethostbyname.restype = POINTER(hostent)
>>> he = libc.gethostbyname("www.google.com")[0]
>>> he.h_aliases[0]
'www.google.com'
Also, h_addr_list
should not be declared as POINTER(c_char_p)
, because c_char_p
is used for null-terminated strings. In this case POINTER(POINTER(c_ubyte))
would be better, and then the first address would be he.h_addr_list[0][:4]
if it's an IPv4 address.
Upvotes: 5