initial code
authorTero Marttila <terom@fixme.fi>
Sun, 16 Aug 2009 02:58:32 +0300
changeset 0 975801b28d85
child 1 0ca9278146d7
initial code
libc.pxd
libc.pyx
sctp/__init__.py
sctp/sock.pyx
sock/__init__.py
sock/addr.pyx
test.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libc.pxd	Sun Aug 16 02:58:32 2009 +0300
@@ -0,0 +1,131 @@
+"""
+    Libc stuff
+"""
+
+cdef extern from "stdint.h":
+    # yes, these are "wrong"
+    ctypedef unsigned char uint8_t
+    ctypedef unsigned short uint16_t
+    ctypedef unsigned int uint32_t
+    
+    ctypedef signed char int8_t
+    ctypedef signed short int16_t
+    ctypedef signed int int32_t
+
+# <linux/types.h>
+cdef extern from "linux/types.h" :
+    ctypedef uint8_t __u8
+    ctypedef uint16_t __u16
+    ctypedef uint32_t __u32
+    
+    ctypedef int8_t __s8
+    ctypedef int16_t __s16
+    ctypedef int32_t __s32
+
+cdef extern from "alloca.h" :
+    void* alloca (size_t size)
+
+# <sys/socket.h>
+cdef extern from "sys/socket.h" :
+    enum :
+        SOCK_STREAM
+        SOCK_DGRAM
+        SOCK_SEQPACKET
+
+    enum :
+        PF_UNSPEC
+        PF_LOCAL
+        PF_INET
+        PF_INET6
+    
+    # these are #defines
+    enum :
+        PF_UNIX
+
+        AF_UNSPEC
+        AF_LOCAL
+        AF_UNIX
+        AF_INET
+        AF_INET6
+
+    ctypedef uint16_t sa_family_t
+    ctypedef uint16_t in_port_t
+    ctypedef uint32_t in_addr_t
+
+    ## AF_INET
+    struct in_addr :
+        in_addr_t s_addr
+
+    struct sockaddr_in :
+        sa_family_t     sin_family
+        in_port_t       sin_port
+        in_addr         sin_addr
+
+    # XXX: should these be in another cdef?
+    in_addr_t INADDR_ANY
+
+    ## AF_INET6
+    struct in6_addr :
+        pass
+
+    struct sockaddr_in6 :
+        sa_family_t     sin6_family
+        in_port_t       sin6_port
+        uint32_t        sin6_flowinfo
+        in6_addr        sin6_addr
+        uint32_t        sin6_scope_id
+
+    # common in6_addr's
+    in6_addr in6addr_any
+    in6_addr in6addr_loopback
+
+    ## actually from bits/socket.h...
+    struct sockaddr :
+        sa_family_t     sa_family
+
+    struct sockaddr_storage :
+        sa_family_t     ss_family
+
+cdef extern from "arpa/inet.h" :
+    uint16_t htons(uint16_t)
+    uint32_t htonl(uint32_t)
+    uint16_t ntohs(uint16_t)
+    uint32_t ntohl(uint32_t)
+    
+    # XXX: correct?!
+    ctypedef size_t socklen_t
+
+    ## constants
+    enum :
+        INET_ADDRSTRLEN
+        INET6_ADDRSTRLEN
+    
+    char* c_inet_ntop "inet_ntop" (int af, void *sockaddr, char *buf, socklen_t len)
+    int c_inet_pton "inet_pton" (int af, char *src, void *dst)
+
+cdef extern from "netdb.h" :
+    int c_getnameinfo "getnameinfo" (
+            sockaddr *sa, socklen_t salen, 
+            char *host, size_t hostlen, 
+            char *serv, size_t servlen, 
+            int flags
+    )
+
+    enum :
+        NI_NOFQDN
+        NI_NUMERICHOST
+        NI_NAMEREQD
+        NI_NUMERICSERV
+        NI_DGRAM
+
+        NI_MAXHOST
+        NI_MAXSERV
+
+    char* gai_strerror (int err)
+
+# python-friendly wrapper around inet_ntop
+cdef object inet_ntop (int af, void *sockaddr)
+cdef object inet_pton (int af, char *addr, void *sockaddr_out)
+
+# sockaddr, flags -> (host, service)
+cdef object getnameinfo (sockaddr *sa, socklen_t salen, int flags)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libc.pyx	Sun Aug 16 02:58:32 2009 +0300
@@ -0,0 +1,35 @@
+from libc cimport *
+
+cdef object inet_ntop (int af, void *sockaddr) :
+    """
+        Wrapper around inet_ntop, returning a PyString
+    """
+
+    # XXX: longest possible address...
+    cdef char buf[INET6_ADDRSTRLEN]
+
+    if c_inet_ntop(af, sockaddr, buf, sizeof(buf)) == NULL :
+        # XXX: errno?
+        raise OSError()
+    
+    # autoconvert -> str
+    return buf
+
+cdef object inet_pton (int af, char *addr, void *sockaddr_out) :
+    if c_inet_pton(af, addr, sockaddr_out) < 0 :
+        raise OSError()
+
+cdef object getnameinfo (sockaddr *sa, socklen_t salen, int flags) :
+    cdef char hostbuf[NI_MAXHOST]
+    cdef char servbuf[NI_MAXSERV]
+    cdef int err
+
+    err = c_getnameinfo(sa, salen, hostbuf, sizeof(hostbuf), servbuf, sizeof(servbuf), flags)
+
+    if err :
+        # XXX: raise a GAIError
+        raise Exception(gai_strerror(err))
+
+    else :
+        return hostbuf, servbuf
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sctp/sock.pyx	Sun Aug 16 02:58:32 2009 +0300
@@ -0,0 +1,236 @@
+"""
+    This C(ython) extension module provides an interface to the libsctp library and associated socket API.
+"""
+
+from libc cimport *
+
+# <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
+    int SOL_SCTP
+    int IPPROTO_SCTP
+
+    # sockopts
+    int SCTP_RTOINFO
+    int SCTP_ASSOCINFO
+    int SCTP_INITMSG
+    int SCTP_NODELAY
+    int SCTP_AUTOCLOSE
+    int SCTP_SET_PEER_PRIMARY_ADDR
+    int SCTP_PRIMARY_ADDR
+    int SCTP_ADAPTATION_LAYER
+    int SCTP_DISABLE_FRAGMENTS
+    int SCTP_PEER_ADDR_PARAMS
+    int SCTP_DEFAULT_SEND_PARAM
+    int SCTP_EVENTS
+    int SCTP_I_WANT_MAPPED_V4_ADDR
+    int SCTP_MAXSEG
+    int SCTP_STATUS
+    int SCTP_GET_PEER_ADDR_INFO
+    int SCTP_DELAYED_ACK_TIME
+    int SCTP_CONTEXT
+    int SCTP_FRAGMENT_INTERLEAVE
+    int SCTP_PARTIAL_DELIVERY_POINT
+    int 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
+
+    # 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
+        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
+    
+
+    ## sctp_bindx
+    int SCTP_BINDX_ADD_ADDR
+    int SCTP_BINDX_REM_ADDR
+
+    int sctp_bindx (int sd, sockaddr *addrs, int addrcnt, int flags)
+    int sctp_connectx (int sd, sockaddr *addrs, int addrcnt)
+
+
+def bindx (int sd, object addrs, int flags) :
+    """
+        Bind the given SOCK_SEQPACKET to the given set of local addresses.
+
+            sd          the system socket FD
+            addresses   the list of address tuples
+            flags       one of SCTP_BINDX_ADD/REM_ADDR
+    """
+    
+    # automatically allocated array of sockaddr_storage's for passing given addresses to sctp_bindx
+    cdef sockaddr_storage *addrs_out
+
+    # used to iterate through addrs_out
+    cdef sockaddr_in *addr_in
+    
+    # number of addresses given
+    cdef size_t addr_count = len(addrs)
+
+    # alloc stack storage for converting addresses
+    addrs_out = <sockaddr_storage *> alloca(addr_count * sizeof(sockaddr_storage))
+
+    for i, address in enumerate(addrs) :
+        # XXX: assume socket-style IPv4 for now...
+        addr, port = address
+
+        addr_in = <sockaddr_in *>(&addrs_out[i])
+        
+        addr_in.sin_family = AF_INET
+        addr_in.sin_port = htons(port)
+        inet_aton(addr, &addr_in.sin_addr)
+    
+    return sctp_bindx(sd, <sockaddr *>addrs_out, addr_count, flags)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sock/addr.pyx	Sun Aug 16 02:58:32 2009 +0300
@@ -0,0 +1,220 @@
+"""
+    Socket addresses
+"""
+
+cimport libc
+
+cdef class sockaddr :
+    """
+        A network-level socket address
+
+        >>> sockaddr().family
+        0
+        >>> sockaddr().port
+        Traceback (most recent call last):
+          ...
+        NotImplementedError
+        >>> sockaddr().getnameinfo()
+        Traceback (most recent call last):
+          ...
+        NotImplementedError
+    """
+    
+    # address family
+    cdef readonly libc.sa_family_t family
+
+    cdef void _init_family (self, libc.sa_family_t family=libc.AF_UNSPEC) :
+        self.family = family
+
+    cdef int _get_sockaddr (self, libc.sockaddr **sa_ptr, libc.socklen_t *sa_len) except -1 :
+        """
+            Get the sockaddr pointer and sockaddr length for this address
+        """ 
+
+        raise NotImplementedError()
+
+    def getnameinfo (self) :
+        """
+            Returns a (host, serv) tuple for this address à la getnameinfo
+        """
+
+        cdef libc.sockaddr *sa
+        cdef libc.socklen_t sa_len
+
+        # XXX: take as args?
+        cdef int flags = libc.NI_NUMERICHOST | libc.NI_NUMERICSERV
+        
+        # get our abstract sockaddr
+        self._get_sockaddr(&sa, &sa_len)
+
+        # get nice text format
+        return libc.getnameinfo(sa, sa_len, flags)
+
+    property addr :
+        """
+            The ASCII literal network address
+        """
+
+        def __get__ (self) :
+            """
+                Default implmentation using getnameinfo()
+            """
+
+            addr, port = self.getnameinfo()
+
+            return addr
+
+    property port :
+        """
+            The integer port number
+        """
+
+        def __get__ (self) :
+            """
+                Default implementation using getnameinfo() and int()
+            """
+
+            addr, port = self.getnameinfo()
+
+            return int(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'
+        >>> str(sockaddr_in())
+        '0.0.0.0:0'
+    """
+
+    # the struct sockaddr_in
+    cdef libc.sockaddr_in sockaddr
+
+    def __init__ (self, object addr=None, libc.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)
+        """
+
+        # store our family
+        # XXX: this should be a class attribute...
+        self._init_family(libc.AF_INET)
+
+        # constant af
+        self.sockaddr.sin_family = self.family
+        
+        # set the sin_port
+        self.sockaddr.sin_port = libc.htons(port)
+        
+        if addr :
+            # set the sin_addr
+            # this automatically converts the addr from str -> char *
+            libc.inet_pton(self.family, addr, &self.sockaddr.sin_addr)
+
+        else :
+            # set as INADDR_ANY
+            self.sockaddr.sin_addr.s_addr = libc.INADDR_ANY
+    
+    cdef int _get_sockaddr (self, libc.sockaddr **sa_ptr, libc.socklen_t *sa_len) except -1 :
+        if sa_ptr :
+            sa_ptr[0] = <libc.sockaddr *> &self.sockaddr
+
+        if sa_len :
+            sa_len[0] = sizeof(self.sockaddr)
+
+        return 0
+
+    property port :
+        """
+            The integer port number
+        """
+
+        def __get__ (self) :
+            return libc.ntohs(self.sockaddr.sin_port)
+
+    def __str__ (self) :
+        """
+            Return the literal ASCII representation for this sockaddr as an '<addr>:<port> string
+        """
+        
+        # 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'
+        >>> str(sockaddr_in6())
+        '[::]:0'
+    """
+
+    cdef libc.sockaddr_in6 sockaddr
+
+    def __init__ (self, object addr=None, libc.in_port_t port=0) :
+        """
+            Construct using given literal IPv6 address and TCP/UDP port
+
+                addr        - IPv6 address, defaults to in6addr_any (::)
+                port        - TCP/UDP port, defaults to 0 (ephemeral)
+        """
+
+        # store our family
+        # XXX: this should be a class attribute...
+        self._init_family(libc.AF_INET6)
+
+        # constant af
+        self.sockaddr.sin6_family = self.family
+        
+        # set the sin_port
+        self.sockaddr.sin6_port = libc.htons(port)
+        
+        if addr :
+            # set the sin_addr
+            # this automatically converts the addr from str -> char *
+            libc.inet_pton(self.family, addr, &self.sockaddr.sin6_addr)
+
+        else :
+            # set as INADDR_ANY
+            self.sockaddr.sin6_addr = libc.in6addr_any
+
+    cdef int _get_sockaddr (self, libc.sockaddr **sa_ptr, libc.socklen_t *sa_len) except -1 :
+        if sa_ptr :
+            sa_ptr[0] = <libc.sockaddr *> &self.sockaddr
+
+        if sa_len :
+            sa_len[0] = sizeof(self.sockaddr)
+
+        return 0
+
+    property port :
+        """
+            The integer port number
+        """
+
+        def __get__ (self) :
+            return libc.ntohs(self.sockaddr.sin6_port)
+
+
+    def __str__ (self) :
+        """
+            Return the literal ASCII representation for this sockaddr as a '[<addr>]:<port> string
+        """
+        
+        # format
+        return "[%s]:%s" % self.getnameinfo()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test.py	Sun Aug 16 02:58:32 2009 +0300
@@ -0,0 +1,6 @@
+import doctest
+
+import sock.addr
+
+doctest.testmod(sock.addr)
+