P1sec / pysctp

SCTP stack for Python
http://www.p1sec.com
165 stars 67 forks source link

bindx doesn't work with more than one address #30

Closed neilschark closed 4 years ago

neilschark commented 4 years ago

Giving a list with more than one address tupel to sctpsocket_tcp.bindx() results in an OSError: [Errno 22] Invalid argument.

I looked into the _sctp.c library and apparently the parsing into saddrs fails.

I created some prints in _sctp.c:

static PyObject* bindx(PyObject* dummy, PyObject* args)
{
    PyObject* ret = 0;
    int fd;
    PyObject* addrs;
    struct sockaddr saddr;
    struct sockaddr* saddrs;
    int saddr_len, saddrs_len;
    int addrcount;
    int flags;
    int x;

    if (! PyArg_ParseTuple(args, "iOi", &fd, &addrs, &flags)) {
        return ret;
    }

    if (! PySequence_Check(addrs)) {
        PyErr_SetString(PyExc_ValueError, "Second parameter must be a sequence of address/port tuples");
        return ret;
    }

    addrcount = PySequence_Length(addrs);
    if (addrcount <= 0) {
        PyErr_SetString(PyExc_ValueError, "Second parameter must be a non-empty sequence");
        return ret;
    }

    saddrs_len = 0;
    saddrs = (struct sockaddr*) malloc(saddrs_len);

    for(x = 0; x < addrcount; ++x) {
        const char* caddr;
        int iport;

        printf("###################################\n");
        printf("caddr: %d\n", *caddr);
        printf("iport: %d\n", iport);

        PyObject* otuple = PySequence_GetItem(addrs, x);

        if (! PyArg_ParseTuple(otuple, "si", &caddr, &iport)) {
            free(saddrs);
            return ret;
        }

        if (! to_sockaddr(caddr, iport, &saddr, &saddr_len)) {
            PyErr_Format(PyExc_ValueError, "Invalid address: %s", caddr);
            free(saddrs);
            return ret;
        }

        if (saddr_len == 0) {
            PyErr_Format(PyExc_ValueError, "Invalid address family: %s", caddr);
            free(saddrs);
            return ret;
        }

        saddrs = realloc(saddrs, saddrs_len + saddr_len);
        memcpy( ((char*) saddrs) + saddrs_len, &saddr, saddr_len);
        saddrs_len += saddr_len;
    }
    printf("***************************************************\n");
    printf("sa_data 0: %s sa_family 0: %d\n", saddrs[0].sa_data, saddrs[0].sa_family);
    printf("sa_data 1: %s sa_family 1: %d\n", saddrs[1].sa_data, saddrs[1].sa_family);
    printf("sa_data 2: %s sa_family 2: %d\n", saddrs[2].sa_data, saddrs[2].sa_family);
    printf("sa_data 3: %s sa_family 3: %d\n", saddrs[3].sa_data, saddrs[3].sa_family);
    printf("fd: %d\n", fd);
    printf("addrcount: %d\n", addrcount);
    printf("flags: %d\n", flags);
    if (sctp_bindx(fd, saddrs, addrcount, flags)) {
        PyErr_SetFromErrno(PyExc_IOError);
    } else {
        ret = Py_None; Py_INCREF(ret);
    }

    free(saddrs);
    return ret;
}

For example, giving the addresses:

addresses.append(("127.0.0.1", 10000))
addresses.append(("127.0.0.2", 10001))
addresses.append(("127.0.0.3", 10002))
addresses.append(("127.0.0.4", 10003))

results in the following command line output:

###################################
caddr: 1
iport: 21956
###################################
caddr: 49
iport: 10000
###################################
caddr: 49
iport: 10001
###################################
caddr: 49
iport: 10002
***************************************************
sa_data 0: ' sa_family 0: 2
sa_data 1: ' sa_family 1: 2
sa_data 2: ' sa_family 2: 2
sa_data 3: ' sa_family 3: 2
fd: 3
addrcount: 4
flags: 1
p1-bmu commented 4 years ago

Actually, bindx() works correctly, with multiple addresses too. You just need to set the same port for all the interfaces to bind.

This is the output produced after the last commit (after enabling the debug output during the build stage):

$ ipython
fromPython 3.5.2 (default, Oct  8 2019, 13:06:37)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from sctp import *; from socket import *

In [2]: sk = sctpsocket_tcp(AF_INET); a1, a2 = ('127.1.1.1', 5000), ('127.2.1.1', 5000); sk.bindx([a1, a2])
[DEBUG to_sockaddr] converting caddr: 127.1.1.1, port: 5000
[DEBUG to_sockaddr] sockaddr result is family: 0x2, s_addr: 0x101017f, port: 0x8813
[DEBUG bindx] x: 0, caddr: 127.1.1.1, iport: 5000, saddrs_len: 16
[DEBUG to_sockaddr] converting caddr: 127.2.1.1, port: 5000
[DEBUG to_sockaddr] sockaddr result is family: 0x2, s_addr: 0x101027f, port: 0x8813
[DEBUG bindx] x: 1, caddr: 127.2.1.1, iport: 5000, saddrs_len: 32

In [3]: sk.close()