Internals

We ran into three main problems developing this: Exceptions, callbacks and accessing socket methods. This is what this chapter is about.

Exceptions

We realized early that most of the exceptions would be raised by the I/O functions of OpenSSL, so it felt natural to mimic OpenSSL’s error code system, translating them into Python exceptions. This naturally gives us the exceptions SSL.ZeroReturnError, SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError and SSL.SysCallError.

For more information about this, see section SSL — An interface to the SSL-specific parts of OpenSSL.

Callbacks

Callbacks were more of a problem when pyOpenSSL was written in C. Having switched to being written in Python using cffi, callbacks are now straightforward. The problems that originally existed no longer do (if you are interested in the details you can find descriptions of those problems in the version control history for this document).

Accessing Socket Methods

We quickly saw the benefit of wrapping socket methods in the SSL.Connection class, for an easy transition into using SSL. The problem here is that the socket module lacks a C API, and all the methods are declared static. One approach would be to have OpenSSL as a submodule to the socket module, placing all the code in socketmodule.c, but this is obviously not a good solution, since you might not want to import tonnes of extra stuff you’re not going to use when importing the socket module. The other approach is to somehow get a pointer to the method to be called, either the C function, or a callable Python object. This is not really a good solution either, since there’s a lot of lookups involved.

The way it works is that you have to supply a socket- like transport object to the SSL.Connection. The only requirement of this object is that it has a fileno() method that returns a file descriptor that’s valid at the C level (i.e. you can use the system calls read and write). If you want to use the connect() or accept() methods of the SSL.Connection object, the transport object has to supply such methods too. Apart from them, any method lookups in the SSL.Connection object that fail are passed on to the underlying transport object.

Future changes might be to allow Python-level transport objects, that instead of having fileno() methods, have read() and write() methods, so more advanced features of Python can be used. This would probably entail some sort of OpenSSL BIOs, but converting Python strings back and forth is expensive, so this shouldn’t be used unless necessary. Other nice things would be to be able to pass in different transport objects for reading and writing, but then the fileno() method of SSL.Connection becomes virtually useless. Also, should the method resolution be used on the read-transport or the write-transport?