diff --git a/Makefile.am b/Makefile.am index 7370ed8ed7fc0223f382c1f9f0ad1046c011a6f9..098c87b5a74a61e961bf3d3b5dad9049c3f57d33 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,15 +7,22 @@ # See the file COPYING for details. AUTOMAKE_OPTIONS = foreign 1.2 -lib_LTLIBRARIES = liboop.la -noinst_PROGRAMS = test +lib_LTLIBRARIES = liboop.la liboop-adns.la +noinst_PROGRAMS = test-oop test-adns -liboop_la_LDFLAGS = -version-info 0:0:0 # version:revision:age -liboop_la_SOURCES = sys.c test.c alloc.c -include_HEADERS = oop.h +INCLUDES = -I$(srcdir)/foreign -LDADD = liboop.la -test_SOURCES = test.c +liboop_la_LDFLAGS = -version-info 1:0:1 # version:revision:age +liboop_la_SOURCES = sys.c select.c alloc.c + +liboop_adns_la_LDFLAGS = -version-info 0:0:0 # version:revision:age +liboop_adns_la_SOURCES = adns.c + +include_HEADERS = oop.h oop-adns.h + +LDADD = liboop-adns.la liboop.la -ladns +test_oop_SOURCES = test-oop.c +test_adns_SOURCES = test-adns.c release: dist gzip -dc $(PACKAGE)-$(VERSION).tar.gz | bzip2 -9 \ diff --git a/Makefile.in b/Makefile.in index 16b894237aa2ddf88c71d42ddd441c8812baae98..8f10a9b4f64a34c9eccb5a64f7777ac4b9319bcd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -80,15 +80,22 @@ RANLIB = @RANLIB@ VERSION = @VERSION@ AUTOMAKE_OPTIONS = foreign 1.2 -lib_LTLIBRARIES = liboop.la -noinst_PROGRAMS = test +lib_LTLIBRARIES = liboop.la liboop-adns.la +noinst_PROGRAMS = test-oop test-adns -liboop_la_LDFLAGS = -version-info 0:0:0 # version:revision:age -liboop_la_SOURCES = sys.c test.c alloc.c -include_HEADERS = oop.h +INCLUDES = -I$(srcdir)/foreign -LDADD = liboop.la -test_SOURCES = test.c +liboop_la_LDFLAGS = -version-info 1:0:1 # version:revision:age +liboop_la_SOURCES = sys.c select.c alloc.c + +liboop_adns_la_LDFLAGS = -version-info 0:0:0 # version:revision:age +liboop_adns_la_SOURCES = adns.c + +include_HEADERS = oop.h oop-adns.h + +LDADD = liboop-adns.la liboop.la -ladns +test_oop_SOURCES = test-oop.c +test_adns_SOURCES = test-adns.c ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = @@ -100,13 +107,19 @@ CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ liboop_la_LIBADD = -liboop_la_OBJECTS = sys.lo test.lo alloc.lo +liboop_la_OBJECTS = sys.lo select.lo alloc.lo +liboop_adns_la_LIBADD = +liboop_adns_la_OBJECTS = adns.lo PROGRAMS = $(noinst_PROGRAMS) -test_OBJECTS = test.o -test_LDADD = $(LDADD) -test_DEPENDENCIES = liboop.la -test_LDFLAGS = +test_oop_OBJECTS = test-oop.o +test_oop_LDADD = $(LDADD) +test_oop_DEPENDENCIES = liboop-adns.la liboop.la +test_oop_LDFLAGS = +test_adns_OBJECTS = test-adns.o +test_adns_LDADD = $(LDADD) +test_adns_DEPENDENCIES = liboop-adns.la liboop.la +test_adns_LDFLAGS = CFLAGS = @CFLAGS@ COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -123,9 +136,10 @@ DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) TAR = tar GZIP_ENV = --best -DEP_FILES = .deps/alloc.P .deps/sys.P .deps/test.P -SOURCES = $(liboop_la_SOURCES) $(test_SOURCES) -OBJECTS = $(liboop_la_OBJECTS) $(test_OBJECTS) +DEP_FILES = .deps/adns.P .deps/alloc.P .deps/select.P .deps/sys.P \ +.deps/test-adns.P .deps/test-oop.P +SOURCES = $(liboop_la_SOURCES) $(liboop_adns_la_SOURCES) $(test_oop_SOURCES) $(test_adns_SOURCES) +OBJECTS = $(liboop_la_OBJECTS) $(liboop_adns_la_OBJECTS) $(test_oop_OBJECTS) $(test_adns_OBJECTS) all: all-redirect .SUFFIXES: @@ -205,6 +219,9 @@ maintainer-clean-libtool: liboop.la: $(liboop_la_OBJECTS) $(liboop_la_DEPENDENCIES) $(LINK) -rpath $(libdir) $(liboop_la_LDFLAGS) $(liboop_la_OBJECTS) $(liboop_la_LIBADD) $(LIBS) +liboop-adns.la: $(liboop_adns_la_OBJECTS) $(liboop_adns_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(liboop_adns_la_LDFLAGS) $(liboop_adns_la_OBJECTS) $(liboop_adns_la_LIBADD) $(LIBS) + mostlyclean-noinstPROGRAMS: clean-noinstPROGRAMS: @@ -214,9 +231,13 @@ distclean-noinstPROGRAMS: maintainer-clean-noinstPROGRAMS: -test: $(test_OBJECTS) $(test_DEPENDENCIES) - @rm -f test - $(LINK) $(test_LDFLAGS) $(test_OBJECTS) $(test_LDADD) $(LIBS) +test-oop: $(test_oop_OBJECTS) $(test_oop_DEPENDENCIES) + @rm -f test-oop + $(LINK) $(test_oop_LDFLAGS) $(test_oop_OBJECTS) $(test_oop_LDADD) $(LIBS) + +test-adns: $(test_adns_OBJECTS) $(test_adns_DEPENDENCIES) + @rm -f test-adns + $(LINK) $(test_adns_LDFLAGS) $(test_adns_OBJECTS) $(test_adns_LDADD) $(LIBS) install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) diff --git a/adns.c b/adns.c new file mode 100644 index 0000000000000000000000000000000000000000..1dd7ff6c1062f71eebcafdaa76d5c8f6ca606510 --- /dev/null +++ b/adns.c @@ -0,0 +1,142 @@ +/* adns.c, liboop, copyright 1999 Dan Egnor + + This is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License, version 2.1 or later. + See the file COPYING for details. */ + +#include "oop.h" +#include "adns.h" +#include "oop-adns.h" + +#include <assert.h> + +struct oop_adapter_adns { + oop_source *source; + oop_adapter_select *select; + adns_state state; + int count; +}; + +struct oop_adns_query { + oop_adapter_adns *a; + adns_query query; + oop_adns_call *call; + void *data; +}; + +static oop_call_select on_select; +static oop_call_time on_process; +static void set_select(oop_adapter_adns *); + +oop_adapter_adns *oop_adns_new( + oop_source *source, + adns_initflags flags,FILE *diag) +{ + oop_adapter_adns *a = oop_malloc(sizeof(*a)); + if (NULL == a) return NULL; + a->select = NULL; + a->state = NULL; + + if (adns_init(&a->state,flags | adns_if_noautosys,diag) + || (NULL == (a->select = oop_select_new(source,on_select,a)))) { + if (NULL != a->state) adns_finish(a->state); + if (NULL != a->select) oop_select_delete(a->select); + oop_free(a); + return NULL; + } + + a->source = source; + a->count = 0; + return a; +} + +void oop_adns_delete(oop_adapter_adns *a) { + assert(0 == a->count && + "deleting oop_adapter_adns with outstanding queries"); + a->source->cancel_time(a->source,OOP_TIME_NOW,on_process,a); + adns_finish(a->state); + oop_free(a); +} + +oop_adns_query *oop_adns_submit( + oop_adapter_adns *a, + const char *owner,adns_rrtype type,adns_queryflags flags, + oop_adns_call *call,void *data) +{ + oop_adns_query *q = oop_malloc(sizeof(*q)); + if (NULL == q) return NULL; + if (adns_submit(a->state,owner,type,flags,q,&q->query)) { + oop_free(q); + return NULL; + } + + q->a = a; + q->call = call; + q->data = data; + ++q->a->count; + set_select(a); + return q; +} + +void oop_adns_cancel(oop_adns_query *q) { + adns_cancel(q->query); + --q->a->count; + oop_free(q); + set_select(q->a); +} + +static void set_select(oop_adapter_adns *a) { + fd_set rfd,wfd,xfd; + struct timeval buf,*out = NULL,now; + int maxfd = 0; + FD_ZERO(&rfd); + FD_ZERO(&wfd); + FD_ZERO(&xfd); + gettimeofday(&now,NULL); + adns_beforeselect(a->state,&maxfd,&rfd,&wfd,&xfd,&out,&buf,&now); + oop_select_set(a->select,maxfd,&rfd,&wfd,out); +} + +static void *on_process(oop_source *source,struct timeval when,void *data) { + oop_adapter_adns *a = (oop_adapter_adns *) data; + adns_answer *r; + adns_query query; + oop_adns_query *q = NULL; + + adns_forallqueries_begin(a->state); + while (NULL == q + && (query = adns_forallqueries_next(a->state,NULL))) { + void *data; + if (0 == adns_check(a->state,&query,&r,&data)) { + q = (oop_adns_query *) data; + assert(query == q->query); + } + } + + set_select(a); + + if (NULL != q) { + oop_adns_call *call = q->call; + void *data = q->data; + assert(a == q->a); + --q->a->count; + oop_free(q); + source->on_time(source,when,on_process,a); + return call(a,r,data); + } + + return OOP_CONTINUE; +} + +static void *on_select( + oop_adapter_select *select, + int num,fd_set *rfd,fd_set *wfd, + struct timeval now,void *data) +{ + oop_adapter_adns *a = (oop_adapter_adns *) data; + fd_set xfd; + + FD_ZERO(&xfd); + adns_afterselect(a->state,num,rfd,wfd,&xfd,&now); + return on_process(a->source,OOP_TIME_NOW,a); +} diff --git a/foreign/README b/foreign/README new file mode 100644 index 0000000000000000000000000000000000000000..b3734283a9805a1e741ba9cb526d8f01af0b5125 --- /dev/null +++ b/foreign/README @@ -0,0 +1,23 @@ +This directory contains public header files for those foreign components for +which liboop includes "adapters". By including them here, we can build the +adapters without requiring the presence of the foreign components in question. + +An adapter will still not function without the code it adapts, of course, but +at least this way we eliminate pesky build ordering issues. + +Many of these are licensed under specific agreements: + +adns.h + + adns is Ian Jackson's asynchronous DNS access package. + <URL:http://www.chiark.greenend.org.uk/~ian/adns/> + + While adns is licensed under the GPL, Ian has graciously consented to the + distribution of its public header file (adns.h) under the LGPL: + + I hereby relicence adns.h - and only adns.h - to you (and all third + parties) under the GNU Library General Public Licence. This + statement applies to all versions of adns.h I have released so far, + and to any files adns.h with similar contents and function I might + release during the remainder of this calendar year (1999). + diff --git a/foreign/adns.h b/foreign/adns.h new file mode 100644 index 0000000000000000000000000000000000000000..550c6f453c18883841fad3c9751605fafb657b21 --- /dev/null +++ b/foreign/adns.h @@ -0,0 +1,573 @@ +/* + * adns.h + * - adns user-visible API (single-threaded, without any locking) + */ +/* + * This file is part of adns, which is Copyright (C) 1997-1999 Ian Jackson + * + * This program 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, or (at your option) + * any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Id: adns.h,v 1.1 1999-08-09 07:11:52 egnor Exp $ + */ + +#ifndef ADNS_H_INCLUDED +#define ADNS_H_INCLUDED +#ifdef __cplusplus +extern "C" { /* I really dislike this - iwj. */ +#endif + +#include <stdio.h> + +#include <sys/socket.h> +#include <netinet/in.h> + +/* All struct in_addr anywhere in adns are in NETWORK byte order. */ + +typedef struct adns__state *adns_state; +typedef struct adns__query *adns_query; + +typedef enum { + adns_if_noenv= 0x0001, /* do not look at environment */ + adns_if_noerrprint= 0x0002, /* never print output to stderr (_debug overrides) */ + adns_if_noserverwarn= 0x0004, /* do not warn to stderr about duff nameservers etc */ + adns_if_debug= 0x0008, /* enable all output to stderr plus debug msgs */ + adns_if_noautosys= 0x0010, /* do not make syscalls at every opportunity */ + adns_if_eintr= 0x0020, /* allow _wait and _synchronous to return EINTR */ + adns_if_nosigpipe= 0x0040, /* applic has SIGPIPE set to SIG_IGN, do not protect */ +} adns_initflags; + +typedef enum { + adns_qf_search= 0x000001, /* use the searchlist */ + adns_qf_usevc= 0x000002, /* use a virtual circuit (TCP connection) */ + adns_qf_owner= 0x000004, /* fill in the owner field in the answer */ + adns_qf_quoteok_query= 0x000010, /* allow quote-requiring chars in query domain */ + adns_qf_quoteok_cname= 0x000020, /* allow ... in CNAME we go via */ + adns_qf_quoteok_anshost= 0x000040, /* allow ... in answers expected to be hostnames */ + adns_qf_cname_loose= 0x000100, /* allow refs to CNAMEs - without, get _s_cname */ + adns_qf_cname_forbid= 0x000200, /* don't follow CNAMEs, instead give _s_cname */ + adns__qf_internalmask= 0x0ff000 +} adns_queryflags; + +typedef enum { + adns__rrt_typemask= 0x0ffff, + adns__qtf_deref= 0x10000, /* dereference domains and perhaps produce extra data */ + adns__qtf_mail822= 0x20000, /* make mailboxes be in RFC822 rcpt field format */ + + adns_r_none= 0, + + adns_r_a= 1, + + adns_r_ns_raw= 2, + adns_r_ns= adns_r_ns_raw|adns__qtf_deref, + + adns_r_cname= 5, + + adns_r_soa_raw= 6, + adns_r_soa= adns_r_soa_raw|adns__qtf_mail822, + + adns_r_ptr_raw= 12, + adns_r_ptr= adns_r_ptr_raw|adns__qtf_deref, + + adns_r_hinfo= 13, + + adns_r_mx_raw= 15, + adns_r_mx= adns_r_mx_raw|adns__qtf_deref, + + adns_r_txt= 16, + + adns_r_rp_raw= 17, + adns_r_rp= adns_r_rp_raw|adns__qtf_mail822, + + adns_r_addr= adns_r_a|adns__qtf_deref + +} adns_rrtype; + +/* In queries without qf_quoteok_*, all domains must have standard + * legal syntax. In queries _with_ qf_quoteok_*, domains in the query + * or response may contain any characters, quoted according to RFC1035 + * 5.1. On input to adns, the char* is a pointer to the interior of a + * " delimited string, except that " may appear in it, and on output, + * the char* is a pointer to a string which would be legal either + * inside or outside " delimiters, and any characters not usually + * legal in domain names will be quoted as \X (if the character is + * 33-126 except \ and ") or \DDD. + * + * Do not ask for _raw records containing mailboxes without + * specifying _qf_anyquote. + */ + +typedef enum { + adns_s_ok, + + /* locally induced errors */ + adns_s_nomemory, + adns_s_unknownrrtype, + adns_s_systemfail, + + adns_s_max_localfail= 29, + + /* remotely induced errors, detected locally */ + adns_s_timeout, + adns_s_allservfail, + adns_s_norecurse, + adns_s_invalidresponse, + adns_s_unknownformat, + + adns_s_max_remotefail= 59, + + /* remotely induced errors, reported by remote server to us */ + adns_s_rcodeservfail, + adns_s_rcodeformaterror, + adns_s_rcodenotimplemented, + adns_s_rcoderefused, + adns_s_rcodeunknown, + + adns_s_max_tempfail= 99, + + /* remote configuration errors */ + adns_s_inconsistent, /* PTR gives domain whose A does not exist and match */ + adns_s_prohibitedcname, /* CNAME found where eg A expected (not if _qf_loosecname) */ + adns_s_answerdomaininvalid, + adns_s_answerdomaintoolong, + adns_s_invaliddata, + + adns_s_max_misconfig= 199, + + /* permanent problems with the query */ + adns_s_querydomainwrong, + adns_s_querydomaininvalid, + adns_s_querydomaintoolong, + + adns_s_max_misquery= 299, + + /* permanent errors */ + adns_s_nxdomain, + adns_s_nodata, + +} adns_status; + +typedef struct { + int len; + union { + struct sockaddr sa; + struct sockaddr_in inet; + } addr; +} adns_rr_addr; + +typedef struct { + char *host; + adns_status astatus; + int naddrs; /* temp fail => -1, perm fail => 0, s_ok => >0 */ + adns_rr_addr *addrs; +} adns_rr_hostaddr; + +typedef struct { + char *(array[2]); +} adns_rr_strpair; + +typedef struct { + int i; + adns_rr_hostaddr ha; +} adns_rr_inthostaddr; + +typedef struct { + /* Used both for mx_raw, in which case i is the preference and str the domain, + * and for txt, in which case each entry has i for the `text' length, + * and str for the data (which will have had an extra nul appended + * so that if it was plain text it is now a null-terminated string). + */ + int i; + char *str; +} adns_rr_intstr; + +typedef struct { + adns_rr_intstr array[2]; +} adns_rr_intstrpair; + +typedef struct { + char *mname, *rname; + unsigned long serial, refresh, retry, expire, minimum; +} adns_rr_soa; + +typedef struct { + adns_status status; + char *cname; /* always NULL if query was for CNAME records */ + char *owner; /* only set if requested in query flags */ + adns_rrtype type; /* guaranteed to be same as in query */ + time_t expires; /* expiry time, defined only if _s_ok, nxdomain or nodata. NOT TTL! */ + int nrrs, rrsz; + union { + void *untyped; + unsigned char *bytes; + char *(*str); /* ns_raw, cname, ptr, ptr_raw */ + adns_rr_intstr *(*manyistr); /* txt (list of strings ends with i=-1, str=0) */ + adns_rr_addr *addr; /* addr */ + struct in_addr *inaddr; /* a */ + adns_rr_hostaddr *hostaddr; /* ns */ + adns_rr_intstrpair *intstrpair; /* hinfo */ + adns_rr_strpair *strpair; /* rp, rp_raw */ + adns_rr_inthostaddr *inthostaddr; /* mx */ + adns_rr_intstr *intstr; /* mx_raw */ + adns_rr_soa *soa; /* soa, soa_raw */ + } rrs; +} adns_answer; + +/* Memory management: + * adns_state and adns_query are actually pointers to malloc'd state; + * On submission questions are copied, including the owner domain; + * Answers are malloc'd as a single piece of memory; pointers in the + * answer struct point into further memory in the answer. + * query_io: + * Must always be non-null pointer; + * If *query_io is 0 to start with then any query may be returned; + * If *query_io is !0 adns_query then only that query may be returned. + * If the call is successful, *query_io, *answer_r, and *context_r + * will all be set. + * Errors: + * Return values are 0 or an errno value. + * + * For _init, _init_strcfg, _submit and _synchronous, system errors + * (eg, failure to create sockets, malloc failure, etc.) return errno + * values. + * + * For _wait and _check failures are reported in the answer + * structure, and only 0, ESRCH or (for _check) EWOULDBLOCK is + * returned: if no (appropriate) requests are done adns_check returns + * EWOULDBLOCK; if no (appropriate) requests are outstanding both + * adns_query and adns_wait return ESRCH. + * + * Additionally, _wait can return EINTR if you set adns_if_eintr. + * + * All other errors (nameserver failure, timed out connections, &c) + * are returned in the status field of the answer. After a + * successful _wait or _check, if status is nonzero then nrrs will be + * 0, otherwise it will be >0. type will always be the type + * requested. + */ + +int adns_init(adns_state *newstate_r, adns_initflags flags, + FILE *diagfile /*0=>stderr*/); + +int adns_init_strcfg(adns_state *newstate_r, adns_initflags flags, + FILE *diagfile /*0=>discard*/, const char *configtext); + +int adns_synchronous(adns_state ads, + const char *owner, + adns_rrtype type, + adns_queryflags flags, + adns_answer **answer_r); + +/* NB: if you set adns_if_noautosys then _submit and _check do not + * make any system calls; you must use some of the asynch-io event + * processing functions to actually get things to happen. + */ + +int adns_submit(adns_state ads, + const char *owner, + adns_rrtype type, + adns_queryflags flags, + void *context, + adns_query *query_r); + +int adns_check(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r); + +int adns_wait(adns_state ads, + adns_query *query_io, + adns_answer **answer_r, + void **context_r); + +void adns_cancel(adns_query query); + +/* The adns_query you get back from _submit is valid (ie, can be + * legitimately passed into adns functions) until it is returned by + * adns_check or adns_wait, or passed to adns_cancel. After that it + * must not be used. You can rely on it not being reused until the + * first adns_submit or _transact call using the same adns_state after + * it became invalid, so you may compare it for equality with other + * query handles until you next call _query or _transact. + */ + +void adns_finish(adns_state ads); +/* You may call this even if you have queries outstanding; + * they will be cancelled. + */ + + +void adns_forallqueries_begin(adns_state ads); +adns_query adns_forallqueries_next(adns_state ads, void **context_r); +/* Iterator functions, which you can use to loop over the outstanding + * (submitted but not yet successfuly checked/waited) queries. + * + * You can only have one iteration going at once. You may call _begin + * at any time; after that, an iteration will be in progress. You may + * only call _next when an iteration is in progress - anything else + * may coredump. The iteration remains in progress until _next + * returns 0, indicating that all the queries have been walked over, + * or ANY other adns function is called with the same adns_state (or a + * query in the same adns_state). There is no need to explicitly + * finish an iteration. + * + * context_r may be 0. *context_r may not be set when _next returns 0. + */ + +/* + * Example expected/legal calling sequence for submit/check/wait: + * adns_init + * adns_submit 1 + * adns_submit 2 + * adns_submit 3 + * adns_wait 1 + * adns_check 3 -> EWOULDBLOCK + * adns_wait 2 + * adns_wait 3 + * .... + * adns_finish + */ + +/* + * Entrypoints for generic asynch io: + * (these entrypoints are not very useful except in combination with * + * some of the other I/O model calls which can tell you which fds to + * be interested in): + * + * Note that any adns call may cause adns to open and close fds, so + * you must call beforeselect or beforepoll again just before + * blocking, or you may not have an up-to-date list of it's fds. + */ + +int adns_processany(adns_state ads); +/* Gives adns flow-of-control for a bit. This will never block, and + * can be used with any threading/asynch-io model. If some error + * occurred which might cause an event loop to spin then the errno + * value is returned. + */ + +int adns_processreadable(adns_state ads, int fd, const struct timeval *now); +int adns_processwriteable(adns_state ads, int fd, const struct timeval *now); +int adns_processexceptional(adns_state ads, int fd, const struct timeval *now); +/* Gives adns flow-of-control so that it can process incoming data + * from, or send outgoing data via, fd. Very like _processany. If it + * returns zero then fd will no longer be readable or writeable + * (unless of course more data has arrived since). adns will _only_ + * that fd and only in the manner specified, regardless of whether + * adns_if_noautosys was specified. + * + * adns_processexceptional should be called when select(2) reports an + * exceptional condition, or poll(2) reports POLLPRI. + * + * It is fine to call _processreabable or _processwriteable when the + * fd is not ready, or with an fd that doesn't belong to adns; it will + * then just return 0. + * + * If some error occurred which might prevent an event loop to spin + * then the errno value is returned. + */ + +void adns_processtimeouts(adns_state ads, const struct timeval *now); +/* Gives adns flow-of-control so that it can process any timeouts + * which might have happened. Very like _processreadable/writeable. + * + * now may be 0; if it isn't, *now must be the current time, recently + * obtained from gettimeofday. + */ + +void adns_firsttimeout(adns_state ads, + struct timeval **tv_mod, struct timeval *tv_buf, + struct timeval now); +/* Asks adns when it would first like the opportunity to time + * something out. now must be the current time, from gettimeofday. + * + * If tv_mod points to 0 then tv_buf must be non-null, and + * _firsttimeout will fill in *tv_buf with the time until the first + * timeout, and make *tv_mod point to tv_buf. If adns doesn't have + * anything that might need timing out it will leave *tv_mod as 0. + * + * If *tv_mod is not 0 then tv_buf is not used. adns will update + * *tv_mod if it has any earlier timeout, and leave it alone if it + * doesn't. + * + * This call will not actually do any I/O, or change the fds that adns + * is using. It always succeeds and never blocks. + */ + +void adns_globalsystemfailure(adns_state ads); +/* If serious problem(s) happen which globally affect your ability to + * interact properly with adns, or adns's ability to function + * properly, you or adns can call this function. + * + * All currently outstanding queries will be made to fail with + * adns_s_systemfail, and adns will close any stream sockets it has + * open. + * + * This is used by adns, for example, if gettimeofday() fails. + * Without this the program's event loop might start to spin ! + * + * This call will never block. + */ + +/* + * Entrypoints for select-loop based asynch io: + */ + +void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds, + fd_set *writefds, fd_set *exceptfds, + struct timeval **tv_mod, struct timeval *tv_buf, + const struct timeval *now); +/* Find out file descriptors adns is interested in, and when it would + * like the opportunity to time something out. If you do not plan to + * block then tv_mod may be 0. Otherwise, tv_mod and tv_buf are as + * for adns_firsttimeout. readfds, writefds, exceptfds and maxfd_io may + * not be 0. + * + * If *now is not 0 then this will never actually do any I/O, or + * change the fds that adns is using or the timeouts it wants. In any + * case it won't block. + */ + +void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds, + const fd_set *writefds, const fd_set *exceptfds, + const struct timeval *now); +/* Gives adns flow-of-control for a bit; intended for use after + * select. This is just a fancy way of calling adns_processreadable/ + * writeable/timeouts as appropriate, as if select had returned the + * data being passed. Always succeeds. + */ + +/* + * Example calling sequence: + * + * adns_init _noautosys + * loop { + * adns_beforeselect + * select + * adns_afterselect + * ... + * adns_submit / adns_check + * ... + * } + */ + +/* + * Entrypoints for poll-loop based asynch io: + */ + +struct pollfd; +/* In case your system doesn't have it or you forgot to include + * <sys/poll.h>, to stop the following declarations from causing + * problems. If your system doesn't have poll then the following + * entrypoints will not be defined in libadns. Sorry ! + */ + +int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io, + const struct timeval *now); +/* Finds out which fd's adns is interested in, and when it would like + * to be able to time things out. This is in a form suitable for use + * with poll(2). + * + * On entry, usually fds should point to at least *nfds_io structs. + * adns will fill up to that many structs will information for poll, + * and record in *nfds_io how many structs it filled. If it wants to + * listen for more structs then *nfds_io will be set to the number + * required and _beforepoll will return ERANGE. + * + * You may call _beforepoll with fds==0 and *nfds_io 0, in which case + * adns will fill in the number of fds that it might be interested in + * in *nfds_io, and always return either 0 (if it is not interested in + * any fds) or ERANGE (if it is). + * + * NOTE that (unless timeout_io is 0) adns may acquire additional fds + * from one call to the next, so you must put adns_beforepoll in a + * loop, rather than assuming that the second call (with the buffer + * size requested by the first) will not return ERANGE. + * + * adns only ever sets POLLIN, POLLOUT and POLLPRI in its pollfd + * structs, and only ever looks at those bits. POLLPRI is required to + * detect TCP Urgent Data (which should not be used by a DNS server) + * so that adns can know that the TCP stream is now useless. + * + * In any case, *timeout_io should be a timeout value as for poll(2), + * which adns will modify downwards as required. If the caller does + * not plan to block then *timeout_io should be 0 on entry, or + * alternatively, timeout_io may be 0. (Alternatively, the caller may + * use _beforeselect with timeout_io==0 to find out about file + * descriptors, and use _firsttimeout is used to find out when adns + * might want to time something out.) + * + * adns_beforepoll will return 0 on success, and will not fail for any + * reason other than the fds buffer being too small (ERANGE). + * + * This call will never actually do any I/O, or change the fds that + * adns is using or the timeouts it wants; and in any case it won't + * block. + */ + +#define ADNS_POLLFDS_RECOMMENDED 2 +/* If you allocate an fds buf with at least RECOMMENDED entries then + * you are unlikely to need to enlarge it. You are recommended to do + * so if it's convenient. However, you must be prepared for adns to + * require more space than this. + */ + +void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds, + const struct timeval *now); +/* Gives adns flow-of-control for a bit; intended for use after + * poll(2). fds and nfds should be the results from poll(). pollfd + * structs mentioning fds not belonging to adns will be ignored. + */ + + +adns_status adns_rr_info(adns_rrtype type, + const char **rrtname_r, const char **fmtname_r, + int *len_r, + const void *datap, char **data_r); +/* Gets information in human-readable (but non-i18n) form + * for eg debugging purposes. type must be specified, + * and the official name of the corresponding RR type will + * be returned in *rrtname_r, and information about the processing + * style in *fmtname_r. The length of the table entry in an answer + * for that type will be returned in in *len_r. + * Any or all of rrtname_r, fmtname_r and len_r may be 0. + * If fmtname_r is non-null then *fmtname_r may be + * null on return, indicating that no special processing is + * involved. + * + * data_r be must be non-null iff datap is. In this case + * *data_r will be set to point to a human-readable text + * string representing the RR data. The text will have + * been obtained from malloc() and must be freed by the caller. + * + * Usually this routine will succeed. Possible errors include: + * adns_s_nomemory + * adns_s_rrtypeunknown + * adns_s_invaliddata (*datap contained garbage) + * If an error occurs then no memory has been allocated, + * and *rrtname_r, *fmtname_r, *len_r and *data_r are undefined. + */ + +const char *adns_strerror(adns_status st); +const char *adns_errabbrev(adns_status st); +/* Like strerror but for adns_status values. adns_errabbrev returns + * the abbreviation of the error - eg, for adns_s_timeout it returns + * "timeout". You MUST NOT call these functions with status values + * not returned by the same adns library. + */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif +#endif diff --git a/oop-adns.h b/oop-adns.h new file mode 100644 index 0000000000000000000000000000000000000000..e702243d26aff56a69cd686c55c1842abf5f0089 --- /dev/null +++ b/oop-adns.h @@ -0,0 +1,36 @@ +/* oop-adns.h, liboop, copyright 1999 Dan Egnor + + This is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License, version 2.1 or later. + See the file COPYING for details. */ + +#ifndef OOP_ADNS_H +#define OOP_ADNS_H + +#ifndef ADNS_H_INCLUDED +#error You must include "adns.h" before "oop_adns.h"! +#endif + +#include "oop.h" + +typedef struct oop_adapter_adns oop_adapter_adns; +typedef struct oop_adns_query oop_adns_query; +typedef void *oop_adns_call(oop_adapter_adns *,adns_answer *,void *); + +/* A liboop adns adapter creates an adns instance tied to a liboop source. + oop_adns_new() Returns NULL on failure.*/ +oop_adapter_adns *oop_adns_new(oop_source *,adns_initflags,FILE *diag); +void oop_adns_delete(oop_adapter_adns *); + +/* Submit an asynchronous DNS query. Returns NULL on system failure. + The returned pointer is valid until the callback occurs or the + query is cancelled (see below). */ +oop_adns_query *oop_adns_submit( + oop_adapter_adns *, + const char *owner,adns_rrtype type,adns_queryflags flags, + oop_adns_call *,void *); + +/* Cancel a running query. */ +void oop_adns_cancel(oop_adns_query *); + +#endif diff --git a/oop.h b/oop.h index 16fd116f7f083335608090173ed0b1bc17dea106..c046f223daa70c3a5a69f7ef677a5ab8b8c9cdc2 100644 --- a/oop.h +++ b/oop.h @@ -9,6 +9,7 @@ #include <sys/time.h> #include <sys/types.h> +#include <unistd.h> /* ------------------------------------------------------------------------- */ @@ -71,4 +72,24 @@ void oop_sys_delete(oop_source_sys *); /* Get the event registration interface for a system event source. */ oop_source *oop_sys_source(oop_source_sys *); +/* ------------------------------------------------------------------------- */ + +/* Helper for select-style asynchronous interfaces. */ +typedef struct oop_adapter_select oop_adapter_select; +typedef void *oop_call_select( + oop_adapter_select *, + int num,fd_set *r,fd_set *w, + struct timeval now,void *); + +oop_adapter_select *oop_select_new( + oop_source *, + oop_call_select *, + void *); + +void oop_select_set( + oop_adapter_select *,int num_fd, + fd_set *rfd,fd_set *wfd,struct timeval *timeout); + +void oop_select_delete(oop_adapter_select *); + #endif diff --git a/select.c b/select.c new file mode 100644 index 0000000000000000000000000000000000000000..ec86aca094c66950fb9a4b0861580ebfa64ca3c2 --- /dev/null +++ b/select.c @@ -0,0 +1,166 @@ +/* select.c, liboop, copyright 1999 Dan Egnor + + This is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License, version 2.1 or later. + See the file COPYING for details. */ + +#include "oop.h" + +#include <assert.h> + +struct select_set { + fd_set rfd,wfd; +}; + +struct oop_adapter_select { + oop_source *source; + struct select_set watch,active; + struct timeval timeout; + int num_fd,do_timeout,is_active,num_fd_active; + oop_call_select *call; + void *data; +}; + +static oop_call_fd on_fd; +static oop_call_time on_timeout,on_collect; + +oop_adapter_select *oop_select_new( + oop_source *source, + oop_call_select *call,void *data) +{ + oop_adapter_select *s = oop_malloc(sizeof(*s)); + if (NULL == s) return s; + s->source = source; + FD_ZERO(&s->watch.rfd); + FD_ZERO(&s->watch.wfd); + FD_ZERO(&s->active.rfd); + FD_ZERO(&s->active.wfd); + s->num_fd = 0; + s->do_timeout = 0; + s->is_active = 0; + s->call = call; + s->data = data; + return s; +} + +static void *activate(oop_adapter_select *s) { + if (!s->is_active) { + s->is_active = 1; + s->source->on_time(s->source,OOP_TIME_NOW,on_collect,s); + } + return OOP_CONTINUE; +} + +static void deactivate(oop_adapter_select *s) { + if (s->is_active) { + s->source->cancel_time(s->source,OOP_TIME_NOW,on_collect,s); + s->is_active = 0; + s->num_fd_active = 0; + FD_ZERO(&s->active.rfd); + FD_ZERO(&s->active.wfd); + } +} + +void oop_select_set( + oop_adapter_select *s,int num_fd, + fd_set *rfd,fd_set *wfd,struct timeval *timeout) +{ + int fd; + for (fd = 0; fd < num_fd || fd < s->num_fd; ++fd) { + int rfd_set = fd < num_fd && FD_ISSET(fd,rfd); + int wfd_set = fd < num_fd && FD_ISSET(fd,wfd); + int w_rfd_set = fd < s->num_fd && FD_ISSET(fd,&s->watch.rfd); + int w_wfd_set = fd < s->num_fd && FD_ISSET(fd,&s->watch.wfd); + + if (rfd_set && !w_rfd_set) { + s->source->on_fd(s->source,fd,OOP_READ,on_fd,s); + FD_SET(fd,&s->watch.rfd); + } + + if (wfd_set && !w_wfd_set) { + s->source->cancel_fd(s->source,fd,OOP_READ,on_fd,s); + FD_CLR(fd,&s->watch.rfd); + } + + if (!rfd_set && w_rfd_set) { + s->source->on_fd(s->source,fd,OOP_WRITE,on_fd,s); + FD_SET(fd,&s->watch.wfd); + } + + if (!wfd_set && w_wfd_set) { + s->source->cancel_fd(s->source,fd,OOP_WRITE,on_fd,s); + FD_CLR(fd,&s->watch.wfd); + } + } + + s->num_fd = num_fd; + + if (s->do_timeout) { + s->source->cancel_time(s->source,s->timeout,on_timeout,s); + s->do_timeout = 0; + } + + if (NULL != timeout) { + gettimeofday(&s->timeout,NULL); + s->timeout.tv_sec += timeout->tv_sec; + s->timeout.tv_usec += timeout->tv_usec; + while (s->timeout.tv_usec >= 1000000) { + ++s->timeout.tv_sec; + s->timeout.tv_usec -= 1000000; + } + s->do_timeout = 1; + s->source->on_time(s->source,s->timeout,on_timeout,s); + } + + deactivate(s); +} + +void oop_select_delete(oop_adapter_select *s) { + fd_set fd; + FD_ZERO(&fd); + oop_select_set(s,0,&fd,&fd,NULL); + oop_free(s); +} + +static void set_fd(int fd,fd_set *fds,int *num) { + if (!FD_ISSET(fd,fds)) { + FD_SET(fd,fds); + if (fd >= *num) *num = fd + 1; + } +} + +static void *on_fd(oop_source *source,int fd,oop_event event,void *data) { + oop_adapter_select *s = (oop_adapter_select *) data; + switch (event) { + case OOP_READ: + assert(FD_ISSET(fd,&s->watch.rfd)); + set_fd(fd,&s->active.rfd,&s->num_fd_active); + break; + case OOP_WRITE: + assert(FD_ISSET(fd,&s->watch.wfd)); + set_fd(fd,&s->active.wfd,&s->num_fd_active); + break; + default: + assert(0); + break; + } + return activate(s); +} + +static void *on_timeout(oop_source *source,struct timeval when,void *data) { + oop_adapter_select *s = (oop_adapter_select *) data; + assert(s->do_timeout); + return activate(s); +} + +static void *on_collect(oop_source *source,struct timeval when,void *data) { + oop_adapter_select *s = (oop_adapter_select *) data; + struct timeval now; + void *r; + + gettimeofday(&now,NULL); + r = s->call(s,s->num_fd_active,&s->active.rfd,&s->active.wfd,now,s->data); + deactivate(s); + + return r; +} diff --git a/sys.c b/sys.c index 936d8ae9b4c3c22c8c52874c2d73bd57e441b91c..14495d4942ddd725cfb53cf1d8a3724a3e64e53a 100644 --- a/sys.c +++ b/sys.c @@ -269,12 +269,13 @@ static void *sys_time_run(oop_source_sys *sys) { } void *oop_sys_run(oop_source_sys *sys) { - void *ret = NULL; + void * volatile ret = NULL; assert(!sys->in_run && "oop_sys_run is not reentrant"); sys->in_run = 1; while (0 != sys->num_events && NULL == ret) { - struct timeval tv,*ptv = NULL; + struct timeval * volatile ptv = NULL; + struct timeval tv; fd_set rfd,wfd; int i,rv; diff --git a/test.c b/test-oop.c similarity index 98% rename from test.c rename to test-oop.c index a4cc9d24e32294e8f9879834ec986cd952c8778f..bd021ae6edf937cc57d9d07efbe3b99a9ebc4550 100644 --- a/test.c +++ b/test-oop.c @@ -42,7 +42,6 @@ oop_call_fd on_data; void *on_data(oop_source *source,int fd,oop_event event,void *data) { char buf[BUFSIZ]; int r = read(fd,buf,sizeof(buf)); - if (r <= 0) return OOP_HALT; write(1,buf,r); return OOP_CONTINUE; }