summaryrefslogtreecommitdiffstats
path: root/Modules/socketmodule.c
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2011-08-22 01:55:57 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2011-08-22 01:55:57 (GMT)
commit96fe56abec36d2cb82c56c9ddafea0096f4f6c7e (patch)
treefa5e6293d1fc1cc00b41c57cd192df49a6a6b7ff /Modules/socketmodule.c
parent8983729dc08a05d5419d35ec3f431c7b442401a6 (diff)
downloadcpython-96fe56abec36d2cb82c56c9ddafea0096f4f6c7e.zip
cpython-96fe56abec36d2cb82c56c9ddafea0096f4f6c7e.tar.gz
cpython-96fe56abec36d2cb82c56c9ddafea0096f4f6c7e.tar.bz2
Add support for the send/recvmsg API to the socket module. Patch by David Watson and Heiko Wundram. (Closes #6560)
Diffstat (limited to 'Modules/socketmodule.c')
-rw-r--r--Modules/socketmodule.c809
1 files changed, 809 insertions, 0 deletions
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 8de84b7..75cde79 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -263,6 +263,7 @@ if_indextoname(index) -- return the corresponding interface name\n\
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
+#include <unistd.h>
/* Generic socket object definitions and includes */
#define PySocket_BUILDING_SOCKET
@@ -469,6 +470,17 @@ static PyTypeObject sock_type;
#include <sys/poll.h>
#endif
+/* Largest value to try to store in a socklen_t (used when handling
+ ancillary data). POSIX requires socklen_t to hold at least
+ (2**31)-1 and recommends against storing larger values, but
+ socklen_t was originally int in the BSD interface, so to be on the
+ safe side we use the smaller of (2**31)-1 and INT_MAX. */
+#if INT_MAX > 0x7fffffff
+#define SOCKLEN_T_LIMIT 0x7fffffff
+#else
+#define SOCKLEN_T_LIMIT INT_MAX
+#endif
+
#ifdef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE
/* Platform can select file descriptors beyond FD_SETSIZE */
#define IS_SELECTABLE(s) 1
@@ -1678,6 +1690,117 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret)
}
+/* Support functions for the sendmsg() and recvmsg[_into]() methods.
+ Currently, these methods are only compiled if the RFC 2292/3542
+ CMSG_LEN() macro is available. Older systems seem to have used
+ sizeof(struct cmsghdr) + (length) where CMSG_LEN() is used now, so
+ it may be possible to define CMSG_LEN() that way if it's not
+ provided. Some architectures might need extra padding after the
+ cmsghdr, however, and CMSG_LEN() would have to take account of
+ this. */
+#ifdef CMSG_LEN
+/* If length is in range, set *result to CMSG_LEN(length) and return
+ true; otherwise, return false. */
+static int
+get_CMSG_LEN(size_t length, size_t *result)
+{
+ size_t tmp;
+
+ if (length > (SOCKLEN_T_LIMIT - CMSG_LEN(0)))
+ return 0;
+ tmp = CMSG_LEN(length);
+ if (tmp > SOCKLEN_T_LIMIT || tmp < length)
+ return 0;
+ *result = tmp;
+ return 1;
+}
+
+#ifdef CMSG_SPACE
+/* If length is in range, set *result to CMSG_SPACE(length) and return
+ true; otherwise, return false. */
+static int
+get_CMSG_SPACE(size_t length, size_t *result)
+{
+ size_t tmp;
+
+ /* Use CMSG_SPACE(1) here in order to take account of the padding
+ necessary before *and* after the data. */
+ if (length > (SOCKLEN_T_LIMIT - CMSG_SPACE(1)))
+ return 0;
+ tmp = CMSG_SPACE(length);
+ if (tmp > SOCKLEN_T_LIMIT || tmp < length)
+ return 0;
+ *result = tmp;
+ return 1;
+}
+#endif
+
+/* Return true iff msg->msg_controllen is valid, cmsgh is a valid
+ pointer in msg->msg_control with at least "space" bytes after it,
+ and its cmsg_len member inside the buffer. */
+static int
+cmsg_min_space(struct msghdr *msg, struct cmsghdr *cmsgh, size_t space)
+{
+ size_t cmsg_offset;
+ static const size_t cmsg_len_end = (offsetof(struct cmsghdr, cmsg_len) +
+ sizeof(cmsgh->cmsg_len));
+
+ if (cmsgh == NULL || msg->msg_control == NULL || msg->msg_controllen < 0)
+ return 0;
+ if (space < cmsg_len_end)
+ space = cmsg_len_end;
+ cmsg_offset = (char *)cmsgh - (char *)msg->msg_control;
+ return (cmsg_offset <= (size_t)-1 - space &&
+ cmsg_offset + space <= msg->msg_controllen);
+}
+
+/* If pointer CMSG_DATA(cmsgh) is in buffer msg->msg_control, set
+ *space to number of bytes following it in the buffer and return
+ true; otherwise, return false. Assumes cmsgh, msg->msg_control and
+ msg->msg_controllen are valid. */
+static int
+get_cmsg_data_space(struct msghdr *msg, struct cmsghdr *cmsgh, size_t *space)
+{
+ size_t data_offset;
+ char *data_ptr;
+
+ if ((data_ptr = (char *)CMSG_DATA(cmsgh)) == NULL)
+ return 0;
+ data_offset = data_ptr - (char *)msg->msg_control;
+ if (data_offset > msg->msg_controllen)
+ return 0;
+ *space = msg->msg_controllen - data_offset;
+ return 1;
+}
+
+/* If cmsgh is invalid or not contained in the buffer pointed to by
+ msg->msg_control, return -1. If cmsgh is valid and its associated
+ data is entirely contained in the buffer, set *data_len to the
+ length of the associated data and return 0. If only part of the
+ associated data is contained in the buffer but cmsgh is otherwise
+ valid, set *data_len to the length contained in the buffer and
+ return 1. */
+static int
+get_cmsg_data_len(struct msghdr *msg, struct cmsghdr *cmsgh, size_t *data_len)
+{
+ size_t space, cmsg_data_len;
+
+ if (!cmsg_min_space(msg, cmsgh, CMSG_LEN(0)) ||
+ cmsgh->cmsg_len < CMSG_LEN(0))
+ return -1;
+ cmsg_data_len = cmsgh->cmsg_len - CMSG_LEN(0);
+ if (!get_cmsg_data_space(msg, cmsgh, &space))
+ return -1;
+ if (space >= cmsg_data_len) {
+ *data_len = cmsg_data_len;
+ return 0;
+ }
+ *data_len = space;
+ return 1;
+}
+#endif /* CMSG_LEN */
+
+
/* s._accept() -> (fd, address) */
static PyObject *
@@ -2631,6 +2754,333 @@ PyDoc_STRVAR(recvfrom_into_doc,
Like recv_into(buffer[, nbytes[, flags]]) but also return the sender's address info.");
+/* The sendmsg() and recvmsg[_into]() methods require a working
+ CMSG_LEN(). See the comment near get_CMSG_LEN(). */
+#ifdef CMSG_LEN
+/*
+ * Call recvmsg() with the supplied iovec structures, flags, and
+ * ancillary data buffer size (controllen). Returns the tuple return
+ * value for recvmsg() or recvmsg_into(), with the first item provided
+ * by the supplied makeval() function. makeval() will be called with
+ * the length read and makeval_data as arguments, and must return a
+ * new reference (which will be decrefed if there is a subsequent
+ * error). On error, closes any file descriptors received via
+ * SCM_RIGHTS.
+ */
+static PyObject *
+sock_recvmsg_guts(PySocketSockObject *s, struct iovec *iov, int iovlen,
+ int flags, Py_ssize_t controllen,
+ PyObject *(*makeval)(ssize_t, void *), void *makeval_data)
+{
+ ssize_t bytes_received = -1;
+ int timeout;
+ sock_addr_t addrbuf;
+ socklen_t addrbuflen;
+ static const struct msghdr msg_blank;
+ struct msghdr msg;
+ PyObject *cmsg_list = NULL, *retval = NULL;
+ void *controlbuf = NULL;
+ struct cmsghdr *cmsgh;
+ size_t cmsgdatalen = 0;
+ int cmsg_status;
+
+ /* XXX: POSIX says that msg_name and msg_namelen "shall be
+ ignored" when the socket is connected (Linux fills them in
+ anyway for AF_UNIX sockets at least). Normally msg_namelen
+ seems to be set to 0 if there's no address, but try to
+ initialize msg_name to something that won't be mistaken for a
+ real address if that doesn't happen. */
+ if (!getsockaddrlen(s, &addrbuflen))
+ return NULL;
+ memset(&addrbuf, 0, addrbuflen);
+ SAS2SA(&addrbuf)->sa_family = AF_UNSPEC;
+
+ if (controllen < 0 || controllen > SOCKLEN_T_LIMIT) {
+ PyErr_SetString(PyExc_ValueError,
+ "invalid ancillary data buffer length");
+ return NULL;
+ }
+ if (controllen > 0 && (controlbuf = PyMem_Malloc(controllen)) == NULL)
+ return PyErr_NoMemory();
+
+ /* Make the system call. */
+ if (!IS_SELECTABLE(s)) {
+ select_error();
+ goto finally;
+ }
+
+ BEGIN_SELECT_LOOP(s)
+ Py_BEGIN_ALLOW_THREADS;
+ msg = msg_blank; /* Set all members to 0 or NULL */
+ msg.msg_name = SAS2SA(&addrbuf);
+ msg.msg_namelen = addrbuflen;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iovlen;
+ msg.msg_control = controlbuf;
+ msg.msg_controllen = controllen;
+ timeout = internal_select_ex(s, 0, interval);
+ if (!timeout)
+ bytes_received = recvmsg(s->sock_fd, &msg, flags);
+ Py_END_ALLOW_THREADS;
+ if (timeout == 1) {
+ PyErr_SetString(socket_timeout, "timed out");
+ goto finally;
+ }
+ END_SELECT_LOOP(s)
+
+ if (bytes_received < 0) {
+ s->errorhandler();
+ goto finally;
+ }
+
+ /* Make list of (level, type, data) tuples from control messages. */
+ if ((cmsg_list = PyList_New(0)) == NULL)
+ goto err_closefds;
+ /* Check for empty ancillary data as old CMSG_FIRSTHDR()
+ implementations didn't do so. */
+ for (cmsgh = ((msg.msg_controllen > 0) ? CMSG_FIRSTHDR(&msg) : NULL);
+ cmsgh != NULL; cmsgh = CMSG_NXTHDR(&msg, cmsgh)) {
+ PyObject *bytes, *tuple;
+ int tmp;
+
+ cmsg_status = get_cmsg_data_len(&msg, cmsgh, &cmsgdatalen);
+ if (cmsg_status != 0) {
+ if (PyErr_WarnEx(PyExc_RuntimeWarning,
+ "received malformed or improperly-truncated "
+ "ancillary data", 1) == -1)
+ goto err_closefds;
+ }
+ if (cmsg_status < 0)
+ break;
+ if (cmsgdatalen > PY_SSIZE_T_MAX) {
+ PyErr_SetString(socket_error, "control message too long");
+ goto err_closefds;
+ }
+
+ bytes = PyBytes_FromStringAndSize((char *)CMSG_DATA(cmsgh),
+ cmsgdatalen);
+ tuple = Py_BuildValue("iiN", (int)cmsgh->cmsg_level,
+ (int)cmsgh->cmsg_type, bytes);
+ if (tuple == NULL)
+ goto err_closefds;
+ tmp = PyList_Append(cmsg_list, tuple);
+ Py_DECREF(tuple);
+ if (tmp != 0)
+ goto err_closefds;
+
+ if (cmsg_status != 0)
+ break;
+ }
+
+ retval = Py_BuildValue("NOiN",
+ (*makeval)(bytes_received, makeval_data),
+ cmsg_list,
+ (int)msg.msg_flags,
+ makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
+ ((msg.msg_namelen > addrbuflen) ?
+ addrbuflen : msg.msg_namelen),
+ s->sock_proto));
+ if (retval == NULL)
+ goto err_closefds;
+
+finally:
+ Py_XDECREF(cmsg_list);
+ PyMem_Free(controlbuf);
+ return retval;
+
+err_closefds:
+#ifdef SCM_RIGHTS
+ /* Close all descriptors coming from SCM_RIGHTS, so they don't leak. */
+ for (cmsgh = ((msg.msg_controllen > 0) ? CMSG_FIRSTHDR(&msg) : NULL);
+ cmsgh != NULL; cmsgh = CMSG_NXTHDR(&msg, cmsgh)) {
+ cmsg_status = get_cmsg_data_len(&msg, cmsgh, &cmsgdatalen);
+ if (cmsg_status < 0)
+ break;
+ if (cmsgh->cmsg_level == SOL_SOCKET &&
+ cmsgh->cmsg_type == SCM_RIGHTS) {
+ size_t numfds;
+ int *fdp;
+
+ numfds = cmsgdatalen / sizeof(int);
+ fdp = (int *)CMSG_DATA(cmsgh);
+ while (numfds-- > 0)
+ close(*fdp++);
+ }
+ if (cmsg_status != 0)
+ break;
+ }
+#endif /* SCM_RIGHTS */
+ goto finally;
+}
+
+
+static PyObject *
+makeval_recvmsg(ssize_t received, void *data)
+{
+ PyObject **buf = data;
+
+ if (received < PyBytes_GET_SIZE(*buf))
+ _PyBytes_Resize(buf, received);
+ Py_XINCREF(*buf);
+ return *buf;
+}
+
+/* s.recvmsg(bufsize[, ancbufsize[, flags]]) method */
+
+static PyObject *
+sock_recvmsg(PySocketSockObject *s, PyObject *args)
+{
+ Py_ssize_t bufsize, ancbufsize = 0;
+ int flags = 0;
+ struct iovec iov;
+ PyObject *buf = NULL, *retval = NULL;
+
+ if (!PyArg_ParseTuple(args, "n|ni:recvmsg", &bufsize, &ancbufsize, &flags))
+ return NULL;
+
+ if (bufsize < 0) {
+ PyErr_SetString(PyExc_ValueError, "negative buffer size in recvmsg()");
+ return NULL;
+ }
+ if ((buf = PyBytes_FromStringAndSize(NULL, bufsize)) == NULL)
+ return NULL;
+ iov.iov_base = PyBytes_AS_STRING(buf);
+ iov.iov_len = bufsize;
+
+ /* Note that we're passing a pointer to *our pointer* to the bytes
+ object here (&buf); makeval_recvmsg() may incref the object, or
+ deallocate it and set our pointer to NULL. */
+ retval = sock_recvmsg_guts(s, &iov, 1, flags, ancbufsize,
+ &makeval_recvmsg, &buf);
+ Py_XDECREF(buf);
+ return retval;
+}
+
+PyDoc_STRVAR(recvmsg_doc,
+"recvmsg(bufsize[, ancbufsize[, flags]]) -> (data, ancdata, msg_flags, address)\n\
+\n\
+Receive normal data (up to bufsize bytes) and ancillary data from the\n\
+socket. The ancbufsize argument sets the size in bytes of the\n\
+internal buffer used to receive the ancillary data; it defaults to 0,\n\
+meaning that no ancillary data will be received. Appropriate buffer\n\
+sizes for ancillary data can be calculated using CMSG_SPACE() or\n\
+CMSG_LEN(), and items which do not fit into the buffer might be\n\
+truncated or discarded. The flags argument defaults to 0 and has the\n\
+same meaning as for recv().\n\
+\n\
+The return value is a 4-tuple: (data, ancdata, msg_flags, address).\n\
+The data item is a bytes object holding the non-ancillary data\n\
+received. The ancdata item is a list of zero or more tuples\n\
+(cmsg_level, cmsg_type, cmsg_data) representing the ancillary data\n\
+(control messages) received: cmsg_level and cmsg_type are integers\n\
+specifying the protocol level and protocol-specific type respectively,\n\
+and cmsg_data is a bytes object holding the associated data. The\n\
+msg_flags item is the bitwise OR of various flags indicating\n\
+conditions on the received message; see your system documentation for\n\
+details. If the receiving socket is unconnected, address is the\n\
+address of the sending socket, if available; otherwise, its value is\n\
+unspecified.\n\
+\n\
+If recvmsg() raises an exception after the system call returns, it\n\
+will first attempt to close any file descriptors received via the\n\
+SCM_RIGHTS mechanism.");
+
+
+static PyObject *
+makeval_recvmsg_into(ssize_t received, void *data)
+{
+ return PyLong_FromSsize_t(received);
+}
+
+/* s.recvmsg_into(buffers[, ancbufsize[, flags]]) method */
+
+static PyObject *
+sock_recvmsg_into(PySocketSockObject *s, PyObject *args)
+{
+ Py_ssize_t ancbufsize = 0;
+ int flags = 0;
+ struct iovec *iovs = NULL;
+ Py_ssize_t i, nitems, nbufs = 0;
+ Py_buffer *bufs = NULL;
+ PyObject *buffers_arg, *fast, *retval = NULL;
+
+ if (!PyArg_ParseTuple(args, "O|ni:recvmsg_into",
+ &buffers_arg, &ancbufsize, &flags))
+ return NULL;
+
+ if ((fast = PySequence_Fast(buffers_arg,
+ "recvmsg_into() argument 1 must be an "
+ "iterable")) == NULL)
+ return NULL;
+ nitems = PySequence_Fast_GET_SIZE(fast);
+ if (nitems > INT_MAX) {
+ PyErr_SetString(socket_error, "recvmsg_into() argument 1 is too long");
+ goto finally;
+ }
+
+ /* Fill in an iovec for each item, and save the Py_buffer
+ structs to release afterwards. */
+ if (nitems > 0 && ((iovs = PyMem_New(struct iovec, nitems)) == NULL ||
+ (bufs = PyMem_New(Py_buffer, nitems)) == NULL)) {
+ PyErr_NoMemory();
+ goto finally;
+ }
+ for (; nbufs < nitems; nbufs++) {
+ if (!PyArg_Parse(PySequence_Fast_GET_ITEM(fast, nbufs),
+ "w*;recvmsg_into() argument 1 must be an iterable "
+ "of single-segment read-write buffers",
+ &bufs[nbufs]))
+ goto finally;
+ iovs[nbufs].iov_base = bufs[nbufs].buf;
+ iovs[nbufs].iov_len = bufs[nbufs].len;
+ }
+
+ retval = sock_recvmsg_guts(s, iovs, nitems, flags, ancbufsize,
+ &makeval_recvmsg_into, NULL);
+finally:
+ for (i = 0; i < nbufs; i++)
+ PyBuffer_Release(&bufs[i]);
+ PyMem_Free(bufs);
+ PyMem_Free(iovs);
+ Py_DECREF(fast);
+ return retval;
+}
+
+PyDoc_STRVAR(recvmsg_into_doc,
+"recvmsg_into(buffers[, ancbufsize[, flags]]) -> (nbytes, ancdata, msg_flags, address)\n\
+\n\
+Receive normal data and ancillary data from the socket, scattering the\n\
+non-ancillary data into a series of buffers. The buffers argument\n\
+must be an iterable of objects that export writable buffers\n\
+(e.g. bytearray objects); these will be filled with successive chunks\n\
+of the non-ancillary data until it has all been written or there are\n\
+no more buffers. The ancbufsize argument sets the size in bytes of\n\
+the internal buffer used to receive the ancillary data; it defaults to\n\
+0, meaning that no ancillary data will be received. Appropriate\n\
+buffer sizes for ancillary data can be calculated using CMSG_SPACE()\n\
+or CMSG_LEN(), and items which do not fit into the buffer might be\n\
+truncated or discarded. The flags argument defaults to 0 and has the\n\
+same meaning as for recv().\n\
+\n\
+The return value is a 4-tuple: (nbytes, ancdata, msg_flags, address).\n\
+The nbytes item is the total number of bytes of non-ancillary data\n\
+written into the buffers. The ancdata item is a list of zero or more\n\
+tuples (cmsg_level, cmsg_type, cmsg_data) representing the ancillary\n\
+data (control messages) received: cmsg_level and cmsg_type are\n\
+integers specifying the protocol level and protocol-specific type\n\
+respectively, and cmsg_data is a bytes object holding the associated\n\
+data. The msg_flags item is the bitwise OR of various flags\n\
+indicating conditions on the received message; see your system\n\
+documentation for details. If the receiving socket is unconnected,\n\
+address is the address of the sending socket, if available; otherwise,\n\
+its value is unspecified.\n\
+\n\
+If recvmsg_into() raises an exception after the system call returns,\n\
+it will first attempt to close any file descriptors received via the\n\
+SCM_RIGHTS mechanism.");
+#endif /* CMSG_LEN */
+
+
/* s.send(data [,flags]) method */
static PyObject *
@@ -2826,6 +3276,237 @@ Like send(data, flags) but allows specifying the destination address.\n\
For IP sockets, the address is a pair (hostaddr, port).");
+/* The sendmsg() and recvmsg[_into]() methods require a working
+ CMSG_LEN(). See the comment near get_CMSG_LEN(). */
+#ifdef CMSG_LEN
+/* s.sendmsg(buffers[, ancdata[, flags[, address]]]) method */
+
+static PyObject *
+sock_sendmsg(PySocketSockObject *s, PyObject *args)
+{
+ Py_ssize_t i, ndataparts, ndatabufs = 0, ncmsgs, ncmsgbufs = 0;
+ Py_buffer *databufs = NULL;
+ struct iovec *iovs = NULL;
+ sock_addr_t addrbuf;
+ static const struct msghdr msg_blank;
+ struct msghdr msg;
+ struct cmsginfo {
+ int level;
+ int type;
+ Py_buffer data;
+ } *cmsgs = NULL;
+ void *controlbuf = NULL;
+ size_t controllen, controllen_last;
+ ssize_t bytes_sent = -1;
+ int addrlen, timeout, flags = 0;
+ PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, *data_fast = NULL,
+ *cmsg_fast = NULL, *retval = NULL;
+
+ if (!PyArg_ParseTuple(args, "O|OiO:sendmsg",
+ &data_arg, &cmsg_arg, &flags, &addr_arg))
+ return NULL;
+
+ msg = msg_blank; /* Set all members to 0 or NULL */
+
+ /* Parse destination address. */
+ if (addr_arg != NULL && addr_arg != Py_None) {
+ if (!getsockaddrarg(s, addr_arg, SAS2SA(&addrbuf), &addrlen))
+ goto finally;
+ msg.msg_name = &addrbuf;
+ msg.msg_namelen = addrlen;
+ }
+
+ /* Fill in an iovec for each message part, and save the Py_buffer
+ structs to release afterwards. */
+ if ((data_fast = PySequence_Fast(data_arg,
+ "sendmsg() argument 1 must be an "
+ "iterable")) == NULL)
+ goto finally;
+ ndataparts = PySequence_Fast_GET_SIZE(data_fast);
+ if (ndataparts > INT_MAX) {
+ PyErr_SetString(socket_error, "sendmsg() argument 1 is too long");
+ goto finally;
+ }
+ msg.msg_iovlen = ndataparts;
+ if (ndataparts > 0 &&
+ ((msg.msg_iov = iovs = PyMem_New(struct iovec, ndataparts)) == NULL ||
+ (databufs = PyMem_New(Py_buffer, ndataparts)) == NULL)) {
+ PyErr_NoMemory();
+ goto finally;
+ }
+ for (; ndatabufs < ndataparts; ndatabufs++) {
+ if (!PyArg_Parse(PySequence_Fast_GET_ITEM(data_fast, ndatabufs),
+ "y*;sendmsg() argument 1 must be an iterable of "
+ "buffer-compatible objects",
+ &databufs[ndatabufs]))
+ goto finally;
+ iovs[ndatabufs].iov_base = databufs[ndatabufs].buf;
+ iovs[ndatabufs].iov_len = databufs[ndatabufs].len;
+ }
+
+ if (cmsg_arg == NULL)
+ ncmsgs = 0;
+ else {
+ if ((cmsg_fast = PySequence_Fast(cmsg_arg,
+ "sendmsg() argument 2 must be an "
+ "iterable")) == NULL)
+ goto finally;
+ ncmsgs = PySequence_Fast_GET_SIZE(cmsg_fast);
+ }
+
+#ifndef CMSG_SPACE
+ if (ncmsgs > 1) {
+ PyErr_SetString(socket_error,
+ "sending multiple control messages is not supported "
+ "on this system");
+ goto finally;
+ }
+#endif
+ /* Save level, type and Py_buffer for each control message,
+ and calculate total size. */
+ if (ncmsgs > 0 && (cmsgs = PyMem_New(struct cmsginfo, ncmsgs)) == NULL) {
+ PyErr_NoMemory();
+ goto finally;
+ }
+ controllen = controllen_last = 0;
+ while (ncmsgbufs < ncmsgs) {
+ size_t bufsize, space;
+
+ if (!PyArg_Parse(PySequence_Fast_GET_ITEM(cmsg_fast, ncmsgbufs),
+ "(iiy*):[sendmsg() ancillary data items]",
+ &cmsgs[ncmsgbufs].level,
+ &cmsgs[ncmsgbufs].type,
+ &cmsgs[ncmsgbufs].data))
+ goto finally;
+ bufsize = cmsgs[ncmsgbufs++].data.len;
+
+#ifdef CMSG_SPACE
+ if (!get_CMSG_SPACE(bufsize, &space)) {
+#else
+ if (!get_CMSG_LEN(bufsize, &space)) {
+#endif
+ PyErr_SetString(socket_error, "ancillary data item too large");
+ goto finally;
+ }
+ controllen += space;
+ if (controllen > SOCKLEN_T_LIMIT || controllen < controllen_last) {
+ PyErr_SetString(socket_error, "too much ancillary data");
+ goto finally;
+ }
+ controllen_last = controllen;
+ }
+
+ /* Construct ancillary data block from control message info. */
+ if (ncmsgbufs > 0) {
+ struct cmsghdr *cmsgh = NULL;
+
+ if ((msg.msg_control = controlbuf =
+ PyMem_Malloc(controllen)) == NULL) {
+ PyErr_NoMemory();
+ goto finally;
+ }
+ msg.msg_controllen = controllen;
+
+ /* Need to zero out the buffer as a workaround for glibc's
+ CMSG_NXTHDR() implementation. After getting the pointer to
+ the next header, it checks its (uninitialized) cmsg_len
+ member to see if the "message" fits in the buffer, and
+ returns NULL if it doesn't. Zero-filling the buffer
+ ensures that that doesn't happen. */
+ memset(controlbuf, 0, controllen);
+
+ for (i = 0; i < ncmsgbufs; i++) {
+ size_t msg_len, data_len = cmsgs[i].data.len;
+ int enough_space = 0;
+
+ cmsgh = (i == 0) ? CMSG_FIRSTHDR(&msg) : CMSG_NXTHDR(&msg, cmsgh);
+ if (cmsgh == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "unexpected NULL result from %s()",
+ (i == 0) ? "CMSG_FIRSTHDR" : "CMSG_NXTHDR");
+ goto finally;
+ }
+ if (!get_CMSG_LEN(data_len, &msg_len)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "item size out of range for CMSG_LEN()");
+ goto finally;
+ }
+ if (cmsg_min_space(&msg, cmsgh, msg_len)) {
+ size_t space;
+
+ cmsgh->cmsg_len = msg_len;
+ if (get_cmsg_data_space(&msg, cmsgh, &space))
+ enough_space = (space >= data_len);
+ }
+ if (!enough_space) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "ancillary data does not fit in calculated "
+ "space");
+ goto finally;
+ }
+ cmsgh->cmsg_level = cmsgs[i].level;
+ cmsgh->cmsg_type = cmsgs[i].type;
+ memcpy(CMSG_DATA(cmsgh), cmsgs[i].data.buf, data_len);
+ }
+ }
+
+ /* Make the system call. */
+ if (!IS_SELECTABLE(s)) {
+ select_error();
+ goto finally;
+ }
+
+ BEGIN_SELECT_LOOP(s)
+ Py_BEGIN_ALLOW_THREADS;
+ timeout = internal_select_ex(s, 1, interval);
+ if (!timeout)
+ bytes_sent = sendmsg(s->sock_fd, &msg, flags);
+ Py_END_ALLOW_THREADS;
+ if (timeout == 1) {
+ PyErr_SetString(socket_timeout, "timed out");
+ goto finally;
+ }
+ END_SELECT_LOOP(s)
+
+ if (bytes_sent < 0) {
+ s->errorhandler();
+ goto finally;
+ }
+ retval = PyLong_FromSsize_t(bytes_sent);
+
+finally:
+ PyMem_Free(controlbuf);
+ for (i = 0; i < ncmsgbufs; i++)
+ PyBuffer_Release(&cmsgs[i].data);
+ PyMem_Free(cmsgs);
+ Py_XDECREF(cmsg_fast);
+ for (i = 0; i < ndatabufs; i++)
+ PyBuffer_Release(&databufs[i]);
+ PyMem_Free(databufs);
+ PyMem_Free(iovs);
+ Py_XDECREF(data_fast);
+ return retval;
+}
+
+PyDoc_STRVAR(sendmsg_doc,
+"sendmsg(buffers[, ancdata[, flags[, address]]]) -> count\n\
+\n\
+Send normal and ancillary data to the socket, gathering the\n\
+non-ancillary data from a series of buffers and concatenating it into\n\
+a single message. The buffers argument specifies the non-ancillary\n\
+data as an iterable of buffer-compatible objects (e.g. bytes objects).\n\
+The ancdata argument specifies the ancillary data (control messages)\n\
+as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n\
+cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n\
+protocol level and protocol-specific type respectively, and cmsg_data\n\
+is a buffer-compatible object holding the associated data. The flags\n\
+argument defaults to 0 and has the same meaning as for send(). If\n\
+address is supplied and not None, it sets a destination address for\n\
+the message. The return value is the number of bytes of non-ancillary\n\
+data sent.");
+#endif /* CMSG_LEN */
+
+
/* s.shutdown(how) method */
static PyObject *
@@ -2952,6 +3633,14 @@ static PyMethodDef sock_methods[] = {
setsockopt_doc},
{"shutdown", (PyCFunction)sock_shutdown, METH_O,
shutdown_doc},
+#ifdef CMSG_LEN
+ {"recvmsg", (PyCFunction)sock_recvmsg, METH_VARARGS,
+ recvmsg_doc},
+ {"recvmsg_into", (PyCFunction)sock_recvmsg_into, METH_VARARGS,
+ recvmsg_into_doc,},
+ {"sendmsg", (PyCFunction)sock_sendmsg, METH_VARARGS,
+ sendmsg_doc},
+#endif
{NULL, NULL} /* sentinel */
};
@@ -4377,6 +5066,68 @@ Returns the interface name corresponding to the interface index if_index.");
#endif /* HAVE_IF_NAMEINDEX */
+#ifdef CMSG_LEN
+/* Python interface to CMSG_LEN(length). */
+
+static PyObject *
+socket_CMSG_LEN(PyObject *self, PyObject *args)
+{
+ Py_ssize_t length;
+ size_t result;
+
+ if (!PyArg_ParseTuple(args, "n:CMSG_LEN", &length))
+ return NULL;
+ if (length < 0 || !get_CMSG_LEN(length, &result)) {
+ PyErr_Format(PyExc_OverflowError, "CMSG_LEN() argument out of range");
+ return NULL;
+ }
+ return PyLong_FromSize_t(result);
+}
+
+PyDoc_STRVAR(CMSG_LEN_doc,
+"CMSG_LEN(length) -> control message length\n\
+\n\
+Return the total length, without trailing padding, of an ancillary\n\
+data item with associated data of the given length. This value can\n\
+often be used as the buffer size for recvmsg() to receive a single\n\
+item of ancillary data, but RFC 3542 requires portable applications to\n\
+use CMSG_SPACE() and thus include space for padding, even when the\n\
+item will be the last in the buffer. Raises OverflowError if length\n\
+is outside the permissible range of values.");
+
+
+#ifdef CMSG_SPACE
+/* Python interface to CMSG_SPACE(length). */
+
+static PyObject *
+socket_CMSG_SPACE(PyObject *self, PyObject *args)
+{
+ Py_ssize_t length;
+ size_t result;
+
+ if (!PyArg_ParseTuple(args, "n:CMSG_SPACE", &length))
+ return NULL;
+ if (length < 0 || !get_CMSG_SPACE(length, &result)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "CMSG_SPACE() argument out of range");
+ return NULL;
+ }
+ return PyLong_FromSize_t(result);
+}
+
+PyDoc_STRVAR(CMSG_SPACE_doc,
+"CMSG_SPACE(length) -> buffer size\n\
+\n\
+Return the buffer size needed for recvmsg() to receive an ancillary\n\
+data item with associated data of the given length, along with any\n\
+trailing padding. The buffer space needed to receive multiple items\n\
+is the sum of the CMSG_SPACE() values for their associated data\n\
+lengths. Raises OverflowError if length is outside the permissible\n\
+range of values.");
+#endif /* CMSG_SPACE */
+#endif /* CMSG_LEN */
+
+
/* List of functions exported by this module. */
static PyMethodDef socket_methods[] = {
@@ -4440,6 +5191,14 @@ static PyMethodDef socket_methods[] = {
{"if_indextoname", socket_if_indextoname,
METH_O, if_indextoname_doc},
#endif
+#ifdef CMSG_LEN
+ {"CMSG_LEN", socket_CMSG_LEN,
+ METH_VARARGS, CMSG_LEN_doc},
+#ifdef CMSG_SPACE
+ {"CMSG_SPACE", socket_CMSG_SPACE,
+ METH_VARARGS, CMSG_SPACE_doc},
+#endif
+#endif
{NULL, NULL} /* Sentinel */
};
@@ -4927,6 +5686,15 @@ PyInit__socket(void)
#ifdef SO_SETFIB
PyModule_AddIntConstant(m, "SO_SETFIB", SO_SETFIB);
#endif
+#ifdef SO_PASSCRED
+ PyModule_AddIntConstant(m, "SO_PASSCRED", SO_PASSCRED);
+#endif
+#ifdef SO_PEERCRED
+ PyModule_AddIntConstant(m, "SO_PEERCRED", SO_PEERCRED);
+#endif
+#ifdef LOCAL_PEERCRED
+ PyModule_AddIntConstant(m, "LOCAL_PEERCRED", LOCAL_PEERCRED);
+#endif
/* Maximum number of connections for "listen" */
#ifdef SOMAXCONN
@@ -4935,6 +5703,17 @@ PyInit__socket(void)
PyModule_AddIntConstant(m, "SOMAXCONN", 5); /* Common value */
#endif
+ /* Ancilliary message types */
+#ifdef SCM_RIGHTS
+ PyModule_AddIntConstant(m, "SCM_RIGHTS", SCM_RIGHTS);
+#endif
+#ifdef SCM_CREDENTIALS
+ PyModule_AddIntConstant(m, "SCM_CREDENTIALS", SCM_CREDENTIALS);
+#endif
+#ifdef SCM_CREDS
+ PyModule_AddIntConstant(m, "SCM_CREDS", SCM_CREDS);
+#endif
+
/* Flags for send, recv */
#ifdef MSG_OOB
PyModule_AddIntConstant(m, "MSG_OOB", MSG_OOB);
@@ -4966,6 +5745,33 @@ PyInit__socket(void)
#ifdef MSG_ETAG
PyModule_AddIntConstant(m, "MSG_ETAG", MSG_ETAG);
#endif
+#ifdef MSG_NOSIGNAL
+ PyModule_AddIntConstant(m, "MSG_NOSIGNAL", MSG_NOSIGNAL);
+#endif
+#ifdef MSG_NOTIFICATION
+ PyModule_AddIntConstant(m, "MSG_NOTIFICATION", MSG_NOTIFICATION);
+#endif
+#ifdef MSG_CMSG_CLOEXEC
+ PyModule_AddIntConstant(m, "MSG_CMSG_CLOEXEC", MSG_CMSG_CLOEXEC);
+#endif
+#ifdef MSG_ERRQUEUE
+ PyModule_AddIntConstant(m, "MSG_ERRQUEUE", MSG_ERRQUEUE);
+#endif
+#ifdef MSG_CONFIRM
+ PyModule_AddIntConstant(m, "MSG_CONFIRM", MSG_CONFIRM);
+#endif
+#ifdef MSG_MORE
+ PyModule_AddIntConstant(m, "MSG_MORE", MSG_MORE);
+#endif
+#ifdef MSG_EOF
+ PyModule_AddIntConstant(m, "MSG_EOF", MSG_EOF);
+#endif
+#ifdef MSG_BCAST
+ PyModule_AddIntConstant(m, "MSG_BCAST", MSG_BCAST);
+#endif
+#ifdef MSG_MCAST
+ PyModule_AddIntConstant(m, "MSG_MCAST", MSG_MCAST);
+#endif
/* Protocol level and numbers, usable for [gs]etsockopt */
#ifdef SOL_SOCKET
@@ -5105,6 +5911,9 @@ PyInit__socket(void)
#ifdef IPPROTO_VRRP
PyModule_AddIntConstant(m, "IPPROTO_VRRP", IPPROTO_VRRP);
#endif
+#ifdef IPPROTO_SCTP
+ PyModule_AddIntConstant(m, "IPPROTO_SCTP", IPPROTO_SCTP);
+#endif
#ifdef IPPROTO_BIP
PyModule_AddIntConstant(m, "IPPROTO_BIP", IPPROTO_BIP);
#endif