Calling getaddrinfo directly from Python: ai_addr is null pointer - python

I'm trying to call getaddrinfo from Python, through ctypes / libc, on Mac OS, in order to find the IP address of a domain.
The call appears to succeed: no error code is returned, and ai_addrlen is set to 28, which I understand is the appropriate length for an IPv6 address. However, ai_addr appears to be a null pointer, and I'm not sure how to begin to debug it.
How can I find the IP address of a domain using libc.getaddrinfo ?
from ctypes import (
byref,
c_char, c_char_p, c_int, c_size_t, c_void_p,
CDLL,
POINTER,
pointer,
Structure,
)
libc = CDLL(None)
class c_addrinfo(Structure):
pass
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
('ai_addr', c_void_p),
('ai_canonname', c_char_p),
('ai_next', POINTER(c_addrinfo)),
]
c_addrinfo_p = POINTER(c_addrinfo)
result = c_addrinfo_p()
error = libc.getaddrinfo(
c_char_p(b'www.google.com'),
None,
None,
byref(result),
)
print(error)  # 0
print(result.contents.ai_canonname) # b'\x1c\x1e
print(result.contents.ai_addrlen) # 28
print(bool(result.contents.ai_addr)) # False === null pointer
libc.freeaddrinfo(result)

According to the linux man page for getaddrinfo the addrinfo struct which results form getaddrinfo are stored is defined as
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
and according to the FreeBSD man page for getaddrinfo (or one of Apple's man pages for getaddrinfo which is similar), its addrinfo looks the same, assuming all the types match up.
struct addrinfo {
int ai_flags; /* input flags */
int ai_family; /* address family for socket */
int ai_socktype; /* socket type */
int ai_protocol; /* protocol for socket */
socklen_t ai_addrlen; /* length of socket-address */
struct sockaddr *ai_addr; /* socket-address for socket */
char *ai_canonname; /* canonical name for service location */
struct addrinfo *ai_next; /* pointer to next in list */
};
However looking in the FreeBSD source (or one of the open source Apple projects which is similar), we see a subtly different definition:
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* AF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
It's very easy to miss, but ai_canonname and ai_addr are the other way around to how they are documented. This means that the Python ctypes definition, for Mac(/similar) should be
class c_addrinfo(Structure):
pass
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
('ai_canonname', c_char_p),
('ai_addr', c_void_p),
('ai_next', POINTER(c_addrinfo)),
]
or one that works on both Mac and Linux (and with no comment on other platforms)
import platform
c_addrinfo._fields_ = [
('ai_flags', c_int),
('ai_family', c_int),
('ai_socktype', c_int),
('ai_protocol', c_int),
('ai_addrlen', c_size_t),
] + ([
('ai_canonname', c_char_p),
('ai_addr', c_void_p),
] if platform.system() == 'Darwin' else [
('ai_addr', c_void_p),
('ai_canonname', c_char_p),
]) + [
('ai_next', POINTER(c_addrinfo)),
]
And with these versions, on Mac, the pointer ai_addr is no longer null. You can also see an early/experimental version that parses the addresses themselves that works in both Mac and Linux.
Edit: it looks like the documentation issue has already been reported to FreeBSD

Related

Python ctypes mutable string in struct

I have the following C struct that contains a mutable C-style string.
typedef struct XMLCFG_PARAMS {
unsigned long ulMedia; // Save/load to file or buffer
unsigned long ulCfgFlags; // Flags say what type of info to save/load
char* pszBufOrFilename; // The buffer or the filename
unsigned long ulBufLen; // How long is the buffer (in bytes)
} XMLCFG_PARAMS;
I've defined the structure in Python like so:
class XMLCFG_PARAMS(ctypes.Structure):
_fields_ = [
("ulMedia", ctypes.c_ulong),
("ulCfgFlags", ctypes.c_ulong),
("pszBufOrFilename", ctypes.c_char_p),
("ulBufLen", ctypes.c_ulong),
]
This structure is then instantiated:
xml_cfg = XMLCFG_PARAMS()
xml_cfg.ulMedia = XMLCFG_BUFFER
xml_cfg.ulCfgFlags = config_flags
xml_buffer = ctypes.create_string_buffer(xml_text, size=1024 * 1024)
xml_cfg.pszBufOrFilename = ctypes.c_char_p(ctypes.addressof(xml_buffer))
xml_cfg.ulBufLen = len(xml_buffer)
The C call returns with the error WindowsError: exception: access violation reading 0x6E6F697B.
I suspect that I'm doing something wrong with the way I'm instantiating theXMLCFG_PARAMS struct, specifically the pszBufOrFilename field as this is the first time I've encountered a mutable C-style string in a struct.
C Function Signature
BOOL Control(unsigned long ulEngId,
unsigned long cmd_id,
unsigned long *ulParam0 = 0,
unsigned long *ulParam1 = 0,
unsigned long *ulParam2 = 0,
unsigned long *ulParam3 = 0);
Python ctypes call
dll_handle.Control(engine_id, cmd_id, ctypes.pointer(xml_cfg), None, None, None)
Output of hex(ctypes.addressof(xml_buffer)) is 0x3940020 while the exception keeps referring to 0x6E6F697B. This is on Win XP, so I guess this memory address doesn't change because the OS lacks ASLR.

Wrapper for libeay32.dll: how to import macro?

I'm writing small wrapper for OpenSLL libeay32.dll in Python. For majority of functions it is possible to import them as follows:
self.Function_Name = self._dll.Function_Name
self.Function_Name.restype = ctypes.c_int #for example
self.Function_Name.argtypes = [list of ctypes arguments]
Unfortunately I'm not able to import this way any macros:
X509_get_notAfter, X509_get_notBefore etc.
Any ideas, how to do it with ctypes?
You can't import macros. What you're importing are functions from a DLL. Macros aren't exported from the DLL, so there's nothing to import.
Fortunately, most macros are very simple, so you can just reimplement them in Python.
Or, alternatively, create a wrapper DLL in C that defines a function for each macro, compile and link that, and import the wrapper functions with ctypes.
Or you may want to use Cython or some other technology instead of ctypes. The way they generally work is to generate and compile C code that wraps the C library and exports Python types and functions, and it's generally as easy to export a C macro as a Python function as it is to export a C function.
Or, simplest of all… does PyOpenSSL already wrap everything you need?
After some research I decided to answer my own question. Macro for X509_get_notAfter looks like this:
#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
x - is X509 structure, which contains X509_CINF structure, which contains X509_VAL structure which contain pointer to notAfter :)
So my plan is to implement whole X509, X509_CINF and X509_VAL structure within python code.
In C it looks like this
struct x509_st
{
X509_CINF *cert_info;
X509_ALGOR *sig_alg;
ASN1_BIT_STRING *signature;
int valid;
int references;
char *name;
CRYPTO_EX_DATA ex_data;
/* These contain copies of various extension values */
long ex_pathlen;
long ex_pcpathlen;
unsigned long ex_flags;
unsigned long ex_kusage;
unsigned long ex_xkusage;
unsigned long ex_nscert;
ASN1_OCTET_STRING *skid;
AUTHORITY_KEYID *akid;
X509_POLICY_CACHE *policy_cache;
STACK_OF(DIST_POINT) *crldp;
STACK_OF(GENERAL_NAME) *altname;
NAME_CONSTRAINTS *nc;
#ifndef OPENSSL_NO_RFC3779
STACK_OF(IPAddressFamily) *rfc3779_addr;
struct ASIdentifiers_st *rfc3779_asid;
#endif
#ifndef OPENSSL_NO_SHA
unsigned char sha1_hash[SHA_DIGEST_LENGTH];
#endif
X509_CERT_AUX *aux;
} /* X509 */;
And X509_CINF looks like this:
typedef struct x509_cinf_st
{
ASN1_INTEGER *version; /* [ 0 ] default of v1 */
ASN1_INTEGER *serialNumber;
X509_ALGOR *signature;
X509_NAME *issuer;
X509_VAL *validity;
X509_NAME *subject;
X509_PUBKEY *key;
ASN1_BIT_STRING *issuerUID; /* [ 1 ] optional in v2 */
ASN1_BIT_STRING *subjectUID; /* [ 2 ] optional in v2 */
STACK_OF(X509_EXTENSION) *extensions; /* [ 3 ] optional in v3 */
ASN1_ENCODING enc;
} X509_CINF;
And here is X509_VAL:
typedef struct X509_val_st
{
ASN1_TIME *notBefore;
ASN1_TIME *notAfter;
} X509_VAL;
To make whole task easier, I decided to replace all pointers to structures which I don't want to access with ctypes.c_void_p
So my python code looks now like this:
class X509_val_st(ctypes.Structure):
_fields_ = [('notBefore', ctypes.c_void_p),
('notAfter', ctypes.c_void_p)]
class X509_cinf_st(ctypes.Structure):
_fields_ = [('version', ctypes.c_void_p),
('serialNumber', ctypes.c_void_p),
('signature', ctypes.c_void_p),
('issuer', ctypes.c_void_p),
('validity', X509_val_st),
('subject', ctypes.c_void_p),
('key', ctypes.c_void_p),
('issuerUID', ctypes.c_void_p),
('subjectUID', ctypes.c_void_p),
('extensions', ctypes.c_void_p),
('enc', ctypes.c_uint)]
class X509_st(ctypes.Structure):
_fields_ = [('cert_info', X509_cinf_st),
('sig_alg', ctypes.c_void_p),
('signature', ctypes.c_void_p),
('valid', ctypes.c_int),
('references', ctypes.c_int),
('name', ctypes.c_void_p),
('ex_data', ctypes.c_int),
('ex_pathlen', ctypes.c_long),
('ex_pcpathlen', ctypes.c_long),
('ex_flags', ctypes.c_ulong),
('ex_kusage', ctypes.c_ulong),
('ex_xkusage', ctypes.c_ulong),
('ex_nscert', ctypes.c_ulong),
('skid', ctypes.c_void_p),
('akid', ctypes.c_void_p),
('policy_cache', ctypes.c_void_p),
('crldp', ctypes.c_void_p),
('altname', ctypes.c_void_p),
('nc', ctypes.c_void_p),
('rfc3779_addr', ctypes.c_void_p),
('rfc3779_asid', ctypes.c_void_p),
('sha1_hash', ctypes.c_char),
('aux', ctypes.c_void_p)]
And the last step: to assign structure to the pointer received from function X509_new():
self.X509_new = self._lib.X509_new
self.X509_new.restype = ctypes.POINTER(X509_st)
self.X509_new.argtypes = []
So python function for OpenSSL macro X509_get_notBefore will look like this:
def X509_get_notBefore(self):
return self.X509[0].cert_info.validity.notBefore

Using struct timeval in Python

I have a C program containing a structure
struct S{
int x;
struct timeval t;
};
and a function
int func(struct S s1, struct S s2)
I need to call this function from my python program.
I am using ctypes.The parallel structure on Python
import ctypes
from ctypes import *
class S(Structure):
_fields_ = [("x",c_int),
("t", ?)]
Now, my question is what will I write in the ? place and any dependencies related to it.
Thanks in advance.
Find the definition of struct timeval in your platform's C include files (the Internet suggests sys/time.h), then transcode that into a ctypes structure.
On my platform a struct timeval is
struct timeval {
long tv_sec;
long tv_usec;
};
(and I suppose this is the standard anyway), so
class timeval(Structure):
_fields_ = [("tv_sec", c_long), ("tv_usec", c_long)]
class S(Structure):
_fields_ = [("x",c_int), ("t", timeval)]
would probably fit the bill.
without any further info, it will be the definition of timeval:
class timeval(Structure):
_fields_ = [("tv_sec",c_long),
("tv_usec", c_long)]
class S(Structure):
_fields_ = [("x",c_int),
("t", timeval)]

Python equivalent of C struct (porting app form C to python)

I'm porting a simple bluetooth app, which sends "magic" packet on L2Cap protocol to bluetooth device..
I have a problem with converting struct object in C to python equivalent..
In c:
/* command types */
#define CFGBT_TYPE_SETREQ 0x00
#define CFGBT_TYPE_SETRES 0x01
#define CFGBT_TYPE_GETREQ 0x02
#define CFGBT_TYPE_GETRES 0x03
/* varid types */
#define CFG_VARIDT_UINT16 0x0000
#define CFG_VARIDT_UINT32 0x1000
#define CFG_VARIDT_STRING16 0x2000
typedef struct {
uint8_t type, status;
uint16_t varid;
uint8_t value[16];
} __attribute__((packed)) CFGBTFRAME;
static CFGBTFRAME c;
and then in app it's used like that:
/* set up */
c.type = CFGBT_TYPE_GETREQ;
c.varid = strtol(argv[3], NULL, 0);
c.status = 0;
memset(c.value, 0, 16);
/* send frame */
write(s, &c, sizeof(c));
Can you point me out how to construct same packet/stuct-like structure using python?
I know I will probably need to use ctypes and create "empty" class, but how to get all this together?
You can go about it with the struct module to pack values into a byte string, for example:
>>> import struct
>>> type, status, varid, value = 1, 0, 16, b'Hello'
>>> buffer = struct.pack('>BBH16s', type, status, varid, value)
>>> buffer
b'\x01\x00\x00\x10Hello\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Alternatively, you can use ctypes.Structure to define a class that will be representing your structure. It has the advantage of being easier to use with Python code, but you have to take into account alignment and padding issues and resolve them yourself (perhaps with struct).
If your goal is to group a set of key/value in an object, you may use dict or namedtuple.
a dict would be:
CFGBTFRAME = {
'type' : myType,
'status' : ...
}
access: CFGBTFRAME['type']
with namedtuple:
from collections import namedtuple
CFGBTFRAME = namedtuple('CFGBTFRAME', ['type', 'status', ...])
c = CFGBTFRAME()
c.type = myType
see http://docs.python.org/library/collections.html#collections.namedtuple for more information about namedtuple.

python using ctypes to work with dll - structure OUT argument

In the header file of the dll I have the following structure
typedef struct USMC_Devices_st{
DWORD NOD; // Number of the devices ready to work
char **Serial; // Array of 16 byte ASCII strings
char **Version; // Array of 4 byte ASCII strings
} USMC_Devices; // Structure representing connected devices
I would like to call a dll function:
DWORD USMC_Init( USMC_Devices &Str );
I tried with this:
class USMCDevices(Structure):
_fields_ = [("NOD", c_long),
("Serial", c_char_p),
("Version", c_char_p)]
usmc = cdll.USMCDLL #this is the dll file
init = usmc.USMC_Init
init.restype = c_int32; # return type
init.argtypes = [USMCDevices]; # argument
dev = USMCDevices()
init(dev)
I get an error here. I guess the problem is with "Serial" and "Version" which both are array corresponding to the NOD (number of devices).
Any ideas how to solve this problem?
I really appreciate your help!!!
Use POINTER(c_char_p) for the char ** pointers. Indexing Serial or Version creates a Python string for the given null-terminated string. Note that indexing in the array beyond NOD - 1 either produces garbage values or will crash the interpreter.
C:
#include <windows.h>
typedef struct USMC_Devices_st {
DWORD NOD; // Number of the devices ready to work
char **Serial; // Array of 16 byte ASCII strings
char **Version; // Array of 4 byte ASCII strings
} USMC_Devices;
char *Serial[] = {"000000000000001", "000000000000002"};
char *Version[] = {"001", "002"};
__declspec(dllexport) DWORD USMC_Init(USMC_Devices *devices) {
devices->NOD = 2;
devices->Serial = Serial;
devices->Version = Version;
return 0;
}
// build: cl usmcdll.c /LD
Python:
import ctypes
from ctypes import wintypes
class USMCDevices(ctypes.Structure):
_fields_ = [("NOD", wintypes.DWORD),
("Serial", ctypes.POINTER(ctypes.c_char_p)),
("Version", ctypes.POINTER(ctypes.c_char_p))]
usmc = ctypes.cdll.USMCDLL
init = usmc.USMC_Init
init.restype = wintypes.DWORD
init.argtypes = [ctypes.POINTER(USMCDevices)]
dev = USMCDevices()
init(ctypes.byref(dev))
devices = [dev.Serial[i] + b':' + dev.Version[i]
for i in range(dev.NOD)]
print('\n'.join(d.decode('ascii') for d in devices))
Output:
000000000000001:001
000000000000002:002

Categories