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
Related
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
I'm writing framework in Python 2.7 which would provide functionality to test C written APIs. I have the DLL and the C source code itself. The problem is that some of the C API functions require as an input structure of a callback functions and I can't find the way how to create structure in Python which fields are the C function pointers. I know how to create callback function in Python using ctypes, but I need to pack those callbacks in C structure to pass to C API. All these in code looks like this:
The C function pointers and the C structure:
#define DRIVERCALLAPI
typedef void*( DRIVERCALLAPI *fn_DRV_COMM_Open )(void*);
typedef void(DRIVERCALLAPI *fn_DRV_COMM_Close)(void*);
typedef int(DRIVERCALLAPI *fn_DRV_COMM_Transfer)(void *, unsigned char *, int);
typedef struct DriverCallbacks_t
{
fn_DRV_COMM_Open drvcomm_Open;
fn_DRV_COMM_Close drvcomm_Close;
fn_DRV_COMM_Transfer drvcomm_Transfer;
} DriverCallbacks;
typedef struct InitDataEntry_t
{
char iface[64];
void* handle;
} InitDataEntry;
Where handle points to an object of DriverCallbacks.
typedef struct InitDataContainer_t
{
uint32_t size;
uint32_t id;
InitDataEntry* data;
} InitDataContainer;
The pointer of a InitDataContainer should be passed to the API function.
void* dev_Create( void* args )
The API initializes the callback functions with appropriate functions to use them later. I need to somehow create Python structures of DriverCallbacks InitDataEntry and InitDataContainer. Any hint on how it can be achieved ? Thanks in advance !
After many experiments I finally found how to create Python structures which correspond to C structures with function pointers as fields. The idea is to use void pointers instead i.e. c_void_p from ctypes. For the provided example the python code would be:
from ctypes import *
class DriverCallbacks(Structure):
_fields_ = [
("drvcomm_Open", c_void_p),
("drvcomm_Close", c_void_p),
("drvcomm_Transfer", c_void_p)
]
class InitDataEntry(Structure):
_fields_ = [
("iface", 64 * c_byte),
("handle", c_void_p)
]
class InitDataContainer(Structure):
_fields_ = [
("size", c_uint),
("id", c_uint),
("data", POINTER(InitDataEntry))
]
The creation of the objects and the library function call could be like this (it's tested and works for me):
lib = cdll.LoadLibrary(LIBNAME)
driverFuncList = DriverCallbacks()
driverFuncList.drvcomm_Open = cast(lib.ftdi_open, c_void_p)
driverFuncList.drvcomm_Close = cast(lib.ftdi_close, c_void_p)
driverFuncList.drvcomm_Transfer = cast(lib.ftdi_transfer, c_void_p)
initData = InitDataEntry()
libc = cdll.msvcrt
libc.strcpy(byref(initData.iface), c_char_p("DriverCallbacks"))
initData.handle = cast(pointer(driverFuncList), c_void_p)
initDataCont = InitDataContainer()
initDataCont.size = c_uint(3)
initDataCont.id = c_uint(0)
initDataCont.data = pointer(initData)
ret = lib.dev_Create(byref(initDataCont))
The driverFuncList object can be filled also from within the C library if there is a such function which sets the callback function pointers.
Im trying to get SendKeysCtypes working on py2.7 and win7 64bit. Here is src
Problem:
Run SendKeysCtypes.py and nothing happens. Tests should open notepad and write some text.
The problem code is this:
def GetInput(self):
"Build the INPUT structure for the action"
actions = 1
# if both up and down
if self.up and self.down:
actions = 2
inputs = (INPUT * actions)()
vk, scan, flags = self._get_key_info()
for inp in inputs:
inp.type = INPUT_KEYBOARD
inp._.ki.wVk = vk
inp._.ki.wScan = scan
inp._.ki.dwFlags |= flags
# if we are releasing - then let it up
if self.up:
inputs[-1]._.ki.dwFlags |= KEYEVENTF_KEYUP
return inputs
def Run(self):
"Execute the action"
inputs = self.GetInput()
return SendInput(
len(inputs),
ctypes.byref(inputs),
ctypes.sizeof(INPUT))
SendInput() in above code does nothing.
Other tests
I tried the code in this answer and it worked ok. But this code
had some other issues.
According to the author it should work with py3.1
SendInput() returns '0' , which means "blocked by another
thread"
A call to ctypes.GetLastError() gives us error code 87, which means "ERROR_INVALID_PARAMETER"
And here we are stuck because my windows programming is very limited, can anyone shed some light on this?
EDIT 1:
Following the "64bit is problem" trace, lead me to this SO question, will see if I can convert it.
There are two problems with the Structure and Union classes you're using. I'll reproduce a snippet from the code you linked to with one of the classes as example here:
class KEYBDINPUT(ctypes.Structure):
"A particular keyboard event"
_pack_ = 2 # FIXME: don't do this
_fields_ = [
# C:/PROGRA~1/MICROS~4/VC98/Include/winuser.h 4292
('wVk', WORD),
('wScan', WORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', DWORD), # FIXME: use correct data type
]
(The FIXMEs were added by me to point out the problematic parts.)
The first is that you're using _pack_ = 2, which is incorrect. You should not use any _pack_ alignment specifier at all. The defaults work fine both on 32-bit and on 64-bit Windows.
It's purely by chance that it worked with _pack_ = 2 on 32-bit Windows: All 2-byte sized data types used in the code come in pairs and start on already aligned boundaries and therefore happen to produce the same structures as with 4-byte alignment.
However, under 64-bit Windows the fundamental alignment is 8 byte, so the structures would be misaligned and have wrong sizes if you used _pack_ = 2 or _pack_ = 4 there.
The second problem is that there's no ULONG_PTR in ctypes.wintypes, which would be the correct data type for dwExtraInfo. Under 32-bit Windows ULONG_PTR is an unsigned 32-bit integer whereas under 64-bit Windows it is an unsigned 64-bit integer.
The MSDN article about Windows Data Types shows how it's defined:
#if defined(_WIN64)
typedef unsigned __int64 ULONG_PTR;
#else
typedef unsigned long ULONG_PTR;
#endif
Using DWORD or ULONG for dwExtraInfo therefore only happens to work under 32-bit Windows and produces the wrong size otherwise.
While using some POINTER based data type instead would happen to produce a correctly aligned and sized structure, it would be wrong both in meaning as well as in usage, as ULONG_PTR is "an unsigned integer that can also store a (casted) pointer" rather than an actual pointer.
Looking through wintypes, you'll notice that WPARAM happens to be defined exactly like what ULONG_PTR is supposed to be. So a quick and dirty (but still reasonably robust) way to get ULONG_PTR would be:
from ctypes.wintypes import WPARAM as ULONG_PTR
Alternatively, you could use a piece of code inspired by theirs and define it yourself:
import ctypes
for ULONG_PTR in [ctypes.c_ulong, ctypes.c_ulonglong]:
if ctypes.sizeof(ULONG_PTR) == ctypes.sizeof(ctypes.c_void_p):
break
else:
raise TypeError("cannot find a suitable type for ULONG_PTR")
Armed with those adaptions, you could define the structures like this:
class MOUSEINPUT(ctypes.Structure):
_fields_ = [
('dw', LONG),
('dy', LONG),
('mouseData', DWORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ULONG_PTR),
]
class KEYBDINPUT(ctypes.Structure):
_fields_ = [
('wVk', WORD),
('wScan', WORD),
('dwFlags', DWORD),
('time', DWORD),
('dwExtraInfo', ULONG_PTR),
]
class HARDWAREINPUT(ctypes.Structure):
_fields_ = [
('uMsg', DWORD),
('wParamL', WORD),
('wParamH', WORD),
]
class _INPUT(ctypes.Union):
_fields_ = [
('mi', MOUSEINPUT),
('ki', KEYBDINPUT),
('hi', HARDWAREINPUT),
]
class INPUT(ctypes.Structure):
_anonymous_ = ['']
_fields_ = [
('type', DWORD),
('', _INPUT),
]
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)]
I am porting visual c++ 6 application to python using ctypes.
It uses same windows library dll. and I am only porting windows forms to python application.
and I am stucked when I am sending cpp structure to dll in python.
I have tried to find solution on the struct differeces. from cpp code, there is a struct tagging(tagTest) but there is no equivalent in python code. is there any solution for that?
this is cpp that I want to port from.
========================cpp part================================
BOOL CSMessage(HWND hWnd, const char* string1, const char* string2, const char* ltagTest, int structlen);
typedef struct tagTest
{
char item1 [ 8]; char _item1;
char item2 [ 1]; char _item2;
} TcTest, *LPTest;
void Initiator()
{
TcTest ctest ={0};
memset(&ctest,0x20,sizeof TcTest);
move(ctest.item1,"TESTSTRI");
move(ctest.item2,"1");
mhandler.CSMessage(GetSafeHwnd(),"String","Call",(char*)&ctest,sizeof TcTest);
}
=========================pythonpart============================
csMessageProto=ctypes.WINFUNCTYPE(BOOL, HWND, c_char_p, c_char_p, c_char_p, c_int)
csMessageParams=((1,"hWnd",0),(1,"string1",0),(1,"string2",0),(1,"ltagTest",0),(1,"structlen",0))
CSMessage=csMessageProto(("CSMessage",testDll), csMessageParams)
class tagTest(ctypes.Structure):
_fields_ = [
('item1', c_char*8),('_item1', c_char),
('item2', c_char*1),('_item2', c_char)
]
PtagTest=ctypes.POINTER(tagTest)
utestblock=tagTest()
utestblock.item1=str("TESTSTRI")
utestblock.item2=str(1)
CSMessage(hwnd, "String", "Call", ctypes.byref(utestblock), ctypes.sizeof(utestblock));
revision history
added declaration of dll function both cpp and python
revised with #avl_sweden suggestions
=====================result==============================
after applying #avl_sweden's suggestion,
After test, the error msg is this.
ctypes.ArgumentError: argument 4: <type 'exceptions.TypeError'>: wrong type
and for the type
print "Is Type : "+str(ctypes.byref(utestblock))
Is Type : <cparam 'P' (023e5968)>
=========================================================
so it's looks like types are different in def and instance.
in first line in python code
1: csMessageProto=ctypes.WINFUNCTYPE(BOOL, HWND, c_char_p, c_char_p, c_char_p, c_int)
the fourth c_char_p is not matching
16: CSMessage(hwnd, "String", "Call", ctypes.byref(utestblock), ctypes.sizeof(utestblock));
to match the differences.
I changed code to
1: csMessageProto=ctypes.WINFUNCTYPE(BOOL, HWND, c_char_p, PtagTest, c_char_p, c_int)
and code returns no error. but I cannot find if it is sent through. becuase there was no return from dll.
do you think this is the right solution?
=====================================================================================
i have been tested and it seems it is right solution.
Thank you again.
I see four things wrong here:
First, your python Structure definition doesn't match the one in the C-code snippet. The python one should be:
class tagTest(ctypes.Structure):
_fields_ = [
('item1', c_char*8), ('_item1',c_char),
('item2', c_char*1), ('_item2',c_char)
]
The second thing I see, is that you're sending a pointer to a pointer to the CSMessage function.
Could you try:
utestblock=tagTest()
utestblock.item1=str("TESTSTRI")
utestblock.item2=str(1)
loadeddll.CSMessage(hwnd, "String", "Call", ctypes.byref(utestblock),
ctypes.sizeof(utestblock))
The third issue is less severe, and this is that the string "TESTSTRING" does not fit into 8 bytes.
The fourth issue I see, is that the types of the default parameters is wrong.
Try this instead:
csMessageParams=((1, "hWnd", 0), (1, "string1", ""), (1, "string2", ""),
(1, "ltagTest", ""), (1, "structlen", 0))