-# -*-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, <void *>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 = <File>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, <void *>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 = <Timer>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, <void *>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 = <Connect>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, <void *>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, <void *>me)
- me._activep = 1
- def __init__(me, addr):
- pass
-
-cdef void _resfunc(hostent *h, void *arg):
- cdef Resolve r
- cdef int i
- r = <Resolve>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((<in_addr *>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, <void *>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 = <Signal>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, <void *>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 = <LineBuffer>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, <void *>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 = <PacketBuffer>arg
- if p is NULL:
- pb.eof()
- else:
- r = pb.packet(PyString_FromStringAndSize(<char *>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, <sockaddr *>&s_in, &sz_in) or \
- getpeername(fd, <sockaddr *>&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, <void *>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 = <Identify>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 ----------------------------------------------------