qmsk/net/socket/sctp.pyx
author Tero Marttila <terom@fixme.fi>
Fri, 25 Sep 2009 21:34:04 +0300
changeset 49 e2f79e68418a
parent 24 f18b5787c46c
permissions -rw-r--r--
fix up circular cimports related to the sockaddr type, and touch up endpoint/getaddrinfo a bit
"""
    This C(ython) extension module provides an interface to the libsctp library and associated socket API.
    
    >>> from __future__ import absolute_import;
    >>> from qmsk.net.socket.af_inet import sockaddr_in
    >>> from qmsk.net.socket.constants import *
    >>> s = sctp_socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)
    >>> s.sctp_bindx([sockaddr_in('127.0.0.1', 1337), sockaddr_in('127.0.0.2')], 0x01)
    >>>

"""

from qmsk.net.socket.address cimport sockaddr

cimport qmsk.net.socket.platform as platform
cimport qmsk.net.libc as libc

from qmsk.net.py cimport raise_errno

cdef size_t addrsoup_len (object addrs) except -1 :
    """
        Calculate the length of the addr_buf required to store the given addrsoup
    """

    cdef sockaddr addr
    cdef size_t addr_size = 0

    # whoever decided that sctp_bindx takes an array of mixed sockaddr_in/sockaddr_in6's should be shot
    for addr in addrs :
        addr_size += addr._get_sockaddr_len()
    
    return addr_size

cdef addrsoup_store (object addrs, char *addr_buf) :
    """
        Store the sockaddr_*'s for the given addresses into the given buffer, which should be addrsoup_len() bytes long
    """

    cdef sockaddr addr
    cdef char *addr_ptr = addr_buf

    # fill it
    cdef platform.sockaddr *sa
    cdef platform.socklen_t sa_len

    for addr in addrs :
        # get address's sockaddr info
        addr._get_sockaddr(&sa, &sa_len)
        
        # copy to buffer
        libc.memcpy(addr_ptr, sa, sa_len)
        
        # move to next
        addr_ptr += sa_len

cdef class sctp_socket (socket.socket) :

    def __init__ (self, int family = platform.AF_INET, int socktype = platform.SOCK_SEQPACKET, int protocol = IPPROTO_SCTP, int fd = -1) :
        """
            Same behaviour as socket.__init__, but different defaults.
        """

        socket.socket.__init__(self, family, socktype, protocol, fd)

    def sctp_bindx (self, object addrs, int flags) :
        """
            Bind this IPPROTO_SCTP socket to the given set of local addresses.

            This may be called multiple times on a socket, even after bind(), to change the set of local addresses.
            This may affect ongoing associations, or only new associations.

                addresses   the list of sockaddr's
                flags       one of SCTP_BINDX_ADD/REM_ADDR
            
        """

        # ensure that addrs stays the same... ?
        addrs = tuple(addrs)

        # alloc buffer to hold all the sockaddr_*'s
        cdef char *addr_buf = <char *> libc.alloca(addrsoup_len(addrs))

        # store
        addrsoup_store(addrs, addr_buf)

        # then call
        if sctp_bindx(self.fd, <platform.sockaddr *> addr_buf, len(addrs), flags) :
            raise_errno('sctp_bindx')

    def sctp_connectx (self, int sd, object addrs) :
        """
            Establish an association with the given set of remote sockaddr's.

                addresses   the list of sockaddr's
            
        """

        # ensure that addrs stays the same... ?
        addrs = tuple(addrs)

        # alloc buffer to hold all the sockaddr_*'s
        cdef char *addr_buf = <char *> libc.alloca(addrsoup_len(addrs))

        # store
        addrsoup_store(addrs, addr_buf)

        # then call
        if sctp_connectx(self.fd, <platform.sockaddr *> addr_buf, len(addrs)) :
            raise_errno('sctp_connectx')