#include <Python.h>
#define SSL_MODULE
#include <openssl/err.h>
#include "ssl.h"
#ifndef MS_WINDOWS
# include <sys/socket.h>
# include <netinet/in.h>
# if !(defined(__BEOS__) || defined(__CYGWIN__))
# include <netinet/tcp.h>
# endif
#else
# include <winsock.h>
#endif
static char *CVSid = "@(#) $Id: connection.c,v 1.2 2004/09/23 14:25:28 murata Exp $";
static void
syscall_from_errno(void)
{
#ifdef MS_WINDOWS
int errnum = WSAGetLastError();
if (errnum)
{
static struct { int num; const char *msg; } *msgp, msgs[] = {
{ WSAEINTR, "Interrupted system call" },
{ WSAEBADF, "Bad file descriptor" },
{ WSAEACCES, "Permission denied" },
{ WSAEFAULT, "Bad address" },
{ WSAEINVAL, "Invalid argument" },
{ WSAEMFILE, "Too many open files" },
{ WSAEWOULDBLOCK, "The socket operation could not complete "
"without blocking" },
{ WSAEINPROGRESS, "Operation now in progress" },
{ WSAEALREADY, "Operation already in progress" },
{ WSAENOTSOCK, "Socket operation on non-socket" },
{ WSAEDESTADDRREQ, "Destination address required" },
{ WSAEMSGSIZE, "Message too long" },
{ WSAEPROTOTYPE, "Protocol wrong type for socket" },
{ WSAENOPROTOOPT, "Protocol not available" },
{ WSAEPROTONOSUPPORT, "Protocol not supported" },
{ WSAESOCKTNOSUPPORT, "Socket type not supported" },
{ WSAEOPNOTSUPP, "Operation not supported" },
{ WSAEPFNOSUPPORT, "Protocol family not supported" },
{ WSAEAFNOSUPPORT, "Address family not supported" },
{ WSAEADDRINUSE, "Address already in use" },
{ WSAEADDRNOTAVAIL, "Can't assign requested address" },
{ WSAENETDOWN, "Network is down" },
{ WSAENETUNREACH, "Network is unreachable" },
{ WSAENETRESET, "Network dropped connection on reset" },
{ WSAECONNABORTED, "Software caused connection abort" },
{ WSAECONNRESET, "Connection reset by peer" },
{ WSAENOBUFS, "No buffer space available" },
{ WSAEISCONN, "Socket is already connected" },
{ WSAENOTCONN, "Socket is not connected" },
{ WSAESHUTDOWN, "Can't send after socket shutdown" },
{ WSAETOOMANYREFS, "Too many references: can't splice" },
{ WSAETIMEDOUT, "Operation timed out" },
{ WSAECONNREFUSED, "Connection refused" },
{ WSAELOOP, "Too many levels of symbolic links" },
{ WSAENAMETOOLONG, "File name too long" },
{ WSAEHOSTDOWN, "Host is down" },
{ WSAEHOSTUNREACH, "No route to host" },
{ WSAENOTEMPTY, "Directory not empty" },
{ WSAEPROCLIM, "Too many processes" },
{ WSAEUSERS, "Too many users" },
{ WSAEDQUOT, "Disc quota exceeded" },
{ WSAESTALE, "Stale NFS file handle" },
{ WSAEREMOTE, "Too many levels of remote in path" },
{ WSASYSNOTREADY, "Network subsystem is unvailable" },
{ WSAVERNOTSUPPORTED, "WinSock version is not supported" },
{ WSANOTINITIALISED, "Successful WSAStartup() not yet performed" },
{ WSAEDISCON, "Graceful shutdown in progress" },
{ WSAHOST_NOT_FOUND, "No such host is known" },
{ WSATRY_AGAIN, "Host not found, or server failed" },
{ WSANO_RECOVERY, "Unexpected server error encountered" },
{ WSANO_DATA, "Valid name without requested data" },
{ WSANO_ADDRESS, "No address, look for MX record" },
{ 0, NULL }
};
PyObject *v;
const char *msg = "winsock error";
for (msgp = msgs; msgp->msg; msgp++)
{
if (errnum == msgp->num)
{
msg = msgp->msg;
break;
}
}
v = Py_BuildValue("(is)", errnum, msg);
if (v != NULL)
{
PyErr_SetObject(ssl_SysCallError, v);
Py_DECREF(v);
}
return;
}
#else
PyErr_SetFromErrno(ssl_SysCallError);
#endif
}
static void
handle_ssl_errors(SSL *ssl, int err, int ret)
{
switch (err)
{
case SSL_ERROR_ZERO_RETURN:
PyErr_SetNone(ssl_ZeroReturnError);
break;
case SSL_ERROR_WANT_READ:
PyErr_SetNone(ssl_WantReadError);
break;
case SSL_ERROR_WANT_WRITE:
PyErr_SetNone(ssl_WantWriteError);
break;
case SSL_ERROR_WANT_X509_LOOKUP:
PyErr_SetNone(ssl_WantX509LookupError);
break;
case SSL_ERROR_SYSCALL:
if (ERR_peek_error() == 0)
{
if (ret < 0)
{
syscall_from_errno();
}
else
{
PyObject *v;
v = Py_BuildValue("(is)", -1, "Unexpected EOF");
if (v != NULL)
{
PyErr_SetObject(ssl_SysCallError, v);
Py_DECREF(v);
}
}
break;
}
case SSL_ERROR_SSL:
;
default:
exception_from_error_queue();
break;
}
}
static char ssl_Connection_get_context_doc[] = "\n\
Get session context\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: A Context object\n\
";
static PyObject *
ssl_Connection_get_context(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":get_context"))
return NULL;
Py_INCREF(self->context);
return (PyObject *)self->context;
}
static char ssl_Connection_pending_doc[] = "\n\
Get the number of bytes that can be safely read from the connection\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: \n\
";
static PyObject *
ssl_Connection_pending(ssl_ConnectionObj *self, PyObject *args)
{
int ret;
if (!PyArg_ParseTuple(args, ":pending"))
return NULL;
ret = SSL_pending(self->ssl);
return PyInt_FromLong((long)ret);
}
static char ssl_Connection_send_doc[] = "\n\
Send data on the connection. NOTE: If you get one of the WantRead,\n\
WantWrite or WantX509Lookup exceptions on this, you have to call the\n\
method again with the SAME buffer.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be:\n\
buf - The string to send\n\
flags - (optional) Included for compatability with the socket\n\
API, the value is ignored\n\
Returns: The number of bytes written\n\
";
static PyObject *
ssl_Connection_send(ssl_ConnectionObj *self, PyObject *args)
{
char *buf;
int len, ret, err, flags;
if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
return NULL;
MY_BEGIN_ALLOW_THREADS(self->tstate)
ret = SSL_write(self->ssl, buf, len);
MY_END_ALLOW_THREADS(self->tstate)
if (PyErr_Occurred())
{
flush_error_queue();
return NULL;
}
err = SSL_get_error(self->ssl, ret);
if (err == SSL_ERROR_NONE)
{
return PyInt_FromLong((long)ret);
}
else
{
handle_ssl_errors(self->ssl, err, ret);
return NULL;
}
}
static char ssl_Connection_sendall_doc[] = "\n\
Send \"all\" data on the connection. This calls send() repeatedly until\n\
all data is sent. If an error occurs, it's impossible to tell how much data\n\
has been sent.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be:\n\
buf - The string to send\n\
flags - (optional) Included for compatability with the socket\n\
API, the value is ignored\n\
Returns: The number of bytes written\n\
";
static PyObject *
ssl_Connection_sendall(ssl_ConnectionObj *self, PyObject *args)
{
char *buf;
int len, ret, err, flags;
PyObject *pyret = Py_None;
if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
return NULL;
do {
MY_BEGIN_ALLOW_THREADS(self->tstate)
ret = SSL_write(self->ssl, buf, len);
MY_END_ALLOW_THREADS(self->tstate)
if (PyErr_Occurred())
{
flush_error_queue();
pyret = NULL;
break;
}
err = SSL_get_error(self->ssl, ret);
if (err == SSL_ERROR_NONE)
{
buf += ret;
len -= ret;
}
else if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL ||
err == SSL_ERROR_ZERO_RETURN)
{
handle_ssl_errors(self->ssl, err, ret);
pyret = NULL;
break;
}
} while (len > 0);
Py_XINCREF(pyret);
return pyret;
}
static char ssl_Connection_recv_doc[] = "\n\
Receive data on the connection. NOTE: If you get one of the WantRead,\n\
WantWrite or WantX509Lookup exceptions on this, you have to call the\n\
method again with the SAME buffer.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be:\n\
bufsiz - The maximum number of bytes to read\n\
flags - (optional) Included for compatability with the socket\n\
API, the value is ignored\n\
Returns: The number of bytes read\n\
";
static PyObject *
ssl_Connection_recv(ssl_ConnectionObj *self, PyObject *args)
{
int bufsiz, ret, err, flags;
PyObject *buf;
if (!PyArg_ParseTuple(args, "i|i:recv", &bufsiz, &flags))
return NULL;
buf = PyString_FromStringAndSize(NULL, bufsiz);
if (buf == NULL)
return NULL;
MY_BEGIN_ALLOW_THREADS(self->tstate)
ret = SSL_read(self->ssl, PyString_AsString(buf), bufsiz);
MY_END_ALLOW_THREADS(self->tstate)
if (PyErr_Occurred())
{
Py_DECREF(buf);
flush_error_queue();
return NULL;
}
err = SSL_get_error(self->ssl, ret);
if (err == SSL_ERROR_NONE)
{
if (ret != bufsiz && _PyString_Resize(&buf, ret) < 0)
return NULL;
return buf;
}
else
{
handle_ssl_errors(self->ssl, err, ret);
Py_DECREF(buf);
return NULL;
}
}
static char ssl_Connection_renegotiate_doc[] = "\n\
Renegotiate the session\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: True if the renegotiation can be started, false otherwise\n\
";
static PyObject *
ssl_Connection_renegotiate(ssl_ConnectionObj *self, PyObject *args)
{
int ret;
if (!PyArg_ParseTuple(args, ":renegotiate"))
return NULL;
MY_BEGIN_ALLOW_THREADS(self->tstate);
ret = SSL_renegotiate(self->ssl);
MY_END_ALLOW_THREADS(self->tstate);
if (PyErr_Occurred())
{
flush_error_queue();
return NULL;
}
return PyInt_FromLong((long)ret);
}
static char ssl_Connection_do_handshake_doc[] = "\n\
Perform an SSL handshake (usually called after renegotiate() or one of\n\
set_*_state()). This can raise the same exceptions as send and recv.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: None.\n\
";
static PyObject *
ssl_Connection_do_handshake(ssl_ConnectionObj *self, PyObject *args)
{
int ret, err;
if (!PyArg_ParseTuple(args, ":do_handshake"))
return NULL;
MY_BEGIN_ALLOW_THREADS(self->tstate);
ret = SSL_do_handshake(self->ssl);
MY_END_ALLOW_THREADS(self->tstate);
if (PyErr_Occurred())
{
flush_error_queue();
return NULL;
}
err = SSL_get_error(self->ssl, ret);
if (err == SSL_ERROR_NONE)
{
Py_INCREF(Py_None);
return Py_None;
}
else
{
handle_ssl_errors(self->ssl, err, ret);
return NULL;
}
}
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L
static char ssl_Connection_renegotiate_pending_doc[] = "\n\
Check if there's a renegotiation in progress, it will return false once\n\
a renegotiation is finished.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: Whether there's a renegotiation in progress\n\
";
static PyObject *
ssl_Connection_renegotiate_pending(ssl_ConnectionObj *self, PyObject *args)
{
int ret;
if (!PyArg_ParseTuple(args, ":renegotiate_pending"))
return NULL;
return PyInt_FromLong((long)SSL_renegotiate_pending(self->ssl));
}
#endif
static char ssl_Connection_total_renegotiations_doc[] = "\n\
Find out the total number of renegotiations.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: The number of renegotiations.\n\
";
static PyObject *
ssl_Connection_total_renegotiations(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":total_renegotiations"))
return NULL;
return PyInt_FromLong(SSL_total_renegotiations(self->ssl));
}
static char ssl_Connection_set_accept_state_doc[] = "\n\
Set the connection to work in server mode. The handshake will be handled\n\
automatically by read/write.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: None\n\
";
static PyObject *
ssl_Connection_set_accept_state(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":set_accept_state"))
return NULL;
SSL_set_accept_state(self->ssl);
Py_INCREF(Py_None);
return Py_None;
}
static char ssl_Connection_set_connect_state_doc[] = "\n\
Set the connection to work in client mode. The handshake will be handled\n\
automatically by read/write.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: None\n\
";
static PyObject *
ssl_Connection_set_connect_state(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":set_connect_state"))
return NULL;
SSL_set_connect_state(self->ssl);
Py_INCREF(Py_None);
return Py_None;
}
static char ssl_Connection_connect_doc[] = "\n\
Connect to remote host and set up client-side SSL\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be:\n\
addr - A remote address\n\
Returns: What the socket's connect method returns\n\
";
static PyObject *
ssl_Connection_connect(ssl_ConnectionObj *self, PyObject *args)
{
PyObject *meth, *ret;
if ((meth = PyObject_GetAttrString(self->socket, "connect")) == NULL)
return NULL;
SSL_set_connect_state(self->ssl);
ret = PyEval_CallObject(meth, args);
Py_DECREF(meth);
if (ret == NULL)
return NULL;
return ret;
}
static char ssl_Connection_connect_ex_doc[] = "\n\
Connect to remote host and set up client-side SSL. Note that if the socket's\n\
connect_ex method doesn't return 0, SSL won't be initialized.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be:\n\
addr - A remove address\n\
Returns: What the socket's connect_ex method returns\n\
";
static PyObject *
ssl_Connection_connect_ex(ssl_ConnectionObj *self, PyObject *args)
{
PyObject *meth, *ret;
if ((meth = PyObject_GetAttrString(self->socket, "connect_ex")) == NULL)
return NULL;
SSL_set_connect_state(self->ssl);
ret = PyEval_CallObject(meth, args);
Py_DECREF(meth);
if (ret == NULL)
return NULL;
if (PyInt_Check(ret) && PyInt_AsLong(ret) != 0)
return ret;
return ret;
}
static char ssl_Connection_accept_doc[] = "\n\
Accept incoming connection and set up SSL on it\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: A (conn,addr) pair where conn is a Connection and addr is an\n\
address\n\
";
static PyObject *
ssl_Connection_accept(ssl_ConnectionObj *self, PyObject *args)
{
PyObject *tuple, *socket, *address, *meth;
ssl_ConnectionObj *conn;
if ((meth = PyObject_GetAttrString(self->socket, "accept")) == NULL)
return NULL;
tuple = PyEval_CallObject(meth, args);
Py_DECREF(meth);
if (tuple == NULL)
return NULL;
socket = PyTuple_GetItem(tuple, 0);
Py_INCREF(socket);
address = PyTuple_GetItem(tuple, 1);
Py_INCREF(address);
Py_DECREF(tuple);
conn = ssl_Connection_New(self->context, socket);
Py_DECREF(socket);
if (conn == NULL)
{
Py_DECREF(address);
return NULL;
}
SSL_set_accept_state(conn->ssl);
tuple = Py_BuildValue("(OO)", conn, address);
Py_DECREF(conn);
Py_DECREF(address);
return tuple;
}
static char ssl_Connection_shutdown_doc[] = "\n\
Send closure alert\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: True if the shutdown completed successfully (i.e. both sides\n\
have sent closure alerts), false otherwise (i.e. you have to\n\
wait for a ZeroReturnError on a recv() method call\n\
";
static PyObject *
ssl_Connection_shutdown(ssl_ConnectionObj *self, PyObject *args)
{
int ret;
if (!PyArg_ParseTuple(args, ":shutdown"))
return NULL;
MY_BEGIN_ALLOW_THREADS(self->tstate)
ret = SSL_shutdown(self->ssl);
MY_END_ALLOW_THREADS(self->tstate)
if (PyErr_Occurred())
{
flush_error_queue();
return NULL;
}
if (ret < 0)
{
exception_from_error_queue();
return NULL;
}
else if (ret > 0)
{
Py_INCREF(Py_True);
return Py_True;
}
else
{
Py_INCREF(Py_False);
return Py_False;
}
}
static char ssl_Connection_get_cipher_list_doc[] = "\n\
Get the session cipher list\n\
WARNING: API change! This used to take an optional argument, and return a\n\
string.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: A list of cipher strings\n\
";
static PyObject *
ssl_Connection_get_cipher_list(ssl_ConnectionObj *self, PyObject *args)
{
int idx = 0;
const char *ret;
PyObject *lst, *item;
if (!PyArg_ParseTuple(args, ":get_cipher_list"))
return NULL;
lst = PyList_New(0);
while ((ret = SSL_get_cipher_list(self->ssl, idx)) != NULL)
{
item = PyString_FromString(ret);
PyList_Append(lst, item);
Py_DECREF(item);
idx++;
}
return lst;
}
static char ssl_Connection_makefile_doc[] = "\n\
The makefile() method is not implemented, since there is no dup semantics\n\
for SSL connections\n\
XXX: Return self instead?\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: NULL\n\
";
static PyObject *
ssl_Connection_makefile(ssl_ConnectionObj *self, PyObject *args)
{
PyErr_SetString(PyExc_NotImplementedError, "Cannot make file object of SSL.Connection");
return NULL;
}
static char ssl_Connection_get_app_data_doc[] = "\n\
Get application data\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: The application data\n\
";
static PyObject *
ssl_Connection_get_app_data(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":get_app_data"))
return NULL;
Py_INCREF(self->app_data);
return self->app_data;
}
static char ssl_Connection_set_app_data_doc[] = "\n\
Set application data\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be\n\
data - The application data\n\
Returns: None\n\
";
static PyObject *
ssl_Connection_set_app_data(ssl_ConnectionObj *self, PyObject *args)
{
PyObject *data;
if (!PyArg_ParseTuple(args, "O:set_app_data", &data))
return NULL;
Py_DECREF(self->app_data);
Py_INCREF(data);
self->app_data = data;
Py_INCREF(Py_None);
return Py_None;
}
static char ssl_Connection_state_string_doc[] = "\n\
Get a verbose state description\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: A string representing the state\n\
";
static PyObject *
ssl_Connection_state_string(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":state_string"))
return NULL;
return PyString_FromString(SSL_state_string_long(self->ssl));
}
static char ssl_Connection_sock_shutdown_doc[] = "\n\
See shutdown(2)\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be whatever the\n\
socket's shutdown() method expects\n\
Returns: What the socket's shutdown() method returns\n\
";
static PyObject *
ssl_Connection_sock_shutdown(ssl_ConnectionObj *self, PyObject *args)
{
PyObject *meth, *ret;
if ((meth = PyObject_GetAttrString(self->socket, "shutdown")) == NULL)
return NULL;
ret = PyEval_CallObject(meth, args);
Py_DECREF(meth);
return ret;
}
static char ssl_Connection_get_peer_certificate_doc[] = "\n\
Retrieve the other side's certificate (if any)\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: The peer's certificate\n\
";
static PyObject *
ssl_Connection_get_peer_certificate(ssl_ConnectionObj *self, PyObject *args)
{
X509 *cert;
if (!PyArg_ParseTuple(args, ":get_peer_certificate"))
return NULL;
cert = SSL_get_peer_certificate(self->ssl);
if (cert != NULL)
{
return (PyObject *)crypto_X509_New(cert, 1);
}
else
{
Py_INCREF(Py_None);
return Py_None;
}
}
static char ssl_Connection_want_read_doc[] = "\n\
Checks if more data has to be read from the transport layer to complete an\n\
operation.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: True iff more data has to be read\n\
";
static PyObject *
ssl_Connection_want_read(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":want_read"))
return NULL;
return PyInt_FromLong((long)SSL_want_read(self->ssl));
}
static char ssl_Connection_want_write_doc[] = "\n\
Checks if there is data to write to the transport layer to complete an\n\
operation.\n\
\n\
Arguments: self - The Connection object\n\
args - The Python argument tuple, should be empty\n\
Returns: True iff there is data to write\n\
";
static PyObject *
ssl_Connection_want_write(ssl_ConnectionObj *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, ":want_write"))
return NULL;
return PyInt_FromLong((long)SSL_want_write(self->ssl));
}
#define ADD_METHOD(name) \
{ #name, (PyCFunction)ssl_Connection_##name, METH_VARARGS, ssl_Connection_##name##_doc }
#define ADD_ALIAS(name,real) \
{ #name, (PyCFunction)ssl_Connection_##real, METH_VARARGS, ssl_Connection_##real##_doc }
static PyMethodDef ssl_Connection_methods[] =
{
ADD_METHOD(get_context),
ADD_METHOD(pending),
ADD_METHOD(send),
ADD_ALIAS (write, send),
ADD_METHOD(sendall),
ADD_METHOD(recv),
ADD_ALIAS (read, recv),
ADD_METHOD(renegotiate),
ADD_METHOD(do_handshake),
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L
ADD_METHOD(renegotiate_pending),
#endif
ADD_METHOD(total_renegotiations),
ADD_METHOD(connect),
ADD_METHOD(connect_ex),
ADD_METHOD(accept),
ADD_METHOD(shutdown),
ADD_METHOD(get_cipher_list),
ADD_METHOD(makefile),
ADD_METHOD(get_app_data),
ADD_METHOD(set_app_data),
ADD_METHOD(state_string),
ADD_METHOD(sock_shutdown),
ADD_METHOD(get_peer_certificate),
ADD_METHOD(want_read),
ADD_METHOD(want_write),
ADD_METHOD(set_accept_state),
ADD_METHOD(set_connect_state),
{ NULL, NULL }
};
#undef ADD_ALIAS
#undef ADD_METHOD
ssl_ConnectionObj *
ssl_Connection_New(ssl_ContextObj *ctx, PyObject *sock)
{
ssl_ConnectionObj *self;
int fd;
self = PyObject_New(ssl_ConnectionObj, &ssl_Connection_Type);
if (self == NULL)
return NULL;
Py_INCREF(ctx);
self->context = ctx;
Py_INCREF(sock);
self->socket = sock;
self->ssl = NULL;
Py_INCREF(Py_None);
self->app_data = Py_None;
self->tstate = NULL;
fd = PyObject_AsFileDescriptor(self->socket);
if (fd < 0)
{
Py_DECREF(self);
return NULL;
}
self->ssl = SSL_new(self->context->ctx);
SSL_set_app_data(self->ssl, self);
SSL_set_fd(self->ssl, (SOCKET_T)fd);
return self;
}
static void
ssl_Connection_dealloc(ssl_ConnectionObj *self)
{
Py_DECREF(self->context);
Py_DECREF(self->socket);
if (self->ssl != NULL)
SSL_free(self->ssl);
Py_DECREF(self->app_data);
PyObject_Del(self);
}
static PyObject *
ssl_Connection_getattr(ssl_ConnectionObj *self, char *name)
{
PyObject *meth;
meth = Py_FindMethod(ssl_Connection_methods, (PyObject *)self, name);
if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError))
{
PyErr_Clear();
meth = PyObject_GetAttrString(self->socket, name);
}
return meth;
}
PyTypeObject ssl_Connection_Type = {
PyObject_HEAD_INIT(NULL)
0,
"Connection",
sizeof(ssl_ConnectionObj),
0,
(destructor)ssl_Connection_dealloc,
NULL,
(getattrfunc)ssl_Connection_getattr,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
int
init_ssl_connection(PyObject *dict)
{
ssl_Connection_Type.ob_type = &PyType_Type;
Py_INCREF(&ssl_Connection_Type);
if (PyDict_SetItemString(dict, "ConnectionType", (PyObject *)&ssl_Connection_Type) != 0)
return 0;
return 1;
}