20bce5e9 |
1 | # -*-pyrex-*- |
2 | # |
3 | # $Id$ |
4 | # |
5 | # Selectery |
6 | # |
7 | # (c) 2005 Straylight/Edgeware |
8 | # |
9 | |
10 | #----- Licensing notice ----------------------------------------------------- |
11 | # |
12 | # This file is part of the Python interface to mLib. |
13 | # |
14 | # mLib/Python is free software; you can redistribute it and/or modify |
15 | # it under the terms of the GNU General Public License as published by |
16 | # the Free Software Foundation; either version 2 of the License, or |
17 | # (at your option) any later version. |
18 | # |
19 | # mLib/Python is distributed in the hope that it will be useful, |
20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | # GNU General Public License for more details. |
23 | # |
24 | # You should have received a copy of the GNU General Public License |
25 | # along with mLib/Python; if not, write to the Free Software Foundation, |
26 | # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
27 | |
28 | #----- External dependencies ------------------------------------------------ |
29 | |
30 | cdef extern from 'stddef.h': |
31 | ctypedef int size_t |
32 | cdef extern from 'string.h': |
33 | void memcpy(void *p, void *q, size_t n) |
34 | cdef extern from 'errno.h': |
35 | int errno |
36 | enum: |
37 | EINTR |
38 | EAGAIN |
39 | cdef extern from 'math.h': |
40 | double modf(double x, double *i) |
41 | cdef extern from 'string.h': |
42 | char *strerror(int err) |
43 | cdef extern from 'sys/time.h': |
44 | struct timeval: |
45 | int tv_sec |
46 | int tv_usec |
47 | cdef extern from 'sys/types.h': |
48 | pass |
49 | cdef extern from 'sys/socket.h': |
50 | struct sockaddr: |
51 | int sa_family |
52 | enum: |
53 | AF_INET |
54 | int getsockname(int fd, sockaddr *pa, size_t *psz) |
55 | int getpeername(int fd, sockaddr *pa, size_t *psz) |
56 | cdef extern from 'arpa/inet.h': |
57 | struct in_addr: |
58 | int s_addr |
59 | int inet_aton(char *rp, in_addr *ia) |
60 | char *inet_ntoa(in_addr ia) |
61 | cdef extern from 'netinet/in.h': |
62 | struct sockaddr_in: |
63 | int sin_family |
64 | in_addr sin_addr |
65 | int sin_port |
66 | int htons(int x) |
67 | int htonl(int x) |
68 | int ntohs(int x) |
69 | int ntohl(int x) |
70 | cdef extern from 'netdb.h': |
71 | struct hostent: |
72 | char *h_name |
73 | char **h_aliases |
74 | int h_addrtype |
75 | int h_length |
76 | char **h_addr_list |
77 | char *h_addr |
78 | int h_errno |
79 | |
80 | cdef extern from 'mLib/sel.h': |
81 | ctypedef struct sel_state: |
82 | pass |
83 | ctypedef struct sel_file: |
84 | int fd |
85 | ctypedef struct sel_timer: |
86 | pass |
87 | enum: |
88 | SEL_READ |
89 | SEL_WRITE |
90 | SEL_EXC |
91 | void sel_init(sel_state *s) |
92 | void sel_initfile(sel_state *s, sel_file *f, int fd, unsigned mode, |
93 | void (*func)(int fd, unsigned mode, void *arg), |
94 | void *arg) |
95 | void sel_force(sel_file *f) |
96 | void sel_addfile(sel_file *f) |
97 | void sel_rmfile(sel_file *f) |
98 | void sel_addtimer(sel_state *s, sel_timer *t, timeval *tv, |
99 | void (*func)(timeval *tv, void *arg), |
100 | void *arg) |
101 | void sel_rmtimer(sel_timer *t) |
102 | int sel_select(sel_state *s) except * |
103 | |
104 | cdef extern from 'mLib/conn.h': |
105 | ctypedef struct conn: |
106 | pass |
107 | int conn_fd(conn *c, sel_state *s, int fd, |
108 | void (*func)(int fd, void *arg), void *arg) |
109 | void conn_kill(conn *c) |
110 | |
111 | cdef extern from 'mLib/bres.h': |
112 | ctypedef struct bres_client: |
113 | pass |
114 | void bres_byname(bres_client *r, char *name, |
115 | void (*func)(hostent *h, void *arg), void *arg) |
116 | void bres_byaddr(bres_client *r, in_addr addr, |
117 | void (*func)(hostent *h, void *arg), void *arg) |
118 | void bres_abort(bres_client *r) |
119 | void bres_exec(char *null) |
120 | void bres_init(sel_state *s) |
121 | |
122 | cdef extern from 'mLib/sig.h': |
123 | ctypedef struct sig: |
124 | pass |
125 | void sig_add(sig *s, int n, void (*func)(int n, void *arg), void *arg) |
126 | void sig_remove(sig *s) |
127 | void sig_init(sel_state *s) |
128 | |
129 | cdef extern from 'mLib/lbuf.h': |
130 | cdef struct lbuf: |
131 | int f |
132 | int delim |
133 | size_t sz |
134 | enum: |
135 | LBUF_ENABLE |
136 | LBUF_CRLF |
137 | LBUF_STRICTCRLF |
138 | |
139 | cdef extern from 'mLib/selbuf.h': |
140 | ctypedef struct selbuf: |
141 | sel_file reader |
142 | lbuf b |
143 | void selbuf_enable(selbuf *b) |
144 | void selbuf_disable(selbuf *b) |
145 | void selbuf_setsize(selbuf *b, size_t sz) |
146 | void selbuf_init(selbuf *b, sel_state *s, int fd, |
147 | void (*func)(char *s, size_t len, void *arg), void *arg) |
148 | void selbuf_destroy(selbuf *b) |
149 | |
150 | cdef extern from 'mLib/pkbuf.h': |
151 | ctypedef struct pkbuf: |
152 | int f |
153 | int want |
154 | enum: |
155 | PKBUF_ENABLE |
156 | |
157 | cdef extern from 'mLib/selpk.h': |
158 | ctypedef struct selpk: |
159 | sel_file reader |
160 | pkbuf pk |
161 | void selpk_enable(selpk *b) |
162 | void selpk_disable(selpk *b) |
163 | void selpk_want(selpk *b, size_t sz) |
164 | void selpk_init(selpk *b, sel_state *s, int fd, |
165 | void (*func)(unsigned char *p, size_t n, |
166 | pkbuf *pk, size_t *keep, void *arg), |
167 | void *arg) |
168 | void selpk_destroy(selpk *b) |
169 | |
170 | cdef extern from 'mLib/ident.h': |
171 | ctypedef struct ident_request: |
172 | pass |
173 | enum: |
174 | IDENT_USERID |
175 | IDENT_ERROR |
176 | IDENT_BAD |
177 | struct ident_userid: |
178 | char *os |
179 | char *user |
180 | union ident_u: |
181 | ident_userid userid |
182 | char *error |
183 | ctypedef struct ident_reply: |
184 | int sport |
185 | int dport |
186 | int type |
187 | ident_u u |
188 | void ident(ident_request *rq, sel_state *s, |
189 | sockaddr_in *local, sockaddr_in *remote, |
190 | void (*func)(ident_reply *r, void *arg), |
191 | void *arg) |
192 | void ident_abort(ident_request *rq) |
193 | |
194 | cdef extern from 'Python.h': |
195 | object PyString_FromStringAndSize(char *p, int len) |
196 | int PyString_AsStringAndSize(obj, char **p, int *len) except -1 |
197 | int PyObject_AsReadBuffer(obj, void **buf, int *len) except -1 |
198 | int PyObject_TypeCheck(obj, ty) |
199 | |
200 | cdef extern from 'grim.h': |
201 | int PSIZEOF(void *x) |
202 | |
203 | import socket |
204 | import signal |
205 | |
206 | #----- Utility functions ---------------------------------------------------- |
207 | |
208 | cdef _oserror(): |
209 | raise OSError, (errno, strerror(errno)) |
210 | |
211 | cdef object _tobool(int i): |
212 | if i: |
213 | return True |
214 | else: |
215 | return False |
216 | |
217 | cdef int _getfd(object fdobj): |
218 | try: |
219 | return fdobj |
220 | except TypeError: |
221 | return fdobj.fileno() |
222 | |
223 | #----- The global select state ---------------------------------------------- |
224 | |
225 | cdef sel_state sel |
226 | |
227 | sel_init(&sel) |
228 | bres_init(&sel) |
229 | bres_exec(NULL) |
230 | sig_init(&sel) |
231 | |
232 | def select(): |
233 | if sel_select(&sel) and errno != EINTR and errno != EAGAIN: |
234 | _oserror() |
235 | |
236 | #----- File selectors ------------------------------------------------------- |
237 | |
238 | READ = SEL_READ |
239 | WRITE = SEL_WRITE |
240 | EXCEPT = SEL_EXC |
241 | |
242 | cdef class File: |
243 | cdef sel_file f |
244 | cdef int _activep |
245 | cdef readonly unsigned mode |
246 | def __new__(me, fd, int mode = SEL_READ): |
247 | if (mode != SEL_READ and |
248 | mode != SEL_WRITE and |
249 | mode != SEL_EXC): |
250 | raise ValueError, 'bad select mode' |
251 | sel_initfile(&sel, &me.f, _getfd(fd), mode, _filefunc, <void *>me) |
252 | me._activep = 0 |
253 | me.mode = mode |
254 | def __dealloc__(me): |
255 | if me._activep: |
256 | sel_rmfile(&me.f) |
257 | property fd: |
258 | def __get__(me): |
259 | return me.f.fd |
260 | property activep: |
261 | def __get__(me): |
262 | return _tobool(me._activep) |
263 | def enable(me): |
264 | if me._activep: |
265 | raise ValueError, 'already enabled' |
266 | sel_addfile(&me.f) |
267 | me._activep = 1 |
268 | return me |
269 | def disable(me): |
270 | if not me._activep: |
271 | raise ValueError, 'already disabled' |
272 | sel_rmfile(&me.f) |
273 | me._activep = 0 |
274 | return me |
275 | def force(me): |
276 | sel_force(&me.f) |
277 | return me |
278 | def ready(me): |
279 | pass |
280 | |
281 | cdef void _filefunc(int fd, unsigned mode, void *arg): |
282 | cdef File sf |
283 | sf = <File>arg |
284 | sf.ready() |
285 | |
286 | #----- Timer selectors ------------------------------------------------------ |
287 | |
288 | cdef double _tvtofloat(timeval *tv): |
289 | return tv.tv_sec + (tv.tv_usec / 1000000) |
290 | cdef void _floattotv(timeval *tv, double t): |
291 | cdef double s, us |
292 | us = modf(t, &s) |
293 | tv.tv_sec = s |
294 | tv.tv_usec = us * 1000000 |
295 | |
296 | cdef class Timer: |
297 | cdef sel_timer t |
298 | cdef int _activep |
299 | cdef readonly double time |
300 | def __new__(me, double when): |
301 | cdef timeval tv |
302 | _floattotv(&tv, when) |
303 | sel_addtimer(&sel, &me.t, &tv, _timerfunc, <void *>me) |
304 | me._activep = 1 |
305 | me.time = when |
306 | def __dealloc__(me): |
307 | if me._activep: |
308 | sel_rmtimer(&me.t) |
309 | property activep: |
310 | def __get__(me): |
311 | return _tobool(me._activep) |
312 | def kill(me): |
313 | if not me._activep: |
314 | raise ValueError, 'already dead' |
315 | sel_rmtimer(&me.t) |
316 | me._activep = 0 |
317 | return me |
318 | def timer(me, now): |
319 | pass |
320 | |
321 | cdef void _timerfunc(timeval *now, void *arg): |
322 | cdef Timer st |
323 | st = <Timer>arg |
324 | st._activep = 0 |
325 | st.timer(_tvtofloat(now)) |
326 | |
327 | #----- Connections ---------------------------------------------------------- |
328 | |
329 | cdef class Connect: |
330 | cdef conn c |
331 | cdef int _activep |
332 | cdef readonly object socket |
333 | def __new__(me, sk): |
334 | conn_fd(&me.c, &sel, sk.fileno(), _connfunc, <void *>me) |
335 | me._activep = 1 |
336 | me.socket = sk |
337 | def __dealloc__(me): |
338 | if me._activep: |
339 | conn_kill(&me.c) |
340 | property activep: |
341 | def __get__(me): |
342 | return _tobool(me._activep) |
343 | def kill(me): |
344 | if not me._activep: |
345 | raise ValueError, 'already dead' |
346 | conn_kill(&me.c); |
347 | me._activep = 0 |
348 | return me |
349 | def connected(me): |
350 | pass |
351 | def error(me, errno, strerror): |
352 | pass |
353 | |
354 | cdef void _connfunc(int fd, void *arg): |
355 | cdef Connect c |
356 | c = <Connect>arg |
357 | c._activep = 0 |
358 | if fd == -1: |
359 | c.socket = None |
360 | c.error(errno, strerror(errno)) |
361 | else: |
362 | c.connected() |
363 | |
364 | #----- Background name resolution ------------------------------------------- |
365 | |
366 | cdef class Resolve: |
367 | cdef bres_client r |
368 | cdef int _activep |
369 | def __init__(me, *hunoz, **hukairz): |
370 | raise TypeError, 'abstract class' |
371 | property activep: |
372 | def __get__(me): |
373 | return _tobool(me._activep) |
374 | def kill(me): |
375 | if not me._activep: |
376 | raise ValueError, 'already dead' |
377 | bres_abort(&me.r) |
378 | return me |
379 | def resolved(me, h): |
380 | pass |
381 | def failed(me): |
382 | pass |
383 | |
384 | cdef class ResolveByName (Resolve): |
385 | def __new__(me, char *name): |
386 | bres_byname(&me.r, name, _resfunc, <void *>me) |
387 | me._activep = 1 |
388 | def __init__(me, name): |
389 | pass |
390 | |
391 | cdef class ResolveByAddr (Resolve): |
392 | def __new__(me, char *addr): |
393 | cdef in_addr ia |
394 | if not inet_aton(addr, &ia): |
395 | raise TypeError, 'bad IP address' |
396 | bres_byaddr(&me.r, ia, _resfunc, <void *>me) |
397 | me._activep = 1 |
398 | def __init__(me, addr): |
399 | pass |
400 | |
401 | cdef void _resfunc(hostent *h, void *arg): |
402 | cdef Resolve r |
403 | cdef int i |
404 | r = <Resolve>arg |
405 | r._activep = 0 |
406 | if h is NULL: |
407 | r.failed(r) |
408 | else: |
409 | alias = [] |
410 | addr = [] |
411 | i = 0 |
412 | while h.h_aliases[i]: |
413 | alias.append(h.h_aliases[i]) |
414 | i = i + 1 |
415 | i = 0 |
416 | while h.h_addr_list[i]: |
417 | addr.append(inet_ntoa((<in_addr *>h.h_addr_list[i])[0])) |
418 | i = i + 1 |
419 | r.resolved(h.h_name, alias, addr) |
420 | |
421 | #----- Signal handling ------------------------------------------------------ |
422 | |
423 | cdef class Signal: |
424 | cdef sig s |
425 | cdef int _activep |
426 | cdef readonly int signal |
427 | def __new__(me, int sig): |
428 | if sig < 0 or sig >= signal.NSIG: |
429 | raise ValueError, 'signal number out of range' |
430 | me.signal = sig |
431 | me._activep = 0 |
432 | def __dealloc__(me): |
433 | if me._activep: |
434 | sig_remove(&me.s) |
435 | def enable(me): |
436 | if me._activep: |
437 | raise ValueError, 'already enabled' |
438 | sig_add(&me.s, me.signal, _sigfunc, <void *>me) |
439 | me._activep = 1 |
440 | return me |
441 | def disable(me): |
442 | if not me._activep: |
443 | raise ValueError, 'already disabled' |
444 | sig_remove(&me.s) |
445 | me._activep = 0 |
446 | return me |
447 | def signalled(me): |
448 | pass |
449 | |
450 | cdef void _sigfunc(int sig, void *arg): |
451 | cdef Signal s |
452 | s = <Signal>arg |
453 | s.signalled() |
454 | |
455 | #----- Line buffers --------------------------------------------------------- |
456 | |
457 | CRLF = LBUF_CRLF |
458 | STRICTCRLF = LBUF_STRICTCRLF |
459 | |
460 | cdef class LineBuffer: |
461 | cdef selbuf b |
462 | def __new__(me, fd): |
463 | selbuf_init(&me.b, &sel, _getfd(fd), _lbfunc, <void *>me) |
464 | selbuf_disable(&me.b) |
465 | def __dealloc__(me): |
466 | selbuf_destroy(&me.b) |
467 | property activep: |
468 | def __get__(me): |
469 | return _tobool(me.b.b.f & LFBUF_ENABLE) |
470 | property delim: |
471 | def __get__(me): |
472 | if me.b.b.delim == LBUF_CRLF or me.b.b.delim == LBUF_STRICTCRLF: |
473 | return me.b.b.delim |
474 | else: |
475 | return chr(me.b.b.delim) |
476 | def __set__(me, d): |
477 | if d == LBUF_CRLF or d == LBUF_STRICTCRLF: |
478 | me.b.b.delim = d |
479 | else: |
480 | me.b.b.delim = ord(d) |
481 | property size: |
482 | def __get__(me): |
483 | return me.b.b.sz |
484 | def __set__(me, sz): |
485 | selbuf_setsize(&me.b, sz) |
486 | def enable(me): |
487 | if me.b.b.f & LBUF_ENABLE: |
488 | raise ValueError, 'already enabled' |
489 | selbuf_enable(&me.b) |
490 | return me |
491 | def disable(me): |
492 | if not (me.b.b.f & LBUF_ENABLE): |
493 | raise ValueError, 'already disabled' |
494 | selbuf_disable(&me.b) |
495 | return me |
496 | def line(me, line): |
497 | pass |
498 | def eof(me): |
499 | pass |
500 | |
501 | cdef void _lbfunc(char *s, size_t n, void *arg): |
502 | cdef LineBuffer sb |
503 | sb = <LineBuffer>arg |
504 | if s is NULL: |
505 | sb.eof() |
506 | else: |
507 | sb.line(PyString_FromStringAndSize(s, n)) |
508 | |
509 | #----- Packet buffers ------------------------------------------------------- |
510 | |
511 | cdef class PacketBuffer: |
512 | cdef selpk p |
513 | def __new__(me, fd): |
514 | selpk_init(&me.p, &sel, _getfd(fd), _pkfunc, <void *>me) |
515 | selpk_disable(&me.p) |
516 | def __dealloc__(me): |
517 | selpk_destroy(&me.p) |
518 | property activep: |
519 | def __get__(me): |
520 | return _to_bool(me.p.pk.f & PKBUF_ENABLE) |
521 | property want: |
522 | def __get__(me): |
523 | return me.p.pk.want |
524 | def __set__(me, n): |
525 | selpk_want(&me.p, n) |
526 | def enable(me): |
527 | if me.p.pk.f & PKBUF_ENABLE: |
528 | raise ValueError, 'already enabled' |
529 | selpk_enable(&me.p) |
530 | return me |
531 | def disable(me): |
532 | if not (me.p.pk.f & PKBUF_ENABLE): |
533 | raise ValueError, 'already disabled' |
534 | selpk_disable(&me.p) |
535 | return me |
536 | def packet(me, pk): |
537 | return None |
538 | def eof(me): |
539 | pass |
540 | |
541 | cdef void _pkfunc(unsigned char *p, size_t n, pkbuf *pk, |
542 | size_t *keep, void *arg): |
543 | cdef PacketBuffer pb |
544 | cdef void *rp |
545 | cdef int rn |
546 | pb = <PacketBuffer>arg |
547 | if p is NULL: |
548 | pb.eof() |
549 | else: |
550 | r = pb.packet(PyString_FromStringAndSize(<char *>p, n)) |
551 | if r is not None: |
552 | PyObject_AsReadBuffer(r, &rp, &rn) |
553 | if rn > n: |
554 | raise ValueError, 'remaining buffer too large' |
555 | if rn: |
556 | memcpy(p + n - rn, rp, rn) |
557 | keep[0] = rn |
558 | |
559 | #----- Ident client --------------------------------------------------------- |
560 | |
561 | cdef _inaddr_frompy(sockaddr_in *sin, addr): |
562 | cdef int port |
563 | if len(addr) != 2: |
564 | raise TypeError, 'want address/port pair' |
565 | a = addr[0] |
566 | if not inet_aton(a, &sin.sin_addr): |
567 | raise TypeError, 'bad IP address' |
568 | port = addr[1] |
569 | if not (0 <= port < 65536): |
570 | raise TypeError, 'port number out of range' |
571 | sin.sin_port = htons(port) |
572 | |
573 | cdef _inaddr_topy(sockaddr_in *sin): |
574 | return inet_ntoa(sin.sin_addr), ntohs(sin.sin_port) |
575 | |
576 | cdef class Identify: |
577 | cdef ident_request irq |
578 | cdef int _activep |
579 | cdef readonly localaddr |
580 | cdef readonly remoteaddr |
581 | def __new__(me, sk): |
582 | cdef sockaddr_in s_in, s_out |
583 | cdef size_t sz_in, sz_out |
584 | cdef int fd |
585 | if PyObject_TypeCheck(sk, socket.SocketType): |
586 | fd = sk.fileno() |
587 | sz_in = PSIZEOF(&s_in) |
588 | sz_out = PSIZEOF(&s_out) |
589 | if getsockname(fd, <sockaddr *>&s_in, &sz_in) or \ |
590 | getpeername(fd, <sockaddr *>&s_out, &sz_out): |
591 | _oserror() |
592 | if s_in.sin_family != AF_INET or s_out.sin_family != AF_INET: |
593 | raise TypeError, 'must be internet socket' |
594 | elif len(sk) != 2: |
595 | raise TypeError, 'want pair of addresses' |
596 | else: |
597 | _inaddr_frompy(&s_in, sk[0]) |
598 | _inaddr_frompy(&s_out, sk[1]) |
599 | ident(&me.irq, &sel, &s_in, &s_out, _identfunc, <void *>me) |
600 | me.localaddr = _inaddr_topy(&s_in) |
601 | me.remoteaddr = _inaddr_topy(&s_out) |
602 | me._activep = 1 |
603 | def __dealloc__(me): |
604 | if me._activep: |
605 | ident_abort(&me.irq) |
606 | property activep: |
607 | def __get__(me): |
608 | return _tobool(me._activep) |
609 | def kill(me): |
610 | if not me._activep: |
611 | raise ValueError, 'already disabled' |
612 | ident_abort(&me.irq) |
613 | me._activep = 0 |
614 | def user(me, os, user): |
615 | pass |
616 | def bad(me): |
617 | pass |
618 | def error(me, error): |
619 | pass |
620 | def failed(me, errno, strerror): |
621 | pass |
622 | |
623 | cdef void _identfunc(ident_reply *i, void *arg): |
624 | cdef Identify id |
625 | id = <Identify>arg |
626 | id._activep = 0 |
627 | if i.type == IDENT_BAD: |
628 | ii.bad() |
629 | elif i.type == IDENT_ERROR: |
630 | ii.error(i.u.error) |
631 | elif i.type == IDENT_USER: |
632 | ii.user(i.u.userid.os, i.u.userid.user) |
633 | |
634 | #----- That's all, folks ---------------------------------------------------- |