--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/py.pxd Sun Aug 16 16:38:44 2009 +0300
@@ -0,0 +1,1 @@
+../py.pxd
\ No newline at end of file
--- a/libc.pxd Sun Aug 16 04:15:07 2009 +0300
+++ b/libc.pxd Sun Aug 16 16:38:44 2009 +0300
@@ -27,6 +27,7 @@
cdef extern from "string.h" :
void* memcpy (void *dest, void *src, size_t n)
+ void* memset (void *s, int c, size_t n)
char* strerror (int errno)
@@ -112,6 +113,35 @@
int c_inet_pton "inet_pton" (int af, char *src, void *dst)
cdef extern from "netdb.h" :
+ ## getaddrinfo
+ struct addrinfo :
+ int ai_flags
+ int ai_family
+ int ai_socktype
+ int ai_protocol
+ int ai_addrlen
+ sockaddr *ai_addr
+ char *ai_canonname
+ addrinfo *ai_next
+
+ enum :
+ AI_PASSIVE
+ AI_CANONNAME
+ AI_NUMERICHOST
+ AI_V4MAPPED
+ AI_ALL
+ AI_ADDRCONFIG
+ # AI_*IDN*
+ AI_NUMERICSERV
+
+ int c_getaddrinfo "getaddrinfo" (
+ char *node, char *service,
+ addrinfo *hints, addrinfo **res
+ )
+
+ void c_freeaddrinfo "freeaddrinfo" (addrinfo *res)
+
+ ## getnameinfo
int c_getnameinfo "getnameinfo" (
sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
--- a/libc.pyx Sun Aug 16 04:15:07 2009 +0300
+++ b/libc.pyx Sun Aug 16 16:38:44 2009 +0300
@@ -1,5 +1,7 @@
from libc cimport *
+from py cimport raise_errno
+
cdef object inet_ntop (int af, void *sockaddr) :
"""
Wrapper around inet_ntop, returning a PyString
@@ -9,8 +11,7 @@
cdef char buf[INET6_ADDRSTRLEN]
if c_inet_ntop(af, sockaddr, buf, sizeof(buf)) == NULL :
- # XXX: errno?
- raise OSError(errno)
+ raise_errno()
# autoconvert -> str
return buf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/py.pxd Sun Aug 16 16:38:44 2009 +0300
@@ -0,0 +1,12 @@
+cdef extern from "Python.h" :
+ struct PyObject :
+ pass
+
+ PyObject* PyErr_SetFromErrno (PyObject *type)
+
+ ## built-in exceptions
+ PyObject* PyExc_OSError
+
+# raise OSError with errno
+cdef int raise_errno () except -1
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/py.pyx Sun Aug 16 16:38:44 2009 +0300
@@ -0,0 +1,6 @@
+
+cdef int raise_errno () except -1 :
+ PyErr_SetFromErrno(PyExc_OSError)
+
+ return -1
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sctp/constants.pyx Sun Aug 16 16:38:44 2009 +0300
@@ -0,0 +1,62 @@
+cimport sctp.sock as sctp
+
+SOL_SCTP = sctp.SOL_SCTP
+IPPROTO_SCTP = sctp.IPPROTO_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
+
--- a/sctp/sock.pxd Sun Aug 16 04:15:07 2009 +0300
+++ b/sctp/sock.pxd Sun Aug 16 16:38:44 2009 +0300
@@ -10,31 +10,33 @@
## constants
# sockapi
- int SOL_SCTP
- int IPPROTO_SCTP
+ enum :
+ SOL_SCTP
+ 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
+ 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 :
@@ -192,10 +194,13 @@
## sctp_bindx
- int SCTP_BINDX_ADD_ADDR
- int SCTP_BINDX_REM_ADDR
+ enum :
+ SCTP_BINDX_ADD_ADDR
+ SCTP_BINDX_REM_ADDR
int c_sctp_bindx "sctp_bindx" (int sd, libc.sockaddr *addrs, int addrcnt, int flags)
- int sctp_connectx (int sd, libc.sockaddr *addrs, int addrcnt)
+
+ # XXX: missing return-sctp_assoc_t-id argument!
+ int c_sctp_connectx "sctp_connectx" (int sd, libc.sockaddr *addrs, int addrcnt)
--- a/sctp/sock.pyx Sun Aug 16 04:15:07 2009 +0300
+++ b/sctp/sock.pyx Sun Aug 16 16:38:44 2009 +0300
@@ -9,47 +9,32 @@
"""
-from sctp cimport *
+from sctp.sock cimport *
+from py cimport raise_errno
cimport libc
cimport sock.addr
-cdef extern from "Python.h" :
- struct PyObject :
- pass
-
- PyObject* PyErr_SetFromErrno (PyObject *type)
-
- PyObject* PyExc_OSError
-
-cdef int raise_errno () except -1 :
- PyErr_SetFromErrno(PyExc_OSError)
-
- return -1
-
-def sctp_bindx (int sd, object addrs, int flags) :
+cdef size_t addrsoup_len (object addrs) except -1 :
"""
- Bind the given SOCK_SEQPACKET to the given set of sock.addr.sockaddr's.
-
- sd the system socket FD
- addresses the list of qmsk.net.sock.addr.sockaddr's
- flags one of SCTP_BINDX_ADD/REM_ADDR
-
+ Calculate the length of the addr_buf required to store the given addrsoup
"""
- # ensure that addrs stays the same... ?
- addrs = tuple(addrs)
-
- cdef size_t addr_count = len(addrs)
+ cdef sock.addr.sockaddr addr
cdef size_t addr_size = 0
- cdef sock.addr.sockaddr addr
# 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()
- # alloc buffer to hold all the sockaddr_*'s
- cdef char *addr_buf = <char *> libc.alloca(addr_size)
+ 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 sock.addr.sockaddr addr
cdef char *addr_ptr = addr_buf
# fill it
@@ -66,7 +51,48 @@
# move to next
addr_ptr += sa_len
+def sctp_bindx (int sd, object addrs, int flags) :
+ """
+ Bind the given SOCK_SEQPACKET to the given set of sock.addr.sockaddr's.
+
+ sd the system socket FD
+ addresses the list of qmsk.net.sock.addr.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 c_sctp_bindx(sd, <libc.sockaddr *> addr_buf, addr_count, flags) < 0 :
+ if c_sctp_bindx(sd, <libc.sockaddr *> addr_buf, len(addrs), flags) < 0 :
raise_errno()
+def sctp_connectx (int sd, object addrs) :
+ """
+ Connect the given SOCK_SEQPACKET to the given set of remote sock.addr.sockaddr's.
+
+ sd the system socket FD
+ addresses the list of qmsk.net.sock.addr.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 c_sctp_connectx(sd, <libc.sockaddr *> addr_buf, len(addrs)) < 0 :
+ raise_errno()
+
--- a/setup.py Sun Aug 16 04:15:07 2009 +0300
+++ b/setup.py Sun Aug 16 16:38:44 2009 +0300
@@ -9,8 +9,10 @@
cmdclass = {'build_ext': build_ext},
ext_modules = [
cython_ext("libc", ["libc.pyx"]),
+ cython_ext("py", ["py.pyx"]),
cython_ext("sock.addr", ["sock/addr.pyx"]),
cython_ext("sctp.sock", ["sctp/sock.pyx"], libraries=['sctp']),
+ cython_ext("sctp.constants", ["sctp/constants.pyx"]),
]
)
--- a/sock/addr.pxd Sun Aug 16 04:15:07 2009 +0300
+++ b/sock/addr.pxd Sun Aug 16 16:38:44 2009 +0300
@@ -4,6 +4,8 @@
"""
A network-level socket address
+ XXX: rename to 'address'
+
>>> sockaddr().family
0
>>> sockaddr().port
@@ -20,7 +22,7 @@
# XXX: this should be a class constant! It's part of our type safety!
cdef readonly libc.sa_family_t family
- cdef void _init_family (self, libc.sa_family_t family=?)
+ cdef void _init_family (self, libc.sa_family_t family = ?)
# get the sockaddr/socklen
# each of these can be NULL to ignore it
@@ -28,4 +30,57 @@
cdef libc.sockaddr* _get_sockaddr_ptr (self) except NULL
cdef libc.socklen_t _get_sockaddr_len (self) except -1
+
+ # set the sockaddr, socklen must match
+ cdef int _set_sockaddr (self, libc.sockaddr *sa, size_t sa_len) except -1
+# build a sockaddr from the given sockaddr struct, based on sa_family
+cdef sockaddr build_sockaddr (libc.sockaddr *sa, size_t sa_len)
+
+cdef class addrinfo :
+ """
+ A socket-level endpoint address, which contains the full socket parameters and an bind/connect address
+ """
+
+ cdef readonly int flags
+ cdef readonly int family, socktype, protocol
+ cdef readonly sockaddr addr
+ cdef readonly object canonname
+
+ cdef _init_addrinfo (self, libc.addrinfo *c_ai)
+
+# build and return a new addrinfo instance
+cdef addrinfo build_addrinfo (libc.addrinfo *c_ai)
+
+cdef class endpoint :
+ """
+ A network-level socket endpoint. This is the level that humans mostly work with, but the tricky bit is that
+ an endpoint can map to more than one sockaddr...
+
+ Hence, endpoints are stored as human-readable hostname/service strings, which are then translated to sockaddrs
+ using getaddrinfo.
+
+ >>> import socket
+ >>> e = endpoint('127.0.0.1', 80)
+ >>> str(e)
+ 'hostname=127.0.0.1, service=80'
+ >>> res = e.getaddrinfo(socket.AF_UNSPEC, socket.SOCK_STREAM)
+ >>> len(res)
+ 1
+ >>> str(res[0])
+ 'family=2, socktype=1, protocol=6, addr=127.0.0.1:80, canonname=None'
+ >>> e = endpoint('2001::5', 80)
+ >>> str(e)
+ 'hostname=2001::5, service=80'
+ >>> res = e.getaddrinfo(socket.AF_UNSPEC, socket.SOCK_STREAM)
+ >>> len(res)
+ 1
+ >>> str(res[0])
+ 'family=10, socktype=1, protocol=6, addr=[2001::5]:80, canonname=None'
+
+ """
+
+ # our defining attributes, set via __init__
+ cdef object hostname, service
+
+ cpdef getaddrinfo (self, int family, int socktype, int protocol = ?, int flags = ?)
--- a/sock/addr.pyx Sun Aug 16 04:15:07 2009 +0300
+++ b/sock/addr.pyx Sun Aug 16 16:38:44 2009 +0300
@@ -10,6 +10,7 @@
cdef void _init_family (self, libc.sa_family_t family=libc.AF_UNSPEC) :
self.family = family
+ # XXX:use size_t
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
@@ -41,6 +42,13 @@
return sa_len
+ cdef int _set_sockaddr (self, libc.sockaddr *sa, size_t sa_len) except -1 :
+ """
+ Set the sockaddr value for this address; sa_len must match!
+ """
+
+ raise NotImplementedError()
+
def getnameinfo (self) :
"""
Returns a (host, serv) tuple for this address à la getnameinfo
@@ -140,6 +148,11 @@
return 0
+ cdef int _set_sockaddr (self, libc.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
@@ -209,6 +222,11 @@
return 0
+ cdef int _set_sockaddr (self, libc.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
@@ -226,3 +244,110 @@
# format
return "[%s]:%s" % self.getnameinfo()
+# mapping of AF -> sockaddr, user-modifyable
+SOCKADDR_BY_FAMILY = {
+ libc.AF_INET: sockaddr_in,
+ libc.AF_INET6: sockaddr_in6,
+}
+
+# build a sockaddr from the given sockaddr struct, based on sa_family
+cdef sockaddr build_sockaddr (libc.sockaddr *sa, size_t sa_len) :
+ # lookup correct class to use
+ addr_type = SOCKADDR_BY_FAMILY[sa.sa_family]
+
+ # construct with defaults
+ cdef sockaddr addr = addr_type()
+
+ # store
+ addr._set_sockaddr(sa, sa_len)
+
+ return addr
+
+cdef class addrinfo :
+
+ cdef _init_addrinfo (self, libc.addrinfo *ai) :
+ #ai.flags = c_ai.ai_flags
+ self.family = ai.ai_family
+ self.socktype = ai.ai_socktype
+ self.protocol = ai.ai_protocol
+ self.addr = build_sockaddr(ai.ai_addr, ai.ai_addrlen)
+ self.canonname = ai.ai_canonname if ai.ai_canonname else None
+
+ def __str__ (self) :
+ return "family=%d, socktype=%d, protocol=%d, addr=%s, canonname=%s" % (self.family, self.socktype, self.protocol, self.addr, self.canonname)
+
+cdef addrinfo build_addrinfo (libc.addrinfo *c_ai) :
+ cdef addrinfo ai = addrinfo()
+
+ ai._init_addrinfo(c_ai)
+
+ return ai
+
+cdef class endpoint :
+
+ def __init__ (self, hostname=None, service=None) :
+ """
+ Construct with the given hostname/service, either of which may be None.
+
+ A hostname of None implies all valid local addresses (with AI_PASSIVE), and a service of None implies an
+ ephemeral port.
+
+ hostname - the literal address or DNS hostname or anything else that GAI supports
+ service - the numeric port or service name
+ """
+
+ self.hostname = str(hostname)
+ self.service = str(service)
+
+ cpdef getaddrinfo (self, int family, int socktype, int protocol = 0, int flags = libc.AI_PASSIVE) :
+ """
+ Look up our hostname/service using the given socket parameters, and return a sequence of addrinfo objects.
+ """
+
+ # XXX: Cython doesn't support proper compound value literals...
+ cdef libc.addrinfo hints
+
+ libc.memset(&hints, 0, sizeof(hints))
+ hints.ai_flags = flags
+ hints.ai_family = family
+ hints.ai_socktype = socktype
+ hints.ai_protocol = protocol
+
+ cdef libc.addrinfo *res, *r
+ cdef int err
+ cdef object ret = []
+
+ cdef char *hostname = NULL
+ cdef char *service = NULL
+
+ if self.hostname is not None :
+ hostname = self.hostname
+
+ if self.service is not None :
+ service = self.service
+
+ # operate!
+ err = libc.c_getaddrinfo(hostname, service, &hints, &res)
+
+ try :
+ if err :
+ # XXX: raise a GAIError
+ raise Exception(libc.gai_strerror(err))
+
+ # gather results
+ r = res
+
+ while r :
+ ret.append(build_addrinfo(r))
+
+ r = r.ai_next
+
+ # ok
+ return ret
+
+ finally :
+ libc.c_freeaddrinfo(res)
+
+ def __str__ (self) :
+ return "hostname=%s, service=%s" % (self.hostname, self.service)
+