in Modules/socketmodule.c [4567:4735]
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 bytes-like 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 */
#ifdef HAVE_SOCKADDR_ALG
static PyObject*
sock_sendmsg_afalg(PySocketSockObject *self, PyObject *args, PyObject *kwds)
{
PyObject *retval = NULL;
Py_ssize_t i, ndatabufs = 0;
Py_buffer *databufs = NULL;
PyObject *data_arg = NULL;
Py_buffer iv = {NULL, NULL};
PyObject *opobj = NULL;
int op = -1;
PyObject *assoclenobj = NULL;
int assoclen = -1;
unsigned int *uiptr;
int flags = 0;
struct msghdr msg;
struct cmsghdr *header = NULL;
struct af_alg_iv *alg_iv = NULL;
struct sock_sendmsg ctx;
Py_ssize_t controllen;
void *controlbuf = NULL;
static char *keywords[] = {"msg", "op", "iv", "assoclen", "flags", 0};
if (self->sock_family != AF_ALG) {
PyErr_SetString(PyExc_OSError,
"algset is only supported for AF_ALG");
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"|O$O!y*O!i:sendmsg_afalg", keywords,
&data_arg,
&PyLong_Type, &opobj, &iv,
&PyLong_Type, &assoclenobj, &flags)) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
/* op is a required, keyword-only argument >= 0 */
if (opobj != NULL) {
op = _PyLong_AsInt(opobj);
}
if (op < 0) {
/* override exception from _PyLong_AsInt() */
PyErr_SetString(PyExc_TypeError,
"Invalid or missing argument 'op'");
goto finally;
}
/* assoclen is optional but must be >= 0 */
if (assoclenobj != NULL) {
assoclen = _PyLong_AsInt(assoclenobj);
if (assoclen == -1 && PyErr_Occurred()) {
goto finally;
}
if (assoclen < 0) {
PyErr_SetString(PyExc_TypeError,
"assoclen must be positive");
goto finally;
}
}
controllen = CMSG_SPACE(4);
if (iv.buf != NULL) {
controllen += CMSG_SPACE(sizeof(*alg_iv) + iv.len);
}
if (assoclen >= 0) {
controllen += CMSG_SPACE(4);
}
controlbuf = PyMem_Malloc(controllen);
if (controlbuf == NULL) {
PyErr_NoMemory();
goto finally;
}
memset(controlbuf, 0, controllen);
msg.msg_controllen = controllen;
msg.msg_control = controlbuf;
/* Fill in an iovec for each message part, and save the Py_buffer
structs to release afterwards. */
if (data_arg != NULL) {
if (sock_sendmsg_iovec(self, data_arg, &msg, &databufs, &ndatabufs) == -1) {
goto finally;
}
}
/* set operation to encrypt or decrypt */
header = CMSG_FIRSTHDR(&msg);
if (header == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"unexpected NULL result from CMSG_FIRSTHDR");
goto finally;
}
header->cmsg_level = SOL_ALG;
header->cmsg_type = ALG_SET_OP;
header->cmsg_len = CMSG_LEN(4);
uiptr = (void*)CMSG_DATA(header);
*uiptr = (unsigned int)op;
/* set initialization vector */
if (iv.buf != NULL) {
header = CMSG_NXTHDR(&msg, header);
if (header == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"unexpected NULL result from CMSG_NXTHDR(iv)");
goto finally;
}
header->cmsg_level = SOL_ALG;
header->cmsg_type = ALG_SET_IV;
header->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv.len);
alg_iv = (void*)CMSG_DATA(header);
alg_iv->ivlen = iv.len;
memcpy(alg_iv->iv, iv.buf, iv.len);
}
/* set length of associated data for AEAD */
if (assoclen >= 0) {
header = CMSG_NXTHDR(&msg, header);
if (header == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"unexpected NULL result from CMSG_NXTHDR(assoc)");
goto finally;
}
header->cmsg_level = SOL_ALG;
header->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
header->cmsg_len = CMSG_LEN(4);
uiptr = (void*)CMSG_DATA(header);
*uiptr = (unsigned int)assoclen;
}
ctx.msg = &msg;
ctx.flags = flags;
if (sock_call(self, 1, sock_sendmsg_impl, &ctx) < 0) {
goto finally;
}
retval = PyLong_FromSsize_t(ctx.result);
finally:
PyMem_Free(controlbuf);
if (iv.buf != NULL) {
PyBuffer_Release(&iv);
}
PyMem_Free(msg.msg_iov);
for (i = 0; i < ndatabufs; i++) {
PyBuffer_Release(&databufs[i]);
}
PyMem_Free(databufs);
return retval;
}