# HG changeset patch # User Tero Marttila # Date 1250380712 -10800 # Node ID 975801b28d85d92bcf9c05e952265d83fa303555 initial code diff -r 000000000000 -r 975801b28d85 libc.pxd --- /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 + +# +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) + +# +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) diff -r 000000000000 -r 975801b28d85 libc.pyx --- /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 + diff -r 000000000000 -r 975801b28d85 sctp/sock.pyx --- /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 * + +# +# 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 = alloca(addr_count * sizeof(sockaddr_storage)) + + for i, address in enumerate(addrs) : + # XXX: assume socket-style IPv4 for now... + addr, port = address + + addr_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, addrs_out, addr_count, flags) + diff -r 000000000000 -r 975801b28d85 sock/addr.pyx --- /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] = &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 ': 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] = &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 '[]: string + """ + + # format + return "[%s]:%s" % self.getnameinfo() + diff -r 000000000000 -r 975801b28d85 test.py --- /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) +