mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
Add support for the send/recvmsg API to the socket module. Patch by David Watson and Heiko Wundram. (Closes #6560)
This commit is contained in:
parent
8983729dc0
commit
96fe56abec
7 changed files with 3167 additions and 0 deletions
|
@ -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[] = {
|
||||
|
@ -4439,6 +5190,14 @@ static PyMethodDef socket_methods[] = {
|
|||
METH_VARARGS, if_nametoindex_doc},
|
||||
{"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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue