The ancdata argument specifies the ancillary data()

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;
}