reorganize socket, adding af_inet/af_inet6 modules, and moving sctp in
authorTero Marttila <terom@fixme.fi>
Tue, 18 Aug 2009 23:02:30 +0300
changeset 24 f18b5787c46c
parent 23 15d8bb96b8d4
child 25 e60de189099d
reorganize socket, adding af_inet/af_inet6 modules, and moving sctp in
inc/qmsk.net.socket.addr.pxd
inc/qmsk.net.socket.address.pxd
inc/qmsk.net.socket.sctp.pxd
inc/sctp.sock.pxd
qmsk/net/sctp/sock.pxd
qmsk/net/sctp/sock.pyx
qmsk/net/socket/__init__.py
qmsk/net/socket/address.pyx
qmsk/net/socket/af_inet.pxd
qmsk/net/socket/af_inet.pyx
qmsk/net/socket/af_inet6.pxd
qmsk/net/socket/af_inet6.pyx
qmsk/net/socket/constants.pyx
qmsk/net/socket/sctp.pxd
qmsk/net/socket/sctp.pyx
qmsk/net/socket/socket.pyx
qmsk/net/transport/__init__.py
setup.py
--- a/inc/qmsk.net.socket.addr.pxd	Tue Aug 18 22:24:36 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../qmsk/net/socket/addr.pxd
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/qmsk.net.socket.address.pxd	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,1 @@
+../qmsk/net/socket/address.pxd
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/qmsk.net.socket.sctp.pxd	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,1 @@
+../qmsk/net/socket/sctp.pxd
\ No newline at end of file
--- a/inc/sctp.sock.pxd	Tue Aug 18 22:24:36 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-../qmsk/net/sctp/sock.pxd
\ No newline at end of file
--- a/qmsk/net/sctp/sock.pxd	Tue Aug 18 22:24:36 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,277 +0,0 @@
-"""
-    The socket-based interface for SCTP.
-
-    http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-15
-"""
-
-#from qmsk.net.libc cimport __u8, __u16, __u32, __s8, __s16, __s32, ssize_t
-from qmsk.net.libc cimport *
-
-cimport qmsk.net.socket.platform as platform
-cimport qmsk.net.socket.socket
-
-# <netinet/sctp.h>
-# this defines the kernel SCTP -> userspace API extensions, such as structure types etc.
-cdef extern from "netinet/sctp.h" :
-    ## types
-    ctypedef __s32 sctp_assoc_t
-
-    ## constants
-    # sockapi
-    enum :
-        SOL_SCTP
-        IPPROTO_SCTP
-
-    # sockopts
-    enum :
-        SCTP_RTOINFO
-        SCTP_ASSOCINFO
-        SCTP_INITMSG
-        SCTP_NODELAY
-        SCTP_AUTOCLOSE
-        SCTP_SET_PEER_PRIMARY_ADDR
-        SCTP_PRIMARY_ADDR
-        SCTP_ADAPTATION_LAYER
-        SCTP_DISABLE_FRAGMENTS
-        SCTP_PEER_ADDR_PARAMS
-        SCTP_DEFAULT_SEND_PARAM
-        SCTP_EVENTS
-        SCTP_I_WANT_MAPPED_V4_ADDR
-        SCTP_MAXSEG
-        SCTP_STATUS
-        SCTP_GET_PEER_ADDR_INFO
-        SCTP_DELAYED_ACK_TIME
-        SCTP_CONTEXT
-        SCTP_FRAGMENT_INTERLEAVE
-        SCTP_PARTIAL_DELIVERY_POINT
-        SCTP_MAX_BURST
-
-    ## send/recv-msg cmsghdr's
-    struct sctp_initmsg :
-        __u16 sinit_num_ostreams
-        __u16 sinit_max_instreams
-        __u16 sinit_max_attempts
-        __u16 sinit_max_init_timeo
-
-    struct sctp_sndrcvinfo :
-        __u16 sinfo_stream
-        __u16 sinfo_ssn
-        __u16 sinfo_flags
-        __u32 sinfo_ppid
-        __u32 sinfo_context
-        __u32 sinfo_timetolive
-        __u32 sinfo_tsn
-        __u32 sinfo_cumtsn
-        sctp_assoc_t sinfo_assoc_id
-
-    # XXX: missing sctp_extrcvinfo
-
-    # sctp_sndrcvinfo.sinfo_flags values
-    enum sctp_sinfo_flags :
-        SCTP_UNORDERED          # Send/receive message unordered
-        SCTP_ADDR_OVER          # Override the primary destination
-        SCTP_ABORT              # Send an ABORT message to the peer
-        SCTP_EOF                # Initiate graceful shutdown process
-    
-    ## notifications
-    struct sctp_assoc_change :
-        __u16 sac_type
-        __u16 sac_flags
-        __u32 sac_length
-        __u16 sac_state
-        __u16 sac_error
-        __u16 sac_outbound_streams
-        __u16 sac_inbound_streams
-        sctp_assoc_t sac_assoc_id
-        __u8 sac_info[0]
-    
-    # sctp_assoc_change.sac_state
-    enum sctp_sac_state :
-        SCTP_COMM_UP
-        SCTP_COMM_LOST
-        SCTP_RESTART
-        SCTP_SHUTDOWN_COMP
-        SCTP_CANT_STR_ASSOC
-
-    struct sctp_paddr_change :
-        __u16 spc_type
-        __u16 spc_flags
-        __u32 spc_length
-        platform.sockaddr_storage spc_aaddr
-        int spc_state
-        int spc_error
-        sctp_assoc_t spc_assoc_id
-
-    enum sctp_spc_state :
-        SCTP_ADDR_AVAILABLE
-        SCTP_ADDR_UNREACHABLE
-        SCTP_ADDR_REMOVED
-        SCTP_ADDR_ADDED
-        SCTP_ADDR_MADE_PRIM
-        SCTP_ADDR_CONFIRMED
-
-    struct sctp_remote_error :
-        __u16 sre_type
-        __u16 sre_flags
-        __u32 sre_length
-        __u16 sre_error
-        sctp_assoc_t sre_assoc_id
-        __u8 sre_data[0]
-
-
-    struct sctp_send_failed :
-        __u16 ssf_type
-        __u16 ssf_flags
-        __u32 ssf_length
-        __u32 ssf_error
-        sctp_sndrcvinfo ssf_info
-        sctp_assoc_t ssf_assoc_id
-        __u8 ssf_data[0]
-
-    enum sctp_ssf_flags :
-        SCTP_DATA_UNSENT
-        SCTP_DATA_SENT
-
-    struct sctp_shutdown_event :
-        __u16 sse_type
-        __u16 sse_flags
-        __u32 sse_length
-        sctp_assoc_t sse_assoc_id
-
-    struct sctp_adaptation_event :
-        __u16 sai_type
-        __u16 sai_flags
-        __u32 sai_length
-        __u32 sai_adaptation_ind
-        sctp_assoc_t sai_assoc_id
-
-    struct sctp_pdapi_event :
-        __u16 pdapi_type
-        __u16 pdapi_flags
-        __u32 pdapi_length
-        __u32 pdapi_indication
-        sctp_assoc_t pdapi_assoc_id
-
-    enum :
-        SCTP_PARTIAL_DELIVERY_ABORTED
-
-    struct sctp_event_subscribe :
-        __u8 sctp_data_io_event
-        __u8 sctp_association_event
-        __u8 sctp_address_event
-        __u8 sctp_send_failure_event
-        __u8 sctp_peer_error_event
-        __u8 sctp_shutdown_event
-        __u8 sctp_partial_delivery_event
-        __u8 sctp_adaptation_layer_event
-
-    struct sn_header :
-        __u16 sn_type
-        __u16 sn_flags
-        __u32 sn_length
-
-    union sctp_notification :
-        sn_header sn_header
-
-        sctp_assoc_change sn_assoc_change
-        sctp_paddr_change sn_paddr_change
-        sctp_remote_error sn_remote_error
-        sctp_send_failed sn_send_failed
-        sctp_shutdown_event sn_shutdown_event
-        sctp_adaptation_event sn_adaptation_event
-        sctp_pdapi_event sn_pdapi_event
-
-    enum sctp_sn_type :
-        SCTP_SN_TYPE_BASE
-        SCTP_ASSOC_CHANGE
-        SCTP_PEER_ADDR_CHANGE
-        SCTP_SEND_FAILED
-        SCTP_REMOTE_ERROR
-        SCTP_SHUTDOWN_EVENT
-        SCTP_PARTIAL_DELIVERY_EVENT
-        SCTP_ADAPTATION_INDICATION
-
-    enum sctp_sn_error :
-        SCTP_FAILED_THRESHOLD
-        SCTP_RECEIVED_SACK
-        SCTP_HEARTBEAT_SUCCESS
-        SCTP_RESPONSE_TO_USER_REQ
-        SCTP_INTERNAL_ERROR
-        SCTP_SHUTDOWN_GUARD_EXPIRES
-        SCTP_PEER_FAULTY
-    
-    ctypedef sctp_sn_error sctp_sn_error_t
-    
-    
-    ###
-    ### 8   New Interfaces
-    ###
-
-    ## 8.1  sctp_bindx
-    enum :
-        SCTP_BINDX_ADD_ADDR
-        SCTP_BINDX_REM_ADDR
-
-    int sctp_bindx (int sd, platform.sockaddr *addrs, int addrcnt, int flags)
-
-    ## 8.2 sctp_peeloff
-    int sctp_peeloff (int sd, sctp_assoc_t assoc_id)
-
-    ## 8.3 sctp_getpaddrs
-    int sctp_getpaddrs (int sd, sctp_assoc_t assoc_id, platform.sockaddr **addrs)
-    ## 8.4 sctp_freepaddrs
-    void sctp_freepaddrs (platform.sockaddr *addrs)
-
-    ## 8.5 sctp_getladdrs
-    int sctp_getladdrs (int sd, sctp_assoc_t id, platform.sockaddr **ss)
-    ## 8.6 sctp_freeladdrs
-    void sctp_freeladdrs (platform.sockaddr **ss)
-
-    ## 8.7 sctp_sendmsg
-    ssize_t sctp_sendmsg (
-            int sd,
-            void *msg, size_t len,
-            platform.sockaddr *dst, platform.socklen_t dstlen,
-            uint32_t ppid, uint32_t flags, uint16_t stream_no, uint32_t timetolive, uint32_t context
-    )
-
-    ## 8.8 sctp_Recvmsg
-    ssize_t sctp_recvmsg (
-            int sd,
-            void *msg, size_t len,
-            platform.sockaddr *src, platform.socklen_t *srclen,
-            sctp_sndrcvinfo *sinfo,
-            int *msg_flags
-    )
-
-
-    ## 8.9 sctp_connectx
-    # XXX: missing return-sctp_assoc_t-id argument from RFC!
-    int sctp_connectx (int sd, platform.sockaddr *addrs, int addrcnt)
-
-    ## 8.10 sctp_send
-    int sctp_send (
-            int sd,
-            void *msg, size_t len,
-            sctp_sndrcvinfo *sinfo,
-            int flags
-    )
-
-    ## 8.11 sctp_sendx
-    int sctp_sendx (
-            int sd,
-            void *msg, size_t len,
-            platform.sockaddr *addrs, int addrcnt,
-            sctp_sndrcvinfo *sinfo,
-            int flags
-    )
-
-    ## 8.12 sctp_getaddrlen
-    int sctp_getaddrlen (platform.sa_family_t family)
-
-cdef class sctp_socket (qmsk.net.socket.socket.socket) :
-    """
-        SCTP-specific methods and functionality, built on top of the generic socket interface.
-    """
-
-   
--- a/qmsk/net/sctp/sock.pyx	Tue Aug 18 22:24:36 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-"""
-    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.address 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.sctp.sock cimport *
-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 (qmsk.net.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.
-        """
-
-        qmsk.net.socket.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')
-
-
--- a/qmsk/net/socket/__init__.py	Tue Aug 18 22:24:36 2009 +0300
+++ b/qmsk/net/socket/__init__.py	Tue Aug 18 23:02:30 2009 +0300
@@ -1,6 +1,62 @@
-# combine all the modules
+"""
+    The low-level OS Socket API and related functionality, covering the Internet layer.
+
+    All functionality provided here aims to be a thin wrapper around the native system API, so for the most part, methods
+    map directly to *one* syscall/libc call.
+
+    Provided modules/types:
+    
+    address:
+        Abstract socket addresses.
+
+        sockaddr        - corresponds to the `struct sockaddr` used in socket API calls. This is an abstract type
+                          that cannot itself be instantated, see `sockaddr_in`/`sockaddr_in6`/`endpoint`.
+
+        addrinfo        - corresponds to the `struct addrinfo` used by the GAI API. Hold a concrete `sockaddr`
+
+        endpoint        - abstract internet hostname+service address as handled by getaddrinfo. Maps to a series of `addrinfo`'s.
+    
+    af_inet:
+        AF_INET (IPv4) -specific functionality.
+
+        sockaddr_in     - AF_INET sockaddr with addr, port fields
+
+    af_inet6:
+        AF_INET6 (IPv6) -specific functionality.
+
+        sockaddr_in6    - AF_INET6 sockaddr with addr, port, flowinfo, scope_id fields
+    
+    socket:
+        Core socket API.
+
+        socket          - socket-related syscalls with a guaranteed 1:1 mapping between methods and library
+                          calls.
+        
+    sctp:
+        SCTP-specific socket API
+
+        sctp_socket     - extension of socket.socket to support SCTP-specific socket API extensions
+    
+    constants:
+        Socket-related constants.
+
+        AF_*            - address families
+        SOCK_*          - socket types
+        IPPROTO_*       - inet protocols
+        MSG_*           - send/recv message flags
+
+        etc.
+"""
+
+## XXX: these shadow the modules... how do we want to provide these?
+#from qmsk.net.socket.address import sockaddr, addrinfo, endpoint
+#
+#from qmsk.net.socket.af_inet import sockaddr_in
+#from qmsk.net.socket.af_inet6 import sockaddr_in6
+#
+#from qmsk.net.socket.socket import socket
+#from qmsk.net.socket.sctp import sctp_socket
+#
 #from qmsk.net.socket.constants import *
-#from qmsk.net.socket.platform import *
-#from qmsk.net.socket.addr import *
-#from qmsk.net.socket.socket import *
+#
 
--- a/qmsk/net/socket/address.pyx	Tue Aug 18 22:24:36 2009 +0300
+++ b/qmsk/net/socket/address.pyx	Tue Aug 18 23:02:30 2009 +0300
@@ -99,278 +99,12 @@
     def __repr__ (self) :
         return "sockaddr(%d, %s, %d)" % (self.family, self.addr, self.port)
 
-cdef class sockaddr_in (sockaddr) :
-    """
-        AF_INET struct sockaddr_in
-
-        >>> sa = sockaddr_in("127.0.0.1", 80)
-        >>> sa.addr
-        '127.0.0.1'
-        >>> sa.port
-        80
-        >>> str(sa)
-        '127.0.0.1:80'
-
-        >>> sockaddr_in('2001::5')
-        Traceback (most recent call last):
-          ...
-        NameError: Invalid network address for specified address family: '2001::5'
-
-    """
-
-    # the struct sockaddr_in
-    cdef platform.sockaddr_in sockaddr
-
-    def __init__ (self, object addr=None, platform.in_port_t port=0) :
-        """
-            Construct using given literal IPv4 address and TCP/UDP port
-
-                addr        - IPv4 address, defaults to INADDR_ANY (0.0.0.0)
-                port        - TCP/UDP port, defaults to 0 (ephemeral)
-        """
-
-        # zero
-        libc.memset(&self.sockaddr, 0, sizeof(self.sockaddr))
-
-        # store our family
-        # XXX: this should be a class attribute...
-        self._init_family(platform.AF_INET)
-
-        # constant af
-        self.sockaddr.sin_family = self.family
-        
-        # set the sin_port
-        self.sockaddr.sin_port = platform.htons(port)
-        
-        if addr :
-            # set the sin_addr
-            # this automatically converts the addr from str -> char *
-            platform.inet_pton(self.family, addr, &self.sockaddr.sin_addr)
-
-        else :
-            # set as INADDR_ANY
-            self.sockaddr.sin_addr.s_addr = platform.INADDR_ANY
-    
-    cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1 :
-        if sa_ptr :
-            sa_ptr[0] = <platform.sockaddr *> &self.sockaddr
-
-        if sa_len :
-            sa_len[0] = sizeof(self.sockaddr)
-
-        return 0
-
-    cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1 :
-        assert sa_len == sizeof(self.sockaddr)
-
-        libc.memcpy(&self.sockaddr, sa, sa_len)
-
-    property port :
-        """
-            The integer port number
-
-            >>> sockaddr_in(port=1234).port
-            1234
-        """
-
-        def __get__ (self) :
-            return platform.ntohs(self.sockaddr.sin_port)
-
-    def __cmp__ (self, other_obj) :
-        """
-            A sockaddr_in is equal to any other sockaddr_in which has the same addr and port
-            
-            >>> assert sockaddr_in() == sockaddr_in()
-            >>> assert sockaddr_in('127.0.0.1', 80) == sockaddr_in('127.0.0.1', 80)
-            >>> addr = sockaddr_in(); assert addr == addr
-        """
-
-        if not isinstance(other_obj, sockaddr_in) :
-            return <object> py.Py_NotImplemented
-
-        cdef sockaddr_in other = other_obj
-        cdef platform.sockaddr_in *sa1 = &self.sockaddr, *sa2 = &other.sockaddr
-
-        if other is self :
-            return 0
-
-        return (
-                libc.memcmp(<void *> &sa1.sin_port, <void *> &sa2.sin_port, sizeof(sa1.sin_port))
-            or  libc.memcmp(<void *> &sa1.sin_addr, <void *> &sa2.sin_addr, sizeof(sa1.sin_addr))
-        )
-
-    def __str__ (self) :
-        """
-            Return the literal ASCII representation for this sockaddr as an '<addr>:<port> string
-        
-            >>> str(sockaddr_in())
-            '0.0.0.0:0'
-        """
-        
-        # format
-        return "%s:%s" % self.getnameinfo()
-
-cdef class sockaddr_in6 (sockaddr) :
-    """
-        AF_INET6 struct sockaddr_in6
-
-        >>> sa6 = sockaddr_in6("::1", 80)
-        >>> sa6.addr
-        '::1'
-        >>> sa6.port
-        80
-        >>> str(sa6)
-        '[::1]:80'
-        
-    """
-
-    cdef platform.sockaddr_in6 sockaddr
-
-    def __init__ (self, object addr=None, platform.in_port_t port=0, unsigned int scope_id = 0) :
-        """
-            Construct using given literal IPv6 address and TCP/UDP port
-
-                addr        - IPv6 address, defaults to platform.in6addr_any (::)
-                port        - TCP/UDP port, defaults to 0 (ephemeral)
-                scope_id    - (optional) scope ID representing interface index for link-local addresses
-        """
+# mapping of AF -> sockaddr, user-modifyable
+cimport qmsk.net.socket.af_inet, qmsk.net.socket.af_inet6
 
-        # zero
-        libc.memset(&self.sockaddr, 0, sizeof(self.sockaddr))
-
-        # store our family
-        # XXX: this should be a class attribute...
-        self._init_family(platform.AF_INET6)
-
-        # constant af
-        self.sockaddr.sin6_family = self.family
-        
-        # set the sin_port
-        self.sockaddr.sin6_port = platform.htons(port)
-        
-        if addr :
-            # set the sin_addr
-            # this automatically converts the addr from str -> char *
-            platform.inet_pton(self.family, addr, &self.sockaddr.sin6_addr)
-
-        else :
-            # set as INADDR_ANY
-            self.sockaddr.sin6_addr = platform.in6addr_any
-
-        # scope ID
-        self.sockaddr.sin6_scope_id = scope_id
-
-    cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1 :
-        if sa_ptr :
-            sa_ptr[0] = <platform.sockaddr *> &self.sockaddr
-
-        if sa_len :
-            sa_len[0] = sizeof(self.sockaddr)
-
-        return 0
-
-    cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1 :
-        assert sa_len == sizeof(self.sockaddr)
-
-        libc.memcpy(&self.sockaddr, sa, sa_len)
-
-    property port :
-        """
-            The integer port number.
-
-            This will represent it correctly in host byte order.
-
-            >>> sockaddr_in6(port=1234).port
-            1234
-        """
-
-        def __get__ (self) :
-            return platform.ntohs(self.sockaddr.sin6_port)
-
-
-    property flowinfo :
-        """
-            The integer flowinfo
-
-            XXX: byteorder?
-            
-            >>> sockaddr_in6().flowinfo
-            0
-        """
-
-        def __get__ (self) :
-            return self.sockaddr.sin6_flowinfo
-
-
-    property scope_id :
-        """
-            The scope ID - corresponds to an interface index for link-scope addresses.
-
-            This should be in host byte order...
-
-            >>> sockaddr_in6(scope_id=1337).scope_id
-            1337
-        """
-
-        def __get__ (self) :
-            return self.sockaddr.sin6_scope_id
-
-    def __cmp__ (self, other_obj) :
-        """
-            A sockaddr_in6 is equal to any other sockaddr_in6 which has the same addr, port and scope ID.
-
-            XXX: flowinfo?
-
-            XXX: A sockaddr_in6 is also equal to a sockaddr_in if the sockaddr_in6 represents the given v4-mapped address.
-            
-            >>> assert sockaddr_in6() == sockaddr_in6()
-            >>> assert sockaddr_in6('0:0:0::1', 80) == sockaddr_in6('::1', 80)
-            >>> assert sockaddr_in6('::127.0.0.1') == sockaddr_in('127.0.0.1')
-        """
-
-        if not isinstance(other_obj, sockaddr_in6) :
-            return <object> py.Py_NotImplemented
-
-        cdef sockaddr_in6 other = other_obj
-        cdef platform.sockaddr_in6 *sa1 = &self.sockaddr, *sa2 = &other.sockaddr
-
-        if other is self :
-            return 0
-
-        return (
-                libc.memcmp(<void *> &sa1.sin6_port,        <void *> &sa2.sin6_port,        sizeof(sa1.sin6_port))
-            or  libc.memcmp(<void *> &sa1.sin6_addr,        <void *> &sa2.sin6_addr,        sizeof(sa1.sin6_addr))
-            or  libc.memcmp(<void *> &sa1.sin6_scope_id,    <void *> &sa2.sin6_scope_id,    sizeof(sa1.sin6_scope_id))
-        )
-
-    def __str__ (self) :
-        """
-            Return the literal ASCII representation for this sockaddr as a '[<addr>]:<port> string
-
-            >>> str(sockaddr_in6())
-            '[::]:0'
-
-            >>> str(sockaddr_in6('2001:0::05:1'))
-            '[2001::5:1]'
-
-            >>> str(sockaddr_in6('fe80::abcd', scope_id=5))
-            '[fe80::abcd%5]'
-        """
-
-        addr, port = self.getnameinfo()
-        scope_id = self.scope_id
-
-        # format with scope_id
-        return "[%s%s]:%s" % (
-            addr,
-            "%%%d" % scope_id if scope_id else "",
-            port
-        )
-
-# mapping of AF -> sockaddr, user-modifyable
 SOCKADDR_BY_FAMILY = {
-    platform.AF_INET:   sockaddr_in,
-    platform.AF_INET6:  sockaddr_in6,
+    platform.AF_INET:   qmsk.net.socket.af_inet.sockaddr_in,
+    platform.AF_INET6:  qmsk.net.socket.af_inet6.sockaddr_in6,
 }
 
 # build a sockaddr from the given sockaddr struct, based on sa_family
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/socket/af_inet.pxd	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,33 @@
+"""
+    AF_INET-specific stuff
+"""
+
+from qmsk.net.socket.address cimport *
+
+
+cdef class sockaddr_in (sockaddr) :
+    """
+        AF_INET struct sockaddr_in
+
+        >>> sa = sockaddr_in("127.0.0.1", 80)
+        >>> sa.addr
+        '127.0.0.1'
+        >>> sa.port
+        80
+        >>> str(sa)
+        '127.0.0.1:80'
+
+        >>> sockaddr_in('2001::5')
+        Traceback (most recent call last):
+          ...
+        NameError: Invalid network address for specified address family: '2001::5'
+
+    """
+
+    # the struct sockaddr_in
+    cdef platform.sockaddr_in sockaddr
+
+
+    cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1
+    cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/socket/af_inet.pyx	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,94 @@
+cimport qmsk.net.py as py
+
+cdef class sockaddr_in (sockaddr) :
+
+    def __init__ (self, object addr=None, platform.in_port_t port=0) :
+        """
+            Construct using given literal IPv4 address and TCP/UDP port
+
+                addr        - IPv4 address, defaults to INADDR_ANY (0.0.0.0)
+                port        - TCP/UDP port, defaults to 0 (ephemeral)
+        """
+
+        # zero
+        libc.memset(&self.sockaddr, 0, sizeof(self.sockaddr))
+
+        # store our family
+        # XXX: this should be a class attribute...
+        self._init_family(platform.AF_INET)
+
+        # constant af
+        self.sockaddr.sin_family = self.family
+        
+        # set the sin_port
+        self.sockaddr.sin_port = platform.htons(port)
+        
+        if addr :
+            # set the sin_addr
+            # this automatically converts the addr from str -> char *
+            platform.inet_pton(self.family, addr, &self.sockaddr.sin_addr)
+
+        else :
+            # set as INADDR_ANY
+            self.sockaddr.sin_addr.s_addr = platform.INADDR_ANY
+    
+    cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1 :
+        if sa_ptr :
+            sa_ptr[0] = <platform.sockaddr *> &self.sockaddr
+
+        if sa_len :
+            sa_len[0] = sizeof(self.sockaddr)
+
+        return 0
+
+    cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1 :
+        assert sa_len == sizeof(self.sockaddr)
+
+        libc.memcpy(&self.sockaddr, sa, sa_len)
+
+    property port :
+        """
+            The integer port number
+
+            >>> sockaddr_in(port=1234).port
+            1234
+        """
+
+        def __get__ (self) :
+            return platform.ntohs(self.sockaddr.sin_port)
+
+    def __cmp__ (self, other_obj) :
+        """
+            A sockaddr_in is equal to any other sockaddr_in which has the same addr and port
+            
+            >>> assert sockaddr_in() == sockaddr_in()
+            >>> assert sockaddr_in('127.0.0.1', 80) == sockaddr_in('127.0.0.1', 80)
+            >>> addr = sockaddr_in(); assert addr == addr
+        """
+
+        if not isinstance(other_obj, sockaddr_in) :
+            return <object> py.Py_NotImplemented
+
+        cdef sockaddr_in other = other_obj
+        cdef platform.sockaddr_in *sa1 = &self.sockaddr, *sa2 = &other.sockaddr
+
+        if other is self :
+            return 0
+
+        return (
+                libc.memcmp(<void *> &sa1.sin_port, <void *> &sa2.sin_port, sizeof(sa1.sin_port))
+            or  libc.memcmp(<void *> &sa1.sin_addr, <void *> &sa2.sin_addr, sizeof(sa1.sin_addr))
+        )
+
+    def __str__ (self) :
+        """
+            Return the literal ASCII representation for this sockaddr as an '<addr>:<port> string
+        
+            >>> str(sockaddr_in())
+            '0.0.0.0:0'
+        """
+        
+        # format
+        return "%s:%s" % self.getnameinfo()
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/socket/af_inet6.pxd	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,26 @@
+"""
+    AF_INET6-specific stuff
+"""
+
+from qmsk.net.socket.address cimport *
+
+
+cdef class sockaddr_in6 (sockaddr) :
+    """
+        AF_INET6 struct sockaddr_in6
+
+        >>> sa6 = sockaddr_in6("::1", 80)
+        >>> sa6.addr
+        '::1'
+        >>> sa6.port
+        80
+        >>> str(sa6)
+        '[::1]:80'
+        
+    """
+
+    cdef platform.sockaddr_in6 sockaddr
+
+    cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1
+    cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/socket/af_inet6.pyx	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,146 @@
+cimport qmsk.net.py as py
+
+cdef class sockaddr_in6 (sockaddr) :
+
+    def __init__ (self, object addr=None, platform.in_port_t port=0, unsigned int scope_id = 0) :
+        """
+            Construct using given literal IPv6 address and TCP/UDP port
+
+                addr        - IPv6 address, defaults to platform.in6addr_any (::)
+                port        - TCP/UDP port, defaults to 0 (ephemeral)
+                scope_id    - (optional) scope ID representing interface index for link-local addresses
+        """
+
+        # zero
+        libc.memset(&self.sockaddr, 0, sizeof(self.sockaddr))
+
+        # store our family
+        # XXX: this should be a class attribute...
+        self._init_family(platform.AF_INET6)
+
+        # constant af
+        self.sockaddr.sin6_family = self.family
+        
+        # set the sin_port
+        self.sockaddr.sin6_port = platform.htons(port)
+        
+        if addr :
+            # set the sin_addr
+            # this automatically converts the addr from str -> char *
+            platform.inet_pton(self.family, addr, &self.sockaddr.sin6_addr)
+
+        else :
+            # set as INADDR_ANY
+            self.sockaddr.sin6_addr = platform.in6addr_any
+
+        # scope ID
+        self.sockaddr.sin6_scope_id = scope_id
+
+    cdef int _get_sockaddr (self, platform.sockaddr **sa_ptr, platform.socklen_t *sa_len) except -1 :
+        if sa_ptr :
+            sa_ptr[0] = <platform.sockaddr *> &self.sockaddr
+
+        if sa_len :
+            sa_len[0] = sizeof(self.sockaddr)
+
+        return 0
+
+    cdef int _set_sockaddr (self, platform.sockaddr *sa, size_t sa_len) except -1 :
+        assert sa_len == sizeof(self.sockaddr)
+
+        libc.memcpy(&self.sockaddr, sa, sa_len)
+
+    property port :
+        """
+            The integer port number.
+
+            This will represent it correctly in host byte order.
+
+            >>> sockaddr_in6(port=1234).port
+            1234
+        """
+
+        def __get__ (self) :
+            return platform.ntohs(self.sockaddr.sin6_port)
+
+
+    property flowinfo :
+        """
+            The integer flowinfo
+
+            XXX: byteorder?
+            
+            >>> sockaddr_in6().flowinfo
+            0
+        """
+
+        def __get__ (self) :
+            return self.sockaddr.sin6_flowinfo
+
+
+    property scope_id :
+        """
+            The scope ID - corresponds to an interface index for link-scope addresses.
+
+            This should be in host byte order...
+
+            >>> sockaddr_in6(scope_id=1337).scope_id
+            1337
+        """
+
+        def __get__ (self) :
+            return self.sockaddr.sin6_scope_id
+
+    def __cmp__ (self, other_obj) :
+        """
+            A sockaddr_in6 is equal to any other sockaddr_in6 which has the same addr, port and scope ID.
+
+            XXX: flowinfo?
+
+            XXX: A sockaddr_in6 is also equal to a sockaddr_in if the sockaddr_in6 represents the given v4-mapped address.
+            
+            >>> assert sockaddr_in6() == sockaddr_in6()
+            >>> assert sockaddr_in6('0:0:0::1', 80) == sockaddr_in6('::1', 80)
+            >>> assert sockaddr_in6('::127.0.0.1') == sockaddr_in('127.0.0.1')
+        """
+
+        if not isinstance(other_obj, sockaddr_in6) :
+            return <object> py.Py_NotImplemented
+
+        cdef sockaddr_in6 other = other_obj
+        cdef platform.sockaddr_in6 *sa1 = &self.sockaddr, *sa2 = &other.sockaddr
+
+        if other is self :
+            return 0
+
+        return (
+                libc.memcmp(<void *> &sa1.sin6_port,        <void *> &sa2.sin6_port,        sizeof(sa1.sin6_port))
+            or  libc.memcmp(<void *> &sa1.sin6_addr,        <void *> &sa2.sin6_addr,        sizeof(sa1.sin6_addr))
+            or  libc.memcmp(<void *> &sa1.sin6_scope_id,    <void *> &sa2.sin6_scope_id,    sizeof(sa1.sin6_scope_id))
+        )
+
+    def __str__ (self) :
+        """
+            Return the literal ASCII representation for this sockaddr as a '[<addr>]:<port> string
+
+            >>> str(sockaddr_in6())
+            '[::]:0'
+
+            >>> str(sockaddr_in6('2001:0::05:1'))
+            '[2001::5:1]'
+
+            >>> str(sockaddr_in6('fe80::abcd', scope_id=5))
+            '[fe80::abcd%5]'
+        """
+
+        addr, port = self.getnameinfo()
+        scope_id = self.scope_id
+
+        # format with scope_id
+        return "[%s%s]:%s" % (
+            addr,
+            "%%%d" % scope_id if scope_id else "",
+            port
+        )
+
+
--- a/qmsk/net/socket/constants.pyx	Tue Aug 18 22:24:36 2009 +0300
+++ b/qmsk/net/socket/constants.pyx	Tue Aug 18 23:02:30 2009 +0300
@@ -81,4 +81,65 @@
 NI_MAXHOST = platform.NI_MAXHOST
 NI_MAXSERV = platform.NI_MAXSERV
 
+cimport qmsk.net.socket.sctp as sctp
 
+# XXX: move to platform
+SOL_SCTP = sctp.SOL_SCTP
+
+SCTP_RTOINFO = sctp.SCTP_RTOINFO
+SCTP_ASSOCINFO = sctp.SCTP_ASSOCINFO
+SCTP_INITMSG = sctp.SCTP_INITMSG
+SCTP_NODELAY = sctp.SCTP_NODELAY
+SCTP_AUTOCLOSE = sctp.SCTP_AUTOCLOSE
+SCTP_SET_PEER_PRIMARY_ADDR = sctp.SCTP_SET_PEER_PRIMARY_ADDR
+SCTP_PRIMARY_ADDR = sctp.SCTP_PRIMARY_ADDR
+SCTP_ADAPTATION_LAYER = sctp.SCTP_ADAPTATION_LAYER
+SCTP_DISABLE_FRAGMENTS = sctp.SCTP_DISABLE_FRAGMENTS
+SCTP_PEER_ADDR_PARAMS = sctp.SCTP_PEER_ADDR_PARAMS
+SCTP_DEFAULT_SEND_PARAM = sctp.SCTP_DEFAULT_SEND_PARAM
+SCTP_EVENTS = sctp.SCTP_EVENTS
+SCTP_I_WANT_MAPPED_V4_ADDR = sctp.SCTP_I_WANT_MAPPED_V4_ADDR
+SCTP_MAXSEG = sctp.SCTP_MAXSEG
+SCTP_STATUS = sctp.SCTP_STATUS
+SCTP_GET_PEER_ADDR_INFO = sctp.SCTP_GET_PEER_ADDR_INFO
+SCTP_DELAYED_ACK_TIME = sctp.SCTP_DELAYED_ACK_TIME
+SCTP_CONTEXT = sctp.SCTP_CONTEXT
+SCTP_FRAGMENT_INTERLEAVE = sctp.SCTP_FRAGMENT_INTERLEAVE
+SCTP_PARTIAL_DELIVERY_POINT = sctp.SCTP_PARTIAL_DELIVERY_POINT
+SCTP_MAX_BURST = sctp.SCTP_MAX_BURST
+
+SCTP_ADDR_AVAILABLE = sctp.SCTP_ADDR_AVAILABLE
+SCTP_ADDR_UNREACHABLE = sctp.SCTP_ADDR_UNREACHABLE
+SCTP_ADDR_REMOVED = sctp.SCTP_ADDR_REMOVED
+SCTP_ADDR_ADDED = sctp.SCTP_ADDR_ADDED
+SCTP_ADDR_MADE_PRIM = sctp.SCTP_ADDR_MADE_PRIM
+SCTP_ADDR_CONFIRMED = sctp.SCTP_ADDR_CONFIRMED
+
+
+SCTP_DATA_UNSENT = sctp.SCTP_DATA_UNSENT
+SCTP_DATA_SENT = sctp.SCTP_DATA_SENT
+
+SCTP_PARTIAL_DELIVERY_ABORTED = sctp.SCTP_PARTIAL_DELIVERY_ABORTED
+
+# enum sctp_sn_type :
+SCTP_SN_TYPE_BASE = sctp.SCTP_SN_TYPE_BASE
+SCTP_ASSOC_CHANGE = sctp.SCTP_ASSOC_CHANGE
+SCTP_PEER_ADDR_CHANGE = sctp.SCTP_PEER_ADDR_CHANGE
+SCTP_SEND_FAILED = sctp.SCTP_SEND_FAILED
+SCTP_REMOTE_ERROR = sctp.SCTP_REMOTE_ERROR
+SCTP_SHUTDOWN_EVENT = sctp.SCTP_SHUTDOWN_EVENT
+SCTP_PARTIAL_DELIVERY_EVENT = sctp.SCTP_PARTIAL_DELIVERY_EVENT
+SCTP_ADAPTATION_INDICATION = sctp.SCTP_ADAPTATION_INDICATION
+
+# enum sctp_sn_error :
+SCTP_FAILED_THRESHOLD = sctp.SCTP_FAILED_THRESHOLD
+SCTP_RECEIVED_SACK = sctp.SCTP_RECEIVED_SACK
+SCTP_HEARTBEAT_SUCCESS = sctp.SCTP_HEARTBEAT_SUCCESS
+SCTP_RESPONSE_TO_USER_REQ = sctp.SCTP_RESPONSE_TO_USER_REQ
+SCTP_INTERNAL_ERROR = sctp.SCTP_INTERNAL_ERROR
+SCTP_SHUTDOWN_GUARD_EXPIRES = sctp.SCTP_SHUTDOWN_GUARD_EXPIRES
+SCTP_PEER_FAULTY = sctp.SCTP_PEER_FAULTY
+
+SCTP_BINDX_ADD_ADDR = sctp.SCTP_BINDX_ADD_ADDR
+SCTP_BINDX_REM_ADDR = sctp.SCTP_BINDX_REM_ADDR
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/socket/sctp.pxd	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,277 @@
+"""
+    The socket-based interface for SCTP.
+
+    http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-15
+"""
+
+#from qmsk.net.libc cimport __u8, __u16, __u32, __s8, __s16, __s32, ssize_t
+from qmsk.net.libc cimport *
+
+cimport qmsk.net.socket.platform as platform
+cimport qmsk.net.socket.socket as socket
+
+# <netinet/sctp.h>
+# this defines the kernel SCTP -> userspace API extensions, such as structure types etc.
+cdef extern from "netinet/sctp.h" :
+    ## types
+    ctypedef __s32 sctp_assoc_t
+
+    ## constants
+    # sockapi
+    enum :
+        SOL_SCTP
+        IPPROTO_SCTP
+
+    # sockopts
+    enum :
+        SCTP_RTOINFO
+        SCTP_ASSOCINFO
+        SCTP_INITMSG
+        SCTP_NODELAY
+        SCTP_AUTOCLOSE
+        SCTP_SET_PEER_PRIMARY_ADDR
+        SCTP_PRIMARY_ADDR
+        SCTP_ADAPTATION_LAYER
+        SCTP_DISABLE_FRAGMENTS
+        SCTP_PEER_ADDR_PARAMS
+        SCTP_DEFAULT_SEND_PARAM
+        SCTP_EVENTS
+        SCTP_I_WANT_MAPPED_V4_ADDR
+        SCTP_MAXSEG
+        SCTP_STATUS
+        SCTP_GET_PEER_ADDR_INFO
+        SCTP_DELAYED_ACK_TIME
+        SCTP_CONTEXT
+        SCTP_FRAGMENT_INTERLEAVE
+        SCTP_PARTIAL_DELIVERY_POINT
+        SCTP_MAX_BURST
+
+    ## send/recv-msg cmsghdr's
+    struct sctp_initmsg :
+        __u16 sinit_num_ostreams
+        __u16 sinit_max_instreams
+        __u16 sinit_max_attempts
+        __u16 sinit_max_init_timeo
+
+    struct sctp_sndrcvinfo :
+        __u16 sinfo_stream
+        __u16 sinfo_ssn
+        __u16 sinfo_flags
+        __u32 sinfo_ppid
+        __u32 sinfo_context
+        __u32 sinfo_timetolive
+        __u32 sinfo_tsn
+        __u32 sinfo_cumtsn
+        sctp_assoc_t sinfo_assoc_id
+
+    # XXX: missing sctp_extrcvinfo
+
+    # sctp_sndrcvinfo.sinfo_flags values
+    enum sctp_sinfo_flags :
+        SCTP_UNORDERED          # Send/receive message unordered
+        SCTP_ADDR_OVER          # Override the primary destination
+        SCTP_ABORT              # Send an ABORT message to the peer
+        SCTP_EOF                # Initiate graceful shutdown process
+    
+    ## notifications
+    struct sctp_assoc_change :
+        __u16 sac_type
+        __u16 sac_flags
+        __u32 sac_length
+        __u16 sac_state
+        __u16 sac_error
+        __u16 sac_outbound_streams
+        __u16 sac_inbound_streams
+        sctp_assoc_t sac_assoc_id
+        __u8 sac_info[0]
+    
+    # sctp_assoc_change.sac_state
+    enum sctp_sac_state :
+        SCTP_COMM_UP
+        SCTP_COMM_LOST
+        SCTP_RESTART
+        SCTP_SHUTDOWN_COMP
+        SCTP_CANT_STR_ASSOC
+
+    struct sctp_paddr_change :
+        __u16 spc_type
+        __u16 spc_flags
+        __u32 spc_length
+        platform.sockaddr_storage spc_aaddr
+        int spc_state
+        int spc_error
+        sctp_assoc_t spc_assoc_id
+
+    enum sctp_spc_state :
+        SCTP_ADDR_AVAILABLE
+        SCTP_ADDR_UNREACHABLE
+        SCTP_ADDR_REMOVED
+        SCTP_ADDR_ADDED
+        SCTP_ADDR_MADE_PRIM
+        SCTP_ADDR_CONFIRMED
+
+    struct sctp_remote_error :
+        __u16 sre_type
+        __u16 sre_flags
+        __u32 sre_length
+        __u16 sre_error
+        sctp_assoc_t sre_assoc_id
+        __u8 sre_data[0]
+
+
+    struct sctp_send_failed :
+        __u16 ssf_type
+        __u16 ssf_flags
+        __u32 ssf_length
+        __u32 ssf_error
+        sctp_sndrcvinfo ssf_info
+        sctp_assoc_t ssf_assoc_id
+        __u8 ssf_data[0]
+
+    enum sctp_ssf_flags :
+        SCTP_DATA_UNSENT
+        SCTP_DATA_SENT
+
+    struct sctp_shutdown_event :
+        __u16 sse_type
+        __u16 sse_flags
+        __u32 sse_length
+        sctp_assoc_t sse_assoc_id
+
+    struct sctp_adaptation_event :
+        __u16 sai_type
+        __u16 sai_flags
+        __u32 sai_length
+        __u32 sai_adaptation_ind
+        sctp_assoc_t sai_assoc_id
+
+    struct sctp_pdapi_event :
+        __u16 pdapi_type
+        __u16 pdapi_flags
+        __u32 pdapi_length
+        __u32 pdapi_indication
+        sctp_assoc_t pdapi_assoc_id
+
+    enum :
+        SCTP_PARTIAL_DELIVERY_ABORTED
+
+    struct sctp_event_subscribe :
+        __u8 sctp_data_io_event
+        __u8 sctp_association_event
+        __u8 sctp_address_event
+        __u8 sctp_send_failure_event
+        __u8 sctp_peer_error_event
+        __u8 sctp_shutdown_event
+        __u8 sctp_partial_delivery_event
+        __u8 sctp_adaptation_layer_event
+
+    struct sn_header :
+        __u16 sn_type
+        __u16 sn_flags
+        __u32 sn_length
+
+    union sctp_notification :
+        sn_header sn_header
+
+        sctp_assoc_change sn_assoc_change
+        sctp_paddr_change sn_paddr_change
+        sctp_remote_error sn_remote_error
+        sctp_send_failed sn_send_failed
+        sctp_shutdown_event sn_shutdown_event
+        sctp_adaptation_event sn_adaptation_event
+        sctp_pdapi_event sn_pdapi_event
+
+    enum sctp_sn_type :
+        SCTP_SN_TYPE_BASE
+        SCTP_ASSOC_CHANGE
+        SCTP_PEER_ADDR_CHANGE
+        SCTP_SEND_FAILED
+        SCTP_REMOTE_ERROR
+        SCTP_SHUTDOWN_EVENT
+        SCTP_PARTIAL_DELIVERY_EVENT
+        SCTP_ADAPTATION_INDICATION
+
+    enum sctp_sn_error :
+        SCTP_FAILED_THRESHOLD
+        SCTP_RECEIVED_SACK
+        SCTP_HEARTBEAT_SUCCESS
+        SCTP_RESPONSE_TO_USER_REQ
+        SCTP_INTERNAL_ERROR
+        SCTP_SHUTDOWN_GUARD_EXPIRES
+        SCTP_PEER_FAULTY
+    
+    ctypedef sctp_sn_error sctp_sn_error_t
+    
+    
+    ###
+    ### 8   New Interfaces
+    ###
+
+    ## 8.1  sctp_bindx
+    enum :
+        SCTP_BINDX_ADD_ADDR
+        SCTP_BINDX_REM_ADDR
+
+    int sctp_bindx (int sd, platform.sockaddr *addrs, int addrcnt, int flags)
+
+    ## 8.2 sctp_peeloff
+    int sctp_peeloff (int sd, sctp_assoc_t assoc_id)
+
+    ## 8.3 sctp_getpaddrs
+    int sctp_getpaddrs (int sd, sctp_assoc_t assoc_id, platform.sockaddr **addrs)
+    ## 8.4 sctp_freepaddrs
+    void sctp_freepaddrs (platform.sockaddr *addrs)
+
+    ## 8.5 sctp_getladdrs
+    int sctp_getladdrs (int sd, sctp_assoc_t id, platform.sockaddr **ss)
+    ## 8.6 sctp_freeladdrs
+    void sctp_freeladdrs (platform.sockaddr **ss)
+
+    ## 8.7 sctp_sendmsg
+    ssize_t sctp_sendmsg (
+            int sd,
+            void *msg, size_t len,
+            platform.sockaddr *dst, platform.socklen_t dstlen,
+            uint32_t ppid, uint32_t flags, uint16_t stream_no, uint32_t timetolive, uint32_t context
+    )
+
+    ## 8.8 sctp_Recvmsg
+    ssize_t sctp_recvmsg (
+            int sd,
+            void *msg, size_t len,
+            platform.sockaddr *src, platform.socklen_t *srclen,
+            sctp_sndrcvinfo *sinfo,
+            int *msg_flags
+    )
+
+
+    ## 8.9 sctp_connectx
+    # XXX: missing return-sctp_assoc_t-id argument from RFC!
+    int sctp_connectx (int sd, platform.sockaddr *addrs, int addrcnt)
+
+    ## 8.10 sctp_send
+    int sctp_send (
+            int sd,
+            void *msg, size_t len,
+            sctp_sndrcvinfo *sinfo,
+            int flags
+    )
+
+    ## 8.11 sctp_sendx
+    int sctp_sendx (
+            int sd,
+            void *msg, size_t len,
+            platform.sockaddr *addrs, int addrcnt,
+            sctp_sndrcvinfo *sinfo,
+            int flags
+    )
+
+    ## 8.12 sctp_getaddrlen
+    int sctp_getaddrlen (platform.sa_family_t family)
+
+cdef class sctp_socket (socket.socket) :
+    """
+        SCTP-specific methods and functionality, built on top of the generic socket interface.
+    """
+
+   
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/socket/sctp.pyx	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,112 @@
+"""
+    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.sctp cimport *
+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')
+
+
--- a/qmsk/net/socket/socket.pyx	Tue Aug 18 22:24:36 2009 +0300
+++ b/qmsk/net/socket/socket.pyx	Tue Aug 18 23:02:30 2009 +0300
@@ -112,15 +112,16 @@
 
         return self.fd
     
-    def setblocking (self, bint blocking = True) :
-        """
-            Control the OS-level nonblocking-IO mode flag for this socket.
-
-                blocking    - True for normal blocking operation, False to use non-blocking operation
-        """
-        
-        # fcntl magic
-        libc.fcntl_set_flag(self.fd, libc.O_NONBLOCK, not blocking)
+#    # XXX: does this *really* belong here?
+#    def setblocking (self, bint blocking = True) :
+#        """
+#            Control the OS-level nonblocking-IO mode flag for this socket.
+#
+#                blocking    - True for normal blocking operation, False to use non-blocking operation
+#        """
+#        
+#        # fcntl magic
+#        libc.fcntl_set_flag(self.fd, libc.O_NONBLOCK, not blocking)
 
     def bind (self, sockaddr addr) :
         """
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/transport/__init__.py	Tue Aug 18 23:02:30 2009 +0300
@@ -0,0 +1,5 @@
+"""
+    Transport-layer functionality.
+
+    This implements TCP/UDP/SCTP functionality, plus relevant Transport-Application layer stuff like TLS, SOCKS, SSH-channels, etc.
+"""
--- a/setup.py	Tue Aug 18 22:24:36 2009 +0300
+++ b/setup.py	Tue Aug 18 23:02:30 2009 +0300
@@ -31,8 +31,9 @@
         cython_ext("qmsk.net.socket.constants", ["qmsk/net/socket/constants.pyx"]),
         cython_ext("qmsk.net.socket.address",   ["qmsk/net/socket/address.pyx"]),
         cython_ext("qmsk.net.socket.socket",    ["qmsk/net/socket/socket.pyx"]),
-        cython_ext("qmsk.net.sctp.sock",        ["qmsk/net/sctp/sock.pyx"], libraries=['sctp']),
-        cython_ext("qmsk.net.sctp.constants",   ["qmsk/net/sctp/constants.pyx"]),
+        cython_ext("qmsk.net.socket.af_inet",   ["qmsk/net/socket/af_inet.pyx"]),
+        cython_ext("qmsk.net.socket.af_inet6",  ["qmsk/net/socket/af_inet6.pyx"]),
+        cython_ext("qmsk.net.socket.sctp",      ["qmsk/net/socket/sctp.pyx"], libraries=['sctp']),
     ]
 )