diff --git a/twisted/python/sendmsg.c b/twisted/python/sendmsg.c deleted file mode 100644 index 9f2fd8c..0000000 --- a/twisted/python/sendmsg.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright (c) Twisted Matrix Laboratories. - * See LICENSE for details. - */ - -#define PY_SSIZE_T_CLEAN 1 -#include - -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -/* This may cause some warnings, but if you want to get rid of them, upgrade - * your Python version. */ -typedef int Py_ssize_t; -#endif - -#include -#include -#include - -#include - -#ifdef BSD -#include -#endif - -/* - * As per - * : - * - * "To forestall portability problems, it is recommended that applications - * not use values larger than (2**31)-1 for the socklen_t type." - */ - -#define SOCKLEN_MAX 0x7FFFFFFF - -PyObject *sendmsg_socket_error; - -static PyObject *sendmsg_sendmsg(PyObject *self, PyObject *args, PyObject *keywds); -static PyObject *sendmsg_recvmsg(PyObject *self, PyObject *args, PyObject *keywds); -static PyObject *sendmsg_getsockfam(PyObject *self, PyObject *args, PyObject *keywds); - -static char sendmsg_doc[] = "\ -Bindings for sendmsg(2), recvmsg(2), and a minimal helper for inspecting\n\ -address family of a socket.\n\ -"; - -static char sendmsg_sendmsg_doc[] = "\ -Wrap the C sendmsg(2) function for sending \"messages\" on a socket.\n\ -\n\ -@param fd: The file descriptor of the socket over which to send a message.\n\ -@type fd: C{int}\n\ -\n\ -@param data: Bytes to write to the socket.\n\ -@type data: C{str}\n\ -\n\ -@param flags: Flags to affect how the message is sent. See the C{MSG_}\n\ - constants in the sendmsg(2) manual page. By default no flags are set.\n\ -@type flags: C{int}\n\ -\n\ -@param ancillary: Extra data to send over the socket outside of the normal\n\ - datagram or stream mechanism. By default no ancillary data is sent.\n\ -@type ancillary: C{list} of C{tuple} of C{int}, C{int}, and C{str}.\n\ -\n\ -@raise OverflowError: Raised if too much ancillary data is given.\n\ -@raise socket.error: Raised if the underlying syscall indicates an error.\n\ -\n\ -@return: The return value of the underlying syscall, if it succeeds.\n\ -"; - -static char sendmsg_recvmsg_doc[] = "\ -Wrap the C recvmsg(2) function for receiving \"messages\" on a socket.\n\ -\n\ -@param fd: The file descriptor of the socket over which to receve a message.\n\ -@type fd: C{int}\n\ -\n\ -@param flags: Flags to affect how the message is sent. See the C{MSG_}\n\ - constants in the sendmsg(2) manual page. By default no flags are set.\n\ -@type flags: C{int}\n\ -\n\ -@param maxsize: The maximum number of bytes to receive from the socket\n\ - using the datagram or stream mechanism. The default maximum is 8192.\n\ -@type maxsize: C{int}\n\ -\n\ -@param cmsg_size: The maximum number of bytes to receive from the socket\n\ - outside of the normal datagram or stream mechanism. The default maximum is 4096.\n\ -\n\ -@raise OverflowError: Raised if too much ancillary data is given.\n\ -@raise socket.error: Raised if the underlying syscall indicates an error.\n\ -\n\ -@return: A C{tuple} of three elements: the bytes received using the\n\ - datagram/stream mechanism, flags as an C{int} describing the data\n\ - received, and a C{list} of C{tuples} giving ancillary received data.\n\ -"; - -static char sendmsg_getsockfam_doc[] = "\ -Retrieve the address family of a given socket.\n\ -\n\ -@param fd: The file descriptor of the socket the address family of which\n\ - to retrieve.\n\ -@type fd: C{int}\n\ -\n\ -@raise socket.error: Raised if the underlying getsockname call indicates\n\ - an error.\n\ -\n\ -@return: A C{int} representing the address family of the socket. For\n\ - example, L{socket.AF_INET}, L{socket.AF_INET6}, or L{socket.AF_UNIX}.\n\ -"; - -static PyMethodDef sendmsg_methods[] = { - {"send1msg", (PyCFunction) sendmsg_sendmsg, METH_VARARGS | METH_KEYWORDS, - sendmsg_sendmsg_doc}, - {"recv1msg", (PyCFunction) sendmsg_recvmsg, METH_VARARGS | METH_KEYWORDS, - sendmsg_recvmsg_doc}, - {"getsockfam", (PyCFunction) sendmsg_getsockfam, - METH_VARARGS | METH_KEYWORDS, sendmsg_getsockfam_doc}, - {NULL, NULL, 0, NULL} -}; - - -PyMODINIT_FUNC initsendmsg(void) { - PyObject *module; - - sendmsg_socket_error = NULL; /* Make sure that this has a known value - before doing anything that might exit. */ - - module = Py_InitModule3("sendmsg", sendmsg_methods, sendmsg_doc); - - if (!module) { - return; - } - - /* - The following is the only value mentioned by POSIX: - http://www.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html - */ - - if (-1 == PyModule_AddIntConstant(module, "SCM_RIGHTS", SCM_RIGHTS)) { - return; - } - - - /* BSD, Darwin, Hurd */ -#if defined(SCM_CREDS) - if (-1 == PyModule_AddIntConstant(module, "SCM_CREDS", SCM_CREDS)) { - return; - } -#endif - - /* Linux */ -#if defined(SCM_CREDENTIALS) - if (-1 == PyModule_AddIntConstant(module, "SCM_CREDENTIALS", SCM_CREDENTIALS)) { - return; - } -#endif - - /* Apparently everywhere, but not standardized. */ -#if defined(SCM_TIMESTAMP) - if (-1 == PyModule_AddIntConstant(module, "SCM_TIMESTAMP", SCM_TIMESTAMP)) { - return; - } -#endif - - module = PyImport_ImportModule("socket"); - if (!module) { - return; - } - - sendmsg_socket_error = PyObject_GetAttrString(module, "error"); - if (!sendmsg_socket_error) { - return; - } -} - -static PyObject *sendmsg_sendmsg(PyObject *self, PyObject *args, PyObject *keywds) { - - int fd; - int flags = 0; - Py_ssize_t sendmsg_result, iovec_length; - struct msghdr message_header; - struct iovec iov[1]; - PyObject *ancillary = NULL; - PyObject *iterator = NULL; - PyObject *item = NULL; - PyObject *result_object = NULL; - - static char *kwlist[] = {"fd", "data", "flags", "ancillary", NULL}; - - if (!PyArg_ParseTupleAndKeywords( - args, keywds, "it#|iO:sendmsg", kwlist, - &fd, - &iov[0].iov_base, - &iovec_length, - &flags, - &ancillary)) { - return NULL; - } - - iov[0].iov_len = iovec_length; - - message_header.msg_name = NULL; - message_header.msg_namelen = 0; - - message_header.msg_iov = iov; - message_header.msg_iovlen = 1; - - message_header.msg_control = NULL; - message_header.msg_controllen = 0; - - message_header.msg_flags = 0; - - if (ancillary) { - - if (!PyList_Check(ancillary)) { - PyErr_Format(PyExc_TypeError, - "send1msg argument 3 expected list, got %s", - ancillary->ob_type->tp_name); - goto finished; - } - - iterator = PyObject_GetIter(ancillary); - - if (iterator == NULL) { - goto finished; - } - - size_t all_data_len = 0; - - /* First we need to know how big the buffer needs to be in order to - have enough space for all of the messages. */ - while ( (item = PyIter_Next(iterator)) ) { - int type, level; - Py_ssize_t data_len; - size_t prev_all_data_len; - char *data; - - if (!PyTuple_Check(item)) { - PyErr_Format(PyExc_TypeError, - "send1msg argument 3 expected list of tuple, " - "got list containing %s", - item->ob_type->tp_name); - goto finished; - } - - if (!PyArg_ParseTuple( - item, "iit#:sendmsg ancillary data (level, type, data)", - &level, &type, &data, &data_len)) { - goto finished; - } - - prev_all_data_len = all_data_len; - all_data_len += CMSG_SPACE(data_len); - - Py_DECREF(item); - item = NULL; - - if (all_data_len < prev_all_data_len) { - PyErr_Format(PyExc_OverflowError, - "Too much msg_control to fit in a size_t: %zu", - prev_all_data_len); - goto finished; - } - } - - Py_DECREF(iterator); - iterator = NULL; - - /* Allocate the buffer for all of the ancillary elements, if we have - * any. */ - if (all_data_len) { - if (all_data_len > SOCKLEN_MAX) { - PyErr_Format(PyExc_OverflowError, - "Too much msg_control to fit in a socklen_t: %zu", - all_data_len); - goto finished; - } - message_header.msg_control = PyMem_Malloc(all_data_len); - if (!message_header.msg_control) { - PyErr_NoMemory(); - goto finished; - } - } else { - message_header.msg_control = NULL; - } - message_header.msg_controllen = (socklen_t) all_data_len; - - iterator = PyObject_GetIter(ancillary); /* again */ - - if (!iterator) { - goto finished; - } - - /* Unpack the tuples into the control message. */ - struct cmsghdr *control_message = CMSG_FIRSTHDR(&message_header); - while ( (item = PyIter_Next(iterator)) ) { - int type, level; - Py_ssize_t data_len; - size_t data_size; - unsigned char *data, *cmsg_data; - - /* We explicitly allocated enough space for all ancillary data - above; if there isn't enough room, all bets are off. */ - assert(control_message); - - if (!PyArg_ParseTuple(item, - "iit#:sendmsg ancillary data (level, type, data)", - &level, - &type, - &data, - &data_len)) { - goto finished; - } - - control_message->cmsg_level = level; - control_message->cmsg_type = type; - data_size = CMSG_LEN(data_len); - - if (data_size > SOCKLEN_MAX) { - PyErr_Format(PyExc_OverflowError, - "CMSG_LEN(%zd) > SOCKLEN_MAX", data_len); - goto finished; - } - - control_message->cmsg_len = (socklen_t) data_size; - - cmsg_data = CMSG_DATA(control_message); - memcpy(cmsg_data, data, data_len); - - Py_DECREF(item); - item = NULL; - - control_message = CMSG_NXTHDR(&message_header, control_message); - } - Py_DECREF(iterator); - iterator = NULL; - - if (PyErr_Occurred()) { - goto finished; - } - } - - sendmsg_result = sendmsg(fd, &message_header, flags); - - if (sendmsg_result < 0) { - PyErr_SetFromErrno(sendmsg_socket_error); - goto finished; - } - - result_object = Py_BuildValue("n", sendmsg_result); - - finished: - - if (item) { - Py_DECREF(item); - item = NULL; - } - if (iterator) { - Py_DECREF(iterator); - iterator = NULL; - } - if (message_header.msg_control) { - PyMem_Free(message_header.msg_control); - message_header.msg_control = NULL; - } - return result_object; -} - -static PyObject *sendmsg_recvmsg(PyObject *self, PyObject *args, PyObject *keywds) { - int fd = -1; - int flags = 0; - int maxsize = 8192; - int cmsg_size = 4096; - size_t cmsg_space; - size_t cmsg_overhead; - Py_ssize_t recvmsg_result; - - struct msghdr message_header; - struct cmsghdr *control_message; - struct iovec iov[1]; - char *cmsgbuf; - PyObject *ancillary; - PyObject *final_result = NULL; - - static char *kwlist[] = {"fd", "flags", "maxsize", "cmsg_size", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|iii:recvmsg", kwlist, - &fd, &flags, &maxsize, &cmsg_size)) { - return NULL; - } - - cmsg_space = CMSG_SPACE(cmsg_size); - - /* overflow check */ - if (cmsg_space > SOCKLEN_MAX) { - PyErr_Format(PyExc_OverflowError, - "CMSG_SPACE(cmsg_size) greater than SOCKLEN_MAX: %d", - cmsg_size); - return NULL; - } - - message_header.msg_name = NULL; - message_header.msg_namelen = 0; - - iov[0].iov_len = maxsize; - iov[0].iov_base = PyMem_Malloc(maxsize); - - if (!iov[0].iov_base) { - PyErr_NoMemory(); - return NULL; - } - - message_header.msg_iov = iov; - message_header.msg_iovlen = 1; - - cmsgbuf = PyMem_Malloc(cmsg_space); - - if (!cmsgbuf) { - PyMem_Free(iov[0].iov_base); - PyErr_NoMemory(); - return NULL; - } - - memset(cmsgbuf, 0, cmsg_space); - message_header.msg_control = cmsgbuf; - /* see above for overflow check */ - message_header.msg_controllen = (socklen_t) cmsg_space; - - recvmsg_result = recvmsg(fd, &message_header, flags); - if (recvmsg_result < 0) { - PyErr_SetFromErrno(sendmsg_socket_error); - goto finished; - } - - ancillary = PyList_New(0); - if (!ancillary) { - goto finished; - } - - for (control_message = CMSG_FIRSTHDR(&message_header); - control_message; - control_message = CMSG_NXTHDR(&message_header, - control_message)) { - PyObject *entry; - - /* Some platforms apparently always fill out the ancillary data - structure with a single bogus value if none is provided; ignore it, - if that is the case. */ - - if ((!(control_message->cmsg_level)) && - (!(control_message->cmsg_type))) { - continue; - } - - /* - * Figure out how much of the cmsg size is cmsg structure overhead - in - * other words, how much is not part of the application data. This lets - * us compute the right application data size below. There should - * really be a CMSG_ macro for this. - */ - cmsg_overhead = (char*)CMSG_DATA(control_message) - (char*)control_message; - - entry = Py_BuildValue( - "(iis#)", - control_message->cmsg_level, - control_message->cmsg_type, - CMSG_DATA(control_message), - (Py_ssize_t) (control_message->cmsg_len - cmsg_overhead)); - - if (!entry) { - Py_DECREF(ancillary); - goto finished; - } - - if (PyList_Append(ancillary, entry) < 0) { - Py_DECREF(ancillary); - Py_DECREF(entry); - goto finished; - } else { - Py_DECREF(entry); - } - } - - final_result = Py_BuildValue( - "s#iO", - iov[0].iov_base, - recvmsg_result, - message_header.msg_flags, - ancillary); - - Py_DECREF(ancillary); - - finished: - PyMem_Free(iov[0].iov_base); - PyMem_Free(cmsgbuf); - return final_result; -} - -static PyObject *sendmsg_getsockfam(PyObject *self, PyObject *args, - PyObject *keywds) { - int fd; - struct sockaddr sa; - static char *kwlist[] = {"fd", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "i", kwlist, &fd)) { - return NULL; - } - socklen_t sz = sizeof(sa); - if (getsockname(fd, &sa, &sz)) { - PyErr_SetFromErrno(sendmsg_socket_error); - return NULL; - } - return Py_BuildValue("i", sa.sa_family); -} diff --git a/twisted/python/sendmsg.py b/twisted/python/sendmsg.py new file mode 100644 index 0000000..f4519fe --- /dev/null +++ b/twisted/python/sendmsg.py @@ -0,0 +1,268 @@ +""" +Bindings for sendmsg(2), recvmsg(2), and a minimal helper for inspecting +address family of a socket. +""" + +import os +import socket + +import cffi + +_ffi = cffi.FFI() +_ffi.cdef(""" +const static int SCM_RIGHTS; + +struct iovec { + char *iov_base; + size_t iov_len; +}; + +struct msghdr { + void *msg_name; + int msg_namelen; + struct iovec *msg_iov; + unsigned msg_iovlen; + void *msg_control; + unsigned msg_controllen; + unsigned msg_flags; + ...; +}; + +struct cmsghdr { + int cmsg_level; + int cmsg_type; + unsigned cmsg_len; + ...; +}; + +struct sockaddr { + unsigned char sa_family; + ...; +}; + +ssize_t sendmsg(int, const struct msghdr *, int); +ssize_t recvmsg(int, struct msghdr *, int); + +int getsockname(int, struct sockaddr *, size_t *); + +size_t CMSG_SPACE(size_t); +size_t CMSG_LEN(size_t); +struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *); +struct cmsghdr *CMSG_NXTHDR(struct msghdr *, struct cmsghdr *); +unsigned char *CMSG_DATA(struct cmsghdr *); +""") +_lib = _ffi.verify(""" +#include +#include +#include + +#include + +#ifdef BSD +# include +#endif +""") +_SOCKLEN_MAX = 0x7FFFFFFF + + +SCM_RIGHTS = _lib.SCM_RIGHTS + + +def send1msg(fd, data, flags=0, ancillary=None): + """ + Wrap the C sendmsg(2) function for sending "messages" on a socket. + + @param fd: The file descriptor of the socket over which to send a message. + @type fd: C{int} + + @param data: Bytes to write to the socket. + @type data: C{str} + + @param flags: Flags to affect how the message is sent. See the C{MSG_} + constants in the sendmsg(2) manual page. By default no flags are set. + @type flags: C{int} + + @param ancillary: Extra data to send over the socket outside of the normal + datagram or stream mechanism. By default no ancillary data is sent. + @type ancillary: C{list} of C{tuple} of C{int}, C{int}, and C{str}. + + @raise OverflowError: Raised if too much ancillary data is given. + @raise socket.error: Raised if the underlying syscall indicates an error. + + @return: The return value of the underlying syscall, if it succeeds. + """ + data_ptr = _ffi.new("char[]", data) + iov = _ffi.new("struct iovec[1]") + iov[0].iov_base = data_ptr + iov[0].iov_len = len(data) + + message_header = _ffi.new("struct msghdr *") + message_header.msg_name = _ffi.NULL + message_header.msg_namelen = 0 + message_header.msg_iov = iov + message_header.msg_iovlen = 1 + message_header.msg_control = _ffi.NULL + message_header.msg_controllen = 0 + message_header.msg_flags = 0 + + if ancillary is not None: + if not isinstance(ancillary, list): + raise TypeError( + "send1msg argument 3 expected list, got %s" % ( + type(ancillary).__name__ + ) + ) + + all_data_len = 0 + for item in ancillary: + if not isinstance(item, tuple): + raise TypeError( + "send1msg argument 3 expected list of tuple, got list " + "containing %s" % type(item).__name__ + ) + try: + level, tp, data = item + except ValueError: + raise TypeError + prev_all_data_len = all_data_len + all_data_len += _lib.CMSG_SPACE(len(data)) + if int(_ffi.cast("size_t", all_data_len)) < prev_all_data_len: + raise OverflowError( + "Too much msg_control to fit in a size_t: %d" % ( + prev_all_data_len + ) + ) + + if all_data_len: + if all_data_len > _SOCKLEN_MAX: + raise OverflowError( + "Too much msg_control to fit in a socklen_t: %d" % ( + all_data_len + ) + ) + msg_control = _ffi.new("char[]", all_data_len) + message_header.msg_control = msg_control + else: + message_header.msg_control = _ffi.NULL + + message_header.msg_controllen = all_data_len + + control_message = _lib.CMSG_FIRSTHDR(message_header) + for level, tp, data in ancillary: + control_message.cmsg_level = level + control_message.cmsg_type = tp + data_size = _lib.CMSG_LEN(len(data)) + + if data_size > _SOCKLEN_MAX: + raise OverflowError("CMSG_LEN(%zd) > SOCKLEN_MAX" % len(data)) + control_message.cmsg_len = data_size + cmsg_data = _lib.CMSG_DATA(control_message) + _ffi.buffer(cmsg_data, len(data))[:] = data + control_message = _lib.CMSG_NXTHDR(message_header, control_message) + + sendmsg_result = _lib.sendmsg(fd, message_header, flags) + + if sendmsg_result < 0: + raise socket.error(_ffi.errno, os.strerror(_ffi.errno)) + + return sendmsg_result + + + +def recv1msg(fd, flags=0, maxsize=8192, cmsg_size=4096): + """ + Wrap the C recvmsg(2) function for receiving \"messages\" on a socket. + + @param fd: The file descriptor of the socket over which to receve a message. + @type fd: C{int} + + @param flags: Flags to affect how the message is sent. See the C{MSG_} + constants in the sendmsg(2) manual page. By default no flags are set. + @type flags: C{int} + + @param maxsize: The maximum number of bytes to receive from the socket + using the datagram or stream mechanism. The default maximum is 8192. + @type maxsize: C{int} + + @param cmsg_size: The maximum number of bytes to receive from the socket + outside of the normal datagram or stream mechanism. The default maximum is 4096. + + @raise OverflowError: Raised if too much ancillary data is given. + @raise socket.error: Raised if the underlying syscall indicates an error. + + @return: A C{tuple} of three elements: the bytes received using the + datagram/stream mechanism, flags as an C{int} describing the data + received, and a C{list} of C{tuples} giving ancillary received data. + """ + cmsg_space = _lib.CMSG_SPACE(cmsg_size) + if cmsg_space > _SOCKLEN_MAX: + raise OverflowError( + "CMSG_SPACE(cmsg_size) greater than SOCKLEN_MAX: %d" % cmsg_size + ) + + message_header = _ffi.new("struct msghdr *") + message_header.msg_name = _ffi.NULL + message_header.msg_namelen = 0 + + iov = _ffi.new("struct iovec[1]") + data_ptr = _ffi.new("char[]", maxsize) + iov[0].iov_len = maxsize + iov[0].iov_base = data_ptr + + message_header.msg_iov = iov + message_header.msg_iovlen = 1 + + cmsgbuf = _ffi.new("char[]", cmsg_space) + message_header.msg_control = cmsgbuf + message_header.msg_controllen = cmsg_space + + recvmsg_result = _lib.recvmsg(fd, message_header, flags) + if recvmsg_result < 0: + raise socket.error(_ffi.errno, os.strerror(_ffi.errno)) + + ancillary = [] + control_message = _lib.CMSG_FIRSTHDR(message_header) + while control_message != _ffi.NULL: + # Some platforms apparently always fill out the ancillary data + # structure with a single bogus value if none is provided; ignore it, + # if that is the case. + if control_message.cmsg_level or control_message.cmsg_type: + cmsg_overhead = _lib.CMSG_DATA(control_message) - _ffi.cast("unsigned char *", control_message) + entry = ( + control_message.cmsg_level, + control_message.cmsg_type, + _ffi.buffer(_lib.CMSG_DATA(control_message), control_message.cmsg_len - cmsg_overhead)[:] + ) + ancillary.append(entry) + + + control_message = _lib.CMSG_NXTHDR(message_header, control_message) + + return ( + _ffi.buffer(data_ptr, recvmsg_result)[:], + message_header.msg_flags, + ancillary + ) + + +def getsockfam(fd): + """ + Retrieve the address family of a given socket. + + @param fd: The file descriptor of the socket the address family of which + to retrieve. + @type fd: C{int} + + @raise socket.error: Raised if the underlying getsockname call indicates + an error. + + @return: A C{int} representing the address family of the socket. For + example, L{socket.AF_INET}, L{socket.AF_INET6}, or L{socket.AF_UNIX}. + + """ + sa = _ffi.new("struct sockaddr *") + sz = _ffi.new("size_t *", _ffi.sizeof("struct sockaddr")) + res = _lib.getsockname(fd, sa, sz) + if res: + raise socket.error(_ffi.errno, os.strerror(_ffi.errno)) + return sa.sa_family diff --git a/twisted/topfiles/setup.py b/twisted/topfiles/setup.py index 5f802a0..de61b59 100644 --- a/twisted/topfiles/setup.py +++ b/twisted/topfiles/setup.py @@ -31,10 +31,6 @@ extensions = [ "twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c"], libraries=["ws2_32"], condition=lambda _: _isCPython and sys.platform == "win32"), - - Extension("twisted.python.sendmsg", - sources=["twisted/python/sendmsg.c"], - condition=lambda _: sys.platform != "win32"), ] if sys.version_info[:2] <= (2, 6):