# -*-pyrex-*- # # $Id$ # # Selectery # # (c) 2005 Straylight/Edgeware # #----- Licensing notice ----------------------------------------------------- # # This file is part of the Python interface to mLib. # # mLib/Python is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # mLib/Python is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with mLib/Python; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #----- External dependencies ------------------------------------------------ cdef extern from 'stddef.h': ctypedef int size_t cdef extern from 'string.h': void memcpy(void *p, void *q, size_t n) cdef extern from 'errno.h': int errno enum: EINTR EAGAIN cdef extern from 'math.h': double modf(double x, double *i) cdef extern from 'string.h': char *strerror(int err) cdef extern from 'sys/time.h': struct timeval: int tv_sec int tv_usec cdef extern from 'sys/types.h': pass cdef extern from 'sys/socket.h': struct sockaddr: int sa_family enum: AF_INET int getsockname(int fd, sockaddr *pa, size_t *psz) int getpeername(int fd, sockaddr *pa, size_t *psz) cdef extern from 'arpa/inet.h': struct in_addr: int s_addr int inet_aton(char *rp, in_addr *ia) char *inet_ntoa(in_addr ia) cdef extern from 'netinet/in.h': struct sockaddr_in: int sin_family in_addr sin_addr int sin_port int htons(int x) int htonl(int x) int ntohs(int x) int ntohl(int x) cdef extern from 'netdb.h': struct hostent: char *h_name char **h_aliases int h_addrtype int h_length char **h_addr_list char *h_addr int h_errno cdef extern from 'mLib/sel.h': ctypedef struct sel_state: pass ctypedef struct sel_file: int fd ctypedef struct sel_timer: pass enum: SEL_READ SEL_WRITE SEL_EXC void sel_init(sel_state *s) void sel_initfile(sel_state *s, sel_file *f, int fd, unsigned mode, void (*func)(int fd, unsigned mode, void *arg), void *arg) void sel_force(sel_file *f) void sel_addfile(sel_file *f) void sel_rmfile(sel_file *f) void sel_addtimer(sel_state *s, sel_timer *t, timeval *tv, void (*func)(timeval *tv, void *arg), void *arg) void sel_rmtimer(sel_timer *t) int sel_select(sel_state *s) except * cdef extern from 'mLib/conn.h': ctypedef struct conn: pass int conn_fd(conn *c, sel_state *s, int fd, void (*func)(int fd, void *arg), void *arg) void conn_kill(conn *c) cdef extern from 'mLib/bres.h': ctypedef struct bres_client: pass void bres_byname(bres_client *r, char *name, void (*func)(hostent *h, void *arg), void *arg) void bres_byaddr(bres_client *r, in_addr addr, void (*func)(hostent *h, void *arg), void *arg) void bres_abort(bres_client *r) void bres_exec(char *null) void bres_init(sel_state *s) cdef extern from 'mLib/sig.h': ctypedef struct sig: pass void sig_add(sig *s, int n, void (*func)(int n, void *arg), void *arg) void sig_remove(sig *s) void sig_init(sel_state *s) cdef extern from 'mLib/lbuf.h': cdef struct lbuf: int f int delim size_t sz enum: LBUF_ENABLE LBUF_CRLF LBUF_STRICTCRLF cdef extern from 'mLib/selbuf.h': ctypedef struct selbuf: sel_file reader lbuf b void selbuf_enable(selbuf *b) void selbuf_disable(selbuf *b) void selbuf_setsize(selbuf *b, size_t sz) void selbuf_init(selbuf *b, sel_state *s, int fd, void (*func)(char *s, size_t len, void *arg), void *arg) void selbuf_destroy(selbuf *b) cdef extern from 'mLib/pkbuf.h': ctypedef struct pkbuf: int f int want enum: PKBUF_ENABLE cdef extern from 'mLib/selpk.h': ctypedef struct selpk: sel_file reader pkbuf pk void selpk_enable(selpk *b) void selpk_disable(selpk *b) void selpk_want(selpk *b, size_t sz) void selpk_init(selpk *b, sel_state *s, int fd, void (*func)(unsigned char *p, size_t n, pkbuf *pk, size_t *keep, void *arg), void *arg) void selpk_destroy(selpk *b) cdef extern from 'mLib/ident.h': ctypedef struct ident_request: pass enum: IDENT_USERID IDENT_ERROR IDENT_BAD struct ident_userid: char *os char *user union ident_u: ident_userid userid char *error ctypedef struct ident_reply: int sport int dport int type ident_u u void ident(ident_request *rq, sel_state *s, sockaddr_in *local, sockaddr_in *remote, void (*func)(ident_reply *r, void *arg), void *arg) void ident_abort(ident_request *rq) cdef extern from 'Python.h': object PyString_FromStringAndSize(char *p, int len) int PyString_AsStringAndSize(obj, char **p, int *len) except -1 int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 int PyObject_TypeCheck(obj, ty) cdef extern from 'grim.h': int PSIZEOF(void *x) import socket import signal #----- Utility functions ---------------------------------------------------- cdef _oserror(): raise OSError, (errno, strerror(errno)) cdef object _tobool(int i): if i: return True else: return False cdef int _getfd(object fdobj): try: return fdobj except TypeError: return fdobj.fileno() #----- The global select state ---------------------------------------------- cdef sel_state sel sel_init(&sel) bres_init(&sel) bres_exec(NULL) sig_init(&sel) def select(): if sel_select(&sel) and errno != EINTR and errno != EAGAIN: _oserror() #----- File selectors ------------------------------------------------------- READ = SEL_READ WRITE = SEL_WRITE EXCEPT = SEL_EXC cdef class File: cdef sel_file f cdef int _activep cdef readonly unsigned mode def __new__(me, fd, int mode = SEL_READ): if (mode != SEL_READ and mode != SEL_WRITE and mode != SEL_EXC): raise ValueError, 'bad select mode' sel_initfile(&sel, &me.f, _getfd(fd), mode, _filefunc, me) me._activep = 0 me.mode = mode def __dealloc__(me): if me._activep: sel_rmfile(&me.f) property fd: def __get__(me): return me.f.fd property activep: def __get__(me): return _tobool(me._activep) def enable(me): if me._activep: raise ValueError, 'already enabled' sel_addfile(&me.f) me._activep = 1 return me def disable(me): if not me._activep: raise ValueError, 'already disabled' sel_rmfile(&me.f) me._activep = 0 return me def force(me): sel_force(&me.f) return me def ready(me): pass cdef void _filefunc(int fd, unsigned mode, void *arg): cdef File sf sf = arg sf.ready() #----- Timer selectors ------------------------------------------------------ cdef double _tvtofloat(timeval *tv): return tv.tv_sec + (tv.tv_usec / 1000000) cdef void _floattotv(timeval *tv, double t): cdef double s, us us = modf(t, &s) tv.tv_sec = s tv.tv_usec = us * 1000000 cdef class Timer: cdef sel_timer t cdef int _activep cdef readonly double time def __new__(me, double when): cdef timeval tv _floattotv(&tv, when) sel_addtimer(&sel, &me.t, &tv, _timerfunc, me) me._activep = 1 me.time = when def __dealloc__(me): if me._activep: sel_rmtimer(&me.t) property activep: def __get__(me): return _tobool(me._activep) def kill(me): if not me._activep: raise ValueError, 'already dead' sel_rmtimer(&me.t) me._activep = 0 return me def timer(me, now): pass cdef void _timerfunc(timeval *now, void *arg): cdef Timer st st = arg st._activep = 0 st.timer(_tvtofloat(now)) #----- Connections ---------------------------------------------------------- cdef class Connect: cdef conn c cdef int _activep cdef readonly object socket def __new__(me, sk): conn_fd(&me.c, &sel, sk.fileno(), _connfunc, me) me._activep = 1 me.socket = sk def __dealloc__(me): if me._activep: conn_kill(&me.c) property activep: def __get__(me): return _tobool(me._activep) def kill(me): if not me._activep: raise ValueError, 'already dead' conn_kill(&me.c); me._activep = 0 return me def connected(me): pass def error(me, errno, strerror): pass cdef void _connfunc(int fd, void *arg): cdef Connect c c = arg c._activep = 0 if fd == -1: c.socket = None c.error(errno, strerror(errno)) else: c.connected() #----- Background name resolution ------------------------------------------- cdef class Resolve: cdef bres_client r cdef int _activep def __init__(me, *hunoz, **hukairz): raise TypeError, 'abstract class' property activep: def __get__(me): return _tobool(me._activep) def kill(me): if not me._activep: raise ValueError, 'already dead' bres_abort(&me.r) return me def resolved(me, h): pass def failed(me): pass cdef class ResolveByName (Resolve): def __new__(me, char *name): bres_byname(&me.r, name, _resfunc, me) me._activep = 1 def __init__(me, name): pass cdef class ResolveByAddr (Resolve): def __new__(me, char *addr): cdef in_addr ia if not inet_aton(addr, &ia): raise TypeError, 'bad IP address' bres_byaddr(&me.r, ia, _resfunc, me) me._activep = 1 def __init__(me, addr): pass cdef void _resfunc(hostent *h, void *arg): cdef Resolve r cdef int i r = arg r._activep = 0 if h is NULL: r.failed(r) else: alias = [] addr = [] i = 0 while h.h_aliases[i]: alias.append(h.h_aliases[i]) i = i + 1 i = 0 while h.h_addr_list[i]: addr.append(inet_ntoa((h.h_addr_list[i])[0])) i = i + 1 r.resolved(h.h_name, alias, addr) #----- Signal handling ------------------------------------------------------ cdef class Signal: cdef sig s cdef int _activep cdef readonly int signal def __new__(me, int sig): if sig < 0 or sig >= signal.NSIG: raise ValueError, 'signal number out of range' me.signal = sig me._activep = 0 def __dealloc__(me): if me._activep: sig_remove(&me.s) def enable(me): if me._activep: raise ValueError, 'already enabled' sig_add(&me.s, me.signal, _sigfunc, me) me._activep = 1 return me def disable(me): if not me._activep: raise ValueError, 'already disabled' sig_remove(&me.s) me._activep = 0 return me def signalled(me): pass cdef void _sigfunc(int sig, void *arg): cdef Signal s s = arg s.signalled() #----- Line buffers --------------------------------------------------------- CRLF = LBUF_CRLF STRICTCRLF = LBUF_STRICTCRLF cdef class LineBuffer: cdef selbuf b def __new__(me, fd): selbuf_init(&me.b, &sel, _getfd(fd), _lbfunc, me) selbuf_disable(&me.b) def __dealloc__(me): selbuf_destroy(&me.b) property activep: def __get__(me): return _tobool(me.b.b.f & LFBUF_ENABLE) property delim: def __get__(me): if me.b.b.delim == LBUF_CRLF or me.b.b.delim == LBUF_STRICTCRLF: return me.b.b.delim else: return chr(me.b.b.delim) def __set__(me, d): if d == LBUF_CRLF or d == LBUF_STRICTCRLF: me.b.b.delim = d else: me.b.b.delim = ord(d) property size: def __get__(me): return me.b.b.sz def __set__(me, sz): selbuf_setsize(&me.b, sz) def enable(me): if me.b.b.f & LBUF_ENABLE: raise ValueError, 'already enabled' selbuf_enable(&me.b) return me def disable(me): if not (me.b.b.f & LBUF_ENABLE): raise ValueError, 'already disabled' selbuf_disable(&me.b) return me def line(me, line): pass def eof(me): pass cdef void _lbfunc(char *s, size_t n, void *arg): cdef LineBuffer sb sb = arg if s is NULL: sb.eof() else: sb.line(PyString_FromStringAndSize(s, n)) #----- Packet buffers ------------------------------------------------------- cdef class PacketBuffer: cdef selpk p def __new__(me, fd): selpk_init(&me.p, &sel, _getfd(fd), _pkfunc, me) selpk_disable(&me.p) def __dealloc__(me): selpk_destroy(&me.p) property activep: def __get__(me): return _to_bool(me.p.pk.f & PKBUF_ENABLE) property want: def __get__(me): return me.p.pk.want def __set__(me, n): selpk_want(&me.p, n) def enable(me): if me.p.pk.f & PKBUF_ENABLE: raise ValueError, 'already enabled' selpk_enable(&me.p) return me def disable(me): if not (me.p.pk.f & PKBUF_ENABLE): raise ValueError, 'already disabled' selpk_disable(&me.p) return me def packet(me, pk): return None def eof(me): pass cdef void _pkfunc(unsigned char *p, size_t n, pkbuf *pk, size_t *keep, void *arg): cdef PacketBuffer pb cdef void *rp cdef int rn pb = arg if p is NULL: pb.eof() else: r = pb.packet(PyString_FromStringAndSize(p, n)) if r is not None: PyObject_AsReadBuffer(r, &rp, &rn) if rn > n: raise ValueError, 'remaining buffer too large' if rn: memcpy(p + n - rn, rp, rn) keep[0] = rn #----- Ident client --------------------------------------------------------- cdef _inaddr_frompy(sockaddr_in *sin, addr): cdef int port if len(addr) != 2: raise TypeError, 'want address/port pair' a = addr[0] if not inet_aton(a, &sin.sin_addr): raise TypeError, 'bad IP address' port = addr[1] if not (0 <= port < 65536): raise TypeError, 'port number out of range' sin.sin_port = htons(port) cdef _inaddr_topy(sockaddr_in *sin): return inet_ntoa(sin.sin_addr), ntohs(sin.sin_port) cdef class Identify: cdef ident_request irq cdef int _activep cdef readonly localaddr cdef readonly remoteaddr def __new__(me, sk): cdef sockaddr_in s_in, s_out cdef size_t sz_in, sz_out cdef int fd if PyObject_TypeCheck(sk, socket.SocketType): fd = sk.fileno() sz_in = PSIZEOF(&s_in) sz_out = PSIZEOF(&s_out) if getsockname(fd, &s_in, &sz_in) or \ getpeername(fd, &s_out, &sz_out): _oserror() if s_in.sin_family != AF_INET or s_out.sin_family != AF_INET: raise TypeError, 'must be internet socket' elif len(sk) != 2: raise TypeError, 'want pair of addresses' else: _inaddr_frompy(&s_in, sk[0]) _inaddr_frompy(&s_out, sk[1]) ident(&me.irq, &sel, &s_in, &s_out, _identfunc, me) me.localaddr = _inaddr_topy(&s_in) me.remoteaddr = _inaddr_topy(&s_out) me._activep = 1 def __dealloc__(me): if me._activep: ident_abort(&me.irq) property activep: def __get__(me): return _tobool(me._activep) def kill(me): if not me._activep: raise ValueError, 'already disabled' ident_abort(&me.irq) me._activep = 0 def user(me, os, user): pass def bad(me): pass def error(me, error): pass def failed(me, errno, strerror): pass cdef void _identfunc(ident_reply *i, void *arg): cdef Identify id id = arg id._activep = 0 if i.type == IDENT_BAD: ii.bad() elif i.type == IDENT_ERROR: ii.error(i.u.error) elif i.type == IDENT_USER: ii.user(i.u.userid.os, i.u.userid.user) #----- That's all, folks ----------------------------------------------------