pax_global_header00006660000000000000000000000064130243577050014520gustar00rootroot0000000000000052 comment=a515ed28396bedfc5850c698526c88e3c61051fe cqueues-rel-20161214/000077500000000000000000000000001302435770500142145ustar00rootroot00000000000000cqueues-rel-20161214/.gitignore000066400000000000000000000001611302435770500162020ustar00rootroot00000000000000doc/*.aux doc/*.idx doc/*.log doc/*.out doc/*.toc src/5.1/ src/5.2/ src/5.3/ src/errno.c src/lib/*.o src/lib/*.a cqueues-rel-20161214/GNUmakefile000066400000000000000000000200301302435770500162610ustar00rootroot00000000000000# non-recursive prologue sp := $(sp).x dirstack_$(sp) := $(d) d := $(abspath $(lastword $(MAKEFILE_LIST))/..) ifeq ($(origin GUARD_$(d)), undefined) GUARD_$(d) := 1 all: # default target # # G N U M A K E F U N C T I O N S # KNOWN_APIS = 5.1 5.2 5.3 # template for invoking luapath script LUAPATH := $(d)/mk/luapath LUAPATH_FN = $(shell env CC='$(subst ',\\',$(CC))' CPPFLAGS='$(subst ',\\',$(CPPFLAGS))' LDFLAGS='$(subst ',\\',$(LDFLAGS))' $(LUAPATH) -krxm3 -I '$(subst ',\\',$(DESTDIR)$(includedir))' -I/usr/include -I/usr/local/include -P '$(subst ',\\',$(DESTDIR)$(bindir))' -P '$(subst ',\\',$(bindir))' -L '$(subst ',\\',$(DESTDIR)$(libdir))' -L '$(subst ',\\',$(libdir))' -v$(1) $(2)) # check whether luapath can locate Lua $(1) headers HAVE_API_FN = $(and $(filter $(1),$(call LUAPATH_FN,$(1),version)),$(1)$(info enabling Lua $(1))) # check whether $(1) in LUA_APIS or $(LUA$(1:.=)_CPPFLAGS) is non-empty WITH_API_FN = $$(and $$(or $$(filter $(1),$$(LUA_APIS)),$$(LUA$(subst .,,$(1))_CPPFLAGS)),$(1)) # # E N V I R O N M E N T C O N F I G U R A T I O N # -include $(d)/.config prefix ?= /usr/local includedir ?= $(prefix)/include libdir ?= $(prefix)/lib datadir ?= $(prefix)/share bindir ?= $(prefix)/bin lua51cpath ?= $(libdir)/lua/5.1 lua51path ?= $(datadir)/lua/5.1 lua52cpath ?= $(libdir)/lua/5.2 lua52path ?= $(datadir)/lua/5.2 lua53cpath ?= $(libdir)/lua/5.3 lua53path ?= $(datadir)/lua/5.3 AR ?= ar RANLIB ?= ranlib M4 ?= m4 MV ?= mv RM ?= rm CP ?= cp RMDIR ?= rmdir MKDIR ?= mkdir CHMOD ?= chmod INSTALL ?= install INSTALL_DATA ?= $(INSTALL) -m 644 TOUCH ?= touch TEE ?= tee TEE_A ?= $(TEE) -a # see Lua Autodetection, below .PHONY: $(d)/config PRINT_$(d) = printf "%s = %s\n" '$(1)' '$(subst ',\\',$(2))' | $(TEE_A) '$(3)' LAZY_$(d) = \ prefix includedir libdir datadir bindir \ lua51cpath lua51path lua52cpath lua52path lua53cpath lua53path \ CC ALL_CPPFLAGS CPPFLAGS ALL_CFLAGS CFLAGS ALL_LDFLAGS LDFLAGS \ ALL_SOFLAGS SOFLAGS ALL_LIB LIBS \ $(foreach API,$(KNOWN_APIS),ALL_LUA$(subst .,,$(API))_CPPFLAGS) \ AR RANLIB M4 MV RM CP RMDIR MKDIR CHMOD INSTALL INSTALL_DATA TOUCH \ TEE TEE_A NONLAZY_$(d) = \ LUA_APIS \ $(foreach API,$(KNOWN_APIS),LUAC$(subst .,,$(API))) \ $(foreach API,$(KNOWN_APIS),$(and $(call WITH_API_FN,$(API)),LUA$(subst .,,$(API))_CPPFLAGS)) $(d)/config: $(TOUCH) $(@D)/.config.tmp @$(foreach V,$(LAZY_$(@D)), $(call PRINT_$(@D),$(V),$(value $(V)),$(@D)/.config.tmp);) @$(foreach V,$(NONLAZY_$(@D)), $(call PRINT_$(@D),$(V),$($(V)),$(@D)/.config.tmp);) $(MV) $(@D)/.config.tmp $(@D)/.config # add local targets if building from inside project tree ifneq "$(filter $(abspath $(d)/..)/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" .PHONY: config configure config configure: $(d)/config endif # # L U A A U T O D E T E C T I O N # # set LUA_APIS if empty or "?" ifeq ($(or $(strip $(LUA_APIS)),?),?) override LUA_APIS := $(call HAVE_API_FN,5.1) $(call HAVE_API_FN,5.2) $(call HAVE_API_FN,5.3) endif define LUAXY_template # set luaXYcpath if empty or "?" ifeq ($$(or $$(strip $$(lua$(subst .,,$(1))cpath)),?),?) override lua$(subst .,,$(1))cpath := $$(or $$(call LUAPATH_FN,$(1),cdir),$$(libdir)/lua/$(1)) endif # set luaXYpath if empty or "?" ifeq ($$(or $$(strip $$(lua$(subst .,,$(1))path)),?),?) override lua$(subst .,,$(1))path = $$(or $$(call LUAPATH_FN,$(1),ldir),$$(datadir)/lua/$(1)) endif # set LUAXY_CPPFLAGS if undefined or "?" (NB: can be empty if path already in $(CPPFLAGS)) ifeq ($$(and $$(findstring undefined,$$(origin LUA$(subst .,,$(1))_CPPFLAGS)),?),?) override LUA$(subst .,,$(1))_CPPFLAGS = $$(and $$(call WITH_API_FN,$(1)),$$(call LUAPATH_FN,$(1),cppflags)) endif # set ALL_LUAXY_CPPFLAGS if empty or "?" ifeq ($$(or $$(strip $$(ALL_LUA$(subst .,,$(1))_CPPFLAGS)),?),?) override ALL_LUA$(subst .,,$(1))_CPPFLAGS = -DLUA_COMPAT_APIINTCASTS $$(LUA$(subst .,,$(1))_CPPFLAGS) endif # set LUAXYC if empty or "?" ifeq ($$(or $$(strip $$(LUAC$(subst .,,$(1)))),?),?) override LUAC$(subst .,,$(1)) = $$(or $$(call LUAPATH_FN,$(1),luac),true) endif endef # LUAXY_template $(eval $(call LUAXY_template,5.1)) $(eval $(call LUAXY_template,5.2)) $(eval $(call LUAXY_template,5.3)) # # A U T O D E T E C T C O M P I L A T I O N F L A G S # cc-option ?= $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi;) VENDOR_OS_$(d) := $(shell $(d)/mk/vendor.os) VENDOR_CC_$(d) := $(shell env CC="$(CC)" $(d)/mk/vendor.cc) # # ALL_CPPFLAGS # ifeq ($(origin ALL_CPPFLAGS), undefined) ifneq ($(VENDOR_OS_$(d)), OpenBSD) ALL_CPPFLAGS += -D_REENTRANT -D_THREAD_SAFE -D_GNU_SOURCE endif ifeq ($(VENDOR_OS_$(d)), SunOS) ALL_CPPFLAGS += -Usun -D_XPG4_2 -D__EXTENSIONS__ endif ALL_CPPFLAGS += $(CPPFLAGS) endif # ALL_CPPFLAGS # # ALL_CFLAGS # ifeq ($(origin ALL_CFLAGS), undefined) ifeq ($(VENDOR_CC_$(d)), gcc) ALL_CFLAGS += -O2 -std=gnu99 -fPIC ALL_CFLAGS += -g -Wall -Wextra $(call cc-option, -Wno-missing-field-initializers) $(call cc-option, -Wno-override-init) -Wno-unused endif ifeq ($(VENDOR_CC_$(d)), clang) ALL_CFLAGS += -O2 -std=gnu99 -fPIC ALL_CFLAGS += -g -Wall -Wextra -Wno-missing-field-initializers -Wno-initializer-overrides -Wno-unused -Wno-dollar-in-identifier-extension endif ifeq ($(VENDOR_CC_$(d)), sunpro) ALL_CFLAGS += -xcode=pic13 ALL_CFLAGS += -g # # Solaris Studio supports anonymous unions just fine; but it complains # incessantly about them. # ALL_CFLAGS += -erroff=E_ANONYMOUS_UNION_DECL endif ifeq ($(VENDOR_OS_$(d)), Darwin) ALL_CFLAGS += -Wno-deprecated-declarations endif ALL_CFLAGS += $(CFLAGS) endif # ALL_CFLAGS # # ALL_SOFLAGS # ifeq ($(origin ALL_SOFLAGS), undefined) ifeq ($(VENDOR_OS_$(d)), Darwin) ALL_SOFLAGS += -bundle -undefined dynamic_lookup else ALL_SOFLAGS += -shared endif ALL_SOFLAGS += $(SOFLAGS) endif # ALL_SOFLAGS # # ALL_LDFLAGS # ifeq ($(origin ALL_LDFLAGS), undefined) ALL_LDFLAGS += -L$(DESTDIR)$(libdir) -L$(libdir) ALL_LDFLAGS += $(LDFLAGS) endif # ALL_LDFLAGS # # ALL_LIBS # ifeq ($(origin ALL_LIBS), undefined) # put $(LIBS) first as they're more likely to be higher-level dependencies ALL_LIBS += $(LIBS) ALL_LIBS += -lssl -lcrypto -lpthread # NetBSD, FreeBSD, OpenBSD (and presumably descendants) lack any libdl; # dlopen, et al are part of libc. ifneq ($(patsubst %BSD,BSD,$(VENDOR_OS_$(d))), BSD) ALL_LIBS += -ldl endif # This only seems to be necessary on Linux. Darwin and OpenBSD lack a librt. # On OpenBSD clock_gettime is part of libc. Others have librt, but linking # it in is unnecessary. ifeq ($(VENDOR_OS_$(d)), Linux) ALL_LIBS += -lrt endif ALL_LIBS += -lm endif # ALL_LIBS # # P R O J E C T R U L E S # include $(d)/src/GNUmakefile include $(d)/regress/GNUmakefile $(d)/config.h: $(d)/config.h.guess $(CP) $< $@ # # C L E A N R U L E S # .PHONY: $(d)/clean~ clean~ $(d)/clean~: $(RM) -f $(@D)/*~ clean~: $(d)/clean~ # # D E B I A N R U L E S # ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" DPKG_BUILDPACKAGE ?= dpkg-buildpackage FAKEROOT ?= fakeroot DPKG_BUILDPACKAGE_OPTIONS ?= -b -uc -us .PHONY: $(d)/debian $(d)/debian-clean debian deb debian-clean deb-clean $(d)/debian: cd $(@D) && $(DPKG_BUILDPACKAGE) -rfakeroot $(DPKG_BUILDPACKAGE_OPTIONS) $(d)/debian-clean: cd $(@D) && $(FAKEROOT) ./debian/rules clean debian deb: $(d)/debian debian-clean deb-clean: $(d)/debian-clean endif # debian guard # # R E D H A T R U L E S # ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" .PHONY: $(d)/redhat $(d)/redhat-clean redhat rpm redhat-clean rpm-clean redhat rpm: $(d)/redhat redhat-clean rpm-clean: $(d)/redhat-clean endif # redhat guard # # R E L E A S E T A R B A L L R U L E S # ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" CQUEUES_VERSION := $(shell $(d)/mk/changelog version) .PHONY: $(d)/cqueues-$(CQUEUES_VERSION).tgz release $(d)/cqueues-$(CQUEUES_VERSION).tgz: cd $(@D) && git archive --format=tar --prefix=$(basename $(@F))/ HEAD | gzip -c > $@ release: $(d)/cqueues-$(CQUEUES_VERSION).tgz endif # release guard endif # include guard # non-recursive epilogue d := $(dirstack_$(sp)) sp := $(basename $(sp)) cqueues-rel-20161214/LICENSE000066400000000000000000000021041302435770500152160ustar00rootroot00000000000000Copyright (c) 2012-2015 William Ahern Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cqueues-rel-20161214/Makefile000066400000000000000000000001161302435770500156520ustar00rootroot00000000000000.POSIX: all: +gmake -f GNUmakefile all .DEFAULT: +gmake -f GNUmakefile $< cqueues-rel-20161214/PORTING.md000066400000000000000000000526731302435770500156750ustar00rootroot00000000000000The following is a list of porting issues discovered while implementing cqueues, maintained in the spirit of [DJB's portability notes](http://cr.yp.to/docs/unixport.html). --------------------------------------------------------------------------- ### `FD_SETSIZE` - `int` : Linux 3.2.0 (glibc 2.15) - `int` : OS X 10.7 - `int` : OpenBSD 5.1 - `int` : Solaris 11.0 - `unsigned int` : FreeBSD 9.0 - `int` : NetBSD 5.1.2 Last updated 2012-08-16. --------------------------------------------------------------------------- ### `.msg_iovlen` of `struct msghdr` - `size_t` : Linux 3.2.0 (glibc 2.15) - `int` : OS X 10.7 - `unsigned int` : OpenBSD 5.1 - `int` : Solaris 11.0 - `int` : FreeBSD 9.0 - `int` : NetBSD 5.1.2 - `size_t` : RFC 2292 - `int` : SUSv4 Last updated 2012-08-16. --------------------------------------------------------------------------- ### `CMSG_SPACE` Not always a constant expression, so cannot be used to declare a compound literal. - constant : Linux 3.2.0 (glibc 2.15) - not constant : OS X 10.7 - constant : OpenBSD 5.1 - constant : Solaris 11.0 - constant : FreeBSD 9.0 - not constant : NetBSD 5.1.2 Last updated 2012-08-16. --------------------------------------------------------------------------- ### `sendmsg`, `SCM_RIGHTS` OS X will completely shutdown a socket and free pending data for an inflight descriptor lacking a process reference. Nonethelss, recvmsg will return a valid socket descriptor as ancillary `SCM_RIGHTS` data. Confirmed OS X 10.7, OS X 10.8. Last updated 2012-08-14. See also [`SO_NOSIGPIPE`](#so_nosigpipe-f_setnosigpipe). --------------------------------------------------------------------------- ### `SO_NOSIGPIPE`, `F_SETNOSIGPIPE` OS X will fail with `EINVAL` an attempt to set the `SO_NOSIGPIPE` option on some streams which have been terminated. Examples: - On a socketpair descriptor where the peer has called `shutdown(SHUT_RDWR)`. - On a TCP stream after both peers exchanged FIN, e.g. by each calling `shutdown(SHUT_WR)`. - On a TCP stream after a send call has returned `EPIPE`, e.g. by the peer calling close, triggering RST. Receiving RST from a peer was not sufficient alone to fail setsockopt. Nor was a mere call to `shutdown(SHUT_WR)` by the host. Confirmed OS X 10.7. Last updated 2012-08-16. See also [shutdown](#shutdown). --------------------------------------------------------------------------- ### shutdown OS X will fail with `ENOTCONN` a shutdown attempt if the specified `SHUT_RD` or `SHUT_WR` flag was already set, or if the respective TCP state was already reached. In particular, if FIN was received from the sender then shutdown(SHUT_RD) will fail. Confirmed OS X 10.7. Last updated 2012-08-16. --------------------------------------------------------------------------- ### `fchmod` on `AF_UNIX` socket - OK : Linux 3.2.0 (glibc 2.15) - `EINVAL` : OS X 10.8.1 - `EINVAL` : OpenBSD 5.1 - OK : Solaris 11.0 - `EINVAL` : FreeBSD 9.0 - `EINVAL` : NetBSD 5.1.2 #### NOTES: - Solaris does not actually obey `AF_UNIX` socket file permissions. - `fchmod` on Linux is useful because it addresses the race condition of `bind`+`chmod`--if you `fchmod` the socket before binding, the directory entry permissions are inherited without having to bother with thread-unfriendly umask fiddling. More portable alternatives are to bind the socket into a private directory first, or to rely on peer credentials at accept. Last updated 2012-09-15. --------------------------------------------------------------------------- ### `AF_UNIX` socket file permissions - obeys : Linux 3.2.0 (glibc 2.15) - obeys : OS X 10.8.1 - obeys : OpenBSD 5.1 - ignores : Solaris 11.0 - obeys : FreeBSD 9.0 - obeys : NetBSD 5.1.2 Last updated 2012-09-15. --------------------------------------------------------------------------- ### `pselect` - OK : Linux 3.2.0 (glibc 2.15) - broken : OS X 10.8.1 - NO : OpenBSD 5.1 - OK : Solaris 11.0 - OK : FreeBSD 9.0 - broken : NetBSD 5.1.2 OS X appears to merely set and reset the signal mask around a call to `select`, which doesn't address the signal race at all. OpenBSD 5.1 does not provide `pselect`. NetBSD 5.1 fails to deliver signals inside `pselect`, including pending signals, although it does interrupt when a signal arrives. A kqueue-portable implementation is feasible using `EVFILT_SIGNAL`: 1. install `EVFILT_SIGNAL` for the signal set currently blocked but soon to be unblocked 2. check sigpending for such signal set 3. install requested signal mask 4. call select with kqueue descriptor added to the read fd_set 5. restore signal mask 6. check return values from select and kqueue Non-obviousness: - Any other signal set than described above could be lost even with a kernel pselect. An implementation could elect to minimize the race condition, but that would merely postpone the inevitable. - `EVFILT_SIGNAL` is edge triggered, so it won't catch pending signals delivered upon unblocking, thus the necessity to call sigpending after `kevent`, but before `sigprocmask`/`pthread_sigmask`. - The above scheme is susceptiple to spurious wakeup, e.g. by `SIG_IGN` handlers which wouldn't interrupt a kernel `pselect`. Last updated 2012-09-21. --------------------------------------------------------------------------- ### `(struct kevent).udata` - `void *` : OS X 10.8.2 - `void *` : OpenBSD 5.1 - `void *` : FreeBSD 9.0 - `intptr_t` : NetBSD 5.1.2 NetBSD circa 2002 changed the .udata type to intptr_t. Workaround: cast to `(__typeof__(((struct kevent *)0)->udata))`. Last updated 2012-09-21. --------------------------------------------------------------------------- ### Linux `connect(2)` associations Linux will not reassociate a UDP socket to a non-loopback address if the first association was to the loopback. No other system exhibits this behavior. An argument can be made that reassociations should fail if the existing source address (perhaps auto-bound on the first association) does not match the new destination network. But this has apparently always been allowed on various systems as far as I can tell, even on previous versions of Linux. A work around is to break an existing association by connecting to `AF_UNSPEC`. This bug/feature can manifest, for example, when your /etc/resolv.conf files looks like nameserver 127.0.0.1 nameserver 1.2.3.4 and the DNS resolver uses `connect(2)` to efficiently filter replies. Failover of queries from 127.0.0.1 to 1.2.3.4 will return a system error because Linux will return `EINVAL` when reassociating the socket with connect(udp-socket, 1.2.3.4). Reassociating from loopback to external address: - `EINVAL`: Linux 3.2.0 (and various 3.x) - OK : OS X 10.8.2 - OK : OpenBSD 5.1 - OK : OpenBSD 5.2 - OK : Solaris 11.0 - OK : FreeBSD 9.0 - OK : NetBSD 5.1.2 Last updated 2013-03-02. --------------------------------------------------------------------------- ### `SO_ACCEPTCONN` `SO_ACCEPTCONN` is unsupported on many BSDs due to an old bug which (supposedly) has been intentionally carried forward. - OK : Linux 3.2.0 - `ENOPROTOOPT` : OS X 10.9.3 - `ENOPROTOOPT` : OpenBSD 5.5 - OK : Solaris 11.1 - OK : FreeBSD 9.0 - `ENOPROTOOPT` : NetBSD 6.1.1 Last updated 2014-07-01. --------------------------------------------------------------------------- ### `getsockname` and `getpeername` on `AF_UNIX` socket The socket address returned for both named and unnamed AF_UNIX sockets differs among systems. Applications must be sure to check the returned socket address length as it won't always match the length of the associated socket address structure, and in some cases might be 0. NOTE: - An rlen of 0 did not mean that the syscall failed. No syscalls failed in the generation of this dataset. - For socketpair the first descriptor was used to query the behavior. ``` system | fd | syscall | rlen | .sa_family | .sun_path ================================================================================= AIX 7.1 | listen | getsockname | 1025 | AF_UNIX | set (named.sock) | accept | getsockname | 1025 | AF_UNIX | set (named.sock) | accept | getpeername | 16 | AF_UNIX | empty | connect | getsockname | 0 | unset | - | connect | getpeername | 1025 | AF_UNIX | set (named.sock) | socketpair | getsockname | 0 | unset | - | socketpair | getpeername | 16 | AF_UNIX | empty --------------------------------------------------------------------------------- Solaris 11.2 | listen | getsockname | 110 | AF_UNIX | set (named.sock) | accept | getsockname | 110 | AF_UNIX | set (named.sock) | accept | getpeername | 16 | AF_UNIX | empty | connect | getsockname | 0 | unset | - | connect | getpeername | 110 | AF_UNIX | set (named.sock) | socketpair | getsockname | 16 | AF_UNIX | empty | socketpair | getpeername | 0 | unset | - --------------------------------------------------------------------------------- Linux 3.16 | listen | getsockname | 13 | AF_UNIX | set (named.sock) | accept | getsockname | 13 | AF_UNIX | set (named.sock) | accept | getpeername | 2 | AF_UNIX | unset | connect | getsockname | 2 | AF_UNIX | unset | connect | getpeername | 13 | AF_UNIX | set (named.sock) | socketpair | getsockname | 2 | AF_UNIX | unset | socketpair | getpeername | 2 | AF_UNIX | unset --------------------------------------------------------------------------------- FreeBSD 10.1 | listen | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getpeername | 16 | AF_UNIX | empty | connect | getsockname | 16 | AF_UNIX | empty | connect | getpeername | 106 | AF_UNIX | set (named.sock) | socketpair | getsockname | 16 | AF_UNIX | empty | socketpair | getpeername | 16 | AF_UNIX | empty --------------------------------------------------------------------------------- NetBSD 6.1.5 | listen | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getpeername | 106 | AF_UNIX | empty | connect | getsockname | 106 | AF_UNIX | empty | connect | getpeername | 106 | AF_UNIX | set (named.sock) | socketpair | getsockname | 106 | AF_UNIX | empty | socketpair | getpeername | 106 | AF_UNIX | empty --------------------------------------------------------------------------------- OpenBSD 5.6 | listen | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getpeername | 16 | AF_UNIX | empty | connect | getsockname | 16 | AF_UNIX | empty | connect | getpeername | 106 | AF_UNIX | set (named.sock) | socketpair | getsockname | 16 | AF_UNIX | empty | socketpair | getpeername | 16 | AF_UNIX | empty --------------------------------------------------------------------------------- Minix 3.3 | listen | getsockname | 106 | AF_UNIX | set (/tmp/getname.012688aa/named.sock) | accept | getsockname | 106 | AF_UNIX | set (/tmp/getname.012688aa/named.sock) | accept | getpeername | 106 | AF_UNIX | set (/tmp/getname.012688aa/named.sock) | connect | getsockname | 106 | AF_UNIX | set (/tmp/getname.012688aa/named.sock) | connect | getpeername | 106 | AF_UNIX | set (/tmp/getname.012688aa/named.sock) | socketpair | getsockname | 106 | AF_UNIX | set (X) | socketpair | getpeername | 106 | AF_UNIX | set (X) --------------------------------------------------------------------------------- OS X 10.10.5 | listen | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getsockname | 106 | AF_UNIX | set (named.sock) | accept | getpeername | 16 | AF_UNIX | empty | connect | getsockname | 16 | AF_UNIX | empty | connect | getpeername | 106 | AF_UNIX | set (named.sock) | socketpair | getsockname | 16 | AF_UNIX | empty | socketpair | getpeername | 16 | AF_UNIX | empty ``` Last updated 2015-08-10. --------------------------------------------------------------------------- ### APPENDIX // fchmod and AF_UNIX socket permission checking // Last updated 2012-09-15 #include #include #include #include #include #include #include #include #include #undef sun #if __clang__ _Pragma("clang diagnostic ignored \"-Wunused\"") #elif __GNUC__ _Pragma("GCC diagnostic ignored \"-Wunused\"") #endif #define expect(rv, cmp, fn, ...) ({ \ __typeof__(rv) tmp = fn(__VA_ARGS__); \ if (rv cmp tmp) { \ puts(#fn ": OK"); \ } else { \ printf(#fn ": %s\n", strerror(errno)); \ _Exit(EXIT_FAILURE); \ } \ tmp; \ }) int main(int argc, char **argv) { extern char *optarg; extern int optind; const char *path = "mode.sock"; int mode = 0, mask = 0777; const char *op; int opt, srv = -1, cli = -1; while (-1 != (opt = getopt(argc, argv, "p:m:u:"))) { switch (opt) { case 'p': path = optarg; break; case 'm': mode = strtol(optarg, NULL, 8); break; case 'u': mask = strtol(optarg, NULL, 8); break; default: break; } } argc -= optind; argv += optind; srv = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); unlink(path); struct sockaddr_un sun; memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; strncpy(sun.sun_path, path, sizeof sun.sun_path); op = (argc > 0)? *argv : "blc"; while (*op) { switch (*op++) { case 'b': expect(0, ==, bind, srv, (void *)&sun, sizeof sun); break; case 'l': expect(0, ==, listen, srv, SOMAXCONN); break; case 'c': expect(0, ==, chmod, path, mode); break; case 'f': expect(0, ==, fchmod, srv, mode); break; case 'u': umask(mask); puts("umask: OK"); break; default: fprintf(stderr, "%c: unknown operation\n", op[-1]); } } cli = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); expect(0, ==, connect, cli, (void *)&sun, sizeof sun); return 0; } /* main() */ --------------------------------------------------------------------------- // pselect #define USE_PTHREAD 0 #include #include #include static void noop() { write(STDERR_FILENO, "rcvd\n", 5); } int main(void) { struct sigaction act; sigset_t omask, mask; act.sa_handler = &noop; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT, &act, NULL); raise(SIGINT); sigemptyset(&mask); sigemptyset(&omask); sigaddset(&mask, SIGINT); #if USE_PTHREAD pthread_sigmask(SIG_BLOCK, &mask, &omask); #else sigprocmask(SIG_BLOCK, &mask, &omask); #endif raise(SIGINT); sigdelset(&omask, SIGINT); pselect(0, NULL, NULL, NULL, NULL, &omask); return 0; } --------------------------------------------------------------------------- // getsockname and getpeername on AF_UNIX socket #include #include #include #include #include #include #include #include #include #include #include #undef sun #define SAY_(file, func, line, fmt, ...) \ fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__) #define SAY(...) SAY_(__FILE__, __func__, __LINE__, __VA_ARGS__, "\n") #define HAI SAY("hai") #define panic(...) do { SAY(__VA_ARGS__); exit(EXIT_FAILURE); } while (0) #define panic_m_(fmt, ...) panic(fmt ": %s", __VA_ARGS__) #define panic_m(...) panic_m_(__VA_ARGS__, strerror(errno)) static struct { char root[128], name[128]; } tmp = { .root = "/tmp/getname.XXXXXXXX", .name = "named.sock", }; static void rmtmpdir(void) { unlink(tmp.name); if (0 != chdir("..")) panic_m("chdir .."); if (0 != rmdir(tmp.root)) panic_m("rmdir %s", tmp.root); } /* rmtmpdir() */ static void mktmpdir(void) { if (!mktemp(tmp.root)) panic_m("mktemp"); if (0 != mkdir(tmp.root, 0700)) panic_m("%s", tmp.root); atexit(&rmtmpdir); if (0 != chdir(tmp.root)) panic_m("chdir %s", tmp.root); } /* mktmpdir() */ struct sockname { int flags; const char *type; const char *call; union { struct sockaddr sa; struct sockaddr_un sun; struct sockaddr_storage ss; } sa; socklen_t salen; struct { _Bool isset; int type; const char *text; } family; struct { _Bool defined; _Bool isset; _Bool empty; const char *text; } path; }; /* struct sockname */ #define GETNAME_LISTENFD 0x01 #define GETNAME_ACCEPTFD 0x02 #define GETNAME_CONNECTFD 0x04 #define GETNAME_SOCKETPAIRFD 0x08 #define GETNAME_SOCKNAME 0x10 #define GETNAME_PEERNAME 0x20 static struct sockname getname(int fd, int flags) { struct sockname name; int ret; memset(&name, 0, sizeof name); name.flags = flags; name.type = (flags & GETNAME_CONNECTFD)? "connect" : (flags & GETNAME_ACCEPTFD)? "accept" : (flags & GETNAME_LISTENFD)? "listen" : "socketpair"; name.call = (flags & GETNAME_PEERNAME)? "getpeername" : "getsockname"; name.family.type = AF_UNSPEC; name.family.text = "unset"; name.path.text = "-"; name.salen = sizeof name.sa; if (flags & GETNAME_PEERNAME) { ret = getpeername(fd, (struct sockaddr *)&name.sa, &name.salen); } else { ret = getsockname(fd, (struct sockaddr *)&name.sa, &name.salen); } if (0 != ret) panic_m("%s", (flags & GETNAME_PEERNAME)? "getpeername" : "getsockname"); if (name.salen >= offsetof(struct sockaddr_un, sun_family) + sizeof (name.sa.sun.sun_family)) { name.family.isset = 1; name.family.type = name.sa.sun.sun_family; switch (name.family.type) { case AF_UNSPEC: name.family.text = "AF_UNSPEC"; break; case AF_UNIX: name.family.text = "AF_UNIX"; break; default: name.family.text = "?"; break; } } if (name.family.type == AF_UNIX) { name.path.defined = 1; if (name.salen > offsetof(struct sockaddr_un, sun_path)) { name.path.isset = 1; name.path.empty = 0 == strnlen(name.sa.sun.sun_path, sizeof name.sa.sun.sun_path); name.path.text = (name.path.empty)? "empty" : "set"; } else { name.path.text = "unset"; } } return name; } /* getname() */ static void printname(struct sockname name) { printf("%-10s | %s | %-4d | %-9s | %-6s", name.type, name.call, (int)name.salen, name.family.text, name.path.text); if (name.path.isset && !name.path.empty) printf(" (%s)", name.sa.sun.sun_path); putchar('\n'); } /* printname() */ int main(void) { struct sockaddr_un sun, none; int listen_fd = -1, accept_fd = -1, connect_fd = -1, pair_fd[2] = { -1, -1 }; int flags, i; struct sockname name[7]; mktmpdir(); if (-1 == (listen_fd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC))) panic_m("socket"); memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; strncpy(sun.sun_path, tmp.name, sizeof sun.sun_path); if (0 != bind(listen_fd, (struct sockaddr *)&sun, sizeof sun)) panic_m("bind %s", tmp.name); if (0 != listen(listen_fd, SOMAXCONN)) panic_m("listen"); name[0] = getname(listen_fd, GETNAME_LISTENFD|GETNAME_SOCKNAME); if (-1 == (connect_fd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC))) panic_m("socket"); if (-1 == (flags = fcntl(connect_fd, F_GETFL))) panic_m("fcntl"); if (0 != fcntl(connect_fd, F_SETFL, flags|O_NONBLOCK)) panic_m("fcntl"); memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; strncpy(sun.sun_path, tmp.name, sizeof sun.sun_path); (void)connect(connect_fd, (struct sockaddr *)&sun, sizeof sun); /* Minix segfaults when passing NULL */ memset(&none, 0, sizeof none); if (-1 == (accept_fd = accept(listen_fd, (struct sockaddr *)&none, &(socklen_t){ sizeof none }))) panic_m("accept"); /* * Minix requires that we connect asynchronously. All other systems * completed the test with a synchrous connect followed by * synchronous accept */ while (0 != connect(connect_fd, (struct sockaddr *)&sun, sizeof sun)) { if (errno == EALREADY || errno == EISCONN) break; if (errno != EINPROGRESS) panic_m("connect %s", tmp.name); } name[1] = getname(accept_fd, GETNAME_ACCEPTFD|GETNAME_SOCKNAME); name[2] = getname(accept_fd, GETNAME_ACCEPTFD|GETNAME_PEERNAME); name[3] = getname(connect_fd, GETNAME_CONNECTFD|GETNAME_SOCKNAME); name[4] = getname(connect_fd, GETNAME_CONNECTFD|GETNAME_PEERNAME); if (0 != socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair_fd)) panic_m("socketpair"); name[5] = getname(pair_fd[0], GETNAME_SOCKETPAIRFD|GETNAME_SOCKNAME); name[6] = getname(pair_fd[0], GETNAME_SOCKETPAIRFD|GETNAME_PEERNAME); for (i = 0; i < (int)(sizeof name / sizeof *name); i++) printname(name[i]); return 0; } /* main() */ --------------------------------------------------------------------------- cqueues-rel-20161214/README.md000066400000000000000000000143501302435770500154760ustar00rootroot00000000000000## API Documentation Please refer to the PDF available at ## Build Dependancies The Makefile requires GNU Make. The source code should build with recent GCC, clang, or Solaris SunPro compilers. If you use your own Makefile, note that GCC and especially clang may emit copious warnings about initializers and unused parameters. These warnings are stupid. Use `-Wno-override-init` (GCC), `-Wno-initializer-overrides` (clang) and `-Wno-unused` to quiet these. For other warnings, patches welcome. M4 and awk are required to generate `errno.c`. It relies on `mk/errno.list` to enumerate the system error macro names. `mk/errno.list` is a small POSIX-compatible shell script. By default it processes GCC's `-dM` macro list (clang also supports this option). For SunPro it uses a slightly cruder method. Because the location of Lua include headers are unpredictable across systems, the build system by default relies on `mk/luapath` to locate the correct headers. `mk/luapath` uses various POSIX utilities. For more information [see the luapath project page](http://25thandclement.com/~william/projects/luapath.html). But see `LUA_APIS`, `LUA51_CPPFLAGS`, `LUA52_CPPFLAGS`, and `LUA53_CPPFLAGS`, below. `cqueues` should work on recent versions of Linux, OS X, Solaris, NetBSD, FreeBSD, OpenBSD, and derivatives. The regression suite is run on all supported platforms before rolling a release, and regularly during the development. In the future support may be added for AIX and the AIX `pollset` interface. Windows support is planned, though initially by relying on BSD `select`. ## Build Overview There is no separate `./configure` step at the moment. System introspection occurs during compile time. However, the `configure` Make target can be used to cache the build environment. ## Build Environment ### Lua APIs `cqueues` targets the three latest Lua APIs---5.1, 5.2, and 5.3---and all can be compiled simultaneously. Supported build targets are automatically detected by default. To override API autodetection specify `LUA_APIS`. For example, ``` $ make LUA_APIS="5.2 5.3" ``` ### Toolchain Flags All the common GNU-style compiler variables are supported, including `CC`, `CPPFLAGS`, `CFLAGS`, `LDFLAGS`, `SOFLAGS`, and `LIBS`. Note that you can specify the path to both Lua 5.1, 5.2, and 5.3 include headers at the same time in `CPPFLAGS`; the build system will work things out to ensure the correct headers are loaded at compile-time. To specify them explicitly provide - `LUA51_CPPFLAGS` - preprocessor flags for Lua 5.1 - `LUA52_CPPFLAGS` - preprocessor flags for Lua 5.2 - `LUA53_CPPFLAGS` - preprocessor flags for Lua 5.3 To completely override all internally-defined flags, specify the `ALL_`-prefixed variant of any of the above. For example, specify `ALL_CPPFLAGS` to override the built-in optimization and warning flags. Note that object files are built using a command similar to ``` $ $(CC) $(ALL_LUA53_CPPFLAGS) $(ALL_CPPFLAGS) ``` where the Lua-specific flags remain separate from more general flags. ### Installation Paths All the common GNU-style installation path variables are supported, including `prefix`, `bindir`, `libdir`, `datadir`, `includedir`, and `DESTDIR`. These additional path variables are also allowed: - `lua51path` - install path for Lua 5.1 modules, e.g. `$(prefix)/share/lua/5.1` - `lua51cpath` - install path for Lua 5.1 C modules, e.g. `$(prefix)/lib/lua/5.1` - `lua52path` - install path for Lua 5.2 modules, e.g. `$(prefix)/share/lua/5.2` - `lua52cpath` - install path for Lua 5.2 C modules, e.g. `$(prefix)/lib/lua/5.2` - `lua53path` - install path for Lua 5.3 modules, e.g. `$(prefix)/share/lua/5.3` - `lua53cpath` - install path for Lua 5.3 C modules, e.g. `$(prefix)/lib/lua/5.3` ### Caching Environment Invoking the `configure` target will cache the Make environment and reload the variable values on subsequent invocations. Variables can be modified on an individual basis after this. ## Build Targets `cqueues` targets the Lua 5.1 (LuaJIT), 5.2, and 5.3 API. For various reasons the build system is capable of building all three modules simultaneously in a single Make invocation. Therefore, there are many seemingly superfluous target names, either out of necessity or for convenience. ### Compile Targets #### liblua5.1-cqueues Build Lua 5.1 cqueues modules #### liblua5.2-cqueues Build Lua 5.2 cqueues modules #### liblua5.3-cqueues Build Lua 5.3 cqueues modules #### all5.1 Synonym for liblua5.1-cqueues #### all5.2 Synonym for liblua5.2-cqueues #### all5.3 Synonym for liblua5.3-cqueues #### all Invokes one or more of the above according to the definition of `LUA_APIS`. ### Install Targets #### liblua5.1-cqueues-install Install Lua 5.1 cqueues modules #### liblua5.2-cqueues-install Install Lua 5.2 cqueues modules #### liblua5.3-cqueues-install Install Lua 5.3 cqueues modules #### install5.1 Invokes liblua5.1-cqueues-install #### install5.2 Invokes liblua5.2-cqueues-install #### install5.3 Invokes liblua5.3-cqueues-install #### install Invokes one of more of the above install targets according to `LUA_APIS`. ### Uninstall Targets #### liblua5.1-cqueues-uninstall Uninstall Lua 5.1 cqueues modules #### liblua5.2-cqueues-uninstall Uninstall Lua 5.2 cqueues modules #### liblua5.3-cqueues-uninstall Uninstall Lua 5.3 cqueues modules #### uninstall5.1 Invokes liblua5.1-cqueues-uninstall #### uninstall5.2 Invokes liblua5.2-cqueues-uninstall #### uninstall5.3 Invokes liblua5.3-cqueues-uninstall #### uninstall Invokes one or more of the above uninstall targets according to `LUA_APIS`. ### Other Targets #### clean rm binary targets, object files, debugging symbols, etc #### clean~ clean + rm *~ #### debian Build debian packages liblua5.1-cqueues and liblua5.2-cqueues using the dpkg-buildpackage utility. The Make variables `DPKG_BUILDPACKAGE` and `DPKG_BUILDPACKAGE_OPTIONS` can be used to manipulate this process. cqueues-rel-20161214/config.h.guess000066400000000000000000000553531302435770500167720ustar00rootroot00000000000000/* ========================================================================== * config.h.guess - Preprocessor-based feature detection * -------------------------------------------------------------------------- * Copyright (c) 2015-2016 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef CONFIG_H_GUESS #define CONFIG_H_GUESS /* * A U T O G U E S S V E R S I O N * * Change AG_VENDOR if maintaining a fork. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define AG_VENDOR "william+autoguess@25thandClement.com" #define AG_VERSION 20161019L /* * C O M P I L E R V E N D O R / V E R S I O N D E T E C T I O N * * See http://sourceforge.net/p/predef/wiki/Compilers/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define AG_GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) #define AG_GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && AG_GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= AG_GNUC_2VER((M), (m), (p))) #define AG_MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p)) #define AG_MSC_PREREQ(M, m, p) (_MSC_VER_FULL > 0 && _MSC_VER_FULL >= AG_MSC_2VER((M), (m), (p))) #define AG_SUNPRO_PREREQ(M, m, p) (__SUNPRO_C > 0 && __SUNPRO_C >= 0x ## M ## m ## p) /* * C O M P I L E R / L A N G U A G E F E A T U R E D E T E C T I O N * * NOTE: The has_ and test_ macros are separate because if the test * expression uses the preprocessor "defined" operator the operand * identifier may be replaced before the expression is evaluated. Most tests * will only use arithmetic operations, but if this is not possible then the * test must be written inline, for example * * #if has_attribute(x) || (!HAVE_C___HAS_ATTRIBUTE && defined FOO) * #define HAVE___ATTRIBUTE___X * #endif * * NOTE: Solaris Studio 12.4 supports __has_attribute, but we must enclose * it in parentheses because the expansion results in a token sequence that * chokes the compiler: __has_attribute(nonnull) becomes * __has_attribute__ (nonnull), with a literal space between the preprocessor * identifier and the open parenthesis. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if defined __has_attribute #define ag_has_attribute(a) __has_attribute(a) #define ag_test_attribute(a, E) (ag_has_attribute(a)) #else #define ag_has_attribute(a) 0 #define ag_test_attribute(a, E) (E) #endif #if defined __has_extension #define ag_has_extension(x) __has_extension(x) #define ag_test_extension(x, E) (ag_has_extension(x)) #else #define ag_has_extension(x) 0 #define ag_test_extension(x, E) (E) #endif #if defined __has_include #define ag_has_include(p) __has_include(p) #define ag_test_include(p, E) (ag_has_include(p)) #else #define ag_has_include(p) 0 #define ag_test_include(p, E) (E) #endif #if defined __has_builtin #define ag_has_builtin(f) __has_builtin(f) #define ag_test_builtin(f, E) (ag_has_builtin(f)) #else #define ag_has_builtin(f) 0 #define ag_test_builtin(f, E) (E) #endif #ifndef HAVE_C___ATTRIBUTE__ #define HAVE_C___ATTRIBUTE__ (__GNUC__ || AG_SUNPRO_PREREQ(5,9,0)) #endif #ifndef HAVE_C___ATTRIBUTE___CONSTRUCTOR #define HAVE_C___ATTRIBUTE___CONSTRUCTOR ag_test_attribute(constructor, __GNUC__) #endif #ifndef HAVE_C___ATTRIBUTE___NONNULL #define HAVE_C___ATTRIBUTE___NONNULL ag_test_attribute(nonnull, AG_GNUC_PREREQ(3,3,1)) #endif #ifndef HAVE_C___ATTRIBUTE___UNUSED #define HAVE_C___ATTRIBUTE___UNUSED ag_test_attribute(unused, __GNUC__) #endif #ifndef HAVE_C___ATTRIBUTE___USED #define HAVE_C___ATTRIBUTE___USED ag_test_attribute(used, __GNUC__) #endif #ifndef HAVE_C___ATTRIBUTE___VISIBILITY #define HAVE_C___ATTRIBUTE___VISIBILITY ag_test_attribute(visibility, __GNUC__) #endif #ifndef HAVE_C___HAS_EXTENSION #define HAVE_C___HAS_EXTENSION (defined __has_extension) #endif #ifndef HAVE_C___HAS_INCLUDE #define HAVE_C___HAS_INCLUDE (defined __has_include) #endif #ifndef HAVE_C___EXTENSION__ #define HAVE_C___EXTENSION__ (__GNUC__) #endif #ifndef HAVE_C___TYPEOF #define HAVE_C___TYPEOF (_MSC_VER || __GNUC__ || AG_SUNPRO_PREREQ(5,9,0)) #endif #ifndef HAVE_C___TYPEOF__ #define HAVE_C___TYPEOF__ (__GNUC__ || __xlc__ || AG_SUNPRO_PREREQ(5,9,0)) #endif #ifndef HAVE_C__GENERIC #define HAVE_C__GENERIC ag_test_extension(c_generic_selections, (AG_GNUC_PREREQ(4,9,0) || __STDC_VERSION__ >= 201112L)) #endif #ifndef HAVE_C_STATEMENT_EXPRESSION #define HAVE_C_STATEMENT_EXPRESSION (__GNUC__ || AG_SUNPRO_PREREQ(5,9,0)) #endif #ifndef HAVE_C_TYPEOF #define HAVE_C_TYPEOF (__GNUC__ || __xlc__ || AG_SUNPRO_PREREQ(5,9,0)) #endif #ifndef HAVE___ATOMIC_FETCH_ADD #define HAVE___ATOMIC_FETCH_ADD (defined __ATOMIC_RELAXED) #endif #ifndef HAVE___ATOMIC_FETCH_SUB #define HAVE___ATOMIC_FETCH_SUB HAVE___ATOMIC_FETCH_ADD #endif #ifndef HAVE___BUILTIN_CHOOSE_EXPR #define HAVE___BUILTIN_CHOOSE_EXPR (AG_GNUC_PREREQ(3,1,1) || __clang__) #endif #ifndef HAVE___BUILTIN_EXPECT #define HAVE___BUILTIN_EXPECT ag_test_builtin(__builtin_expect, __GNUC__) #endif #ifndef HAVE___BUILTIN_NAN #define HAVE___BUILTIN_NAN ag_test_builtin(__builtin_nan, AG_GNUC_PREREQ(3,3,1)) #endif #ifndef HAVE___BUILTIN_TRAP #define HAVE___BUILTIN_TRAP ag_test_builtin(__builtin_trap, AG_GNUC_PREREQ(3,3,1)) #endif #ifndef HAVE___BUILTIN_TYPES_COMPATIBLE_P #define HAVE___BUILTIN_TYPES_COMPATIBLE_P (AG_GNUC_PREREQ(3,1,1) || __clang__) #endif #ifndef HAVE___BUILTIN_UNREACHABLE #define HAVE___BUILTIN_UNREACHABLE ag_test_builtin(__builtin_unreachable, AG_GNUC_PREREQ(4,5,0)) #endif #ifndef HAVE__STATIC_ASSERT #define HAVE__STATIC_ASSERT ag_test_extension(c_static_assert, (AG_GNUC_PREREQ(4,6,0) || __C11FEATURES__ || __STDC_VERSION__ >= 201112L)) #endif /* * S Y S T E M E X T E N S I O N S * * We must set these before including any headers for feature detection. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if AG_USE_SYSTEM_EXTENSIONS /* Solaris */ #ifndef __EXTENSIONS__ #define __EXTENSIONS__ 1 #endif /* AIX */ #ifndef _ALL_SOURCE #define _ALL_SOURCE 1 #endif #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #ifndef _MINIX #define _MINIX 1 #endif /* Solaris */ #ifndef _POSIX_PTHREAD_SEMANTICS #define _POSIX_PTHREAD_SEMANTICS 1 #endif #endif /* AG_USE_SYSTEM_EXTENSIONS */ #if AG_SYS_LARGEFILE /* NOTE: BSDs and musl-libc always provide a 64-bit file API */ /* Apple */ #ifndef _DARWIN_USE_64_BIT_INODE #define _DARWIN_USE_64_BIT_INODE 1 #endif /* Solaris and glibc (per Large File Summit recommendation) */ #ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 #endif /* AIX */ #ifndef _LARGE_FILES #define _LARGE_FILES 1 #endif #endif /* AG_SYS_LARGEFILE */ /* * S Y S T E M D E T E C T I O N (S T A G E 0) * * Define HAVE_FOO macros as arithmetic truth values for any predefined * system macros which have truth values solely based on whether they're * defined. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* NOTE: None so far. See stage 3 below. */ /* * S Y S T E M D E T E C T I O N (S T A G E 1) * * Include any headers necessary for minimal libc feature checking, defining * any prerequisite feature macros. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * NOTE: will indirectly include , , * , , , and similar * system headers which define most of what we care about. Among the typical * feature macros, we also get _DTRACE_VERSION. */ #include #ifndef AG_MUSL_MAYBE #define AG_MUSL_MAYBE (__linux__ && !__GLIBC__ && !__BIONIC__) #endif #ifndef HAVE_SYS_PARAM_H #define HAVE_SYS_PARAM_H ag_test_include(, !AG_MUSL_MAYBE) #endif /* * NOTE: Conditionally load so we don't unnecessarily pollute * the namespace. */ #if HAVE_SYS_PARAM_H && !__linux__ && !__sun && !_AIX #include /* __FreeBSD_version __NetBSD_Prereq__ BSD OpenBSD */ #endif #include /* F_DUPFD_CLOEXEC */ /* * S Y S T E M D E T E C T I O N (S T A G E 2) * * Macros which determine libc vendor and version. * * See http://sourceforge.net/p/predef/wiki/Libraries/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define AG_AIX_PREREQ(M, m) (_AIX # M # m) #if defined __GLIBC_PREREQ && !defined __UCLIBC__ #define AG_GLIBC_PREREQ(M, m) (__GLIBC_PREREQ(M, m)) #else #define AG_GLIBC_PREREQ(M, m) 0 #endif #define AG_FREEBSD_2VER(M, m, p) (((M) * 100000) + ((m) * 1000) + (p)) #define AG_FREEBSD_PREREQ(M, m, p) (__FreeBSD__ > 0 && __FreeBSD_version >= AG_FREEBSD_2VER((M), (m), (p))) #define AG_IPHONE_2VER(M, m) (((M) * 10000) + ((m) * 100)) #if defined __IPHONE_OS_VERSION_MIN_REQUIRED #define AG_IPHONE_PREREQ(M, m) (AG_IPHONE_2VER((M), (m)) <= __IPHONE_OS_VERSION_MIN_REQUIRED) #else #define AG_IPHONE_PREREQ(M, m) 0 #endif #if defined __NetBSD_Prereq__ #define AG_NETBSD_PREREQ(M, m, p) (!__minix && __NetBSD_Prereq__(M, m, p)) #else #define AG_NETBSD_PREREQ(M, m, p) 0 #endif #define AG_MACOS_2VER_10_9(M, m, p) (((M) * 100) + ((m) * 10)) #define AG_MACOS_2VER_10_10(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) #define AG_MACOS_PREREQ_10_10(M, m, p) (((M) > 10 || ((M) == 10 && (m) >= 10)) && AG_MACOS_2VER_10_10((M), (m), (p)) <= __MAC_OS_X_VERSION_MIN_REQUIRED) #define AG_MACOS_PREREQ_10_9(M, m, p) (((M) == 10 && (m) < 10) && AG_MACOS_2VER_10_9((M), (m), (p)) <= __MAC_OS_X_VERSION_MIN_REQUIRED) #if defined __MAC_OS_X_VERSION_MIN_REQUIRED #define AG_MACOS_PREREQ(M, m, p) (AG_MACOS_PREREQ_10_10((M), (m), (p)) || AG_MACOS_PREREQ_10_9((M), (m), (p))) #else #define AG_MACOS_PREREQ(M, m, p) 0 #endif #define AG_OPENBSD_PREREQ_0_0 (__OpenBSD__) #define AG_OPENBSD_PREREQ_5_5 (OpenBSD >= 201405) #define AG_OPENBSD_PREREQ_5_7 (OpenBSD >= 201505) #define AG_OPENBSD_PREREQ(M, m) (AG_OPENBSD_PREREQ_ ## M ## _ ## m) #define AG_SUNOS_PREREQ_5_10 (__sun && _DTRACE_VERSION) #define AG_SUNOS_PREREQ_5_11 (__sun && F_DUPFD_CLOEXEC) #define AG_SUNOS_PREREQ(M, m) (AG_SUNOS_PREREQ_ ## M ## _ ## m) #define AG_UCLIBC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) #if defined __UCLIBC__ #define AG_UCLIBC_PREREQ(M, m, p) (AG_UCLIBC_2VER(__UCLIBC_MAJOR__, __UCLIBC_MINOR__, __UCLIBC_SUBLEVEL__) >= AG_UCLIBC_2VER((M), (m), (p))) #else #define AG_UCLIBC_PREREQ(M, m, p) 0 #endif /* * S Y S T E M D E T E C T I O N (S T A G E 3) * * Define HAVE_FOO macros as arithmetic truth values for any system macros * which have a truth value solely based on whether they're defined. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE___EXTENSIONS__ #ifdef __EXTENSIONS__ #define HAVE___EXTENSIONS__ 1 #endif #endif #ifndef HAVE__ALL_SOURCE #ifdef _ALL_SOURCE #define HAVE__ALL_SOURCE 1 #endif #endif #ifndef HAVE__GNU_SOURCE #ifdef _GNU_SOURCE #define HAVE__GNU_SOURCE 1 #endif #endif #ifndef HAVE__MINIX #if defined _MINIX || (defined __minix && defined _NETBSD_SOURCE) #define HAVE__MINIX 1 #endif #endif #ifndef HAVE__POSIX_PTHREAD_SEMANTICS #ifdef _POSIX_PTHREAD_SEMANTICS #define HAVE__POSIX_PTHREAD_SEMANTICS 1 #endif #endif #ifndef HAVE__REENTRANT #ifdef _REENTRANT #define HAVE__REENTRANT 1 #endif #endif /* * H E A D E R D E T E C T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE_DLFCN_H #define HAVE_DLFCN_H ag_test_include(, 1) #endif #ifndef HAVE_IFADDRS_H #define HAVE_IFADDRS_H_ (!_AIX && (!__sun || AG_SUNOS_PREREQ(5,11))) #define HAVE_IFADDRS_H ag_test_include(, HAVE_IFADDRS_H_) #endif #ifndef HAVE_INTTYPES_H #define HAVE_INTTYPES_H 1 #endif #ifndef HAVE_MACH_CLOCK_H #define HAVE_MACH_CLOCK_H ag_test_include(, __APPLE__) #endif #ifndef HAVE_MACH_MACH_H #define HAVE_MACH_MACH_H ag_test_include(, __APPLE__) #endif #ifndef HAVE_MACH_MACH_TIME_H #define HAVE_MACH_MACH_TIME_H ag_test_include(, __APPLE__) #endif #ifndef HAVE_MEMORY_H #define HAVE_MEMORY_H 1 #endif #ifndef HAVE_PORT_H #define HAVE_PORT_H ag_test_include(, AG_SUNOS_PREREQ(5,10)) #endif /* TODO: Maybe test _POSIX_THREADS from . */ #ifndef HAVE_PTHREAD_H #define HAVE_PTHREAD_H ag_test_include(, !__minix) #endif #ifndef HAVE_STDINT_H #define HAVE_STDINT_H 1 #endif #ifndef HAVE_STDLIB_H #define HAVE_STDLIB_H 1 #endif #ifndef HAVE_STRING_H #define HAVE_STRING_H 1 #endif #ifndef HAVE_STRINGS_H #define HAVE_STRINGS_H 1 #endif #ifndef HAVE_SYS_AUXV_H #define HAVE_SYS_AUXV_H_ (AG_GLIBC_PREREQ(2,16) || (!AG_GLIBC_PREREQ(0,0) && __linux__) || __sun) #define HAVE_SYS_AUXV_H ag_test_include(, HAVE_SYS_AUXV_H_) #endif #ifndef HAVE_SYS_EPOLL_H #define HAVE_SYS_EPOLL_H ag_test_include(, __linux__) #endif #ifndef HAVE_SYS_EVENT_H #define HAVE_SYS_EVENT_H ag_test_include(, BSD) #endif #ifndef HAVE_SYS_EVENTFD_H #define HAVE_SYS_EVENTFD_H_ (AG_GLIBC_PREREQ(2,8) || (!AG_GLIBC_PREREQ(0,0) && __linux__) || defined EFD_CLOEXEC) #define HAVE_SYS_EVENTFD_H ag_test_include(, HAVE_SYS_EVENTFD_H_) #endif #ifndef HAVE_SYS_INOTIFY_H #define HAVE_SYS_INOTIFY_H ag_test_include(, __linux__) #endif #ifndef HAVE_SYS_SIGNALFD_H #define HAVE_SYS_SIGNALFD_H_ (AG_GLIBC_PREREQ(2,8) || (!AG_GLIBC_PREREQ(0,0) && __linux__) || defined SFD_CLOEXEC) #define HAVE_SYS_SIGNALFD_H ag_test_include(, HAVE_SYS_SIGNALFD_H_) #endif #ifndef HAVE_SYS_SOCKIO_H #define HAVE_SYS_SOCKIO_H ag_test_include(, (__sun || BSD)) #endif #ifndef HAVE_SYS_STAT_H #define HAVE_SYS_STAT_H 1 #endif #ifndef HAVE_SYS_SYSCTL_H #define HAVE_SYS_SYSCTL_H ag_test_include(, (BSD || __GLIBC__)) #endif #ifndef HAVE_SYS_TIMERFD_H #define HAVE_SYS_TIMERFD_H_ (AG_GLIBC_PREREQ(2,8) || (!AG_GLIBC_PREREQ(0,0) && __linux__) || defined TFD_CLOEXEC) #define HAVE_SYS_TIMERFD_H ag_test_include(, HAVE_SYS_TIMERFD_H_) #endif #ifndef HAVE_SYS_TYPES_H #define HAVE_SYS_TYPES_H 1 #endif #ifndef HAVE_UNISTD_H #define HAVE_UNISTD_H 1 #endif /* * T Y P E D E T E C T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE_CLOCKID_T #define HAVE_CLOCKID_T (defined CLOCK_MONOTONIC) #endif #ifndef HAVE_STRUCT_SOCKADDR_SA_LEN #define HAVE_STRUCT_SOCKADDR_SA_LEN (!__linux__ && !__sun) #endif #ifndef HAVE_STRUCT_STAT_ST_ATIM #define HAVE_STRUCT_STAT_ST_ATIM (defined st_atime && ((!__APPLE__ && (!__NetBSD__ || AG_NETBSD_PREREQ(7,0,0))) || !HAVE_STRUCT_STAT_ST_ATIMESPEC)) #endif #ifndef HAVE_STRUCT_STAT_ST_CTIM #define HAVE_STRUCT_STAT_ST_CTIM HAVE_STRUCT_STAT_ST_ATIM #endif #ifndef HAVE_STRUCT_STAT_ST_MTIM #define HAVE_STRUCT_STAT_ST_MTIM HAVE_STRUCT_STAT_ST_ATIM #endif #ifndef HAVE_STRUCT_STAT_ST_ATIMESPEC #define HAVE_STRUCT_STAT_ST_ATIMESPEC (__APPLE__ || defined st_atimespec || defined st_atimensec) #endif #ifndef HAVE_STRUCT_STAT_ST_CTIMESPEC #define HAVE_STRUCT_STAT_ST_CTIMESPEC HAVE_STRUCT_STAT_ST_ATIMESPEC #endif #ifndef HAVE_STRUCT_STAT_ST_MTIMESPEC #define HAVE_STRUCT_STAT_ST_MTIMESPEC HAVE_STRUCT_STAT_ST_ATIMESPEC #endif #ifndef HAVE_STRUCT_STAT_ST_BLOCKS #define HAVE_STRUCT_STAT_ST_BLOCKS 1 #endif #ifndef HAVE_STRUCT_STAT_ST_BLKSIZE #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 #endif #ifndef HAVE_STRUCT_STAT_ST_RDEV #define HAVE_STRUCT_STAT_ST_RDEV 1 #endif /* * D E C L A R A T I O N D E T E C T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE___DECL_LIBC_ENABLE_SECURE #define HAVE___DECL_LIBC_ENABLE_SECURE 0 #endif #ifndef HAVE_DECL_CLOCK_GETTIME #define HAVE_DECL_CLOCK_GETTIME HAVE_DECL_CLOCK_MONOTONIC #endif #ifndef HAVE_DECL_CLOCK_MONOTONIC #define HAVE_DECL_CLOCK_MONOTONIC (defined CLOCK_MONOTONIC) #endif #ifndef HAVE_DECL_CLOCK_REALTIME #define HAVE_DECL_CLOCK_REALTIME (defined CLOCK_REALTIME) #endif #ifndef HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME #define HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME (__linux__ && HAVE__GNU_SOURCE) #endif #ifndef HAVE_DECL_PTHREAD_MUTEX_ROBUST #define HAVE_DECL_PTHREAD_MUTEX_ROBUST (defined PTHREAD_MUTEX_ROBUST || AG_GLIBC_PREREQ(2,12)) #endif #ifndef HAVE_DECL_STRERROR_R #define HAVE_DECL_STRERROR_R 1 #endif #ifndef HAVE_DECL_SYS_SIGLIST #define HAVE_DECL_SYS_SIGLIST (!AG_MUSL_MAYBE && !__sun && !_AIX) #endif /* * V A R I A B L E D E T E C T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE___LIBC_ENABLE_SECURE #define HAVE___LIBC_ENABLE_SECURE AG_GLIBC_PREREQ(2,1) /* added to glibc between 2.0.98 and 2.0.99 */ #endif #ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME #define HAVE_PROGRAM_INVOCATION_SHORT_NAME (__linux__) #endif #ifndef HAVE_SYS_SIGLIST #define HAVE_SYS_SIGLIST HAVE_DECL_SYS_SIGLIST #endif /* * F U N C T I O N D E T E C T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE_ACCEPT4 #define HAVE_ACCEPT4 (defined SOCK_CLOEXEC && !__NetBSD__) #endif #ifndef HAVE_ARC4RANDOM #define HAVE_ARC4RANDOM \ (__APPLE__ || __DragonFly__ || __FreeBSD__ || __NetBSD__ || \ __OpenBSD__ || __minix) #endif #ifndef HAVE_ARC4RANDOM_ADDRANDOM #define HAVE_ARC4RANDOM_ADDRANDOM (HAVE_ARC4RANDOM && !AG_OPENBSD_PREREQ(5,5)) #endif #ifndef HAVE_ARC4RANDOM_STIR #define HAVE_ARC4RANDOM_STIR HAVE_ARC4RANDOM_ADDRANDOM #endif #ifndef HAVE_CLOCK_GETTIME #define HAVE_CLOCK_GETTIME (!__APPLE__ || AG_MACOS_PREREQ(10,12,0)) #endif #ifndef HAVE_DLADDR #define HAVE_DLADDR (HAVE_DLOPEN && !_AIX && ((!__GLIBC__ && !AG_MUSL_MAYBE) || HAVE__GNU_SOURCE)) #endif #ifndef HAVE_DLOPEN #define HAVE_DLOPEN HAVE_DLFCN_H #endif #ifndef HAVE_DLSYM #define HAVE_DLSYM HAVE_DLOPEN #endif #ifndef HAVE_DUP2 #define HAVE_DUP2 1 #endif #ifndef HAVE_DUP3 #define HAVE_DUP3 (AG_GLIBC_PREREQ(2,9) || AG_FREEBSD_PREREQ(10,0,0) || AG_NETBSD_PREREQ(6,0,0) || AG_UCLIBC_PREREQ(0,9,34) || AG_MUSL_MAYBE || __BIONIC__ || AG_OPENBSD_PREREQ(5,7)) #endif #ifndef HAVE_FDOPENDIR #define HAVE_FDOPENDIR ( \ (!__APPLE__ || AG_MACOS_PREREQ(10,10,0) || AG_IPHONE_PREREQ(8,0)) \ && (!__NetBSD__ || AG_NETBSD_PREREQ(6,0,0)) \ ) #endif #ifndef HAVE_EPOLL_CREATE #define HAVE_EPOLL_CREATE HAVE_SYS_EPOLL_H #endif #if HAVE_SYS_EPOLL_H #include #endif #ifndef HAVE_EPOLL_CREATE1 #define HAVE_EPOLL_CREATE1 (HAVE_EPOLL_CREATE && (defined EPOLL_CLOEXEC || AG_GLIBC_PREREQ(2,9))) #endif #ifndef HAVE_EPOLL_CTL #define HAVE_EPOLL_CTL HAVE_EPOLL_CREATE #endif #ifndef HAVE_EPOLL_PWAIT #define HAVE_EPOLL_PWAIT (HAVE_EPOLL_WAIT && (AG_GLIBC_PREREQ(2,6) || (!AG_GLIBC_PREREQ(0,0) && defined EPOLL_CLOEXEC))) #endif #ifndef HAVE_EPOLL_WAIT #define HAVE_EPOLL_WAIT HAVE_EPOLL_CREATE #endif #ifndef HAVE_EVENTFD #define HAVE_EVENTFD HAVE_SYS_EVENTFD_H #endif #ifndef HAVE_GETAUXVAL #define HAVE_GETAUXVAL (HAVE_SYS_AUXV_H && !__sun) #endif #ifndef HAVE_GETENV_R #define HAVE_GETENV_R (AG_NETBSD_PREREQ(4,0,0) || __minix) #endif #ifndef HAVE_GETEXECNAME #define HAVE_GETEXECNAME (__sun) #endif #ifndef HAVE_GETIFADDRS #define HAVE_GETIFADDRS (HAVE_IFADDRS_H && !__sun) #endif #ifndef HAVE_GETPROGNAME #define HAVE_GETPROGNAME (HAVE_ARC4RANDOM || AG_SUNOS_PREREQ(5,11)) #endif #ifndef HAVE_INOTIFY_INIT #define HAVE_INOTIFY_INIT HAVE_SYS_INOTIFY_H #endif #ifndef HAVE_INOTIFY_INIT1 #define HAVE_INOTIFY_INIT1 (HAVE_INOTIFY_INIT && defined IN_CLOEXEC) #endif #ifndef HAVE_ISSETUGID #define HAVE_ISSETUGID ((!__linux__ || (AG_MUSL_MAYBE && HAVE__GNU_SOURCE)) && !_AIX) #endif #if HAVE_SYS_EVENT_H #include #endif #ifndef HAVE_KEVENT #define HAVE_KEVENT (defined EV_SET) #endif #ifndef HAVE_KQUEUE #define HAVE_KQUEUE HAVE_KEVENT #endif #ifndef HAVE_KQUEUE1 #define HAVE_KQUEUE1 (HAVE_KQUEUE && AG_NETBSD_PREREQ(6,0,0)) #endif #ifndef HAVE_OPENAT #define HAVE_OPENAT \ ((!__APPLE__ || AG_MACOS_PREREQ(10,10,0) || AG_IPHONE_PREREQ(8,0)) \ && (!__NetBSD__ || AG_NETBSD_PREREQ(7,0,0))) #endif #ifndef HAVE_PACCEPT #define HAVE_PACCEPT AG_NETBSD_PREREQ(6,0,0) #endif #ifndef HAVE_PIPE2 #define HAVE_PIPE2 (AG_GLIBC_PREREQ(2,9) || AG_FREEBSD_PREREQ(10,0,0) || AG_NETBSD_PREREQ(6,0,0) || AG_UCLIBC_PREREQ(0,9,32) || AG_MUSL_MAYBE || __BIONIC__ || AG_OPENBSD_PREREQ(5,7)) #endif #ifndef HAVE_PORT_ALERT #define HAVE_PORT_ALERT HAVE_PORT_CREATE #endif #ifndef HAVE_PORT_ASSOCIATE #define HAVE_PORT_ASSOCIATE HAVE_PORT_CREATE #endif #ifndef HAVE_PORT_CREATE #define HAVE_PORT_CREATE HAVE_PORT_H #endif #ifndef HAVE_PORT_DISSOCIATE #define HAVE_PORT_DISSOCIATE HAVE_PORT_CREATE #endif #ifndef HAVE_PORT_GET #define HAVE_PORT_GET HAVE_PORT_CREATE #endif #ifndef HAVE_PORT_GETN #define HAVE_PORT_GETN HAVE_PORT_CREATE #endif #ifndef HAVE_PORT_SEND #define HAVE_PORT_SEND HAVE_PORT_CREATE #endif #ifndef HAVE_PORT_SENDN #define HAVE_PORT_SENDN HAVE_PORT_CREATE #endif #ifndef HAVE_POSIX_FADVISE #define HAVE_POSIX_FADVISE (defined POSIX_FADV_NORMAL || AG_GLIBC_PREREQ(2,2) || __sun || AG_MUSL_MAYBE || AG_FREEBSD_PREREQ(9,0,0)) #endif #ifndef HAVE_POSIX_FALLOCATE #define HAVE_POSIX_FALLOCATE (_AIX || AG_FREEBSD_PREREQ(9,0,0) || AG_GLIBC_PREREQ(2,2) || AG_MUSL_MAYBE || AG_NETBSD_PREREQ(7,0,0) || __sun) #endif #ifndef HAVE_SIGNALFD #define HAVE_SIGNALFD HAVE_SYS_SIGNALFD_H #endif #ifndef HAVE_SIGTIMEDWAIT #define HAVE_SIGTIMEDWAIT (!__APPLE__ && !__OpenBSD__) #endif #ifndef HAVE_SIGWAIT #define HAVE_SIGWAIT (!__minix) #endif #ifndef HAVE_STATIC_ASSERT #if AG_GLIBC_PREREQ(0,0) && !HAVE__STATIC_ASSERT #define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */ #else #define HAVE_STATIC_ASSERT (defined static_assert) #endif #endif #ifndef HAVE_STRERROR_R #define HAVE_STRERROR_R 1 #endif #ifndef HAVE_SYSCTL #define HAVE_SYSCTL HAVE_SYS_SYSCTL_H #endif #ifndef HAVE_TIMERFD_CREATE #define HAVE_TIMERFD_CREATE HAVE_SYS_TIMERFD_H #endif #ifndef HAVE_TIMERFD_GETTIME #define HAVE_TIMERFD_GETTIME HAVE_TIMERFD_CREATE #endif #ifndef HAVE_TIMERFD_SETTIME #define HAVE_TIMERFD_SETTIME HAVE_TIMERFD_CREATE #endif #ifndef STRERROR_R_CHAR_P #define STRERROR_R_CHAR_P ((AG_GLIBC_PREREQ(0,0) || AG_UCLIBC_PREREQ(0,0,0)) && (HAVE__GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600))) #endif #endif /* CONFIG_H_GUESS */ cqueues-rel-20161214/debian/000077500000000000000000000000001302435770500154365ustar00rootroot00000000000000cqueues-rel-20161214/debian/changelog000066400000000000000000000331071302435770500173140ustar00rootroot00000000000000liblua-cqueues (20161214-0) unstable; urgency=low * Accept SSL instance in socket:starttls. (@daurnimator) * Support cqueue:close. (@daurnimator) * Correctly cancel any controller descriptors before destroying a controller. (@daurnimator) * Fix OpenSSL 1.1 support by correctly defaulting to client connect mode in socket:starttls when neither accept nor connect mode is explicitly specified. -- William Ahern Wed, 14 Dec 2016 16:06:26 -0800 liblua-cqueues (20161018-0) unstable; urgency=low * Support OpenSSL 1.1 API. (@daurnimator) * Support clock_gettime with macOS Sierra / XCode 8.0. (Reported by @ziazon) * Support accept4 and paccept for atomic O_CLOEXEC. (@daurnimator) * Remove assert which aborted process when attempting to resume a dead coroutine as it doesn't necessarily signal process corruption. (@daurnimator) * Enable IPv6 AAAA queries in socket.connect. * Support passing Lua 5.3 integers as arguments to thread.start. (Reported by and preliminary patch from @daurnimator) * Basic SOCK_SEQPACKET support. (@daurnimator) -- William Ahern Wed, 19 Oct 2016 01:38:03 -0700 liblua-cqueues (20160812-0) unstable; urgency=low * Support older Linux environments lacking signalfd by falling back to sigtimedwait as we do for Solaris. * On BSDs set FD_CLOEXEC on the signal.c kqueue descriptor. -- William Ahern Fri, 12 Aug 2016 13:20:05 -0700 liblua-cqueues (20160808-0) unstable; urgency=low * Use userdata values (userdata environments in Lua 5.1) instead of relying on ephemeron tables. * Update dns.c with various bug fixes. * Immediately put resolvers back into resolver pool. (@torhve) -- William Ahern Mon, 08 Aug 2016 20:22:04 -0700 liblua-cqueues (20160318-0) unstable; urgency=low * Refactor build framework to make it easier to avoid automatic Lua dependency checks. But also improve automatic Lua detection by automatically selecting the Lua APIs to build against. * Move most system feature detection to config.h from various source files. -- William Ahern Fri, 18 Mar 2016 20:17:46 -0700 liblua-cqueues (20160316-0) unstable; urgency=low * Allow yielding through cqueues:step. (daurnimator) * Use SSLv23_server_method instead of SSLv3_server_method when guessing the TLS handshake mode--server or client--as many OpenSSL forks have deprecated or removed SSLv3-specific APIs. (daurnimator) * Support SO_BROADCAST. (daurnimator) * Fix v6only flag. (daurnimator) -- William Ahern Wed, 16 Mar 2016 18:34:51 -0700 liblua-cqueues (20150907-0) unstable; urgency=low * Fix bugs with getsockname and getpeername--AF_UNIX sockaddr length can vary considerably based on system call and operating system. Report and fixes from daurnimator. See PORTING.md for extended description of varying behavior. * Fix to socket.fdopen for regular files. Report and fix from daurnimator. * Allow non-string error objects to be returned from cqueues:step, et al. Fix from daurnimator. * Set controller alert on cancellation to ensure any controller polling on that controller wakes up. Report by daurnimator. * Enhance accept to take an options argument. Fix from daurnimator. * Fix wrong order of parameters to fcntl in socket.dup. * Set errno when returning from BIO hooks as OpenSSL expects unrecoverable, generic error values to be in errno. Report by daurnimator. * Fix stack offsets in config:getlookup and config:getsearch. Report and fixes from tibboh. -- William Ahern Mon, 07 Sep 2015 19:30:21 -0700 liblua-cqueues (20150727-0) unstable; urgency=low * Fix bugs in cqueues.cancel. Report and fixes from daurnimator. -- William Ahern Mon, 27 Jul 2015 01:38:54 -0700 liblua-cqueues (20150630-0) unstable; urgency=low * Replace remaining uses of strerror with thread-safe cqs_strerror macro. -- William Ahern Tue, 30 Jun 2015 14:03:15 -0700 liblua-cqueues (20150629-0) unstable; urgency=low * Add autoflush support, enabled by default, which automatically flushes the output buffer during reads and when initiating TLS. * Add pushback support, enabled by default, which pushes data in the input buffer back to the socket layer so it's read as part of the TLS handshake. * Ignore EOPNOTSUPP when trying to reset SO_REUSEPORT. -- William Ahern Tue, 30 Jun 2015 12:04:16 -0700 liblua-cqueues (20150616-0) unstable; urgency=low * Add support for POLLPRI. * Fix Solaris Ports controller alerts. * Add support for eventfd for controller alerts. * Synchronize dns.c with upstream. * Various bug fixes. -- William Ahern Tue, 16 Jun 2015 15:37:34 -0700 liblua-cqueues (20150310-0) unstable; urgency=low * :step, :loop, and :errors now return additional error information. -- William Ahern Tue, 10 Mar 2015 21:38:56 -0700 liblua-cqueues (20150218-0) unstable; urgency=low * Use newproxy to detect object leaks in Lua 5.1/LuaJIT because Lua 5.1 language doesn't support __gc metamethods on tables. (daurnimator) -- William Ahern Wed, 18 Feb 2015 20:45:12 -0800 liblua-cqueues (20150119-0) unstable; urgency=low * Return EAFNOSUPPORT for unknown .sa_bind address family (sa_len == 0) when copying the address. -- William Ahern Mon, 19 Jan 2015 15:33:43 -0800 liblua-cqueues (20150113-0) unstable; urgency=low * Optimize the way we put a controller in a ready state when signaling a condition variable. * Put a controller into a ready state when wrapping a function or attaching a coroutine. * Make compile with latest Lua 5.3 release candidate. -- William Ahern Tue, 13 Jan 2015 19:33:34 -0800 liblua-cqueues (20150112-0) unstable; urgency=low * Rename undocumented socket.fdopen to socket.dup, and add socket.fdopen which takes ownership of the specified descriptor number. * Use F_DUPFD_CLOEXEC in socket.dup if available. * Use SOCK_CLOEXEC with socket(2) and socketpair(2) if available. * Use MSG_CMSG_CLOEXEC with recvmsg(2) if available. * Wrap .sa_bind option of low-level socket library. -- William Ahern Mon, 12 Jan 2015 15:03:41 -0800 liblua-cqueues (20141021-0) unstable; urgency=low * Add IPV6_V6ONLY support. -- William Ahern Tue, 21 Oct 2014 15:45:51 -0700 liblua-cqueues (20141020-0) unstable; urgency=low * Allow :events methods to return event set as integer bitset. * Fix NULL-dereference when calling :connect method on a socket created using socket.pair or socket.fdopen. * Fix cqueues.poll when polling outside of a managed coroutine. -- William Ahern Mon, 20 Oct 2014 17:17:59 -0700 liblua-cqueues (20140930-0) unstable; urgency=low * Fix build dependency where M4 fails but the command line still creates the target. On subsequent invocations make thinks the target is satisfied. * Handle numeric hosts differently so that socket.listen doesn't try to build a resolver. On OS X when the network is not connected /etc/resolv.conf doesn't exist, so building a resolver results in an ENOENT error. This prevented listening on the loopback, 0.0.0.0, or ::1 when the network was disconnected. -- William Ahern Tue, 30 Sep 2014 17:56:31 -0700 liblua-cqueues (20140923-0) unstable; urgency=low * Add TLS Socket Name Indiciation (SNI) support. -- William Ahern Tue, 23 Sep 2014 16:08:59 -0700 liblua-cqueues (20140916-0) unstable; urgency=low * Fix out-of-bounds write when reading inotify events, reported by Timo Teräs. -- William Ahern Tue, 16 Sep 2014 15:18:16 -0700 liblua-cqueues (20140729-0) unstable; urgency=low * Fix compilation on NetBSD 5.1, which lacks constant NAN definition. * Make promise.new function optional. * Fix :recvfd to handle no descriptor in message. * Document promise module. -- William Ahern Tue, 29 Jul 2014 21:08:15 -0700 liblua-cqueues (20140707-0) unstable; urgency=low * Add ability to pass functions as arguments to thread start routine. -- William Ahern Mon, 07 Jul 2014 18:37:23 -0700 liblua-cqueues (20140702-0) unstable; urgency=low * Pass on any additional arguments to cqueues:wrap when first resuming, so user code doesn't have to create an expensive closure every time. * Add promise module. * Add auxlib module. * Move cqueues.assert, cqueues.resume, and cqueues.wrap to auxlib. * Add yieldable tostring to auxlib and use in promise __tostring metamethod. * Add assert3, assert4, which calls error with a greater stack level. * Add auxlib.fileresult to convert socket API return protocol to Lua's file API return protocol. -- William Ahern Wed, 02 Jul 2014 16:41:32 -0700 liblua-cqueues (20140627-0) unstable; urgency=low * Add maxerrs limit to catch unchecked error loops. * Add :setmaxerrs method to change the limit from default of 100. -- William Ahern Fri, 27 Jun 2014 15:08:41 -0700 liblua-cqueues (20140616-0) unstable; urgency=low * Add resolver pool factory. * Add dns.random binding. -- William Ahern Mon, 16 Jun 2014 18:05:27 -0700 liblua-cqueues (20140615-0) unstable; urgency=low * Add automagic string-to-integer conversion of type, class, and section parameters to packet:grep method. -- William Ahern Sun, 15 Jun 2014 22:03:01 -0700 liblua-cqueues (20140612-0) unstable; urgency=low * Don't abort on assert when caller improperly calls dns_res_check after already fetching answer with dns_res_fetch. -- William Ahern Thu, 12 Jun 2014 21:53:39 -0700 liblua-cqueues (20140607-0) unstable; urgency=low * Add SO_REUSEPORT support. -- William Ahern Sat, 07 Jun 2014 19:22:32 -0700 liblua-cqueues (20140527-0) unstable; urgency=low * Implement text mode semantics for block read operations. * Add socket:read format for consuming multipart MIME data efficiently. * Add socket.debug module with unit tests. -- William Ahern Tue, 27 May 2014 02:22:34 -0700 liblua-cqueues (20140508-0) unstable; urgency=low * Add timeout capability to buffered I/O. * Add fast metatable checking to sockets code. * Add .type routine to all modules. * Make all pollable objects consistently implement all three of :pollfd, :timeout, and :events. * Allow :pollfd to return a condition variable. -- William Ahern Wed, 08 May 2014 19:12:51 -0700 liblua-cqueues (20140419-0) unstable; urgency=low * Implement fast metatable checking in the controller code. -- William Ahern Sat, 19 Apr 2014 22:56:19 -0700 liblua-cqueues (20140331-0) unstable; urgency=low * Remove invariant check in condition variable __gc routine because the test was invalid when all objects are collected in a single cycle, as they are when a script terminates. * Add a chat server example. -- William Ahern Fri, 31 Mar 2014 22:23:19 -0700 liblua-cqueues (20140328-0) unstable; urgency=low * Add :peereid and :peerpid socket methods. * Fix endless loop bug when an AF_UNIX socket could not be bound. * Add socket:listen wrapper. -- William Ahern Fri, 28 Mar 2014 22:23:19 -0700 liblua-cqueues (20140322-0) unstable; urgency=low * Add condition variable API. * Allow passing more primitives types to thread.start. * Add Lua 5.3 support. -- William Ahern Sat, 22 Mar 2014 20:23:56 -0800 liblua-cqueues (20140130-0) unstable; urgency=low * Install OpenSSL mutexes when loading thread module. -- William Ahern Thu, 30 Jan 2014 16:22:56 -0800 liblua-cqueues (20131209-0) unstable; urgency=low * Fix slurp operation. * Split openssl module into its own repository. -- William Ahern Sun, 09 Dec 2013 19:21:57 -0800 liblua-cqueues (20131205-1) unstable; urgency=low * Obey SOCK_DGRAM sematics in input byte buffering code. * Implement slurp operation. * Add openssl.rand module. * Add :setbufsiz and :setmaxline. -- William Ahern Sun, 05 Dec 2013 17:30:58 -0800 liblua-cqueues (20131023-0) unstable; urgency=low * Fix bug which cleared O_NONBLOCK flag on thread descriptors. * Fix thread entry function loading for Lua 5.1/LuaJIT. -- William Ahern Wed, 23 Oct 2013 19:36:06 -0700 liblua-cqueues (20130909-0) unstable; urgency=low * Refactor build to be non-recursive. * Integrate luapath script from external fork. * Follow Debian naming conventions for multi-version Lua modules. * Separate cqueues and openssl modules into two independent packages. -- William Ahern Mon, 09 Sep 2013 14:46:25 -0700 lua-cqueues (20130129-0) unstable; urgency=low * Bind interface for acquiring DNS resolver statistics. -- William Ahern Tue, 29 Jan 2013 17:19:28 -0800 lua-cqueues (20130106-0) unstable; urgency=low * Initial debian build. -- William Ahern Mon, 07 Jan 2013 05:00:48 -0000 cqueues-rel-20161214/debian/compat000066400000000000000000000000021302435770500166340ustar00rootroot000000000000009 cqueues-rel-20161214/debian/control000066400000000000000000000010111302435770500170320ustar00rootroot00000000000000Source: liblua-cqueues Section: unknown Priority: extra Maintainer: William Ahern Build-Depends: debhelper (>= 9), m4, dh-lua, libssl-dev Standards-Version: 3.9.2 Package: liblua5.1-cqueues Architecture: any Depends: openssl, liblua5.1-0 Description: Event, socket, signal, thread, and file change notification Lua library. Package: liblua5.2-cqueues Architecture: any Depends: openssl, liblua5.2-0 Description: Event, socket, signal, thread, and file change notification Lua library. cqueues-rel-20161214/debian/copyright000066400000000000000000000022631302435770500173740ustar00rootroot00000000000000Author: William Ahern Download: http://25thandclement.com/~william/projects/cqueues.html Files: * Copyright: © 2012-2015, William Ahern License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cqueues-rel-20161214/debian/liblua5.1-cqueues.files000066400000000000000000000000671302435770500216310ustar00rootroot00000000000000usr/lib/lua/5.1/_cqueues.so usr/share/lua/5.1/cqueues* cqueues-rel-20161214/debian/liblua5.1-cqueues.install000066400000000000000000000001571302435770500221750ustar00rootroot00000000000000debian/tmp/usr/lib/lua/5.1/_cqueues.so usr/lib/lua/5.1 debian/tmp/usr/share/lua/5.1/cqueues* usr/share/lua/5.1 cqueues-rel-20161214/debian/liblua5.2-cqueues.files000066400000000000000000000000671302435770500216320ustar00rootroot00000000000000usr/lib/lua/5.2/_cqueues.so usr/share/lua/5.2/cqueues* cqueues-rel-20161214/debian/liblua5.2-cqueues.install000066400000000000000000000001571302435770500221760ustar00rootroot00000000000000debian/tmp/usr/lib/lua/5.2/_cqueues.so usr/lib/lua/5.2 debian/tmp/usr/share/lua/5.2/cqueues* usr/share/lua/5.2 cqueues-rel-20161214/debian/rules000077500000000000000000000007171302435770500165230ustar00rootroot00000000000000#!/usr/bin/make -f CFLAGS := -O3 -g -fstack-protector --param=ssp-buffer-size=4 DESTDIR=debian/tmp prefix=/usr build=liblua5.1-cqueues liblua5.2-cqueues install=$(addsuffix -install,$(build)) %: dh $@ override_dh_auto_configure: true override_dh_auto_build: make DESTDIR=$(DESTDIR) prefix=$(prefix) $(build) override_dh_auto_install: make DESTDIR=$(DESTDIR) prefix=$(prefix) $(install) override_dh_auto_clean: make clean override_dh_auto_test: true cqueues-rel-20161214/doc/000077500000000000000000000000001302435770500147615ustar00rootroot00000000000000cqueues-rel-20161214/doc/art/000077500000000000000000000000001302435770500155475ustar00rootroot00000000000000cqueues-rel-20161214/doc/art/lua.pdf000066400000000000000000000056361302435770500170350ustar00rootroot00000000000000%PDF-1.4 %Çì¢ 5 0 obj <> stream xœm’»Ž1 †{?…KhŒÄNÒ"¡mhN‡(Ð VBšbAˆ×Ç“Ä9,BSDŸÇ¿ïÏÈ$È×·Þã„7*>ý„iUüñ]:SÏU¤SiXÕÆÛj™nZyÃÙ‘4m‹ôÒÉd˃Gô³ã% ¹”>Ý®èDò°Dq!Qûß@ð šdF3ŠsbM¬”«éz›Ùf­`¥eêm¢1wJy ƒ#®»‡…ó…\U§rEönVò°ÌB—üEÕW+­H8)Ó}ç¶ôœŒØQë|3Õº|@3.Tu[F¾¸/u¢è„pŒe_ÇY¤¸«\«žûy;zöÆI®ç8ñíͳaNJ-+Þ.çá©]c+˜3û¤o'¼zÿëËëÛwxwƒGø„…OùÙý¿zèæCLø®ù=@·”É*Öb{|œ‹ÿ³Zµ´KÕ^„¬Üy]í¤®j£C%OP²ï1Õyµ¤Þ†[XZ›ï–/^ÑMÙËDQʆµ‰QN®2¡d>F¿Dk.øµË\¯ÔÑÙ Uú褫ç.-÷ÑI¬{”x¿†Rü8õþ;_²'ñþ3qùoRö½=ÂL«×…endstream endobj 6 0 obj 455 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 9 0 obj <> endobj 10 0 obj <> endobj 8 0 obj <> endobj 11 0 obj <>stream 2012-09-25T16:58:19-07:00 2012-09-25T16:58:19-07:00 lua@tecgraf.puc-rio.br Lua logo endstream endobj 2 0 obj <>endobj xref 0 12 0000000000 65535 f 0000000779 00000 n 0000002408 00000 n 0000000720 00000 n 0000000559 00000 n 0000000015 00000 n 0000000540 00000 n 0000000844 00000 n 0000000944 00000 n 0000000885 00000 n 0000000914 00000 n 0000001008 00000 n trailer << /Size 12 /Root 1 0 R /Info 2 0 R /ID [<3C3F4BFE5047608B16157C696F90DC81><3C3F4BFE5047608B16157C696F90DC81>] >> startxref 2581 %%EOF cqueues-rel-20161214/doc/art/lua.ps000066400000000000000000000112571302435770500167020ustar00rootroot00000000000000%!PS-Adobe-2.0 EPSF-2.0 %%Title: Lua logo %%Creator: lua@tecgraf.puc-rio.br %%CreationDate: Wed Nov 29 19:04:04 EDT 2000 %%BoundingBox: -45 0 1035 1080 %%Pages: 1 %%EndComments %%EndProlog %------------------------------------------------------------------------------ % % Graphic design by Alexandre Nakonechnyj. % PostScript programming by the Lua team. % This code is hereby placed in the public domain. % % Permission is hereby granted, without written agreement and without license % or royalty fees, to use, copy, and distribute this logo for any purpose, % including commercial applications, subject to the following conditions: % % * The origin of this logo must not be misrepresented; you must not % claim that you drew the original logo. We recommend that you give credit % to the graphics designer in all printed matter that includes the logo. % % * The only modification you can make is to adapt the orbiting text to % your product name. % % * The logo can be used in any scale as long as the relative proportions % of its elements are maintained. % %------------------------------------------------------------------------------ /LABEL () def %-- DO NOT CHANGE ANYTHING BELOW THIS LINE ------------------------------------ /PLANETCOLOR {0 0 0.5 setrgbcolor} bind def /HOLECOLOR {1.0 setgray} bind def /ORBITCOLOR {0.5 setgray} bind def /LOGOFONT {/Helvetica 0.90} def /LABELFONT {/Helvetica 0.36} def %------------------------------------------------------------------------------ /MOONCOLOR {PLANETCOLOR} bind def /LOGOCOLOR {HOLECOLOR} bind def /LABELCOLOR {ORBITCOLOR} bind def /LABELANGLE 125 def /LOGO (Lua) def /DASHANGLE 10 def /HALFDASHANGLE DASHANGLE 2 div def % moon radius. planet radius is 1. /r 1 2 sqrt 2 div sub def /D {0 360 arc fill} bind def /F {exch findfont exch scalefont setfont} bind def % place it nicely on the paper /RESOLUTION 1024 def RESOLUTION 2 div dup translate RESOLUTION 2 div 2 sqrt div dup scale %-------------------------------------------------------------------- planet -- PLANETCOLOR 0 0 1 D %---------------------------------------------------------------------- hole -- HOLECOLOR 1 2 r mul sub dup r D %---------------------------------------------------------------------- moon -- MOONCOLOR 1 1 r D %---------------------------------------------------------------------- logo -- LOGOCOLOR LOGOFONT F LOGO stringwidth pop 2 div neg -0.5 moveto LOGO show %------------------------------------------------------------------------------ % based on code from Blue Book Program 10, on pages 167--169 % available at ftp://ftp.adobe.com/pub/adobe/displaypostscript/bluebook.shar % str ptsize centerangle radius outsidecircletext -- /outsidecircletext { circtextdict begin /radius exch def /centerangle exch def /ptsize exch def /str exch def gsave str radius ptsize findhalfangle centerangle add rotate str { /charcode exch def ( ) dup 0 charcode put outsideplacechar } forall grestore end } def % string radius ptsize findhalfangle halfangle /findhalfangle { 4 div add exch stringwidth pop 2 div exch 2 mul 3.1415926535 mul div 360 mul } def /circtextdict 16 dict def circtextdict begin /outsideplacechar { /char exch def /halfangle char radius ptsize findhalfangle def gsave halfangle neg rotate radius 0 translate -90 rotate char stringwidth pop 2 div neg 0 moveto char show grestore halfangle 2 mul neg rotate } def end %--------------------------------------------------------------------- label -- LABELFONT F /LABELSIZE LABELFONT exch pop def /LABELRADIUS LABELSIZE 3 div 1 r add sub neg 1.02 mul def /HALFANGLE LABEL LABELRADIUS LABELSIZE findhalfangle HALFDASHANGLE div ceiling HALFDASHANGLE mul def /LABELANGLE 60 LABELANGLE HALFANGLE sub lt { HALFANGLE HALFANGLE DASHANGLE div floor DASHANGLE mul eq {LABELANGLE DASHANGLE div ceiling DASHANGLE mul} {LABELANGLE HALFDASHANGLE sub DASHANGLE div round DASHANGLE mul HALFDASHANGLE add} ifelse } {HALFANGLE 60 add} ifelse def LABELCOLOR LABEL LABELSIZE LABELANGLE LABELRADIUS outsidecircletext %--------------------------------------------------------------------- orbit -- ORBITCOLOR 0.03 setlinewidth [1 r add 3.1415926535 180 div HALFDASHANGLE mul mul] 0 setdash newpath 0 0 1 r add 3 copy 30 LABELANGLE HALFANGLE add arcn stroke 60 LABELANGLE HALFANGLE sub 2 copy lt {arc stroke} {4 {pop} repeat} ifelse %------------------------------------------------------------------ copyright -- /COPYRIGHT (Graphic design by A. Nakonechnyj. Copyright (c) 1998, All rights reserved.) def LABELCOLOR LOGOFONT 32 div F 2 sqrt 0.99 mul dup neg moveto COPYRIGHT 90 rotate %show %---------------------------------------------------------------------- done -- showpage %%Trailer %%EOF cqueues-rel-20161214/doc/cqueues.pdf000066400000000000000000011630071302435770500171360ustar00rootroot00000000000000%PDF-1.5 %ÐÔÅØ 2 0 obj << /Type /ObjStm /N 100 /First 798 /Length 1001 /Filter /FlateDecode >> stream xÚ…UMSÛH½ëWô1lmzf4®TªØ@(oÅ@Ø=líA˜IðFHZY Å¿ÏkYä…ñÁ¥vÏ{Ý=ïµl¦œ ÒL–Œ¢@Nçä±&æ@lᄳĞ88RLJ…L)RaAÊyR–tŽ0F%“.ðФ½dÈäÈ82IOÆ ÃdœÍ6èg *ÐÅX*1 «¨ÀpM6Ç7C–qîÈ"Sx²=˜ `‡ÁmAu­%'0ÜÇ{BÉœ&fÎÝ9ò!Ç9Jcx4D`\öþ=\ÐÁIsÙÐÁ½[Çe¿jê_öy>|ÈÞ}lê>ÖýzïÈåmÙö±Ûbë›X/WñðXvŸ7ð³6ve¿ª¿ÑÅúw¯q†ë´ ‘«e׬›¯=ý¹ªošûd7õDú¼ºîÊîõÉ^vQ›.Ÿ‡ò÷ù寄'ûjè|õ|½úââóN¸~‚·ýmË›äUôÆ»vUÅ.‰5OØ“Ó+Z”ßãÛæãÎëu_VU)ô·ÁãWëò[|»»~±:?°:È¥e×ÿ™{ÞTvbïíµoÖ\ÿƒÌ¬þëM^LàñÇë«ü ·x¿º‹ÍÐï~‘Nªæº¬v_vÔñ¸ëš.5q‡'‡óÓθËùâøèìê2 #ô|~~œ€q>â~;<ú”¾]ÞFú4ôC—Ü‹Q§Es3Tq—Lϯâòß!1¥« rÿãÓ£³/)‚Þ&|¹˜Ÿ¥$f3e|<[,æ)¡¹˜ú‡6¦àv _áW·k›u’㦜»¦ndeS?¥,Ëz«!L òÂ%à*ŸÂ×UŒm ?õx¿ê:ýþ«-¯»¸’WV[^ßwer¢-£ëxŸBO]ž•}_.oS„©Ï³]ÓL-žáŸ2 ŸÚ;«š& Ÿš;‹»~t¾E¸kû‡~jïlÙ uŸÂ«-ü®õÔSsgm9¼úÊLX¶VhÝ,¿ÇäXãN<ÿú{/û zÏ+â endstream endobj 203 0 obj << /Type /ObjStm /N 100 /First 853 /Length 880 /Filter /FlateDecode >> stream xÚ•TËnÛH¼ë+ú˜ì!žîž§ä´À~Àæy–I¤ï~}ªÓPÁ>C «úQU”8!Gâ"©àHä# ;JSI8<±œøím&~{]ˆK %D”Äã'À$»·0‰:ÔÆ½2©âN=iD= ¤Ùî3yÔ-äm/¬–Ïà£g`«(Øûà(D“)d;=EFÏ(ª™bÄ|¡PÌ8£Pw¨”¬Œ”¬LØÒfu”±“$¦ŒÙ$yÊñ÷9£v-l÷… vÜ•¨ÁìÅæC v6`Nx0Dqx@;[Ÿ]†¾2£ã75 Täh`ÈÈÀ(œ`a9¨S<`S…+,XUá vU8ÊeÞ°ª= ²b]5{4CSóÇsÂ*›¢ ‡Ø$5Øg»AåªÂ$:\â1à€ÝZpDg…QQCáGÈ­°Š#tSxÅÉaf˜ÅÉšÂ-N¾à•SDøÅ ARÆ"(Jpö¨Ë8Ã/Å3dÓ`¡r`Á5.š 3Ë *ˆ­0NÔÒdɵ8Á:quà˜àjtÅ^YR±®&‹0æUó#ÞÂÇÖÔR`1V8(âqƒ[ä7`1UQ Æy“Dá Ø,Z,ÌpÇ;K/ÇÃçÏt÷/Ýý3}èîoú°ÔÓ:Lã_Ÿ4|¤/_–éô\×Oë—ú±ƒŽ z×:_¦¥KI å4#^õù½„ÒÎòֱƒ÷îxnð—ã0÷Ðò.´6襮?¾]{¿%¼L=ý}ØÐ`þïQâMãëy»mÒ–³/uºöŒóù}ꊱvÕi{ÝçֵŸó°öLN­a¯»øö}<_—§¼õöq8Ÿ{èÖØëø½ö–Zc/u|Æï=BÜÔ?MósŸ698ýè¡·ŸåØ‹A*7µ{øìnª÷ñ›/ñéº>L?{ÿ¾¹µ¶N½?­¬íë<_zÉÉþ†Q‡îá†p1Â/¿maè endstream endobj 404 0 obj << /Type /ObjStm /N 100 /First 853 /Length 963 /Filter /FlateDecode >> stream xÚ…VËŽÛF¼ë+úhçÀ‡`ødÀÜ‚h‰Ò2¦I…¤øïS# X 6´CLWõ£ª©•3– 9È ŽHÊä˜)ZB)àPbI8=±s8qJ qŽ‹%‘´sâH,pHBŽG’ÉYFnÅ)dŠX%Çz²1ßçÜÈãÌíÎ9”öqçr­ˆ h*ߣ9‹:Tž iD>Uò œzò¹OMä=ê{C> ¾·Øìœwr>PÐkPÈ}â øA(b”"r¹à)âÎ…DÉ€ %Ìî MRÙåÞRŽÇ@lLD<˜‰ñ€6\<äLÐMAD†r.AEVKj #£…ˆðNÑ TW¸Â’Ç„/,>Ad¶7ð†-ƒžÝ±Îß$b‹¹5ûc“#ÍÝ,ÅTp™a;Í7ÈœÛT˜Ä¹ Â%´©a«< ³bX…)ìM¾AfŸ1°Š}ö^¡w·S˜Å>!3Üâ`s™â ˆ©˜„CB8ÆYj…ÌsBÍ;…¥P˜Æj)\ã$q§°“Ë¡÷ `ˆÅ)çÄ`… b2Þ‰ÉÔ“„{˜[aŸ°Ã Ft¶S( ÓŠHaes+˜"Ù8(’E@Ob¡±ÂA±Ø3…ƒb=–ÒˆE¯ÅîÓ'zù•^¾N¿Môò…>,Ýaí§ñ—&úôùóîÃ2¾wë~˜í0¶?ºJ((ËÚ®5t,ЇaZþ+ùõÛƒÑØF{çþºv×niºy§ZtÇßp¿ÿQA&ó„l–uÆÃ4oöãÊ~–þ<¶C­ ?†¾ëÉ34·´ÝüæšaÉøcwj¯Cͳ¤%¡_í|¬|Aø†5ú^ƒ‡~7 ± Ìm¿TN|ýy©¡ñUYê9®Ý|™–:§p¸úeíÆ*¡ÐtÿOÛ¯Ux¡è~éÖµÿÑM×usWµÜÕõuîÚcµÔÃ;r[­XÀ—n8Uá©„¯í\œÍ3~ÿçÔ›#ûräqZûÓÏj‘‡{wdõýÄÿÆglsÚóRÅÛ¿%'»>]ºñØÏU†>3öí±ê.û}îêê‡}xmÇs·lJŽc]¡ÇÖüÝáûnª¾9œÞà(0WóÆKs™¦¡ ç7øùáå°ñݰÍܦê$Ëc‹îÈÛVÔ×NlA8 í²l0\ÁX–×Óeƒ¡ïšªÂý3|ÿT¡`lü°`‰|³ŸTÀoÕðÖ”é×m¯Ó{¯/mþ!S­óXª;²y²ÿd†XÎ endstream endobj 605 0 obj << /Type /ObjStm /N 100 /First 854 /Length 893 /Filter /FlateDecode >> stream xÚ“OoÛFÅïúsLzwfwgv §ùí­èÁ‘iG¨**"Õ"ß¾o iw©ƒArýÞüy¿•†D4ŠJ™IY¨$<"qðO%ŽþmÄ©J .8& 0K"‰q£’I’â»Xų’T!BÿStˆ ú¨ Ý¢Q,¨‡³„Zš˜R„.%J†z)Sªè‹žY OL¡ÏBÙ Ï‘r>£'û¹]kª¢‡áO¡)裉Œu£šÉ¢Ÿ2õóJfÐÖe÷D*˜]M©¸ÿ+¾G T‰bçê{ ›šËFÑ»úØCpaÅ ¶VlÍAa©¡ÁS‘!#e­‘EÉsƈ˜u (XBÚXÈxAÚKxAeÁ̆9XPÌNd?¹ÒñTŽ®q>Ñ5•£2qJucŸ“ï=9Á *gödP9#j&Î ;8a@ˆ*«×)v<Tìy>XkØ`±tG v«®“²1ÃÅAS㇡{Ö¦~§ÛðÁÕç5®©l ظú^àÆÕ÷¼È!0ˆN‚×; ^ð„}fÐö™OP ü„kÞŠxñ:˜RÄ뀠ˆ×A‰^%b'Añû[@PRÀeAIâ'¨œ²n>}¢‡_éákÿ[O_èÃÐíÆ}üeËQ>ÒçÏ›§§ÝŸÝ¸íO»þ¹ûý-KœYÎw8ÒÌ1þ> stream xÚuSËnÛ0¼ë+öTÈ€Es)ñu¬ë:¨Ñ6¤"‚›±„êÑXÒÏS;q¡AîÌììâ°7ÿϺ̂Å-ˆ„i£²'@ƒL *6Ì †laV¸Ùc¶Y¬?Ç&ŠqޤäAÛçÞõ®—¢V3.å ø£s‡YcÞôåνP32‘’̈ÅŒiOíL¨pbnÛú÷,¢}Û•ÍþZÏÎ¥HÃ2#qI[ÏÚþ´Üq>¨%aZšÒYqpùnª|ÏÖeåÆƒOEÞì½gÀX3¥,D‚œ\~s]—ïÏ=•ÆÕåTmsÕ¼ ñÆûײéÿL&oÓÑÎÏiŸ¶Uîã)»¹7IÊ\›Kskqn™®&Þww\’†«†—3ßëÐ5„¾6 & S’:ÐKQt»¾ÃKy,pð9 †—ÁAHdÖjZ3+-lëà9`cuZüÑ ?,¾Ô«6¸£ï_1Bm½ZsÆvTÝg±5ðâ±5½OCk))¼}ú¯z'wÑ™=ÿ_H%)Å0­PTU ¸dBOqÞ—UUæ5ÅE±~,ÜÁßå»V ™VCN‚’‘ºé7òpHrÕU:%ù§JÜV endstream endobj 924 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./art/lua.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 933 0 R /BBox [0 0 1080 1080] /Resources << /ProcSet [ /PDF /Text ] /ExtGState << /R7 934 0 R >>/Font << /R8 935 0 R>> >> /Length 455 /Filter /FlateDecode >> stream xœm’»Ž1 †{?…KhŒÄNÒ"¡mhN‡(Ð VBšbAˆ×Ç“Ä9,BSDŸÇ¿ïÏÈ$È×·Þã„7*>ý„iUüñ]:SÏU¤SiXÕÆÛj™nZyÃÙ‘4m‹ôÒÉd˃Gô³ã% ¹”>Ý®èDò°Dq!Qûß@ð šdF3ŠsbM¬”«éz›Ùf­`¥eêm¢1wJy ƒ#®»‡…ó…\U§rEönVò°ÌB—üEÕW+­H8)Ó}ç¶ôœŒØQë|3Õº|@3.Tu[F¾¸/u¢è„pŒe_ÇY¤¸«\«žûy;zöÆI®ç8ñíͳaNJ-+Þ.çá©]c+˜3û¤o'¼zÿëËëÛwxwƒGø„…OùÙý¿zèæCLø®ù=@·”É*Öb{|œ‹ÿ³Zµ´KÕ^„¬Üy]í¤®j£C%OP²ï1Õyµ¤Þ†[XZ›ï–/^ÑMÙËDQʆµ‰QN®2¡d>F¿Dk.øµË\¯ÔÑÙ Uú褫ç.-÷ÑI¬{”x¿†Rü8õþ;_²'ñþ3qùoRö½=ÂL«×… endstream endobj 979 0 obj << /Length 1073 /Filter /FlateDecode >> stream xÚíšKs›H€ïþs„ª0;o 7Ç–UJY–ËÆ›ƒ+,6qåßgx)â!$%hãD:¨QOóuOOw<†'¨¸¾sNþ¹ MËÄÀy&‚Ø2àÌÀ½v:ÅZ,óËBÿè¼WGƒ(•‘ÞÜ S)`mdgR0ƒÙ+RH.…ü’+˜Ê1W?²ÁÞš9€Q4”¾Œ[ùp¬6áÚ¹œë†.ƒ™ ¦žlÌ¡IÌr¼ö=”ø•÷0ã L!gÅë`¨&ňm¢&%–ö‚'¥¥Úí·E,ŸkóßQ êG¨¿ ³íÞeîñ‚™bµ= ¦Ô3Ò| f㕱U»P–Þ–Ö|›©š.¿-‡Œ½i.ÂÇ8·Ñ/˜…ºÒæ¥a'{vz6ì¦o˜9¯š£“ÂÑ/½‡ÈËêÞ ârÁßk—íÀв`ƒÓ.÷ýÈy“zpö#weÉ›ü®xÄÔ#Z7“Eé>¶‚ˆšüHÁ¯ ÖÁííeƒ¶Ý9/-ðÎãO‘tgÍÁéßB{$LÖâÖ}qø,|ž{¾ŒZÒ ë€}ùYA}xu—GÖ±û9Íd#| vx~Üözí``¡RnVÍòIžå‚Eìú¾Ê¶UEÑ•àÓŽ""XOsñw ÷IvÊe?U8˘.놴 RH¾êÄTûQz›¾QcQ3Æþ꽦·b»¤Sâe:uÂ}ßÓ±¦j·ú®EŽkxgæj)ó–2­b‚ðá?9ßÎùÇY…z} ™6DÖ²‚;8_߉´QÀjC-¿Ê Ös9¢ÞêØ{–aw²¶LÈMú¿²~È óµû2&2‹Ô7LHzéÿ ýðÁõ›Y3[èíd•ÍXØjn¦eñ7ˆ¢°¥(ÁøÐËéÝao³‰N‡§£«Î0£,ö£§~ÌëôŶÑ>çX¥ïŒÆƒóÉÓiA ÆöoŒó¯n‡Ýšy® ýzt=èNâèò½Y¡à¹º!Þž_°?#T©—¾ï|’y‹ÿB·¨–ÄIÔè@™”z«`XêéKy¸5³ƒYâ7Ï·8çÇNj?hoÈå /_ŸáO¿$2‘Ý5,§P¨*áàêªþ~A0=àˆUÈÃWç“›þ [‚Ls{Í Ñ©ë˜ŒÔ5»¹Mº“a[@Ä­Mªe:™ÛêTÈlÕél2œži™}Њ¿Íeï­ ÝÔ[éŸ4Ôó‚XFópÑ­#FL…õðvÓ§”ÖªÐs„iW§o÷ª~ˆµYKµ’¡M(L?ÔZUqêSéov6¶?gcíðÒ&ï~œÍêÃÙ¾”óW¬_”W;šø׳úˆl‘\$VÃîÎfõál/‘»gcI¡†¶Ê` bB*ª_-æcÎÉwI@-q endstream endobj 806 0 obj << /Type /ObjStm /N 100 /First 855 /Length 1954 /Filter /FlateDecode >> stream xÚµYMs7½óWà;UñÕÚåJm¥Êª¨,²«ÒaDeîÒîÌ0Vþý¾BvÈØ3Ú*ò"3ÝÝîÆ(RFeÃÊ{•­Q„™ÅO™eL“²ÃÛ¬,Y•U6ŒN9yïH9ïgÙEåcÎÊ壼‘Ñ+ çƒò$ó¤<Ë<«`ÊÁª£S!¿‡YQ‘•9+ XÔ(‚y žSPÑb=`Fe#ð‘„Wt*a9’J!ÍrŒ*E™³J ¹${N¦à§0`ä„w,ϳbUÎV±Ç:Ù)&™“bá—#ì#™ñC°+k"`Ùãþd† ­Ø Z‹e†-¨1¶`-Ë8ed¾°.Ä™¬jp~±¨ ÏX †œañŽÇ¶ypüÁâŸ>, 2ðÖ„ŒƒV`š14- œavË&l…]·Š:pÄ ÇZYa gÆ^\,«Ã‡Ž" 'ºdÜL,äÄ¥ W;ÎXX[ŧ¼#y5D‚¼’Pòƒ”0<Ã4^âƒDG'“òŒáÿà°]F/œI"†Å2àˈ‰ ÎeEû1b`â2²ˆgFœGÀ0¨P XƒY$8°5%Ä-#vH†aµhŒü@´Ù 27—D†T ²/T$¬ÌˆºŪ°HÄy˜=®æjþº¹lÔü…zÒÕ‹~Ù¬¿×6¥§êÇgOÚºkVÔm§»~{ótL#j´MÓjðƳÿnëöÏ1•lUîêÑ5²=TØl¿¦°½):Úk§-í´ ´­;½hÖ·Ky;º”+JºÿsSjøC庯ÛMÓ«…CµuýiTž}ª–ãf‹‡Ýòn]­¦-÷-·i›Ë‰í”P+¢ÓVËûòS›ç=ñg]_õÛnLƒÍÆxˆ±ÝŸˆHvûâS®`¿/¿iV«÷·ÓŽHûލ¶÷«åèæV;I]u]ÝŽ3£=…÷ËUc¶]+Å=%Qø8ênN{ }Óõír}7ª’÷T>µÕæïâ‹Õ‡M—]¿¼¯>nVõ·C]ýæòò\]\¼Uïj·ëÇTгÏ`•%àïÁ\½\|hÔEÝ"©¿_~hëêVÕ]WÝ}mãWR6z§æ¯–ýµè<>›_â©ùyuWÏæ¿4H+ë¾C©Œ"9›¿C:ܶ‹ºªçð謾]V?7÷êÊàADÇ“Ø]ÏÑBÔ rþ¼mn·‹ºUO^Ÿ¿U¯?À+Ý¢]n ¨{Š5ÁZ6ò¢êkõäÅ3g¬3XÌFÊ–0é;c¾ƒÜYs;%r¹ìWx»­Ôª¹kÀ,¾ÚVÿèëÅ][½×›íâ‡vÙèXuÏ/ïû×8õ°ÄoçgÊ>¼ý¹êêW0š¿©Qúå¢zP‘dzùÅö¦¦òð³Ú_-þû?ÿ…æM%´2ëíjuýMXÓ™}™aiñå+)ö¨ê;:ézÌÃDš;L 5ÿý·›#>­_?J>¿ƒGu¯®àõ¯À¹¾ïÕõl4ÿ-ÐPý?á0ÿi½nu54]ÂEz®Ý˜Ë¸Û™4\»Ñ–ѕї1”‘ÊXðBÁ /<*xTð¨àQÁ£‚G <*xTðbÁ‹/¼XðbÁ‹/¼XðbÁ‹/¼TðRÁK;¼× vüKȽ]®ÿƒmÚÛº\`®çoæ¿Î¹²ÃDœ†H¸JVpãÚHìÎÇäu4b?©o'Ê!Õ†O-«² ãÐñS`m}•ÆC>"‹œ4ÚtEÎki® ÃHžtHaÌÚ“‡µf€d”[ 9Mˆwï„øu"_ê·=.›b•ÀYKš±wdLÖÓ„UÜñ­2œ:\È´ÃiÁ-Zg—¦­âNá£Y'¹À‚…An ¥ü6§° œƒ;zP‘xBL:Óc¸øãÇKp:âžn#¨¡ÀBO2-'`á­\”À"ë$ÁŒNd'x„£g¶`¢Nò¥¬Ž¼lÔŽýhfsGg᳑|ºKóÙ*ÏV§FYß'^|!ßs"„½ò)#hÇó«?ÅÙõäµ|<ÁÝ{Hie'ùÉüêœíƒ˜l<Ë-J9”ù*é ‡8¸ñNÀÃÖ¥/LðÈ'àå“|sNš%t‰t×|-’T›¨Å.ä‰vÞœ€ˆe“´Ò^£3β a‚Èñ›W‡òkåÓ?štÕà…Ç4QrNp|¥3³¸bZ&ùæ bè¦O¯;EÁ‘[VÄuø!ÁÚ%áN8Ç€ɱ…—pÑr14¤4%'Èg¥ÎFúb‘ÑÃN”>{‚lQë‚$T›Ñ¨É¿¾pÇIv‚È Š°58ÀÙí2›üÍ¡÷q> stream xÚíšMs›8ÇïùÓCT I€|Ü™¶3{ÜÉ­ÓƒŠå„ ¬MºŸ~…³Áv ˜GYu¦—à— ýü×ó&ñàè.ÂÑ—+ürʼnL÷¦{ñ×—«?n¯>~¦<" ,Ht»‰ËMX”b0s­£¯×ùß­jUƒ´zúðíöÏ—?~&ƒ›SŒÌÜWۻЇŽñÄKŒÓô:ëG?ä<„¼é'º!q&+i­ÌïÏrf)â)È9¬ú$Ù“‘õyýœæ™˜§ßOLn7ÃI¼Æªðø®¬*ÏtcÞrä*\ LÄgˆˆtH«Œ©L3n„lŽŠYFø†{¨ÇÚþ Ø óªÕ6d>©sU†°²R˶Qï+\tCyŒDꤊ9â."oG¦(FqwË!>eÝË=À>«4Uþ ,\ÀöxaB¼ë|"ή ž›{^¯ÅNܯßÎÊË)J(—pg-Óûy_[óŽÙŸµòUjÌ„> stream xÚíšKs›0…÷ù,“… ½€egšÌtÙÉ.“ŲMKAr3ù÷•ANã`ƒ vÚ¬p ÏGWç\Y†Þ܃Þí|½Byåê‡Õ‹ï·_î.®o0õ! ‘w7ó fÄã0˜_M½ûK•Îó8ú¥Wwß^Ÿx}ƒ7s$æOÕ]àjB!<ð‚(ç—>´ßݦœØ‘&>$ä Ê4×¢,¤jG €4؇ڤÚ'ç[-}úØÛ™¥J‹¼]L3AØMÌ®*ÖCLœ›€ÑsœêaçuëŠ2lP*¡uú(ä²A(Áï¶yÌ@06åF5(ÕS0ð]ݲ ŒÉêåz°äi)–B½(E$ë2®G*æT–òøAÅ\z"K5V21—d²¬J/ ;ÏÜñÈ|sGl¡K)õ8ÐÅe2žÎÒìxµÉÝk³‚*b½8+¨±Î¼¸û‚Y}1꤬;ôÍœ©RuKù…TZ9}½ë: «²¾éU Ì»7%K8`Ô~c3MÓÆ?}½»ø‹ Ö± endstream endobj 986 0 obj << /Type /ObjStm /N 100 /First 992 /Length 2496 /Filter /FlateDecode >> stream xÚÍ[ÛªÇ}?_ÑIzwÝúÂ`[( $ l?$zðåDÌ9Apþ>«fOÍ–BìÌi0HšÞ»÷¬®©®^ÕÕ³4êH%VIÕ7Å•“’/Éß÷’jç$©QÇ·-5ë‰J­©WÚ¨Ýf‰Z÷ŸÔDƒÑÕ[âRž˜ºúH,‚®Q+F£Ö2Ð`¾¢!‰{÷.MRhÜaI¨¾©IxÀ&ü P±æ]#I+nX)Iºš·(ÉZþL4¼%IÙª·4© ¹CË’šÿ®&­uëmI;~ŒVO:tëÉŠ?fŒÉ‘‰’‰:2>šv¿—à¹Ê~/i²fc%ëcë­©Ùz[ªäî/ðl•²õŽTU½—áwëÞË”jÃÑbÌ…m½’ª{-ÅÌpÅðyãºõÖÔd¸Uìó%ŽÌ=µÚ¶Þ‘Z'¿݆:²Pꥫ·8uÆäFvx×[š:œ€10y½ÊÖ‹hÕ=Žè飸_s\Ôý"˜Rjn\70}ÞÂ<éf=~2ào‰GÜÖ‹éì•1†z€ GÖ«;z³’›cëÖU¶ÛýÛfÛüj÷Ÿ™Û×·0¿aTïó‘ pÛh²t f¸ñ¿!`4²mZý©©ncz´#°Ý5†ÑÚŽàˆ\®+ÿ ¼}4,böPÂÅ›ÛS t‰FÃj ¹>€O”Ð0à"ìC`M’¨ÿ¬l ³77Ǻ¤oqá“©e _…Jþ,[€*#°Ÿ=»»|óŸß§Ëçïï._øîýöù/oþuwùâñí÷o_@y}ùÓåÏ—/_ÑöáîòÕý÷ïÓ+é5oÁm%_Öòö¸•³˜âwŸ§gÏÒåëtùãã7éò<ýîî|óøð‡Lã÷é³Ïîðç ‘–Á6X¤œÛðí¹ÿ²!\Â’¦M3–¦,÷F;hedgg­=Ûr”kCøé –Á×G€”–¥·‰ò±ÏÓ«Ñ:°¿J—¿ýýà4¥‡?þøúgÃNªYÁ$ŸüîÅãÃûmÜ`ªÑézÇ ñTÄûò%ÑãÜ£ñÁ{lû¼ËË·ß}GM——Ï_¤Ë7÷?½O¯?õÞËoÿywùÃÞ?¼ç˸ùýî¤wÞ~ÿîJÛw½ÿáÍ·_<þ”6¿"|ÁÁÂ/¿}‹»A–|ýÝ6%ï0®çX·ÆSìõZ¯×Þök߯ãze¿Ò~åý*ûuÇ;ÞØñÆŽ7v¼qÅÛrêÞ hp4$ ‹FF‹FF S S S S S S S S S S s s s s s s s s s s K K K K K K K K K K k k k k k k k k k k [ [ [ [ [ [ [ [ [ [ ×+òë§áR0¤Ë3‡]R 6,´1Ë„Qôéˆ- ©ƒ³ç_äòl˜–V$Í ±†´+Å3õ\°ò+˜oÔÕ׆TÍ^†´Š\8±£-°ÃãêÍl æI¸?}î‹X$|.Vl"Vc{t*VeÁö(b5 9«²`±Ss&VeÁö(bõpÈ™XyÂEUwelÔ#ƒÎªøbªÿßßí¶dÉœyÁ.”½pÂî%–ï…²–Y¼. yÃ4uÔý‘mlt„£L Y@òÖ·ÄŠŠkø™æV'•–, yC€’ŸÉìqbPÀØÄ,o^âaós‚£Ö™!}!b¹øáJ¿î;ª™¨N  A‡Áo†HÉV'Å––† ïùáÛ±j<ïÍÒÒÓ¢ž÷>Ú,òžÎÒ.à3í%wTQ¬êyf†ÈC*®ú‘!È{2 ‘Ī&Èwýà3Ï'yO«ÂF"f­¹ùÁlJ';]@¬Ê0àfH׉ hUI2¡< að}›²€VeŒŒmÀ-R øž'OЪoÕ¼(C›’Ò&‰ÆЪ4î-ÑHßó$ÑØZ«Ù>²œ2Ú„Dl«Šò—ÝfÆì=3d«ŠPîC°jy3 tß& ÏЪ äõ÷E‡!ŒïgǾ¶€V¥`j·<ƒMsž:d«òÀö´ô›t/“M®ÂÕCìåïd ç*j=¢äŒ¨u,H8!‡Èôfu²G¶Bõ|Cæ{*B$œˆ›ìùL„´ß„ìyôÿ‘=oÿ—æuÏé™oÚäO$Ì¿V¨LW)ì'Beº f…Pyû¯»*4Ô¥5Ô¥5Ô¥5”£-4©-îjqW‹»ZÜÕB“ÚB“ÚB“ÚB“Ú¹räÈ={ ÷@îܹräÈ#G @<yòäÈ¡±¦ÐXSh¬)4Ök 5…ÆšBcM¡±¦ÐXSh¬)4Ök 5…ÆšBcM¡±¦ÐXSh¬)4ÖkâߎöwŒ5’[r…éž]Æh3Õ&r¥ÄtWvžq "i…Æei»\¸ÆTf玈×5"SŸã…ι٩ D•†tô±¨õP9'ª\!GîË8–O$ÉÙ‹.ðÇKéqÜ aËe–¥Á_ BÅ'Æßýí‰ú¤KVH´I|ïv,Ÿs>¡Eõž=_ÂJ¯Î~N|ûiÐKè¿Ê`°å endstream endobj 1186 0 obj << /Length 1113 /Filter /FlateDecode >> stream xÚíšMo›J†÷ù,ñÂÓùd/¯”öªj¥ªõ®º j‰_ÀIúï;àÁ06 8‰î"Âù¿sÎûžÁÖÆ‚Ö§;x:B YqöMöâû§»¿–w>f!ÈZ>Xˆº€8ÔâPHåÖÖO{%iÒß{öÏòóé‚>¢Ê¹ ù«ü$0›3õˆqncWÝ¥Ž[g«ÎŒŠ2k°Kýx%×… sÛ€5¡ÔE vþó[Ñ/I¿&em+Õ³:åplQÊËèq¥oDæEyë‡ ¼^¥.ŒÓ޼šhêÚÐö^º}…hÁ.ñãëkÌ¥{¹šk|Ïš†àr±<:ªæ‹‹³sêo€Ðìe°ú÷àü¬w ØJwJ®â#Hå›&Cj´ï 1éýv5Š¢¦²¹žo%žrVóñ$ÅS5U<‰–9€Â-“O–VbøÒOOÃYÇ5ZÑÃh‰žÑÆ~…O~|Ýk1 á[xíø–›»kçJ(¤¨_ˆØn°y-¸&káŒPÝ¢R«'Àn®Šä¹ósLM”â:Í,mfÚ[+hÕj™²_«MçÓÎ&€+ˆÅ(@¢Ö é:ñoórw%›fåZrøõ¤¦&îPM“Æ êÁOWÛWªWê”yš­![c± Û,®‡tèr;P‡ªPpJiLû¥qËÖ gqœåðErø¨AcwØéäëOŒ¬sëæÖIºÅ^¯ÖAZ§ÃNávÒµ&Þ4ý¨“=ÝãÓ5·FÚ!ëtò™È“d±ñÇ2ml¢òûÃÔ|¥qä\_¶nÖݺWÑn¤A´ûÿ‰UÉËq¿'Vj NšŽâå¸!±:™‰Ý”†LM»©ÿ`ÆË ǵé¢íO ÎÜžww{ïð÷gïÍó©®'”=ÿ(.ð’ñ>ÓéØP¹Š/ûw¹k:„é ý†&ÏiJ/·º"Êhý‘31~*Þ4JÒ8ØmŒÍx¬Ï½ºªÏ±·×Ô¯@â $q™œKeíQ(V˜4ë0fß¿xûЯ†¹Eò«€#ÎÅ[UßÓœYòu¥ª(@’blÿ½\~›Í ¡ö²|9¾þîK7LªÕoýœ ˆohw·µPÅeÕ屦/Vú~•&Èå|ÉšAjKìûÕ s{+ÕŽ”ð~ü4“ÖZûŒ8×›ÜÐî§ÍÞYZ¢¤]ncß[Eý*ûeŽìÄÛÔM'—Ñ}mQz‰«ÿçha)+¦Ù'íPå ž*t¿¼ûK‹Bä endstream endobj 1125 0 obj << /Type /ObjStm /N 100 /First 1019 /Length 2330 /Filter /FlateDecode >> stream xÚµ[M‹$¹½÷¯ÐÑöA%E„"$öƒ± 6 ;{°½Ìa?³xé6óë慠¬]XPZ0T)«3Ÿ^†^„BRL-¦©¤ZÌReóFOdÍ#Ie4zI­UoÔ¤}xƒR'¿¹sê7wIx —$í-dŸøC£ ÑSÕ61ðÑ‹w=ð1¸{«&rBhQ"ªâ-Nļ%‰dö?Z"%ECÁVç}–h”y_O\d"ĬA­$FÓ[5±4õ%ncçÄÆó>IÜuÞ×’”‚>jQBn/(„—G«'‘:¼3µ‰WKþl­I:Ïß(É0ö§æ· %©áE¼ÕRCwè£*lLÞGµÔTç}=5e¤6ØÿJ%i1g€›•€†…ÜB¶W&–4éðA«'ƒ±ÑõdäÃVi$c·Ê%Ys«U¼> éÏ2%3s~ÌÉFuè¼—æïÁ-õ:Yá;OVN«èÃØµ¸]x¤nâ½´w—E•š0DŽ"”ì( a¸¼%iÀ¨ÞjiàÕ½¥iX(–FoÐU…éÆÅÅàïKúxÓ¹©U)>uÓæØ9ù¢®ÄÚdjØ5ïP»¡÷ߦ‘ØÖ¦l‚ î`s|tÚ\ý›2R¿<"¤ êùXs—ždÓmREoöxxõêáòÍÿþû˜.Ÿ?==|¸¼ýôýÇyý·Ÿžþópùâùýï¿-åÝå/—¿^¾ü¶Î‹‡Ë×?|LßbL2ƒrSÊÍm6ZvnÍ׸íóôêUº¼M—??óœ._¥?|Àƒ?=?ý)ÃÔLŸ}ö€/HDF.°4Õž=è4-YÖLhnèy€ e ˜iEWLxªèLhäïjÌ`d+&òrLá2ÃUC&"-«AȤY1i˜Ütr09§ÝÀä¦ÑžÑYØ ê D9š“Þ¡Øb¹7ú}&Ÿ¾¿‘Éœ)ÛË«V *¥;éø–¾²KßÀ¤á‘žJÁÈ´9R6ÆŠÉxy­ˆ9¤­âºž´ • Ln69˜œ² Õ ªåš‘z$ä?¸†CCCΈv‡TŠ›ù*qî+‚iˆyVhÃôÃC2ÿ*èK‘l}VhÃôÃ62! &ôzø1ôÊËŒZ6DûЫ ÖúN÷)¹nˆõ!×àqJ­"}¨5f¿“jÝêC­“Sjmb}­Šµx“ù± YVë§‚ýoBìW“ÐÉ×éòþ+YMF5=}úùçwqÓëç§ö5^~ôv½ûuõ ž¸ñòæýóoA2]Þ|õ:]¾yüåcz÷Û÷~óÝ¿._ïñéã?÷ò3̯ýõ><zÿÃãüM®¿ýýñÇŸ¾ûâù—4-¢•’ 7ö›ïÞãéäÞ:ï›Æü€~çá™Ó™gg·FƸ5¸D£Fƒ¢ÁÑh´h222²²²²²²²²²²r äÈ-[ ·@nܹr äÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈÈ={ ÷@¾éñÝËNSVàðT ÓêÌ:WŽ¿!™ÑA¹À ±tµÂ¹Õ%“ ÙŒb>Ðnì®kyèjÂlÒU|{ÁmtÔ4/w[Ú†c2+=¹Ï'e²!›Ñ!Xéq8uR&²ÌJ“A09)“ ùLÈ$–ŒçdÒ_~òV=&oE!íÜJm‡#×q݃ ÷a¬¬ûj)Ý6l*¬¯¨o±M‘ä¬×ôºañÚü“ÝÛà>ëE½Ö ¢õE½Þ“ßsƒ£´‰/à믘œÞpò~Ñώ¼¼'7µ†”¡I s¢P=åÉ; FPJ±ã³Á@ëâm;Šhn{R×½¨&nŸ%ÝSCþçuEðn« KÝPáUæ›.18ðŸ^–þ³ã°þC¾éLà?Ô–þ3vTó\{ÔМ¬•Õ<⻥±ÙqN°¶!à‡`“œ¬mø!XQ?ám'k~ö°É)ÁÚ†€ï º×Dè@®ä…<%Ó8ð7lŸú\Üí^0«{–›ì¶!à üǼÖ* hà@fKÚñ1ÿf*|· ˆhé@;*áà@Úî4R[:І-ÔÐÉ-º”ÉŽB8äLýˆ)çdÒwÂ!!0ÿÁä”Lú†B8î„åËA°É¹²¥Âi ¢é½ÄÉJ6Y?ô•p­ÍÁîþ f–ëX©¶ï¨„CpSº7†jU–ª••E9&“ƒ³£8â68b+ Óƒ£ܧŒëQÈm×€©œ> ѵE r,8h ‹ª«Óªn;j‹Ùêýl•ºøÎÀŠÉ†!¦ø‘ÁQåÙê²ò«ÅE¾y¡w›Ì-„•lGÙQÓsÕÉqÞ|J'£î¨é¹êä^ÓsF'cÊ#táíœN逸¡ìû!XLeØ©˜²Ã“‹×èÜ÷u¨ÖuáäØQ®Ñ10­ß‡(œÒ,br™eòW“`¶Í…—¢=þÿÇ»˜è endstream endobj 1192 0 obj << /Length 2102 /Filter /FlateDecode >> stream xÚ¥XKÛF¾Ï¯ r Xmöƒ¯½Å›d1ÉìN)H'Jlˆ¡H…MZžŸª®"G”é8À^Äf±ºº_=ZQðDÁî"~¾ÛÞ½ý^Ë@‘f© ¶‡ UA¢r¡M°-ƒ÷¡\­u‡ßÚ3,dh›Ò6ûʺÕÛüVi€7Q¸5 Ö‰qœñV›¥TqøÈ›»¢¯š'’¸yq½=râ@F"rÉr@!+’³ÿs°Ãx¢œqšX˜$ žñh‹•ÊÂUý²Z›\‡­QU¿nzøPá©]­·´ÓzÜÜÿFßl³R)ˆéÚæ„ë,ìÅj+¾zbé–¶†¦,F®¢&â¹³§Ê1GÅ ô-Z¬YçµÔ"69i¾ªº…òÜë‰O‚Ž¢°±¢ìðp\4m³v}w¼éìiëÚ»)‡b_ÕUïÍG)çn%Öì*-oÛ¡Ö/´Þ·Þjˆ Kö+ºÚûKS}$&²sæ'¼Y“=RÈî«ß#iöE ÉLøäi¾Û|»ö̈ ÂÁ|öQ_ ºl$zäó2uøPyu†K’u*R•Ž;ìµ$Xe"ÑÉ`p3i½iëˆñÄ÷ï>àì"ý„ë¶cŸÜj±–Y,â4tGB+ýeta²Iwl‰‰tx¡ƒžéÍC;B¸ï'uðì\Õ6Ž9ôœ¼F…¢ÿö†žÞÖÊñ×ÿÙBÆŸ¾Ç@vÀ)Ѫ|†{ox᜴¿†ùRJL»êÃ*NB(v\¶6: ·öÀÒ6˜Å~©ÐÑ’¡é\µ«™oŠ ¦¬Ê¢gª’Tá7÷h’§èI¬´¾àÞ#¼{k"ºá¼’cFu=-k¿^,ZF¥V,„˜³ýRT×Ì9+Þv·–ÀD8–>_q %„žGæ±'ܶ#ß¿òý.µyžjÂWD¬«OéL‡âµzK‘ÇñX½¡î§RNÕê7ñ™k>#E”O¨ü×’(©„‰§ ýoµïZ×z*ü¿VMÙ® \A¿ŽÉ-˜$qn>!3H¸8"@ÌfËPâ WQýñävè‰Þˆ@®¤¡ë!SH'ÈNRq]õbhøÍǶ¸”¯"Õ*X_±!ö” X{;:çPt|2u –ȧ¢ábŒß:[8„d‹`eKzæ<@c+ 1,Õ_‹$ÓsX}ã ØcéNÃCמpÅ&#éÔv¼êíÝylªÕ˜Q5}AM | !‘ŸyÇ£¬û·ÄçS  #ÊR ¨}êŠÓi"Ì ÏâŽÔÀ}ÝZh+л•1·=c‹ä›Œ5‰dWÑû‹’úcÑЪåç/ÞîX‚d<¶; +׷݈¤ŒÅõnpQY5Ö1ùºƒÛT¢Â˱ªgöŒñ™ùS'Q88ß°chµT§öc‹>këAê™Q¯]±GŽg¬Ÿ1L([„}.üö¹|òÔù ªÂ@÷1²Å3ênù£G<-É=gq†ð1E,²Å²Ò½ppcwž„ç‚CGïík|™ÂhLÆH¹½î|©¿“µÍÍŽÞ{nEI„„k5 R à šM ©Š$л±ì»¡Æ„ze'Qô8dík·EÒ¥êK. J ×îF“±Ò€K[q#Cñ>£1¸ø$ÓÖ„ <ôAr•s;n:ÃÍÙiËÒd"¡ÌT‘¿0sDÙÈX±"ð¬-Ç®x²¯úûú£a|Kâ›¶ví Mã¶-nã<®"¥`òŒg3*´ÀÎç׬ùÍ=&>s#Ái1ÕcOS|#yðÍpv™wÂü5ÝcÔt“yŠî·o¨“Á -€ùS’þÜÕûZüå@@™ºì¹«àÖuæ>Pî‹îÉö¾T>4özЂS<ÈÐá´ƒ„Â:e0á,ñ:˽§¯öŽ·wü­i{Z`¦Ó§^[ÐSÂßè"yô†Ì`Qx7‚~‚»LDÌžàDÊuG÷(Á¯/•­Ëé:2ŸliÚùNçè»w~®z¼y¶ÖÈ$Î8ˆÄTÒënΟu"Oé¡,å=t“ZÄùŸ Uj‚àx)n6›‡%Ü@e{´K€R0j)s›Ø>Q\»^A3y}¯Âƒ¥Æë+áf¾ë8[œê)Aá·Û‡ Ÿ2½€2¸)­)Ó½<§ÈȉuÂ_‚x¾û7t¶ÎmãªÑåJ«píd9zó38.ö0AËž9iü€Å³ßþB/l(T§×9;Þre%ïõµ_«±¦ÝEÎóQðŒ«ï‚÷¤(\4Ó¸øwWö f¹ú_®ÂñfQº1ˆaéõP´,}úWç“R‰èU1ãýn{÷ç'ñ¿©5”ñ`º{ÿG”@À gÁÅ3™¥B戞:ØÜýLÿ!A c˜—ÓÄÃ+…›h ‘ç7sdPCó²‚YÄ\÷8/Sòå˜|9")XÆðó…(P:€B¾û¶ +à HÙ‚H¾ñe„¦ÂIŸ¥÷ÇÌ*ÍEy¬´ÐYþwÎP"Mâ¹+f&ªLù+?Š’cS´=ë׬ñ¦YÓ•£š¬õÙ m’Ë, #÷]uîýmw°> stream xÚ­XKsÜ6¾ëWðN•ƃ/-[ö*åH^k¶*[I‰±Ì!Ç [ÿ~»Ñ 5”él¹ ñìwÝ"½¿àá{µ»xõN‰H¦ÓTF»}”Ë(çšñDD»*ú-L2µÙ !Óø4<:kª~óÇîg¸—F‚3͵À{<ÚJÎRÒµòËhGNŠÅÉD±"Ïá‚?xrÝFñŸu§·*ÉcÓÒ·; uך†fĺn4=v›-\«ÆÆ^ÂJ*ⱟ7?ÞÝßüz~­g$ˆTQÎtνRg0‘Ý5 #×ä…!W*Úž»iQæÁ:¯y"Nõ@ß²kûº,ö° $árÊYQˆh+K“@fì½ÎYÄÜöfoiÁuãP·Óö×GÛÒèa¬›!,ÖÃ#,Œ¬›(Yäxq½»ør!ÀÊ<eƸQ–§Œë<*¿ýÁ£ 6Ž8Sºˆ¾ú£G°†ÌŒšèþâß Ks(©™,R •°hyM>]_ßî>½¾Ý‘û^†D@#ÿGdH¤d¢Xаû×§ë×oWøæ`ù,ýgøC¡ò%ãû×ï®Ií½5Ãè,y<áÒŸ]xühJ×õ±RÇýXbŒ !šÌjZ寪˜“…âä{%…æ,Y”e˜ý%•-•\²”0@Z*)ˆs_ަÿüƒ|μÙüÁº…ؽNÝ~M'L…é8Ð(D;“Lk=f›m’¨xGFðúñtòHйVêž¾¶5­ÈÔ˜¯O´QÙ½›!@‚’çü¶2×,“ Vˆ„ø~¨ÛÏ.+¦’Ét³Ûn€ü•y ih%q×ÒJßÃ^ÿ&:bÈ"£((0 p¢Àoípuÿ–(˜¶¢Åw›"‰2"†ÝKÚÆŒ÷ÛMÒ_:šº4¡´{D.c?Pt¹Áùà ‰$UÜ€Îxƒ,¨bs0èZœ$Ï…ÀoýÎS>¡/L·Í¦~Ú¹°Vá´ðŽñM WBm7ÐÀ¶Ýxx¤ñ~"íe4©B³–øß)<™È¦ƒg$$¬R÷¢CàÊu|¬C9„WK+Þê¸PY @ÛV¶-Ÿh›è’p¾>ZP;-8V"jÜP—cc‚æq½§[“1ÏÙôÉëƒå3m~ ꩜…߉\âˆ(3nDÜcàPð¥ë/›à-§`ðuG‹e8Àös8ÀæY8àVXÅRNå3¡ã×2-{åà(«ëN®6Cà>³stàw.Rs·:Zûå¿n®îi °¢U3šm ýË·W½+_ýbˆ$ÐJ»šþ|™÷w'Œ<ð,¦â:¥PÓ瀺›â€7Íã” œ5/2%ˆÂå²;žp5 ¯ÞAÏuŽÃEE1cË~|Ø@›cÜš$z*5KⲨG‰òØ–5µ)È›ºäîìÁ¸*ØÑ.À1´4šò9J åˆÌ¹^š¹Ã°…¸>3øøíO=-Î@GD@äç}ƒƒ[Û„õÉH>M’øfOÁ=eÃ’¢.=Š4é±ú.˜ ‰ëÆ®Ù7’RNFCÃ@Óµ ÐÊÙº‡0D ( ÀX4rJ…Šc€Mg+~­­.i›|T<û=À—4_Á<©2à¬_`#ï­iPpè—Äs%š`ŽJ|æÿ­94ºð…5x„îêýß!Ý*²bÞÀ­4ÜÚ=†4î»Ñ•a\vÞSU˜Ö-Ž]?x½a|sGƒ7ZSŽû © Á˜,á9•C¡]_(q`Mè(VTŒ;âT`§Æ píèëqJõ–ËÑ9K6ŸBÒƒyã`WXu¶ çhþ'VˆÈõžVºÊïß¼¹¤ÛecÚCˆ~r% îÇö£ëØ$1¼$¡/ ‰?"}3J6ˆ`\H¯¯Þ Xí½‡áÛƒq¼'<ßÙX¸UÕæÐBèÕe¿š üe$!Þßþa)±œ(dù7ó!¹¿ƒ¢DkÎ~k‡ŠÁ ‰ÈÏGAµKrõØ„¸‡•fèEœšp:ôÏStÀñ·''÷4íè­ˆ' I g>P1~ WÝyìh»»'Åeô¥,ÇžµÆ v–8­™Ö3p‚Jv?ƒñ ˆ…“¼xñ§À·ÚûÛénõ!ðÄgÓÿ‚ÃwO‰4ƒn$Ðo:j¸Ò<Ã2Åòt~ã ¡Wy(ÉòçBQâgƒðm»v‹îžÿÈŠd™¬á¡@ñû„鯡þO,mhÜÀ±“óq=œvt—ý´ªš( í•smø‘jðâ†á¢‚Ì#-ž¢9vTй$ê¯^¡¢È™Ðùò )Ÿ2癜%Šâi¹ü&‹ ¦³,ñ‘CM;4@<˜í ±*ü¤¨®ÊŽÍ$A†­ù¥sï´dz‹$rB8ÖŒY--0}¿³”– ÿ9S…:ÓâÜxÿÉdü} endstream endobj 1207 0 obj << /Length 216 /Filter /FlateDecode >> stream xÚmAoÂ0 …ïý>¦‡˜8Iz­ MÚeÊmÚ¡*u-‚ôÿ¯] ÒN–ŸüÞg[À ì3±Ô­ËV;E 5Úµ%p X F–¨48ŸLæ\Ù‚½ö·Xw]Û¡Ï¿ÜÛjGÀR”¿.Ü$»¸>B®“S)‡¹jÏ! mìF|ŠQO1$¦E¤âRLµyy¯ðÛÿ‡, ¤µ¼6ÃÂ/<Ü×1$î!'6¶O]Ýûûz(×ñ8 7œ/%TF×¥E2&ÕÓLå²Ô†Wæ endstream endobj 1211 0 obj << /Length 2480 /Filter /FlateDecode >> stream xÚ­YK“ÓH¾÷¯Ð 9kT©$n04»l4³˜™‰˜™ÔvÙÖ"KI¦é~üæ£J²Œú0ÚUY¯¬¬Ì/¿q° âà_W±û}±ºúé•Ô‘ÉŒVÛÀÈ •y¤t°Ú„j±T& ?tÅÎ.þZý‡¦ ã©Äéq°Lu”$™›‰ÅR™„?7õB‰ð3þ±Ð”a_6u7î!¢ÞŸÙ϶î»·w\ê< Þ̾#ø±2ƒš}y°à!³›&Q&†™h\­ã‚ÏL²!Ú75áÎÖ¶-z7¥ß»øVómÛqŸM ²îú‚C‰úÛ­ÛòØûqw °„0Q¬õÔ$^{p(èÏ iBçÂè ©É]Dà0;‘a³eAﮂK!²Ð Ür–ÿ ÝÚnö¤ÊÀèùc°hôŒ»²ªxwØùtpJ±¥@8D,>#à F[ÇÍ"kÈLF©É¦Öhm6HÐhh 0 "Ç@‚1€—Ä›¡ôn_®ñ¦{×%Óal`ÏÅô; ¥ ÊhŒ¥F[îv0ÅÛsó!e:|Ý?qÇuQ5»æäºˆ=¸hퟅêòËld Äé0XsV–›“°š#t©¥RН¤ ÞÁ} ¶ð¡áÁsF+¹"Ó—Ï=¤‘‘J•Ÿ]v¼?`8Úës¹!3‚.…)øç®-ŽŸZ–`äÌ©£ Dk6¢®S="ÈŸÓ(K ™áMS¤ ,f¨ˆbrŸGÅÊ"­äcPØ‚ÒÓ96-5äìŸwÝœ*¥.ý©­¹]ð¬ úÈ1vÖ ü@]ÂV˜u” :¾'Чë“ð€auêzîµöP”õ°39hšE"M'‰Ð~)!ìê5ú`*ÃëPVØÎ7ANžsW“_`wóžäc°`W´·È“H¸Æ,Œ™nÒp"#.}2òÚÎzuœ@&¯F9¬Â1 ÉF¥Ïøêf¿Ø%ÛAi ^y:Ôõ©·HNŒbôÆF±Ù”HÚŠ û’)ÍOJqÆú~]¹&ú_×p›t ­Ðp鯒–øb 1=Ž—îHûåX¼agÛ6‡É>´œ@£…ÅîÍqœŒ†NÏO¯„³ùÐFŒÒô°‘ÛÞºÁcÏÍ…û¥¤ÄFغõs”¿°}î~„Ùn×UÓyœ>¢‚ãÛê~Ûà%±?Ñ„ŸÅÛÚVÜçl…›À:+J.Qôswt¶Jsg+p€!£ð»Áй®Ø'³=Åám IòŽ 1Ä=(Ñ£8 ºûzÍ’»²ß_œxÉ;1e2ôã4|ð¶¹-nÁ $X§îa>?qJŽŠg£Ç#=e:Ì$lã¯áðè9Ï_¾âÑÆûõÛ›ë·+nÛ¶mÚ.âðp›"b*éóyEöìKμøžjžNÈ­¢"âÿPàÆ÷XlÉñ†““Á`®9¹ËÔ£-Á¨–%gôý €.jÝÁÆÁFÞbìpŽ£°³Ï8×üßV[nßr^ôôªŽü‚¢žZx=ük–\¨ ä¡¶Øp§¡ØŠýú-@0ìBo+ #Ù}_ʪh+8ãÃ|åCr`á¥^"úÎK((¢Ò1з#ÐâÌJ êh%ñ–áKÔþ†c.cÈiË|j²1,ñŽZ»ä†n{tot Š%§Î³’µK¶Ž7±×C-×Z¬è~ZL,‡9Tdùèh³HÐÔ3ú3ˆs"°ž{AŒ!çl Gç© œ#,PëF©6H¢2%“âê ,˜Çò½šME2Og ‹ÌuØí™©È|”n¸o s®Šñ·.«Ù “EÀ9S© Nãt¶h¹H ,œk‡®ÇoýùÃ(xfRì:VþcûÄiîK9™A)'Ô„ô|¼{‚Ñ#´«L U&±Ï£G Ÿ¬`ÌÕøÚCÙ†rr?˜¥“ûBRRIŠ2N4kïD£ÏáÜœ]è [õ<¶ª4D6p w7oÞ¼~;ë¿@xÀÖªsp°ñq¡‚,žµú®£Õ“zêæS£ÖæRÒVž-ßܼxþöåÌMP²,Ÿ@C‚!Œ•¶€ä0ÉÀNAÃ’HM;õGúþ˜°øMˆî˶Ø55Šî_¼9k‰ÿc‘æ£M^½~³úÖ.`Geÿˆ]40> ^»\ÿþóõ»ÕCvÉÆZàè,sDZrÁ#qéE^®L%y“º„[ô˲ž¸ðö„5ªwÙÊÐF|ô¿ß|T"‚2> stream xÚµYmܶþî_±è—jQ/#’¢^ôCb\Ò+ŒØM @ :-÷N9­t%ÛäÇ÷¥]­eÇm‘»$‡oÙáÌ3T¼¹ÝÄ›oŸÄ¡üúõ“/¾‘f#cQąܼ>l2µÉâBÄ ZûÍÏÑWÍ`û¶ê·[•G¶yÜæ:zºÝé8†;K•8²ÜÙÒÿà˜v,©õÈ›íNeQ]¢QQScå²aZÍsí­í™ðvkÒ¨lÆ0§;ð¤y˦»­«²Ù½üž;hÑçîÑ öÈ¿íNF¼“±‡ñ{¸í¯¯ÿùÅ7z!‰x³“Z˜¤`A¼zùâÅõw2QQä[èªI®ÞšˆT6t§È»p!݃Èw÷i{|5ÜIÛŒÙN3#Ó6P® îE¯±Uˆ\íΆ]·¼áCÙu56%…GUàâxé°eë&¶Êk³ç¡yïçTž‹Lª¥£ƒX½yݯÆLHÍçHô<ìQÆÞJ¨EÒ£f×rY®í˜ÅB*õ9;&œ”íu˜F¿mÙ%Eä¸iÑÕ„¨êº£ àbÖ¡IMÇì>„f}Ó—= gBœf*º*Ã}\ØÒYñüCB°¹TÆÚ1èûxˆ20Z÷–ÑEŸæ°Còô²ní>Ÿw5Œrœâ—7ãCn›îÆc_ò .Ó„/äFJØžQX(`.‹k!…:¹¿Ó¸DBó³s{¶¶”T"9©ê[¿ýºL Ë‚aJ@6/êû€ U¦Ø§Q¥êz_ƒÂé{Xé•z¬D tc ‚©dœå­ã†‡›a iOÂÂØ™[µži~ëSyèúû53ó0põ·U.EžÏðþ”ûÃ…ú²²áuHI Pq Ø–æÌþtoÜjÜ„×÷Rÿ zjŸT&ºêû®wŸÊÏÍ)?ßé4†UŒÞ1" ©óém€ÉÞacä 1’k¿‰¯{ýœærƒ3t* ÙQT?°å°ùµÇèsvUy2:Ë]WÝÛAÀÑÎk§C˜•'…]®ÌNÉ{õ@,uŽ]³;óYQ‚,m [ç÷!ØbâÏk>*²”ùý!ÖüXÚ æÐóÜÞRð“1½B0Õ‹t^ÊîÉ3 ©­L.y¿ÕÄØ \úçÉ":áXÉ éy!zúB–{Ý.V ÿNMï¢JgÝÓ5_çï<ˆ°èCÅ[ÁAj€:² ~SÛ3õ& ??‹ðc)yëÍW«€l,^Ãý`ça}ì3ÐÃb±YmÔ óRù¡†ˆJòS‚†<-0œ…™‹CÏϱ$HÃùƒF"éÆÊK ¤ž&$žv9&œŠHÓÓ¨g¯¾íeIc&ùõ(Ëb N]tˆÞ a­YįÀfC÷Lð–aj¦dôbõûó³†+?½MlÓ»QÈ,íG_áó¼?'çÕ3Êåì1÷ïá(Oœ?³{Z°£]C9üFÃä€:²€:Brœû<‰`R?9‘D!Å6ͱsÃTŸV"3çªOvzšï½‘ÞÕÓ¸›=¡1?­çA¹á¢ìÂùÎ¥yòq‰†9¾xži‚GÞµæ7i2Ýz šî@˜u˜:léÂížûž§>]«O™$èÞ:I\$–Úu-¤’}ãßû0ù}I/ t·R\¨§{8i¥²ÕQ!"Q˜M¤š¤âÇﮢšŠöÝy S]ÀМà“îð€r-f‰(rsáÕÆ)+V•å|sH¶-'>ªtýãiOŸ‘zÎFÚ¹À@|˜"Ôr¬£ ž.ÉE¶|¬oïÎP`?eÍi·ï߸š«3òT$jÎæ®¾zþüê‡~b™ÇýU, Þ™ÙJ ƒ¨ø P&óLHÿÀ~ËÀÐ\–¥ž³L$ÐÔ1PMºx0JÔ&'Ä–0þ–FþÎã ‘gðÿ‡ð8&Ó³±/3D¾ý$Á+¡2}ë[À·Ë‘é˾Þ{W³²“Ê…J.?I-2üÌŠ%–×–Ñ^g‘zS&Ñ«­"Wë¿ùHƒ[J¢U_?ÀÚ¾\c=JÎØã™}kÛ9‘þL)ý"uÒÿ…P€šóƒ‹]Rè^ùrr¾M‹äìÝúR!g×"• §S~EK†/ZÝôA+Zû~_²ÎÕȃæ¯!á£Yá_’ úÂÉß¹ùIãÍo35Ëì/ý¤ñÍß“B‘¨&ÙÅëŠ`P½²lŠj¦o,…c _[7öÖM_õü"üNßû 7”á[¦pîìû™@‘¼ilø4Héý$§céîÅê£ô‰´“Iz¬´Ÿ´c.]Ã3ê·P endstream endobj 1224 0 obj << /Length 2627 /Filter /FlateDecode >> stream xÚYIwÜ6¾ûWô›‹Ùï©ià:§q)£yvì+É!Éb£»1æÒæâ–òüã§6°S]Ô ¨¯¾ª‚‚Åv,~zÈïw/^Ýèx~äáân³HÕ" r?ˆ µ^üî]¿þéõíÏË?ïþ3V*ðã<æ‘òóhFÓãЫ›ðl‘(òã$† ¤X·Ë•ʼõXö* ¯è ¶ÞW¦6ÍÆ³ÆÎгËÐkX¼‡™©×;VýE¢üv,üå*I#ïng¸ë +wí8ØÆ}²1_ð#¦ãæ£5Õú d•Ãr²VQP§À?t°ÅJ޲ µG9¨3ÃØÁu_1e‹ú²zq_AîÚöÙssmAs¨—YäÁ†ã,öÞµýÀ‹¼‘µ¦c‹. …CWì÷t…fý•6‹½µvr±2ÁÊ5v¬Üö ~°pwb¼Ó#ÀµÁ™“ÀÛÀǦlÛô°¯Liïn¹ ½ôz¼Õ8?™áÔ¹ìÇ¢‚sÑØžíUUÜ<ìÀ´„s©4òs¥@Ž»’Î}N@²=/kš²Ýa 5±³b‚ºGî ^0S‡‘¯òôš|ëp‡Z{-šUG^7¬÷"²màz¢(ön–™öÌõ/¬=‹§GI`2w~ù‰NŸqþÔÏóÜé1¤C3ëÂå+X8!ó#•Èòw·ï®|ÿËÝÜœSG¬{·ÃKÖa.ø%ñË2ŽÁ9FÃM§1Y½1 Kdt’ þlmàŽO&«Àë*¦´a„FÄÑû%ôÉŠd<JøŠ‘­´›³¥«vkË“M<6%Âb×µM;ösÀ?1˜Ï^O¼9ÂgJjÚ®æÏ`‹ÈƒUuØ'>Ç´g7Ü=¸Å[ñà®@ßa¥²hšvàñ{–D^[ïv¡¸‘Ö­k³¶Å`W§G^¾§«–= –X†îåÉ `ö€¨þ\‹¸—¦OšÔ îÚ¶;ã4™l¾{» '7þ.ãl5©Î£\ëØÈåÔ×n?\à¢w ûƒÜû¥±s‹߇ё™žZ8ÌòSbÂEÛ!‡3…Ãì(Ã`Wš-·‡–§:;˜“®€Ž?‰ 9Û{+ÄÑU±»g“|¢4ÌóTãÁçî2Í|­Ãsq‘VŠž¾âðôô P}¤¢4ÐVb¯·Û¦¨¸QNñœÖ~ûQF°ÂŸRNŠóÛ9ì´´ÜЙ¢`(½†L†Yø©Ö í籜$žÛxîGÕ˜à»ÅòÊ.[2Ùúí«÷,œÒÄ,:#D§:¿Ò¾-?™áïÀß_æ¶”¨Sž¿Âxyâßý´À¾ø7ÿëBE‹ÒÆ4A£B”ˆ¡ÊÎëÿd‘ùy’D?„Êÿ,ÈO$ä„¥’ëaœŠ1­ð˜¯’9þ§\SÏÇo_ݽýx%ã> stream xÚ¥Y[sܶ~ׯØé‹¨-M€à­}²e;QZYÓXm§çËÅJH¸ä† -k&?¾çpÉí(S=,ƒÛ¹áœï@Ñê~­¾;‹Ü÷ÍÝÙ«÷q²QXD…XÝíV™\eQF zÛÕOAõÛ mÿûñãõ퇋Ÿï~xõ^Ì–D«µŒÂ¤HxŇa!ó`s±†_ÝÁ§H‚­¶Ug6¦¹ç~ÿ ¹Ñ Mã¨*ø|!3XbMÛ\^¬ãH»¶Û—}¯·<»´üý/üÝܼ}r÷öS$ÒÊ”µÛS׺´ÚÍ-;wT_ÞßûLƒ’ ë"U0ëÄUÇÁ½é±¡`«,È‚–»x>ŽÃ ë#¤“ø™Nò0j®Å«Û››ë»(ñ;f ªv¿÷Ì<”ö©¶ïHiØnw<úý»×Š—þé ?EIÔn~xS?ê~è>Õ©JÍXù$bUµ š¿ïÚºÖÝ_xÀìcñt{‘¤aZdp mŒ,1!ò0“±ŸdœJ>}~šS¸^’¿èª¿t”nñxØFýΩÏB%ÆI-ÈÝ=«5½Ž…Eš.+Ü4½î­%­7å^w‰L‚ÝÐT=¸ü ­ðz»¥"VìÍ®jeÊ%‹8‰ôâ8Ö–NU1¨='>Ó3è¾.­³Æ^÷-]ø-\I¥”ó{â,‡N6íð|Õåè'lÝ#ÁÓE®‚eE‹8 ó,_Vô¾mÚÞìIϤTõ-×~²½ÞŸ{nq×¶1•»˜5³^¡^~åx¨“d°CY×O‹s³wwg¿ `#Z‰U¬d(²•ÊÀ×R±ªög?ý­¶0!*Œ‹|õH3÷«8”:c½úxöOŽßs¹b•„JÅ´—L%Kw¯{¯«Ü^ýý9J&aê–ý¿,(°I!Ò9 7·nïn?\_¡A¾ry à:¶i2·iU6•®Q‚Ýö…6½¢%˜À8á@Ã81U’†¢|TÎO‡¾í.—#• ótŒà¥"¯]Œ`JàÀͼäĶûŠç€Opc~¡,Þè^/ÇÉHTòE§'¡<Î4Nx¸Nô¥È˜øÈˆŒj9*€û~i»éKÓh: -&h@YÓYhƒr £'YP|œ ãÞ vN‹PªQA=€vÛ¯D p;7oa¡‚×Íôà¶k‡ 7°ÑLa3·~Ú†Gø~} ]ÉS à(eܨ.+žsÜÂô–"@Rª9Œ˜Û–㇠8:ëf;¦KríÐpÝX:×½»ÈcÂ`¹ È¶ÇÆµÙ¡Ø2¬Ô»Ð,›­£}!¼+eÿ`™h7ëp¨MUbºá9•S*Å-™±Nð bNqf*’háÒelsÞû£ln ;Æú-WÓ®Ûú~œÍÞÔe1d_¼…¹ 3‘½ð$Çäµ/½§D©Ç¥ØI‚µp‡ßbÂyÄyú3ýv¨!¾] ÑËHþy^è‚‘Ñ\UÀÍ£éx }Èšã4ÿ}3ô<›:J¦öáë*UXdb®ób˺Óåö‰½sÃÞ©ŸÛFø¬ û©Ñ8oq˜!O\ôeõó9ÄT ¶Ç^âOæÎöEÕ³žŽ€\‘Oâ.%æ2¿zP1¡#Ò½LU¨8A$Iœøéb¨ ³XB:NÑU&x;…AHös†Ù`{,@xQ·vöÀØ'³¹=ºáõ %\y eFBª‡fè—CßBQd*Hêôž˜]×2‘cû³ uíþdʯìó®Ï-OQeCeÌ™Eԅ˼-{Œ"Áš`¨PiK—(w® #¡°áR«»uÍÑ º~5Ù„îhü‡ÝpºøÐ9`¥ ‰ŠÄû*L²qè=½åïÞÅ; ¨EÄ@-))œNsW”2ÆÆ/€‘c?ãâ|çhÓ>®Û•Ƶè²/¤G™‚Æcdy÷æõÛ÷K. )ǰò¦ÁëøHÅØˆ<è@h{°Ü·¦vãèF¸È@èÖå6a’æ³do°¦ (âK» éãjUtY`ÔôçnÌEÔ'¦cIKš’'‰É-B‹Ñôºå%]y1÷çÉÑ.N&À¥‚î]¬øOÔÃîà‹µ&ŸË‚7츒ÏPpìµÝV»²zÆZ,¢¨M¯ "hÀ½Î݆eÍAŸBºešmyºc½1÷;ñzç6â‰~t ÌJ\ä¶Ÿ€:žùˆŒ–ŽŸ b|ïàÛ‰ìîì7ãÌÇaÞ'”8ñºÇ&ßP¤õ[¨Ñá)z[ÞäñÁx_Ái`a'¬e ]I9†.7âÏ·%×›Áè“Õæ¯M—£ßxàQÑózhè͆ê”Ê6 ¸p^Í1€ø.Ž›Ìø>åDø+}ŽGÖ”ÆÉ;þrD„m}m\Ÿ.\¯J|HJ¤C³L¤•Öµ9&$.™ùUÈ"¸\VäÁÇ㥆Y›áÞ­¬ü#fÂŽþÒChÔ3`úÖÐãÛP÷nçÖ}»Ò—Æ4bécó·©4Éh•§s¼71dãeŠ.­!aÂjù»5¶šDg7J:õi µÙt%Æìl†Í¦&#Bg8,ß ÀZžþað‰F#}䜻² 80deS¤•q<#§v5UåP³Å'¹tt­¯>éM Ö·žqT(óãóÕ+ƒÞþ>¿è¢E†.‰TŽ9¡­ý…Ì×Üpqrᮬ=mŒT™TnD÷Õï'è%ÆZ˜ªbn±Çùý‘¢œ(óRkD„±_¨3Õà:ƒ‹÷Ÿ’éL…gȲï€KCTât KÙk á7ÚvËÓ;@¡8Ô<„ÑYoy¹=|õÌc¦÷[;‰–àÒÈ %¯ò³‡™L®„sÊâ[o2"ÏBA/©“WðŽp–Rí”…*ψA‰Y6‡Âé*‹4UŒ„E" ç‘ãó_MÍèJùÒó:RÁ5oà:Áã|bî¤]û0™µ"L«†®Ãä[0¡Qö0f‘ËÀ0^©ýæPvëJNX‡ F2_~ۜʧa”&~f¥üÚKxqäûXJÅÔ`„5Þ­d‚ (;F41ºMÏü_DÏ$ªlödt½}×jØkªž*IÌ@÷¹|¢(BuútâûÉ‚Œ ÌãXDZ—\º91’—ûC­Ù%]q’f¥+õ0©Œ%ÒÑH “<Ìó¹r®Én1B‹$?wÝcý ¡ñ;ú·žFþ€89öÀôÄC£qÒ¢åvy_Ò‡’„¬ÛÁ×iÐpN!YÉIe­¦ ¹`ûÁÖ ŽŸÄâñžúï3d z"]ƒŸù—Þðÿ#B@ endstream endobj 1233 0 obj << /Length 1741 /Filter /FlateDecode >> stream xÚÍX[“ÔD~çWŒåebß’Nƒ< ¥–%”ìƒPe˜éaãf’!–-å¿{.Ìd6 KɃ/'ÎéÓ_ŸkŸˆÅë…XütG„çwgw¾ùQ' )b'œ\œmV-¬p±0ð¶Y<Öozßû6Þ×eùB$bùòìgX£'kLliÅýåJ¦–);¹ØŒºo»¦_w¬c£5x–z"ßnf=~ ŠCBÂ7àC0Ò:»ãYàÇOdì4RBœC¢%rêb—ª”ÄÃPªa H;™EÛeªhœÓçE‚þ‘Q`*§ñ†ÆWË•µ"zDã›B'¢öˆz¢ QС4èP `dp;§¢7D{¢þÚ¸eV8¹Ôc¬P-*u:‡™"Eç‘Ñ=ÑŽf$XQ¦Ñ=@Þn…:Zaq=l-ˆ’ßj×{¶:8^–9À,I¨BàlÖáV@d–Q°•"E , mDô%¼$Ù-À/À[ÏøØ9Sf @Ïø”ÓGØÊ"€K§r<1TDɇǻÁð¼V@Læ0ßÄZ¨à{\!ë‹.©Bâà4mX*Úø…²f‘ëçÉ$³ÿ‡ÚÁóF/쉮‡ùL ô*‡*DZÑ-QÔ¶TÄD^´©¤gE·iôœüA}IôÁ¼yœÔš2\uC¸‚Ú‡|7 ƒâÄÉ9âFQbÈ é\PWÉñÀ ·Á”àSä…Z1hØwbøÿ¼¿5!)¼=JÕ‘™ZÌ%Œ RÊSš{Bô—#ú˜è¯œRnR/€ž€7Ÿ_½òÓÕ«>§zåÿH½é¼z1 õGi£cÍT\Arê¼!ÁÛ£¿Óµ$hK3cÒÒ5ÈF…C)÷D¡ÆÀ<‚Íó«AÒqÅ5Ê9ÁX±”jzæO)y€¡b _†‡x@­†Ôo‡ ­¸uÆÈ>ªó™šsÀS´°DØšTSðÜ·ðGþÌE Z“¯I(Ó¡üI¸  ®æªÆµârrÀ4ƒr!§k¤˜9¡{”Oøþ†¤¯ TAÒ&¶NMÛÔ¶ô~j©úÝ+ßÜt™¡© ×ä?B#§œ¡¾M9Z#‡~ -_C×Ç›Û7gck²[´opbåÆKÛ´oãý¨x"‚–o²~Ó—>€ËùË%VÒÐ÷{žÛÖaýŸ$²§‹y¨¿ÍÝ ‘/Á5ì@Ù‹êä¼Û:3Ï‚@Æ{Œ,Îd2U}ÓW4Õ¨ü[ªý·±4p)Æ3]"èš' K¨kïÕÁe¡QQ±ƒž´È;ÏŒt§>U$Î÷ÍÐq—W¼Ò¿ókìƒ^CQ©ˆŠ-³æÄv…ÿ—:©Šòv‰*ØÅÐí¿°nÃͧôyõt ~,àÙAA¸ 5…ç¡w[çòn¸ µÞÔj†®2pƒÑòŸá4‡æœoZt€°”ûÚëäÏÀŒUE°§9±'‰Gs®ë4œô™÷³ •‹õ¡ãÍûweñ*ˆŸ:ý4ïˆNÇÄÛ,ðË&§°½¥~ j”='2U±sîØ3õ|ðTþòçûÆ“÷Ó]›í ‚“„ÜC/G?S>hú šûy×åës¶|ð¸[{D¾Ö=8ýš]^å¯ýI,´ûÃ?˜u8†^}sâô¡i“Æ4¤®»ùX@JËXYÐ<´ïYz\pGžÎîü öZå endstream endobj 1255 0 obj << /Length 2218 /Filter /FlateDecode >> stream xÚYIãÆ¾Ï¯ÐÍl Es_æ–x bàéœÆRM–¤²¹È,ÒÝýïó¶"%MMÜݱözË÷¶R´;î¢Ý?>DòýûÇoLó]…uTÇ»‡Ã®LveT‡Q½v÷9hþXô¢?>MêüK”G‡ehf3ÐŽï~}øéÛã«íÑnŸDa^ç¼û‡gÝ,³¾Û§i¸½ØË3ÜŽÛÇF«yTñÒA?q¿§qùÙ ²¢Wƒ:ê–×=Þ%UðÂóIîià ž§±ëôÞí³, ~Æ›ôŒgMƒý‹ÄXúcUXfå•Xì¬I,ŸgÓëq™}¥\>Á>¸ºŽƒqh4·æ°z<¹ŽŒê?‘2Íôñ] lqügè´µ7{„î™ÕÏçÎ4fî^¸o<¹1¿DqF…Qe}̧uVQ \õ‘Ã<,“Ê­¸¡Gy0N|êrC%lßA§ ó4Þíã4̳ZäºLÓÆlT¢T´j¹=ð 4ªI0LÂWùHó$ŒÊÂÆz³>²4̲Ä-Ü`' ‚÷¼Œãbc]„YUºffpÙ$œ'¨:@^ki¹,8Ow9hüaùÀ 3´52˜Ywm#‰,=DQ~-²I9¯älhXù™¥ãYÁØle!àƒnŒxÇeX׫.V‡é˜>Å$µÚ6“9Ït9 ß┩fžW8Bi™´ ™!wG’ˆmý fJ6šå9Øä¢¹…êÆ¯]š¤PÅQðoàzz2VM—{ª³¡Xå’Ýzš.lö$sÀüq[Ôò”j[ƒS÷ƒÐÏóG0¼ªb°¡Y_ª‚-½žLƒP­·!†ðªl®$g‡³è9Ø GkÍ#h›œ^i#Û[•1€|*KÃr9:!ä()P[xÓ¿iÌ't€dQØïöyü¦›™©|IEVIñnš†‹Ëóõr$NIë ©,wÎ$4tÍ­xŽ4“¼bÙçëùMxV„q•¼‡nBþÐJœ©2(ŽzÚÀÝi¿¼N²àÁc½™B r$p:"ÙÍK S$Eæâ¨0òJ8V‡ÙqA uš”1À¯¸¶Ð¢#†ûtÀd†£Ïàó×9àj[ødæ“ø%_ýl쌷ðè#šB;E e¥x@˜šÇó¾C—Tºã!'›,½‘Év>„'É% •ŒK‡’ÊÀtŒGJ6¬ó\ úÆo1õ Q½e@ļ Ì»ëào<ÈŽá2¹ Ü ‚®SÄù 륀[D·\ ¹†a‹ƒtáþ%ôr³åãjp‡Öçåz=ŸXnèE’œ±†–½b G«[FUr T:µ ÛýYM³Ük%z $\ÁáÎÎâ°Nk¯êÙy¿Gù[Ú¥£!ÄATP3ç0€ÿ;Ì奈ÃqÅ Yeä‚·7_ã6#¨=%EfÑ2BUFÅ&£}‘g“|''aQ¯=T'kš.¡¥)Ëü"çLô°tù±5Ø6j¡„4ɨfÁv‘sâø¾H›qÑxÏhd¯ÅÇåpj¸œg¡´3“8ÉBÈæâg‡éQfŸNè5$áäs~ ïÞ j&L%Ò-%6tÀÿíW“¹ç@â…”ox‰‘}Õ°Úîñª¥ƒë|8&] ÷lu‡Õ§¿I“5"×ääÎ÷þ“B5­!›ß} ÛèÍ}ü3¿Ã¸'+‰ 85S±¡¹ã{òU9Ë:÷<Ï ç„*løÊ _ëË}| x‘º:ÉB@ßµÓDÈ%V?ð©îi}=޵ºç>é!ÁÃÉͺ*y:(|ê¼Üxž8˜O·ÈŒâîVLJ^xîQY|_ÁùN+;óh3ö=Ї[=ŒŘøš g fì}¡áŒ){RB}Щ-4áP‰z‰]h '#\ç/ŒwüÄ3žeUdt°éM§d•¥ízPx´©æ¼ÇJ¬q&¡Åô½nš¥ !^”7XšGw¸hÚÚxñ±j–“þã3ÄÙ–œ ÈBÍ °^Öuæw¸¡÷i&øˆ„ ;Ž »[Ü=›-‣ î*KNò ô —ûŠé©Ä[x”U˜Çë34vréÁ›Î•a]®6<’Éåø^›_erW@îåU\ód¤à5ø‚ò²:¬MoþKæ‡v­c¬žCç×Ö¿^v >b•)¾°@ÄdÔWK~xøð?iÂÓ endstream endobj 1188 0 obj << /Type /ObjStm /N 100 /First 921 /Length 1624 /Filter /FlateDecode >> stream xÚ½YÛN7}?_áǤ{_|©P$’ˆ6R+¡Jm”ZàDp¢¶/ýö®í„„9ÆÒ2ž™å}]ÞÞbÌÅc®.r GYl„br*í »T \²+Qm ®(aP£«ÌörB 3<Â’첋Ò^áš1ó4â  jt„WZ J]L Wñ25*ÔÃR° ¡ÌRª¨=±LÓİâšÄquƒ™W3F’ “Qm£•°MØ‘˜8”$•YÄ,G*0Œ"d&JðÒ Í›®ÐQ 8AJ(H±'ÅFð›÷1;ŽL³d×ÔäÇÔ<‚ÿLjú)`TÍ(`üsÙ®ˆ@†$±€Á ŒÔì7G[œˆð6sœ!uœSqPÎî!;\àrÂÁ¬ÀUxZ«z#³" j–aTMò $‚hPEIJ &°¦ˆXâð+’Ì ˆRK…MSC‘h‹%'‰L:CzNæ!Â"ÅèdIA~ð£Êm„ |Ĩ8Hµ€!™š& ÈBS%ˆ4]„ÿ46eÈRSª*%…à‰r° Ê’?R¹˜!ÓXi£&)t¨1Õ2£jÔ6´²‘Øi²´šˆ”¡À09&ã1šÈÔæÞ¶⫵éFþR0ò‚5)wAŒ„@¹ˆŒ'{‘òDAf;;³áÍ?ïænؽ¼\,gÃÁûß–íþ‡³Ë?gÃóÅÕÛùÕaÀâ GÃ÷ëáÅal7³áõüdé¹$ÏFæZ}BŒ$‹e(F¨]·³ã†7|·x³pÃK÷äóΗßx,œ§îÙ³~Ñ\£å_#îmõf¯%NYRÏ’}b)õ ª|µ}1‰G>ú”¿–¾awg§iv[ćƒá§×¯ì÷ÉérùîÛa ]ž_¾}q>¿€éþdq1üû×ÙùùÙñÅðîjñ”^çï××çþtyqþt m¦M¿µzSµU+à)Y3áQÐÿWËÓj¼Å4¤Ûøˆi›@Z`tžÆ‰ª'ì#“@&òŠMxHU=¨`D¹.¼uÕ (ñf¡l_B‚ùxC­«»yE­³û4ËzNða‹­nXl¹k±¡qkfŽCRî."ë ­ùƒ’$ OÑiø€ë4u’‘‡I ñRéy§òV¡ö¤õÒ_/ËòZ–­w¿o–íL0™å1hC–·Dp¼>‡óÞÎǸÁùzçcèp~ÚBñUж·†KèºÐOI¥2 $ì«+áïå4Ö9E~`’Óz’£< ÉÚ“d—¨”¿ÒDå)%õQËBžjRô|¥˜½}±˜âˆéSìÈÖv¦­äÙ¶œW6ºQ=\)•·éw­né‘ 'É:­ˆîO+âZ@V;ШÔi\b4¦›šU jò%È4PŒ,œ¦œ3Nd6cÏ =À€³Tí°Ç~_Kýò£Êûñˆq¨Ü•Ã×Û›©l XzÅrÅr'ÅÆ¸¤( L@T8ÚÌż¶—•(@¶%è4PRlç²/?»Úǰϙîì¼iÿb^gÇN(­±€©ƒ#Ð,XÁ¥XÑas³š;€¡zû: Ôöå«C¢ÝÂÔU  õÙt^¼?ªéFTÍÒ#1¨×g¤²×Ü)Ñ'ꑘ°âSDÍ>lj¢Þ¾øN™±MuðVB°ïnÓ@ÖèmO©O7¾U _¨#…í”}ÿŸJð¡ïËÛ¸`jÙ¸Mj!È+PªëJùþJ¥£@@w¨\Êìí“@¬>0§H m¼Eˆÿ­ on endstream endobj 1260 0 obj << /Length 2323 /Filter /FlateDecode >> stream xÚ­ZKsÛ8¾ûW°æDWY @€/ß<‰3ë]'ñZJíT%>P"eqL‘ ñ¦*?~»Ñ MʰÆYñDѯÝ_CbֽŬ?N˜~þ¾8yóž{gNÄ"n-ÖVàZ‹&á-±¾ØÚÕ©Ú›Ó™Dö¡6MZQGœC³ˆ›ì;ÎJkÝ[¥ÔØáÃ.¡›Ûu¶ÌO¹­GÖ¥^a^æq•Õgð1{Ù6Ô]¥ßÚ¬[æ>/—qŽíÐÞU¥Rb•ÖZZÝÄžØ-ô¼>åœÛÿ¹¡·fS¥qrz·ø'ì~Æ…ãɈv¹Ióm­rpüÍ{Á-ÎÈó\D¦»‘p Šp\Ç¥y.ΓÜa‘ÓÕ´Õ·6mÓÚ©ËÕCÚÐü1Ú3ýÁÌeŽyôÙbƒ»‰\»Ö;Eh¾ ,³"É ó¾¦Æ¿g‰þtÙfy3ˆˆÛï>ÎϨ>¿~³¸îÞ–-¬ñ•qöI+ZÆcaB“ò¬Ð‹6U\Ô9»,œÓ™ ¦¡M¼#[§EmX}º¿(PÏ&ÿq % A’°¡•Ãi¯Ð†Ø«}Ž^²"k20·þâ1k6Ôj42Ü`×çŽ/‚Î"d‰sð˜ªiòÚd’0­e÷Á6m6d…D9†5wxȕɄ+&“\ØIºŽÛ¼¡—«7Ÿ¨±ÕKèIYMϯ\È&ÿíg“þW1@˜:6Ø +`Ëh4°‡JУ[{?t'_÷\ ìÔè‘Gì*«­Z13š¯Íº#¦› 8Lú)èU×¶¬j­P·r»Åmà;øg£5(vÕ×Ñç°ÎùÄ“´A×ÇÍ3ÚÍœXÜP‹\¾ùǺ`ãgôú¸Éºp%Aİ£··×ïGî'í¶ÖNFXc+Iól›iw“öº¬ã-n®ƒÝàs,B'ärès_îLÎÆÆ'ÿÄ ñ2O©¹Ås…ÒîéýyH@eu°i•æúhàÌu“n© a°ÍÀo$Ænì-â-º‡’XÒ ºF»Å¹úØU4°ê<µp#Ðh•·I¯×Å{ÜÞÉåâäÛ ‡mAT´dÞžå Ïa̳VÛ“/wÌJ`Ìáˆ(´ÕÔ­5ÐÊ­ùÉ¿MiIF a-é„Ì'¸>œß\¾=£s)y¤fŒ¬lR+ \ÃJ.w¤ç¥(å µ’ë$ñêãåBÇ5“pRÑ$Ò¹”ægòý ¸Œ9Ìó&QÀeB`0VàóÇ«?µüù§·ÿ2h€RL£Apo¬Á|q{yñálïtÏáõ¸áÀ„Iô¡p|pÏ‘>ïþ¸½ø`Œ3Üç†ÓüØ¥_™ÇÊå_ð௷iÓVÅS>Tû®›ŠN© „c ¿ÑÌ Zk5ÅØÑ$ÑžÖ€B&exè®è&aRøk-LTÇKˆ3žoÿ•®m1àp&˜«ÁWËYnÔ!p ‹õŠÕcV§fðá‚#zFð³òЮ¬•0P‚ržëÙë¶Xav~¥E.’¤ßaÜ帎k+Å4íåðƒ«<®5ª#Z2“R‚íÕLäUQÓ¢Ú¤ßô{V¶Ï?Ögk­…ÒNq³×g6gU˜‘ÚÐHíʪ¡Ö—»x›å?îÆèåw¿êß2R€Á£H©a 84m·i‚”Q‰¤ NtS!]IE>+ =R+ WQ]ìÎIRùÐî´**Ä@cñö†dq¦b>Èû3©j%l¨îŽiø²ü?l¢?#‚ËÕgÄ|à‘âºÝ”sð,? ‡Úpö¥×“æ{c©Ã&è`lÒëŽà™¸»p‚0S÷ñÆö˜»PëëùO¥Až)ZŠþ]Ý·[ i­}_Uœ*bÆä7ÃÀ%z._—kêÂptÆ,xND$¦‚»tOh¢¢XWžè€NÄ]Ë ]ÇRr ÷ÀtrJtˆ¸ #X,xݨ­‹Ûó®<1Ђ¨Œ?".x»T»(ÔqUe»îPŒÅ„UH–­ˆR"&ª77@š¥ó¯ƒQéö¾ïÈc‘€†øc±”xÏu¦zîãe÷`e_ÝhÿL’*­u: †Ê4,oÊmœ4Š^MÌÕýDZÈc{º›~Ù¹€Zx‡oF^Ÿ³x”ÊWç¬I¤÷>5”¾¦»â±½Lc/Üb@a±V¿ˆ©*jɦ¯îº´(iѼŽN†xŸ%öM׬‘Ç‚‰¬ã‹ ä/Xg é½uFÒ;®Ð_á•mÊ®\n*ô+Ð&Õ|ƒ~ÛÃ^Œ±W?M<£%õ X°g€¶€zý 0fqtùÆTùÆ%.ª3…\}ã >åxOÃíóuœ×éËL¡To™¡RŽ¿¿±êÒ^ÿ#Þº7_g)BoõaëcÔ«´­S¬`Y>ˆŽ>ôq±j_ø€ ÞTí!ЧP¨}¨Ðü“Aªe ‰á±Wé‚+â4z{ùy~yñîÝ­ÑØÁÓ-n_׃ =XH&c¾¢ˆäÌ2ÑIâx_’&Щ·éP§ƒ6U´>˜Æ¦C¡Ê¦7Ÿnñ–wñ7y­Äû+›îo# ˆ*˜~UÎFS`Kÿ_É€ endstream endobj 1264 0 obj << /Length 1932 /Filter /FlateDecode >> stream xÚ½Z]wÛ6}÷¯à£tN…à¤ß×Mݪ’7òölOšJ‚lžR¤JRvÝ_¿TH bÒ7O„@˜¹wpgÞ_a÷|÷põæ"‚Q„#›:¹ 5­ÏÒÙX Ñ@ü¨žš}Y@ãÅ4h”¦¡í§Õ“¶_–¯e¥·NjˆMÀDpk½‰÷ie()m@¥zãzârösFæGå]&Yšdº'°8ø  #XFìrŨC„µZÌ=«R“ì@ÑË Â' EwÑùüÝônvk‰+-"+#Œ©éÊŸ|ä §Y^‹Ë2íLqžZ*òrjC “1 ˜ñcj•ьϼVžÖ! :ÐÚ6È˪‘;޲ËD‚á!åÝ5góÙ»éüægËÞZ—«"ÙUMJø?6T >uÙ\9õ_zÕC¢)‚"5‰€Râ2‡0è@bÛ >a\ÔŸr¾žÄöš7Óùíno¾‚C.¢âdG–Éã.ÙY"ϳ(TT ä…éÛ²("¨oˆ¼ˆÅA jXìÔâ§úeý«Yì¬9›/îÞßßÝßšºL1¯Ê3!!ù F0BQÈT¿¿,ÞŸZÁ±Dr (8xAHžZ1{;uFÔ¡ Ï£ UA…jÃ:’uIxTz´÷ëî"?ÉLÏ&é"Ùô”°BІ)4„ø 4/-4†°é° Ú6ðB[ü‹©”•.ªÐ䫸rïä\ïç rgÇ8—:[gñ¶Oi ðŠ “/„€:ŒG—)Í0ndÀ° ®ò,Ó+wèzÊËCìN‰Ó…}µ˜ÝÙžÏc¾€&$|Øì—¢Á$€PÚ\A”U‘d×½ç¬A? ×^ü\é2תŽ?½¶0ãÌ!ðÑþ<‚‘50rŒ¯F•Èq±S%½0(há41rYÐaÐú¶AU»ºÝÞ¤ñ®ÔޤºµiJ²M(«dUŸõÞüÀ:+@UBê%&œ©£7_ý¡+”Âw:û lh‚| ¨íò¢‚.bgëÚ‹ƒ ÅHD®¾ý «}‘¹d­Éô‹÷ºC %Û­^' né«YèxýzäT¼Zi8ìØø:ì^¾ù¼4v™›@áqÏçíñŠÃ žÃgõðÂ:±B i3äÂB*ûððÌ ¶æ|ôÁ‘AuAœ&Ÿ_¦*R²™ÓãVw~%Q¨ÂføKR=”ŽÌ¶û­K凉”à|´Ü»—Ul-1÷(´fÔö.Í®0Í|cŸf+¯=¢ñ„c5Zh72kîs‹™}!Ù‘_D{° 1A|yÁµÍfŒ7L&j«+-¼³ü.N ³>V¯;ýéK;@´w€Q¿CÁ(¼Œ­š˜'-ûZKCb÷„2{B5{Â`&B9z¨oý›SBýuù”ïS÷å²þPÛõ Q=Ÿ½!jïNêlºöÛúBÀö¦æ‹èH£G/@LšZ³0GØ!˜"0ÈßýU*g²[¥vWX µ²™‹qå®a>ܾýÅ»‹8ä3¾S‘‚rx‚L~?&c0ªPÔxý¢CDPÄCUÏ¥"Wï}ÿþƒßs·Ädàš”I(óÊœ \ߦÂ5Š¢o/rÄ‘òĨaO]û õ•¸éÙ@òÍ‹­ý‘o¼´r8Ïü]4rØ\b+{f‰–ìÕ™odÏÙP?왷Dz§xhe¯žáŒìMDøø(õíÔ/"6ùœ†P©«çå~cäo›¯µ->º‚ Lþþ²:žÍU¥Õ(@ªsIï«ÝÞµ—{((™.I~Ûü ô¸HSWAž©-”ÓQÉÌà6ŒÛ‘nݾvnûüêbîÇdÔ…CÔ Y'”$§?uAµH ëÝ›ù„åÿC¨`‰S ëù_1¤¼V¿3;~ßÅ&2Põiþ>‚”5BÉÛÜ'œ¯|-²â,²v!åœk½÷y[ÙhlÚÿyÁ¦@˜$î/ „tAêû/zž; endstream endobj 1268 0 obj << /Length 1895 /Filter /FlateDecode >> stream xÚµXßsã¶~÷_ÁGjÆÂøË}J§I'N“ö<Óé8~ %ÈbŽ"uçþûîb—©£ëÞÍåÁ&°X,°>ì.$£çHF½‘üýóýÍ»t))JYªèþåI”ËRH½}ôûn÷ÁöÂÛþTýÞÔ­ýE¦ò¡nÏCÿ¸Ù¦I?ÜÒ·z¸Ú<ÞÿíÝjaXFÛDŠ´LÉî{Ûo¶Zë¸?Zl˜xoÕаôÇw?Q—Ü> ¿H¥­«ÛçQzª{Oó#aÕ4Ôhí ùn³Mòx·6IÛÞ‹ÍÖë[ÚåÒ}-s‘ÂÆåÜû»‹÷kž•¹Ð™§ˆ5³[#(ò0(DV¤×ØöõÉ~[n¿ ¤ù¿€dkÔ¹†ÊŒPi‚ª@¨Š%TékP%I) S~Õ¸äkP%ÿ*ä"QŠ¡ÊVhhóª—c½;Þ>>:|#ÀÀøˆR0»@Î|F²ÏËß&Yb!3µJ2ôí«‘Ó¥0‰^E®kƒc¶ÃÐîúºk¿=bǪÝ7Öý˜¥)øf®0c¯Ö|(¤P…z °Td2]ìn×µ­Ý}Õ•ü÷¦€+†Ô“&£c¶ÀSßú¾zjj<ÙèIÜwôõÃngíaQ&þÚìÚwdºíxN ŠËÙ/h‘÷ ã§·…­Xœ7*v|07Öùô3Mp0ã„£‹J‹”#—³¬§á¸ÂÃ÷ÅÕ½¥æÎÛßb;ŸBèÁqÞ…pœ*”|¢ÌTqåÙÀ¾®žÛÎOªsà°vÝScOžz5K?¡½npãœàÁžM/¹Ñ4*¾ø¹[Æ‚_lƒÙÞq±çÊUÁ;=" Ü7®;-R˜ XÉ9è1—ü*#åZ‚¸Žô¶ý"&¦×LÔ%3Q—«ÁžžjÀIÅ„|É”s.fIrá"ݳJà" ˆ‹—Ù ž}~ÙÅ‚‹¨vv°äçÓw (5k˜AT€Ô:å¡» ¶z^Í>F l±âm ÀÖdF”ù’ÄÚ4wK3qÓlF\P°t® ^FÊ©3c,¨Æ‚tÆXèÍ JÄXÏ.…Á€|g"ÌÁÏ#/L…;AˆéLaÁTf–Òç„5 ™"»ømŠ4`•#VØ AžºCϱ_ŸNpì°û†5ë L®b0Äîoõù…’Š´gÃÕ~ï¬÷ÔéUO Î~j7ÎúË?Þ¯!»f  cð;,à­(L|DžxR©ù ™¶;F›ábƒˆ`RëÅ&•áL1¹s}ðDaø‡m\VñŸÖÜ¢” ù(÷ŽyëëÓ¹©¡ª6Ö“¤¦d][5W„SYÆß5¾Ã°žÉµüMs:¸Ýðù•"d0 YŽLà SÇ‹U÷ÇÕú;‡Ò¨¼**Å…Ï×W:/„N¦rïÌjõÊP<,aÛc<_+! &<¶ÝµRSúÁá‰JEµåÏ•cîÃÐàq``_õÉð V÷½(¹án†+A©ŒÇÊÖ‚¼û0œIÀÎF[Úæ<=_bÏXžy®ûÝTÀõ°‡¨±Ð…»çn©ý‚kbZ 9¹%aÝÖx|£þÓ±”CµôŸ|˜ñq¸Ì¥¢µ€zG¦§Q,¾ RŒ 9°;#ZþêùùÕ5ÚòaÔòþÆCñqB.ó„뮩íÅEóJRÅn43¾*¦»F‘Y Äj ý)x h1Þ}EÆýÂ(V’¶»?íªÓx^•{Æ"sþªã· ì|­â†Ä˜§ÅÕU\\m^¯¸Ïk§@Í7ªr(õ S¬Wåzï¿~æWýxüƲ1è¯ì-Ó"Oò«GÈëGn„I§7ËÝšÅ|^»¢;ä 6jþz÷ÛÜ÷à\Àò ¿BÀ¹*Ší—{Id&Tþ&ô‰Ht¹z!Ž»¾o{Ø`oï¿u6ùsSíø%²zmp€?Hîÿþž§1+ÁÊI!uv?p|ð—ÇÌqý)©•(.OÉîl[ïìÖ*s•&›Î~¼¶c°Ûñ®ñ1ÖÆ¢ßN?̴σ£ ¢¶6Z$`zYž¹®ïBqñ‰_`•K ö˜ —³§Î ] ¦L|„û#‡ ëhâ×¹ÐϧŸhà_™KþU¢Æ$‘eÊÛÉ×óÍLï_!XzZÒ¯HÂëÁ{vå‡wJÑ4+Öœ^ó@— röua>¯‘§È Ừ›Á‘WÓ›ßßß|¼Áp$#…?k*Y±«hwºyx”Ñäl….‹è%h"UäB•xÆMôþæŸôû(0%ò,ĸ\@E ¶´PÙJYTˆ2ËLxj•*Ĩ¬õ³«é­Ìx¾'@Š­4[-oië^Þõ¬8ýxg>vOô(c»£nOR @Ë3@ä`>þn¤”ÔFíyÙ uñ½ñûy)Bß&øƒ¹¦’kàÿ ÐØç" endstream endobj 1272 0 obj << /Length 2242 /Filter /FlateDecode >> stream xÚ­KsÛÌíî_ÁÉ¥ôŒÅ싯ö”f’N[Ó6ö-_´¸²¶¦H•KFñ7þñKŠ´i9žø`sX‹× nüíŒùï_¯ÏÞ–qÀY”³œ×› AÊòˆ)˜•Á×Ð6ë;Ýýy½Õë»®²¿³˜Á?ÿvý÷Ÿùì( V‚EqÓÉ¿oÎW2KCSÓ÷úòŠ»æ|%²°Ôç+ÅxØê®okK›EM¨ç\q™D9°Æu³×µµUKŒ$i$Äøæ|'áõºsYØt[ÝŒÕD²6Ut¾JX2² û4höŽUGmÎ|_yuQ×MGã‚FžP*â¹ V\F±Ê‰™ª)J]RÊÉÅ¥ ësšŠÆE]DQÓW·mÓÒžíZSßFKRBd‘HÄLuVwßoú jn×”Àu,âðë}­ùCûI…^;8­ ‹_^öÅ àtÿÆTz`bQ[Y”Št€M¤, ?ÔEÕÜ6½'Ô5Døw.UýÕ¨¤›TÃĉ AqÔ˜¦á;ZÍÍ£`<.n­§óeP£²õ0ûV7HÜ)%|®È¦ïö}GÚ™P ¥•¤*„­˜ oz -uKP(ù×iµ‡Züjj ým®Hâç§U©;4»˜ySŸvªÜßÖÆÛÂx¸X!-ŒP][Ô¶*:ÓÔt~ŒE‹UxØšõ9¬léÀÎx»†ñÇ?yŽlWšÆõ<à;³¶NXx‘©>ôåE5¬Éy”óÊ4ÔÞÍÈÇdØøSuSë Ú3ÞÛ¦\:DÀ=Z’ÓSrÆûq¥7Þ,úš°õ­.ÜçQ,2âþÃILÖŸµ+=„ë¿ÓpàmHX…©Ç5¸7ÆQJ­§Ólhg¼Ñ¦©ª1ÆãÞ[ó<‰•8CÊ•¢Ð>nŸ}º>û߇+²€œó(ÍeCLe¬wg_¿± „MB$ó,88Ђ'‘ˆó  ®ÎþCoÒÜx9O!ú®#Æ•#Þs»çe­²,Riú&²žÑ|QÖoCÙËz~]ýãq}ÔèÄi{µ>Òöu©ÛÛ†üÞ{Óåç÷¿\~öN<‰T.Æ'90'Á;‰IGû愨S ž½â B{5ŽSBKÓˆå‰gÜÔE{ÿXluC+A('› p(ŸI}B$JD4ž¼È£ŒeÏKäµ(N $aQHl»«'“G;>c³'[tˆÂ1d¦Õccÿî-íaøëí–b3ä‚Ín_éNW÷´]l:ÂføÏiPËäóF~hM7ÄdóÛ©m¦Ȳ¹"ªŠà‚˜øE1ú0 yĆ×êå0ð&”NIWÆ=c™uãDóQu£r'P4s„ïT[äe{ZÆä¿”Ç |½®ú}Á=ƒ™ >b»¥À©~€¡&’€hf<Å ã1Û Ïu 7K_Œ2å³ÅOFˆ·¡ìMnFZ×Å+Éc0ˆ¾k¼¯?L½¶Z]”4r&NLvÞí©4òq!Æ_¤2‰OJ¢I¸Y0µÂ®Ì†úXàWÿØWfm:šõ.iŸ9 ¬ŽDqr ;hš ¶»iÚÝ~€:È¡[Y õ€ßîÌNÃYbð°Õ5Š"‡¸g:âÀ¨‡ó+ð³«Ë÷×—W´àÜC[¤aË¢+p”¹h‹+> àpxqêÛ$W‰8t-ænu­µCºÄ¨»£«Ùômã8¢'8¨äjîN8¦óâ×Ò·×ã8eÊÊæÓ·ÒXoËø mùù´lÿüUEp/8œB&^vu‘g‘PéOºú›öâ™Q=]¨Ð%ý.yÝ:'*\Å}÷@6-…œÙ4ž¸ w±»†÷4!F 㓳»òuX›¤¸aò¹ï¦íú‚Þ@ô×!çÈsâ^5š¹¶ó½XïîÎé»ãA~Ãâ8÷–ÂÊÐ}$¢­Dµp­;Ü/Z¨Ù,9ÛØð£œf â†ÇÌn‹;÷ÐŽ&™ÃË63ÉŸ0ɘAY+~Éû^ã”yÅÊ%_O½U7ªÍÝ7çîØ x5Ô\_޽ܡ—&Ù±‘èfcá:ôÌ$›W®,Ü¡²Hs¥öÇ ÿÅ.Ð]©.Q÷›™J)ˆxØý)[]OFžMkýªïPµÅº›?_Þ”}ÜFÚøl‰KÕ½›Wá¸vóîbÖ4̰ßÎCz”\»jFûy2õ€rløÄ|Ÿ÷bÞáÂÕ»cR·øú{#G“NÂX'cZõº¾,eoÛ™U¹çRådžlæ… kcöãIDçîX»üáš–h9;öIí‡FÔ"Uù1t j‘"`h‘Æ’D0Ô"M]‹¦ëÂêG\©!×]5'ú¤>ä_oµu­yé눼îG\ *iT™é,Òqpõ¯ÿ|‹(™§ `„³üT‘ðŒìÒaøP*\"ÊýÏfW×_>}ø8Xêîç®Âkzº\sYß5{gèÚ·ýnôg‡Õìöî…c†~«€šy™ú0f„2 wÆýÄ´Cµõ;\K½”`s òr9ƒ5ÿ‹'¼ endstream endobj 1276 0 obj << /Length 2378 /Filter /FlateDecode >> stream xÚ•YK“㶾ϯ`åbªjÄ%ð•›cÏ:›ÄYÇ;>íî#Ak(Rɯ+?>ýEÎÐÉú"ͺÑèþºÅÁ1ˆƒnbiÿróæ­JGe\ªàþäIÇeíƒáÅu›¤Ÿê½Ýo¶I™„C·ù|ÿ·7oõb^šGZå°*Íúso‡§‡ñÀœK & UxÎÛÍVÇqXµ´ºí§Xi»ê'k›/,t·IòðTµGÛ3áùd[îÁ„tìOu{äqMîv£ëyÉCçøËalš­0 ©©[»}I¨³¢C7—q£ÂÕ¶*Ö‘‚Ýn•ŽRS²Þg³·}D\q°MâH'†?¿Ýš„è4 ?¼ÿîïÈtswóï¼q •èH—Iåi¤³<Øo>~Žƒ=|„åàS<ë9ÐQ’kè5Á‡›­šJ²(Ë ­¥²‚uøþ‡Ÿ¿ý‘5èÅ(hÅG2ìУå38Γežº½ŒvóÐD&3Ó+&œ*·ç^SŸk™ÓµL’õò°¯“•»SöÕP]ufêÙö}uôöÛ¦†v°0ò·-êŠ.Æž¦ Ù)q;¨%R†SÝ3# FÚsÝ4Ü{ ý[fÜØîªÁîÁI–…cÛÀÚÌX1ËÅÙ§º…øÐLö+ÂÇ­¨à˜õµ ÁGg«=î.ТšÕPƒÄWæ»ÿÜá<­µXO›ù ¹ãvÚ@FãÃÝÌY  ›Êý*l®zìÏv]ÛûC÷—Ó»š‚½¾ˆ£d¶:M9d„8÷ž”„z_ë„6œUž{¿ Ó­s>fêƒgµËU{¦ïº±'l»iÞ:+6~i«h ÃÐ"€SÊsún÷h„²sõ+žò§8?R˜|ÞlÓ$ ?ÞrË»ÿ ßÕâ±¥ÓR,ýA⩘™¹AF}F£Œg&ÌâQÌ\ ˜Âô"ll{„5ˆçÀ–+^û#Eç…mÃ^1·MUÞÕ`^· W4PËö\´u#K¹5·]koÙÍê–=ðùTƒ®ŒÜüeWõâÞÓΡ/’qfϔƦ@ ¢Mù÷«~{²´¨‚$â¤#½Ä€YÁéQ´;ºÿÖèlÑŠ‚˜R!غh>‰ ãr:¿´&î[vYTC|ËÌ} )M”Ä©O{횟¤‘IÍ•ƒ“í»j7XÑ•¬-û=t o7¶²åg8ÔZf(¼&eei¹<4ß ¥vêqáš{$Z,(„.É,@«ú-mºwïßrçϼ’Iz¼‰iÆ$үˋ[¨Ü—Ma舌šQ²’ª‘È‘w;÷H‰^Á?q?Œ«öqîe:¬Ö“rŒÐ¤˜ð`Jª’»úI+ªÎR¿Ó9¦Ï”„ý Å´"o‚QDkòÚß$A~5V õÙÂ2„UÒÿcÀ”ŠUÆÎÞª±pÞÄËñµB̵|~÷æ}$½· ÈãY2’R‰÷!q×ØÊ‰Õ”þ—š(¯ º œ Kˆ)w_?®¡”?E’BùŸR¥—CG°ˆm¦’± Ë9.vM#pÅe1"WU‹×ÔíËYƒÐRTË"y$qC-svÝùÒX/̰¿“Spë“"êv-mW±ðüL‘2O¶è<ĶýP‰×¢1£‡Eå0œ|Øü¦MP¾'WDžn%oî|½Sbñ >‰e¦„²¢•ARøÀ1'ž{DÏ]²j¦&«BWŽz×ò Žb˜,„Kt2Ÿ›”^ŠÌ×·^S)oèN‚.¤Þ®z&œ ÔŒÞÀzqc2‘QÚ'†gò>4çšõ´Ž2=¥™^œyW£¢ ŒRswÆ!„*vâé’5¥Â†é’&Í€-œî>)mÜŸxLqo(ãX¦¼a×§ðÖ6·˜{ã§>/§*_PºÊ ¸‚ @Ÿ°±Œ_®KÔ”¼²Ã¥qpðTAíá´j_•C ™ü« lâÒóJô,†œäªÄª°¢ˆ´Îü|„DÈèhßYVy:ɪE 9´5‡±åú¾”sLÌTŠe‘çª!Ò›5U ƒÆQ‘ªEÁÿSI™…F€*ŽåÇŸçbñû¹pvžrmAù½‡ÑÅ\¬ö|¥1ìàùäàûÕlüÏn°~‰jX}½ˆ§ˆaôâáÜš ŠŒ5…Øüiޝì+ð†‹˜ó Üϲ†åe®ÔYV2¼ÎT $‹STšZ ð/P8ëŠ.jñ‡ ,Ï& ‹W"øÒ]úµ*ðõ\M;e—k_3Cr]¡cϨ±K’ æW8¤<Ãs×Ü󛃚/ ÿÊÐIW{~r˜¦¡s÷Ó»ŸîäIF4[8.¿!AÙxwÿîÇ»ïßÿrÏCÊ}‰IÑcZ¾¥C…[;»è ùznáð¾|Ãs»»óµÆ1ëê±>Ä^Û¹3dY¨µ„ž¢ ‚¶×'°©ök™Àþziê]Mj ³û'üÇ¡¶L¬.È4]æ¦bytx£;C*"£¥“¾8ɉ™%ºß$KH=èE‰‚¢¹\AïÌt]Úˆ ;»~5¤ëªy%(5þÚ‘šé¦ggÕ‹grUÛ×–ubÒ$;5×§¼8V~NÇí!Û±»Îév`c⬮—¥ø¥Êø«OŠï0GùöÀḲÁcýÄ™>ñÏ_Ð[=u‰<ÁqPQ?nįðu%LèçøJB×W áeßuGgižÀµ<*ó˜Y'‘IÒ@Ge*ÅZ0FÀår;cûÎ?a²¬y‘›04`μô<®°*µUÝ™ì íõðð…qiŠ~ÜáN%Cº—ËÔŽÖÑR‰`Ë+‚Ád:] ZŸ<ä+ *²kyuÕœ‡¼/yƒÀàTáá—Ú6ûžûüt–Ì'P< u|$X‚RZôôå©ö—äœwñÚ6w™ûéBéß<¤êFÓnûê ùãl}QV÷çå[ DÿümŒ<]Q´Àé±·¤Áôè¿x„Î!•¤qNô¿ÞŸU‘GªÌ—/ÐßS8É<£œ˜G¦3°Ç^˜ETâÓ.V²‘ÂrCGE¼4„C([Êé%æt†T$n´ÌF¾”ŒÕ72ʼíüÐq±/p”˜€÷R 0n—vðí« '.°l³<ÊsùD™—6ü/{œ endstream endobj 1282 0 obj << /Length 2438 /Filter /FlateDecode >> stream xÚÅ]sܶñ]¿âÆ/¦:>š Á¯¼¹±ì¸µ+7VŸlÏ:âtlxä$£¨“Ÿ]ìw¤(Åéh¦/G`¹\ì÷.Zݬ¢ÕÛ³ˆŸ½:{ù&IW" ˨««í*WyT†‘„]µúôÝæ'=|×µÚ˜Î|‰Òèóvl7Cݵ_a'ο^ýíå1¡­Öq¦eJ$>éá|äe0ì4-,-ZîT[5Ú„çë´ˆƒ«‡1hS÷ô<¨¾×­·ÝÈo•¹÷º=‹`è¿’Iœ¯a¿AàOøã¸é®! þ£7à €”Q°×ÃŽ°™r«öúJˆ"‰$LeI"9ö’$°§{ü½¶ßjƒä’8¾ ¥o·þÅòÑжÛúF5 H/s.Ї”)ë^•_X%Ðbÿz€Só¤Ð|ÜÐÑS× pC¨ÃÎtxè-½R-£89âÀÉcô0švIè{É`ÓÙÃ+ý*­ò@Ó†hÍù"7À¯Qb>Võô<(3Ð[Ò‹GO‚Š-éML„Ý1¬~e¶j£C'¸cKÃê4@OoÕØ ´a¡péu¢Ý“›OCEäq˜@Þ’½øøîãÅR4$2Œ“Ø¡¡S,Њ“Pä©§uõîÃÅëË]-ÑKóP@t2jU0}sw^$ø]šäwÈ»7zÏ2’ß9»vu{ƒ¯$8cOÚš3ÏAwÅÔ.^½}õî‹Òz…ÔlQR'›ª†sÁB9Z¨óßÎÖy_ÓÝÔ›#¼¿k7ˆ ‚´ÝÈñ}O¦]ÃqãþÈæš:ÚÁè_jÿýIð²©_°ïl ]Y•Ê`Q'èPEX9I‘Çy»«7»oÍŽGŽóØeH\! d¯Š@>%BªèÚªÆÌŸmÝ ÿ}ÝÆ`”†`ÑJ‡ }«Y• c2õÚ$,3ïf·6¬Q®E£'a,²‰áñ`e0Ö͘qžªn×lÀÜ9ϧîeñe”Pvxað™ûÎ8È–žVd\°HFA&4=ÁþmžÓwV rû¬-c •ì'ôÂ%‘š©S#Â<§F|OÎiÕHk¤~){ݨ>@ñäpç²±‹Žv–ò:S¹Ùcغ\_‰„ÔÕ¢åDQ~£Õ }yTN†ýiª–GF¼aýE$ÒÜ>{ ]Ä©.ȦÑÊ€‚þ|Œ|_"kEÊ,©XNâ¡`kÓ‹Ñe~µußx:Þ,¬T‚¾{yÉ$N"eFÝøÐµPJ°É\Âd×?Ø><wYXÄÉ·‡]™O®ˆlØÁãv%…]nÃX"0 Øè² COŠ. Ùž¶l˜G¬#<ãSI)¼}f;*qc–ˆ·cd)Û G¡ˆçñÏ&]ØI ;àÝÃ]äa=¡à“.øìz9ܨHÊXéz#â†ÿ´bx´\h÷ ÝÚcÕ 0f½ÛîëICÅ^}?Ê%9ãEx™†ižýƒ\@t'Y¶åF« U†áE7ø©Þ×2S¦Þê¹ ŸùùEÆÒÇȶn´=u±YàUÒ¡r¿…Ƙ+}…yÎé !$ˆ½\!DöÂ&C„qÎ  "œ]\ý|†U2Z‰•e§Q 1œ†Qœ¯6û³Ï_£U/ÁöaR«[‹ºGô,ŒÓrÕ¬>ý“±)ç")¬ˆX °Ô°ß˜ú€©îþñ9"KHÅc'K`2ʱ}ðè2ƒ/@Œò‹,èä¿´È+ÁžJÜÓCǶÆÁvßÚDа,° ‘$öÔ#ìÁSñDüA‚. _ÓÏ6#õ55;ÑÅåæ5 ó\ÌxmáU¤a‘ÆOÄ+DYžÌYõ]q«¸ë®[ͱ)`¿÷YÀã^\¾'È^𢠇Y–Î|ÿ°€qYBä'O$`$C™‹?'!±¯©rÕð4 ÎdÍOe…ih&ëîY‹$ÊO#k\ÐdÇsY}ï8b)Üv o?ÀÉãR·?45µ'¬|î…å\¨*Ë!¤O$T±œÞsQǼ|˜ù£1Á>úà=vÁQkÈ:ƒØˆäTîõz±¡¡L} Ùc¥âËÁ®ñ€¦ 0=¦Ò4Ì\Åd!OkQŽÅa†:‹©šÆ_Wã^ÃAÙ¦Ö®øK{å¢*º„½"V¨ãikÝnøoTbpå"`mÓIÇA?²ÿ[$ÅçÚñpáÖæ‡««"øxùé WQ€Sºuk€Ž‡¦SUFÌr@ÂM‹ýVŠã°ž䞉T éüôÿŠ«ÁT÷Ž$' ¼6˜òШ~Xé^+9aµS¨÷ŸBŽw\°Œªμ™ë»²ÓÁî!;¾Ë†N­zá 7ÊT ¸(íéÏžÌß[#!gCû g-> stream xÚÅÙŽãÆñ}¿BØÄÇÑ}ì¦ÀˆÝ`ƒ5 8 äawp¤–D EÊ<²3ùz×ÑMR ;F®—îbu±îª>Äj¿«?¿~þáëoÞÊt%Eœ‹\®>ìVF­ŒÈc‘À×võ1êʹۛµÎUä7îÔlEÔŠr TEçQ›e£Ã2ÑP?0êsYU Úæ¾¸¯ndô„Ý߬Ò3éMÛÇ7ëԦѻÚKpžñ±ZmÝ39À®~è˜á$«nü¨ºÛN ß}ø 8`-uœ&9z,Ë#2Ž@—¥Q³Ã9ñb3´­«‘¤gDYŸ†ž‰6¨Å¡¨kW1â~ø$¤v-S¢=זן‰«ÊÚMÄß±®E½ ¢ø·b»-û²©Û7Œ}óÓû%“ú¶¨»ª@za"òÑc#ƒîÊeë¸o›\rµ_¨šzÏ W‰(ÍPo‹œ^ZFÄIƒãûS)(U_É©r“ s¿oݾè)&ð]²[¿ÊSWÖûj1VgfÄ#E˲ÉXKƆÜ-®˜Ñi*)þ2TÔ’ùÜž®Ôpv‘üš“ß„ôbk P$³¹eWŠ7í¼HìKÒœ§¿^ê_Il}NW’Á»Ézo· prÞPArÅ‚& ÒM,€°÷‹Klò½}ÙjþN¬|ß„VÑ2úû ´o§ßóôûgÍÎ2ASKÇHfÃ-4»âLC=¦aúÂíÂîlÐUï)A%ëžà’àš—_/5 =0MÎeš×ç"†ª ®g}*»™†%»{“©uzž²1òÿ )²ó>ñ‚)RA«ÙæbÙêåAÉ—ƒ"t6 ÊŒ{Øá&…¯íp—Åhak“çÿÈ¥Í!ƒû Ô¿1]­°”ó½Ã`Ylq Ú,‘K]*Ó+œùÅË·q½¸këp¿èšÍƒë¿}ÄK &-ß&?Þb֦ѱٺ»ðÑ—G× ýÝÔkÏO /Ðqn%ïK~ß 9»?€©c©yèºÀ7Óð·8UãýSA955Þ¸ì,}ÁÂ:þ,xÝ¿éŠ c¸¬»ž.½Äd7û6¿²óT¼`glÎ×Xï.DzXß6åî)\çÖ6%SÏ.užp ÃUÚȳÇFžâOZG|@ßÇK—1™¨8Õc^`̖ܨò8ËÆJ^FÌÞOŒä·?n´€ŽCäÖu›¶ôôþoX[ˆ°´r-¿qçúkê½´rzùY0ê$ÿ¢™ëÜ¡Mvéñíâ=J.…ú_â <áò8^eñ.*:ôBœŠíðPŒy³§\Dð‹ ýæS¦¥ƒïðň^$x³ÿZ}*þjù~(–C–'éØ(veåXÜ’ŒÔÐõpnǸ][¼>+k’ÙÍ›¯~ÏmŒ3 endstream endobj 1308 0 obj << /Length 1860 /Filter /FlateDecode >> stream xÚ­ÉvÛ6ðî¯àËÅÔ«Å )’éÉMœÔ]b7q_Žß3MA. —ØnûñÁ (ÑaR;íE€ƒÙ7Èw®ßyµçóúÃÙÞÓ—AäßKýT8g+'–Nì§žÂn霻mPݳۛFwêùm×èêúü`6dä–õR]ØM§KU÷Ý ‰ÙÅÙOO_Šmß™Kߋ҈Hÿ¢?Ìdâ*B‹!C/c¸±+„‘aŠð"ñb9`ƒ4a¸W}€n]wI·ËˆcK2ZZЧP„AÚ1…¬ZÆf6ÇkM©;¾ÚòQ®WwpéṴï6–yYÄ¥²(KTÀ™§Æ¡3…) ÎöóØ Á®Š!$’VE4û”!dâ¥ñ€Õ®ë¾?v¯H^Úè ×…Û­ù`U7eÖ¼TmÞhÆçÛðmÂI"^@ „÷¼ÔªîKâ%¾'añ'õŒ½8y¤šlÓû” ÔR9Ƕ¢ 4¨5l“¾Uì«ø%~0v–ªG€1'yß4ªÂPëè€"×€5FNŒžnv®®«ïá[#¦¹¬i­j¦#µuV]«{¼–j•õc¢-µœ öÈÏXÀŒdF%ý±v-2\)g†°ü,¥Á8"…ÀªèÛ5‹óÿR^"“«d}ƒä‚ëþ/Õx³yú1­Ñ¾j¾ZW|P¯ˆÓ’î;„U§qâ>¡o@0Ê®ùæýT0T«);‘ðb?ùöT0 ’ •.8ðá%I:vÍŽÑSn VÂQèÉn®ö”GÇUM«µ—©Xx`e>äÝö -«¾(˜4üᡈ±{Xñ½rÓaxÜѶÅ(ë j"$IÜx·Q§.çÓ EïXÆ/d^W AxÝ7¦&Á Â-ׯFm4ŽZ]¦Ãé?ÕcC–ãÿ%@}Ÿk+ºb1ýALÚÝènMÐ%²ÜgcÎNµhé0tߨ®o*K´é™j]ÑÚöy®Ú]‘³ÊŠ–ȵ U5MÍœóÚ¡µÊtÑ£óLqz„åz(KÝv8x ±þ˜%‹…±%'^Òí}Ú™xO‚Œ@¯Uf¿b¬5š(X„1˜?Ü)Öl£ª%iä?P«­¿R%¹ÁŸšLSèK ü+êMû7)_óº¯ðSg®³–éVpú¸·1B[ÓΑ›¥cu€!]C«¬Ä[È…{¶ÖLhIFºhZY4/ú¥úÜ=»b<‹¢è€üÒTªØo­Gf¦²¦;5 ¹¼n><Â//t›]V¶³ç§xgïèlïã–aߎ€O°p‚…ïÁxääåÞù…ï,á#Xà ÒĹ1¨¥x2*œ·{¿Ñ=æ.Bé ?5´„“ ¯ONûãÁWD"õŒ²ÿ‡RJ/ ðüäÍÏÌžjA誽þ4‹nV #ËP1&T™íP‘ú–+ÐÐ㜕——¢@Äër˜¡¸Ùh¦˜q]úì Dålê tqoðó`Z©`šú÷Á“‘›uHÓ<ÔÄ¢cI¨ Û"š›ñ©¢º¹‡¶Epõ¦³Tú¾ ú©> stream xÚ½Y[sÛ¶~÷¯Ðä%ԌŒ`ûÔ‹Ýúœ6NcgÚ™$3aDHbË‹’Õ¸Óß],H‰í¸O_DXì.v¿½€ fëY0ûá,pÏooÏ^\²pÆ? 6»]Íb>‹ƒÄ$Œ²Ù[ïµn;S5ó…T‰Wê¦I×zCæµfμ¼ZŸÏ!O¼¦ž/xì-çðóÇœ+O·‹ú#¬EÞï@¨q˲%>uEϦ[.å×H¥¼ ùŽ]åÞ€1µYäòlõZ›Ià­ÒöÑþ|‘0á]™Ïßßþ»`ÂeB‡²º*ÔUèªQI!„W¦¸r‡é5m^4ÿÑîÕ45A¬”¡÷3Ù†¦[ÓUË´ÍQIç ±iN[ÑdÚ¸gE‹ö 4µ¬«,Çí>ªÿâRŒ|§áÊWLö§Yþ¡Û¯]e«ì]eCN ÝÒ[7jóR×]ûhñe'|?LBâ{ ᬠ³Òs fHÍ Ìƒi¡ìmƒ]ÞnÜöå².·À HïÐkÕ¸—éfiòm[4¦¼p„çNÈ¦î §Ç`zËU]-þÒ¦^ºZƒ¤)w·†p*¸ôv›ÜbtƒCg(5½m‹´]Õ¦lhhô§.· ’’y& óÜqp âû0Hñ!¼Ÿº”Æï& ·¶I«¬ÐçSÞaäs–À!¬òËOît3å/)ü({ í‘Cï0vÐ< ‘$¾ârl¥½Î „F‰_ù½e"`GÚs»Éˆ +¯4íøh#g›6vËW/®û%æu` a‚K!“¼‡ÌãÊñîCjŸ;p´J‹Æ¤UvWbWâÊ&©ìˆäJ ¶M×fõ®ÂpÛÔ»ÇFT^n —">æâöxõ”h¥|©öþ=Éïq?IìXðJïæFÄïž&#añiS›ô€¼c¡’æÕ0W÷^퓼ƨ¶ë+w†£ï†éÚ yÇ„4ÏÆ€Â¹Ý3rú‚ÉЃ Å| RœX ÁÙÅíÙ§3' f NÊ| øqÏ–åÙÛ÷Á,ƒEÀ/5ÛYÒÉ!ŽÂdVÌnÎ~™*u,ˆ}0`ûQŽ£S÷‰x(Ž(9ŒÕC’yø±@¿Ý+ZIÀy4ƒúîŠI²¹ÿ¸REð|šÓŽd¦UZ¤ëºk‚ai6LÀðæÇ7·§Zs ÚŠ$öɃN¾ÑØPcÙ\&`îp¬õëïïƒÿ‚E±„_¬9,ýîËF #öD¦%8^ý¦I‰POcÚC­}Ý›LÇ–ó_ÌÂQþÓõ µ|‹ÕtóÙ†â8£KNY$ö¨fÕ…ÆdÓΡ K ¬xvlôÖhhmlZ²9 gw é;A¨¥×—4»IšqeXW=¥Îÿ´%1£Û<¡ÍY8‘WÛ®¥9êÒªÒØbk‡e¢­8PBöœBêàk\KC[މ:%!ïæk€itEÝ@ ¢%4­öדõˆby8`æâÕÕ«‹)ÛƒÓ" —PCÖ³#ì2xe=ÙÎ:}:ÅR?V¼§ÍÝ9R:@_Fp긌à–j> m ãˆléP…•AÊÀ+kkxê‚O²+¼8CštÙjÓÐ\_\ðÝ–ý~r÷ ]E®ÛSØíáš±<âܴГß#µÇ®9Lzíá2@ï–à¸l‹»¹žë²òê¨|Ö&Ó®Ò;sìu6íA¼?Ò…P7ÒL¯Ò®h÷j¨Îf(Ïê]Ũ5{Y[ƒ1¸nÒv ¥€¶8ûÖtÔ4M)–`öPQ^°I€aÆf|ˆ¦õ ^ÓBïC˜³>d6¶7I%¤¤Q+è[õQ¥ÆÑ4Úɨ«âަ:2?uØ©pêÆ±{1·ì¹Þ‹Ã ÎõZ«¶wªk…÷—´mu¹m÷î}Dÿ¹ÕÚTi©1S<²ùÜ÷ÓÃhDüE‰×¢IwøSÛEmZ„4Zù>+ût=ÇK6.¸.Üñ2‡"ÆÛÃ>©ãxE"˜÷Íåi“‚Ùö *ªEÞƒ5;Æú,)B?”å%粫—·$ž® ÷èÂWR=‰Øb2ÎNõˆZ¨'®î+Eó·5Yfðó ðSæˆZ|·©·+1-QÕx¡ã*ò®^Åhëp9æŠ*åÞ“º0­OYü3zïó^(?ÊgòK½'}•‚«ØïQüæåÕoNñÓ¯;FCª7p'R}½émKq ›[à 7£]°Ø@¶9Uy|¶íïò±·MíU»G†7fGæ 9” î`oyÓ³«0†œ‰óPÕ¶{¯VGL ƒrwLšõèn ¥,UA]Ò™+FftçîsYšA½ëÍãÁLbè{%bhU4ãì‹0ÀLº ð |½ÁÍ«‹ïœÎ]Ó¥E¯Žý¶Ô•Ú@Ù·A_Ç "ŸQ»ßÿOl 'íÇ„ltkvQ¶4úzg]“é™Ðužý‹|þKÂmF…äJ¾‚$­í·•¡Õ É7Wß^™ýŽ{@z?iD×z»…žSŽZ•ÌÜôS_äh0œÏ#ny1–ì£ýœäß\÷ÿ‰twm’ŸB î¬c nn__|ó3©àº1› °:Û/SjKl Ã<}ýrÉG0Ës"ùP(ÌMÕwXƒ‹Å¸)„d7|q·„iÙ7ÐcìÑ?„óç>¨©Ã²O.rpÆ]ŸŒ#=½‚Y¤šrà‚" 1tÞo}¨ùMÜ8uØFà,&Ü]Þ8.¤S‰í("åý9üÿ1㉡„+nÌìEÜ2bjDxûÑPl endstream endobj 1257 0 obj << /Type /ObjStm /N 100 /First 923 /Length 1290 /Filter /FlateDecode >> stream xÚ½XMoÜ6½ëWðp9Ÿ$#‡6ð­@æÐÖÈÁHEÀ.lHÿ}ßhÓÔ+˳kôàõHzœÏ7#RT{*‰š$%ü«©q¢‘†×zbŽ’XÄ’àzO*–¸hÒj—–Œ<1q2«øïÉz -¹ôÐ ©TYj¸Ë\Rƒ*fM]°”[ê×O ùĸI%”)–hc…OtеY]ÅÖA© +•Ä”ZP¥½‚1)GxVãÀN”¸ìXÁ`ïÐú«D·êl¤FºæÈ` i€ãM‘ <\,U&îÐB„Ûñ˜l^S‘½ÙWŽl@Bl™-x(P*H'+”ÑÎñÕqDeªF <·p©†fªà ŽTá *È/Rž‰¬A¡ ­ uDrCš¡áN›±%ôÃ9zFÎDfë ‡H¤=¢W I¢Qî ©…¾`ˆ±‡äæòtØðI‹ ];l€AâPO=ÊF!ÁF:HFl4›%Øh0)XˆÂH„¤I …àÌ'x¡‡¨‚±®&eõ‚¿Nã!,…÷I•ƒæP¥¾'GÂuiÔšMÒspVìFk™U‡¤a¬ÒÆÁJD¢‘$U»G š¬DȺø$H«•V§‹‹i÷:]¡u-ú6í~ùõ·;"–+tÜ|þôéýôêÕ³@nž‘² Ô\T¶¹¢8GÀËÛ›‡tq‘v—öæ—òõ"†Këp%Á)ó”ìÞÜÝ~øyÿ®ÒîÍëË´{·ÿò¾é÷ן{<¸þ}?í~„­ýÍÃ}´â¼~Ú½Ýßß~¾û°¿? ™ùÞOû\ÿpû%]Èѵó{º¾Ãêò < µ? •-­å£åÛfÔmgÞr·\±ÜQÿM šgk2,5Æ& cZ?SÚ£j¢G;ZåpñÒ¿ÖrÁ€ÇeÆÆ{Ó3k®Okî|zÍ]j~âèéccÈš`R-k0ŒÑžMdÈÚ³U–’É}HVrµ:FŽ# ü[ô—jû¶B?ƒu„õ(•Q‰¾ó*Yô9˜Rdg Þz]8¥ê€aSÊê¼Yñ¾ÎRBÆî-wÐHµgoüý¯’#JÅ.•ú?8¶…ýÛ*+?n•ŸÒ­–ÓéVi€nG ÿ ÛçŽvnº ´Ž×xmÛ@ì¬3É÷ÖèôdûJ²u0ÙóAf‘lI¶-r oïô8«l±ÅË]}ˆÝVf¥œ²¸b<âÿ«[ÚJ·´rzÛH·´ÁniË& ìÝ|HöÌì[cWø”‘ö⥶ÒIíŒNj#Ôd–à*úcD¡bw¼^±Pâ}: g±Œ7Ó0Z³gîQã˜ãº 4¼zÛH0êY‡‚AzÖ·K`´h¤’Í4 ¦«âìê#8l‰¼óê9h‰‹­nQÈe}Ó¶À¡±±iH":4 D²zM<î^Ì£ø:#õ¬¡ߪ–C'>‚œ:tâ+ÍæÐ9=?ý—8—8Lé6ÐpÒ.k]õXÀXjÛ@żã2à£ÄбŽîMyÈÚrH"UÊVä¼ÙÉ4¢§›¡Ó7ñõm›F´ØJcãËkߨ–@sÉ¥ò6P-å² ŒÏ{þøcÜßv2JÍ endstream endobj 1317 0 obj << /Length 1579 /Filter /FlateDecode >> stream xÚ½XÝoÛ6÷_!ì¥2P³¢D}uOéšn»v­3 @ ²ÄÄDeÉ¥döÇïŽGÉ–§f 6ì!Ö‰<ÞýçÜ8žs>óìóÕÅìÅY:Üc©—rçâÚ‰}'öRæ x+œKW×ù7Ù¾ÜIÙìTñÅ =øãó«‹7/Îøè¤ç,|…iH?v²QRÏAì¹íF"ÁÝß–¯i¥¾¦gFë'g(rvz1û>ã Ês¸ÃÓ€yiìDIÂ"9ùvvyå9l¾q<¤‰sgX·NÀü8ªtV³dÖO#(¢ Âßß/??'«¿¼ý;? ˜H’ÿ€E,ŽøÁêâÓéɯa7_ø‰ n¶~Ñä˜<Ëç°¾‘­¯ç~ìÞ¼ê¹ßE6•,éÐÝFVG,ºmd¶EO…Á²à EJXT¥Z••%ÈqêæówëjÎÝJº•›/’$p/6 À‰Äp»l ,ªT­Å…gûí®Òݎ쪛9Š@A1lÃôaEïŸé5«ìþÙT5R¾Z½þ™¶Ú È0"î{-líš]p« I©§æ¡©7²Å|–ª˜Êã0†XúpÆðb‚xTåhá†^ó¬ªê–èCݰ œHC¾3rº‹5ø‚„’m×Tšädt¢BaÝ×” í7r×H-Í~«ª›^™¤mª, б¸£»<—Z[j`lî”¶Ü•*‰Ëø{Pôê¡vsbMS[ym’ýÀÉ~Â.F£¬ó¬¬²­|BëX½•pÖšYO© Æ“!T­ N)JB “£Ø®»–”4Ìx}äÛ’ ·hÀWE¡°\¦“ÁâÀ­-7š@•iêgÒi .4¹I×é6kŸàµ}2¥ÔVáÑfëR™C^@·™B )R€…ïð§&>Ý­Í)Lá ÷«I98øÌJ6 Ä×&¿-žY.ªÄÔìÉl¨xÛd¦@¬+ ® T6‘_<.dYè?Y^wvS0G5{Iè§éP]Hµ›Z‰M­àÆÄOÛ-[CøÚ›Bo½ÄFæRºS‹Ð}t|AŽÁßRf-3IòSW«›*+3qú.–í©j×iª»vXÛdšm•´ ³õƘئÇÜUϪ–µj;$éÍm C2 ·Ì´íS`¼=Dõ, ·v5Y«êê Už—µ~J…Ÿþ±+U®ZìßAÒÛ„Úne¡²Vö;F²e*±ü`*(Š"–¹])0Â0,µÃ9S7fU˜ÆvVáéœoRßJ“0œ©G¢ B!(Áî4r*.Ðô{_¤4rY–8&Éopƒá, C¿w@Êbn¯Up_`ñùÞ!ŸàТ¾Cåß;ÙIÍ  W“paùGÞ5Ü—WÅÁ†ï GÉÐ&€Üf»e¼[€Ð÷º•[¤Óa àrÛ ¼Û,ojmåÕô<&™•tpz'fFű͂C½S0ÐØ¥QÎDdöR?ô KìM€µƒ ÀB&`æx2Çèln÷Žx܇É8œžœŸ,ßOy=öY¸g¾‡!˜&tYK0 Ó7;<ÃcÈt53ý®èº\7•~ÀˆŒÉE£ô8Ä<™*œûL„Á°i. ?3ë0>Ãå Äé „º~úÁ´ÚFÝ…0âÕ­Çþ’×…|òkØ®ép¨iœ/¶¦‘jÚ‡ŽhÓ<´^!&ª_s¶¦çQù™#Çó> MСnDà»K;$M #Q*º•jSóNMVÄþt¸Òmlµ<»|÷nÊøEbÔ0YaŠ—eª"SíôQƒ‰ú?“s¯q”€ ¿ucuÇÐÝËçôåd0êq Î}X^¶(¦G ý>sÃáÀÕÜߢp%‡é‘wV²=ºÆôpqqÍ®ìƒÕ‡RdâšJȇûœE±øwð> stream xÚÍXKsÛ6¾ûWpr¢f,– ÀWnÎäQg<í´vzqr DHBL‘ Qõﻋ]R¢M'Ž;íä"<,ß·/ÈwÖŽï¼;ó¹}usöË[:Â÷R?ÎÍʉ'öSÏW0Ê[·1ë2+¼\¯²®h?ú¡O3³y„îí9µ<÷éæ=È“§òD"½ pœ÷r6QLM íÇ;<ª~Ã'8RÐ21Z6|áÅAˆ/LCZ~­ÛÙ\Jé¶Õ«f'7Y™ºæµ×—ïPþÙ››³/g¨¨ï'ŠÄ‰’Ä‹Tä,·g·Ÿ|'‡ïß“iâìíÒ­ƒ÷“Ð+œë³?Ò±¦½º(K,«èë·WtüªªIQÖXºÍn6W/ÍG_(ó¬½GãMA6JÚ#`Ÿ—5¦Î4ˬÎ^êÔ)uqÂ@@g .N‘º™p™=[ö`MFÍ¢3EkJ|R••±Ú½ ¹#õ0 ÌGG=Ä|P0Ÿ‡‰ï~ht¿Ë4ÔëÛà ÏêHµ=@]ømñú€Ç\H/ OiiNP¹†–ëºîvlÁÍx+aÜŸ¤zQTË»g ý(Ñá÷‰©oMh䣯 âf‰W¾»ï®HDìÂH¸DÐnGOðƒiTºò¹¸ü¿±ëƒµÅwÑù¡Ð0 I™Fÿ¼†ÂÊZJ»oÚP–hÏ5”‘Kzíag«ŸSÔkø§n»º¼¯Y[›rM“ŠN3PašV—º~AC³š†9ö°Ë¨:Sª´q ûE†Jö0ëõgÒ¨ZË‘ûY/Ûsž©'•(?îå—¦˜T"ö””¨ƒz¤Mg) /÷˜ÂÞ”üvñ]f[ÍÆ¸êÊekªò‰„\äùp©q`%s©&);5’œÐ€ÊÞ’²e‘5 ïV·òϲ‚R LW¶ì©Ëæ^*ßÕú«©º‡›ÏþI«åa–H÷안}ŽÇ^¨Ô×Dz)ÔFNò ûHO‚––k½EPc·5ÀŒ ¬AŽ»dÜ‚Dµˆ¿MÜšM€ø+²Îß6=*Á7X¹ûÒéNOá¢R@cðçWׯ§0À•Ô*&S/JÒ~Ý®®Pë¯&×p?•pM£’ÔÝVMKSìiÍW{Gšæ»o2’PÕ€œâ^-è™iHàÞôrÝZ~°ŸQs470QzòG¥l´šK¢¹L¹ðI{RSi/bóž¶¾3¦®õ@õQsüT­¨Ýo4†œ{¢P ÝÚÊéå­ËªÖèzÊgQvÿ©b¹.øTØØDt­Ð¨ÈRøÛùi‘×ßöÍ_o/¯ÐŸo>.,~ ŒKOâ_=.j™V–.¥/ßývï ao¯·[ë9d¦õZ3 ,~cìý7´v«³²¡.Xq·j­£-3 Ä4‡ T†òp} úZ³€†³t™Û”¨Fqɬò#ä:¢¼*G:dÔnªJ²ìyßòÑQý³Ê§+ ¼ôXí]‘ÓtOùª–ü!í}K¨‚÷c:TLäAA¤Têô®„}¶PP£G+äò?s¸;G|ñ¹‘Óêª,}OS‡Õ›ñV5 ±ä¯sHÛÚ Æ`È¢>”Ѐͺn³EÁSD7t²Ý®0Çp¹[{#›Náô^hˆ?hXãíµ. 7€mpB³ÇúÊöÇ(‘>>˜NßõObEÿÞWáçdýÈó1·Ô†3§/Ob 16ñüàv8ØÛ‡^½ eœø•PÄžÍìþ1°Ù9›šý„ÍxÁů]0†°«ÙT]‘S|Xcë9XªóÓC¥µ¯‰l|¯ÄÄ fÁExV@YÞ­7<²œ¦JÓmÖ+ Z£óò*²Ž¢5»bü§£­9•ôß®N~B¥7WðšŽ9ÞþhÔ ÿç̉ endstream endobj 1329 0 obj << /Length 1841 /Filter /FlateDecode >> stream xÚ•XmoÛ6þÞ_aìËd V%Šzq¾u[ZdX×bñÖm)23•%W”’fèß‘w¤$—YÛO’È#y|î¹7E‹›E´xñ$¢çO›'OŸÇé"ŽÂu´Ž›Ý"g‹à5,6fc:rŠ›Œ/è¼;YN•±ðxe7! @ü¿ÑnNJ³¥†l74•‰6«”¥ÁÛ3|Z²yÆ<ÆfÀåŽBç€R–ã#ƒ‡G)àO1ß?š˜V,-Âõ:ŸûÇ Ò¡ÒTe‘¡*;–…-;pþXJ“/X‚0+”/qöõ««Ë¿ñud€þB¤h¡l ú•µü÷äÐFÜÓÚöûë%>­Ýiƒ#Ä»VÛÝcFÍ ?HE±> B>”x¬°‚µ¼îÊÎU›ÜVzÊ!£y¥1ˆO¢Knà ¼¨#fÚÊ„‚6Ù¢—îºöp² ñ€¹ –|R¯ÂŒ>Yƒ°ÊDâ¡ v"ŠQîÐÓN“¤à4Ç' Dð¿:'ž GL9r½] µïT³Ç²+¢7õ ̵ a íîd¹!U>«eqB“ 2ôgTÔdmiês}KS=Vf£‹ô4Õ¨¶ú zŸ± 06 æb:8gÜbá%ÿyY]WlZ'“tmEv=é0Æ:n’V<ê±rn’°z^ÅQ&›Ñ{2"™Æ×y3ñJx¿×· ¿§Â¾®gÝ Ý¥ž{;Øp;/æ““XŸptyMØ;EÕã$9 ?VìJ“úÏÄÿs¾ »‘!•9V|eKM*ÍxˆžˆŸOÙI"šp-§oWߕ׵ë'l›§74 ¢¡ŒR|û}ÙŸ0åqûím&>Ip±0ÀÚDÙî :Qj¾j*ÝJìî x›³Loä5§­9E×Ù–Ä$A¸Œë"ô †øÔ@fT3×-N¶©Ð([ Ëw$ÓÛV6ßÕg‘¼Ñ1Ú•Ä®3t%Ÿé‡¾lôÁAI6T‡ÿ .¢=Ýxb1ñqÔm˜+!ödÝÜŸ'Ô¹½¹ÆÌ†[©à°Ö!|êtüÔéÖÌø<ÈÒÄKa,°fdx±¦€WÒyÐÌ É^îp|ôÔdæ©çâ©ÇOiÏm¾ØØÚ˜$·BU¼Æø7jjC‹i?íg¾À>¨àxð1%)ÂÔEé‹g/ž]þîcD²£b¬Ð;oÀCÎ]p±¹|yñË«?7¾íÒ”sÉÄ$ÏÄÀ§ŸÐyôÒÜb¢GØøiJxÆh|NÎÂGgѯ¦Å--šɱyà“Vp\Œùꕌs(' ì„Ãôû`ôÑTí`;i 5<`–©ÜçÙ¡Tyãi£°|´µ7=ŽÙk(ퟵ±®sQމõù“€ìþL¸?<­îôÝŸ> stream xÚ­Xß›8~Ï_î¥D*®ml }koÓS{½«nU:mW:–8 -îí3Œ!a—Zu_Øc{æ›o~8ÜÛxÜûmÆÝóõröâM(s¡ÓMC¢mEƒeº³ÍszOËM~Ë3|ÃÕ¶nR†ºxÂ0!h ˜4¡Sãäl±œý;  â‰P°8–^ö.¼l7»ºæÞ &ßyœ…IìÝv¢;˜Ô‰Wxgúc›E3‘Š˜6 »²MVçû6¯Ê‡Ç:YsyîdgÃû£G›Ì0^$–Hçü_/¯æqè/gì–Ø>‘ݧ§ƒUá¼™Õ6íè¡Ô‡ßϰOXEOd|,˜P.ßì;Ûm½Ë›f0¼&ŸU_;ÏÍᯜã z¢d¬ÿÅâýâ\äh4šÇO¤{¤`Sý0rV¶°g#GAª“æ^ä¼Fo2£’'Ò[Å,’{’ăž9Õšì \¤ =§ ‚×%óc¨»Ššû©þrñ÷ CѱêA•Š¢N-ð=3bT¥Øº€zò™kj6¶½‚Í!î|úr›…#q³8ékäKàKdèÁcâx3¥u¿àS¥1±bZ©q…¼´í¡.!ð¤”`2=óÖÖi‹`J áˆðºRGóíÖÒËP/Q./ïÍ6.².ØŽŠ.ôYnÓ/³mÃæâÆ_nqîÕwÂîPKvl&&ð%û¯ªñÃM"Ðû…ÝòÒ¯VÉhb éÜ寉,ŒG„½Bu,‘‰¸¶ Ê0¼ö„%á¡1I‹Â%Ímm]®tX³Iög#ã{lïöA¨n¾<¦:Ÿ"E¯;¿ÌÛë÷Y„ê4_àIë_h _Os;d|ô™„‚HBy )ÑtZXªp’ö¿@­pQ¨N. ã‘“Ó^dJuók¤ê4ì¡„Œ…ypöjoK¨`ˆü>m·}ägÓú¸þ!‡   È €©(œlÓP=Îò´ÅÄÁÛ¼ÝÒä\zª8},À(!$!{ø'EfË.xñ{T+z…ë®F£Nƒ­Èw9é4tª§[ˆYÀ%âîµAÂož=?í޽…»I ¶Xz*uòbÒ‰|Ê{/ÓÕªKоÿ”ç–¨Lv0}®}ˆ+ô1Þ:YôY^Ž6˜¢€žð™ìt^jž9×Úuz@ rRDº=„0M(Â0tïB=„ÐYHÕÒmÒ«6ßÙêÐþ8÷iàÁÅmDCLV¶Uña%¡ëpE¹$Ò²Œ¸ÕüK%~Þ¬¡<ô…·ÓmTxûè˜ê /’BŽK!â=¥GÔ½ö²páØºŽ½·{tµD’@«]6ܲŒ8¹à›ï¿à¯Êf²#™ºÝ¿HTȉäJ&ØVtW„ºL šºøó£›Úí »Ãdc|¬¦. Pf›º}ÊŠd7EuÓoд '{ k¸Åßi@dXG§¥ wßUM[ÜÑ©) ¹{ŽË¹-3Û7!%þgpšoëtßß$?êêй+IôºN³¼È[¸u¾þ¢ñ$pU+íÐC»J9’.ýMqK endstream endobj 1337 0 obj << /Length 2063 /Filter /FlateDecode >> stream xÚ•X[oÛÆ~÷¯ ‡$†ärE2ojNݦ@\K§ú&× ŠTx±k ?¾s[êbƵ ¤^ÎÎìÎÌ7·•ïÜ;¾óË™/ZŸ½»PÚ |/õÓÀYß9qèÄ~êù|η¨;ïÁ´]ÙÔúÚ‡ÁùÍú·wÁ‘˜ïÌCßÓ©f©kÓmÝÏÕÂwûáEk*“1pÍ ) wù~}œñ~V¼X^}äÅÃy˜¸|;·F°ÅÿßžÏy“Y›»“ÛJàÝþnÃKÚVYÅÇü÷r%LÛ]e¶†Ží³^îU:fì7 Õƒ6£‘òt”²‘SºxìõÌ9‰—ÑÞ¥_Ó>¡Cëlk¾€ý:Ônÿ´37ö#¯²®¿úrkš¡¿y¥ÿ¯ÚÕú»4hG“W&4Ó‰§’H“Tkº¦B°ß“zS÷¤¾¨Ð lM¿iÈô‚¯!ä:Z”ìYÓÖäz ´†" ©À±ˆiycGáqpNåÏþÂý(Ö͉´DÆ”(Ò#†.¢8¨ Êy%ž’cD;Óƒ€Rîc ÀOø+ ¼$N­ùä{Ù5p焯¡—¦#óŒOÎ[“õ„ŠŠÜŒiµyäÙÖ ù-30"£©À²75!/M(9üÔLžG(‹q'_L+Ów|9à zÀ [§šF¤iÔç‘Z@‰÷UsKÑçc½­ŒU±.¸Ö*{*R ½„s· P[¶‘Ž)lƾàù0‰U’Dc pEíî4ÓA šqÓÇzl1O9fÞq.˜¼üÓ¢\.ËsÓuMûô¯Ú|Ê^–Òü d7ýÓÐþ?+ ‰ÞRnñÌŠ‚7v ë8!)tó¦¾Ã˜l'¢•@õ®kówWÖÆQè.«®ÁLDc7rB–ãq£YH:êÕxN/ÑÀ †n Ä‡{äõ¯Ø›Ê‡s­a”‹t@ô ³‘ HèúvÈ¡b¦oÍ‘NÜ2¨€KˆVâ;´²*oÉÒÍ ¥ °lʯ܋ØÂ„4«ò„5­Ì5dÐI/#Íö#œ´dÖLzÝ¡€¢1–]pØ¥®/ðãà á*” _óôuöóúìë6($Ny0i;*޼4ÒN¾=ûrã;ì¼§ÒÄy$έ£<È7XUÎêì‡'`s ß~ÂÄØø¡.X›8ZØî –õýTIˆà ƒƒA0; °0µ¸ ¾iŒÕi;¨rÇó6nãàˆû>ÄÈÕ7‹¬ñ¦lEÔ¯\ów^ jH|£0¢7ãÚ§Ãq ô4oH%À“ºúJV¦Ï©Ài÷o¢iÅ'¬á/¼Xø“Õ‘²ÿOÇ»0º`v,³é˜(ÆþôîšÚ"5ÿ@E?v?̦Jµ‚¡!Jãq:oßgEѾ42è¬ÔV¹¦LùôùÛ>“¸@'>¼øNÜ46QJÜ¡ê±ÕB¯™Ìšî›8§ØI# Þü‘ Ý2 ƒMPìLkÙ~oh©lÍ ¯+mÓ‘ñð-õ忥¹Jr Ã/·ÙnÇ1ÔœqçÒË£=§æ†ê8˜K†Öe!1@͆ªtSÃÒÏGf‚*ˆu/Òü.¦27sû&Î[h³«f6ýTÑ^â¼íF·xËïŒ&,pØ¥ÕEÞ,¡ØÌá1žjËKIޱ7–9oíG}ì4 Ï•x y1ÊÈqñ¡ã([¹©Á§<ö%üØñS÷¼-7Üq ¿pm7Õº‹Rܦ|·(᜞®ò.k;ñB\ô“påÍVæìn¿»ÙØß%€]­ÍZ*ch~¤ÜCÛJ¨Økºa'ÓnËCÐNL ZYcM—³)».WˆTº.—Ÿ~¦uà®~G/åëj}-<Ÿ>Ëbýy-›KøOˆ«ëó ÿC>¿²<«Õ¯W¶0û7®.¸çoò<.8ßhiAˆ P[Ï#}Âó‘Ìéôw#wL¶!ÜØ·¡o׫åL4Wöuç5ŒÕ¯KîÒãoÌNNÁ©d®BOÇ2Z†êˆ ãùVÖ; endstream endobj 1341 0 obj << /Length 1627 /Filter /FlateDecode >> stream xÚ¥XYsÛ6~÷¯àô¥ÔŒ…$xåMË­:±”Ht›4ÉLi’Š™H¤Â#ÿûîbAš’‘Ó<„Àb±ØÝo/™[-nýqÆõ÷E|öìÒõ,ÁYÄ#aÅ[+p¬€GŒKØeÖ;»ÎÓªÎX{ÈßsW7Ÿà#&⿞]Š£›Üš:œy‘G×yÛÕåd꺮ÝÞæ¸vÓÖEù‘ˆï…+³²¡ =ómŠ-Éwݱ|á ærÞQòA“"d30Z|BÏÓ+Dªn&SÏ·?M„ Ê ;mÏõAm|;, ¢^pY쌯L gPì®ïŠ&gZâïÔ圹"·…,rìïçMž¶EU¢Ëäo9ö7Ú+»«”Ïa­EѦÈòrâ„v[¼çBæ5R…½­«=ßÝæešµí% nbšì•»ÐYRA¥X²¼.¾¢à<;™á¤'°?+¦–M¦ÒìÍa2" TÅSÜ%»,ï'¡ Oõh®p™§£ëõõ|/VKPà ¹=[nþ™¯ÕFØ3|â:þsµ^Äo5 @%Æ‹‹Þ›½dt°És8.—Ìçadú¥Ë»¼a¯ ù h·¾û`Œ õ‡@0ã/}É"3ü%xùg°÷N±'[W¦ÕþPçM“gã,£uVí“¢$ÎRªå8a(_ês£â}xºâN¯¸c«íöR)ôÆÐ†€¹×qƒa$¥½ØÒÙuý;­n«]¦Å&%}gðo,î¼wW_Ê^ "Ã@¿Épôuây™Ö³ÒÏÖõóUVZAtÐÇ­ŒÈâ»[ÉFp’Zc•éMo+“»÷™ ¹Î•e¦ 3ø:Ì!ßA…Œ;‘Ët—4Í/Gá ˜ÇA¨RŸÑ@~#E( Æ+ýÌùr&Bñ}ÓH?/ðÌaÜî~Ùð±qü²WBXB°Èóœ^PÄ¡›­ËŸÃÇ|R0ùß®F&%§úÒQ—¸Xn@#áR9ûrÜ‘ÂM[ÕªhÀZ A%Šýa7°Õ] ¶ë-´ŽäÐt»¤¥&/² ×u‚©{gzŸhYÒ&nh¯0éô½´*ÈÕ˜ …=^:><•´¸òF²² ‰Ê6äK:MËŒNªr§ú <8Â~‘§I×ôÌ#‰Áƒ¶HëßÅ%©.4؉U·‘æ Rý×Ðînºb—‘S¹§àþc-QÕx( R”ÛªÞ'z€“2O¡%$õ=7·U·Ëè膣EµÓÛ¤Ô'MEDr2R(¼"§Ii’pÐeºÌÀ3&èÊ~ä‚ùõ¯®£ã5¾U˜ŸÍã³/g˜ÐVä1)à sßJ÷gï>p+ƒ3¸…"´îçÞr™t1mvÖæìõXBÄ(O–`˜q0ôÒ†;©ÝVzÒ5䥌˜ô†Z¶Ï[h¾ím¥—=´†>¤L ™H‰Å.©‡®¡tùŸví¡kõzÛö­MÚ4­•yÜìô•¬ø¨'‡¶ØÔÒ&¡üFýÄëôåSÇéÏÌF's+ôd)»%5¤ðí=Ä2éGAŽÉÓÓ0Õ"Sï\ŸN¸–_ѽó¸Û#‡ŽÕš†Þ®RÑ lûxõk‘æSrWÝ`ò#¯Wû4•!ƒOªÄ*.Õåwª []žS_»È7Œ»Èz:îªhQy‹œ£y÷ÇCœ†¯:¤U–=o@Ï•~.ôps\‰‰VQQHuÄëkC`ÃÚ¡+õ£¦¢¥*ŽQÄ«€¢? èBA%OàIAÅ÷I:vŒÔ>1@vÚÕµ~p1T­ýÐAz i;XÔ§l­ó Ð[cƒéÑYœ61îfø_|½™Ð¯+@o¹Š—'˜ºöõ« ôç S3ž?×úI°Fb€5rF°Fâ1¬@«Ç€Â~4rŒ€¢øŠŽÍhÃqNbGQ>À“G9)"Õ,”ÀT í Åà§P”ð~H ‰ËÕ|½Æ·V˜sH¹¤ÝÐ5eˆBS²ÿ¾TŸÙâ¥>X¾¹X]ÍKµ%H¯^ÑÏZu¾ž_^oæç&ß>ÜEàß¾Y¯7ó¸µ;ã‡hÁ˜¡ÑPNÃŽÿ]-)^†¿Yĉ'©ŒûÜ¥wyÄÍ÷(§‘ endstream endobj 1345 0 obj << /Length 1692 /Filter /FlateDecode >> stream xÚ­XëoÓHÿÞ¿Ââ ŽÔ,ûð“oÐ'rÒIP ÇÞ$ÇN½N{E÷ÇßÌ>ŒºA-ùb¯×»3³¿yìÌPoíQïÏ3jß,Ξ]ˆÐc”¤4eÞbåÅÜ‹iJh_…÷ÙßeùwÙ‘îv'¿Ð6Ëoðb³ËÅ_Ï.Øh'õ朒0 ÍÆ²Û·õl.„ð»ÄAà«®-ëµ™üÂDPÔÊ|›Oüï3û²{b&Ë•á#Ä‹8a!~šH4% KHÌ…[TZ6™ã'·¸™_Ír6#ÿ›Ì»s;ÓN²§(ÇŽr]V“ìc°Ÿ2íM©$±Gkç‚rBYø%$÷ø²îd»k”<¹Ýò¢(zÉõ9ËOˆfs©èùl t3—W™²`ma5Ý͘ßÀþÄ/Èl¡U­é¨µòºlöʰØÊnÓh®Å¹feu …º%Ÿ„ƒZµ¼Aûû \vËýJ•?.eˆ•7µê Ï®Ì:ÔŒ„±TÍ÷(`N[D¨-²Àÿj¹" ±)aðOÍmfð6˜À`›ý[n‘æ~k&0êE€ùëŽÊ*[Vvz¥•‡³»F]Ö…ñ˜Ë›íŒgd‚„£V*%Aù< ýV«-oÚ‰‰jãÌ¿@È‘:®Ê€¥Ýç¦Í# þ8DçnJ°=ÊÌZ»P`œºÚKÕ•M}ŽÚNÍÙqͶQv»=4N:CD±T¹ «[ó§–9"ko‡ç ÒÿòÈçý‡‘ùXj;ê¤ýg€ã}ÔÙV¢ñÁ²¬.,…ɽÒzs¥N/·ú0S=Eç10D ÍÌðÃ߯?-Þ¼g¾Z©õ¡Ç›LM±nŒ{,o¿'~[duX¦H£áuQy~U(؃â¶:Ü,š/K">¼ye`¼`Õ^>@˜U•­Õcűỳނ‘nu(ÛȬÄ/”…ÀX ê±.ø0ÂcaøÀ‚@Vþ?{½8»:c õ˜ÇP–x Hs/ßž}¾¤^?A‡D¤‰w£—nqyD8P®¼OgÌízp-ˆ.ˆ2VñcÛºË?JIðñ# ’ŒøRåm¹CúË=FÆ´(Ž1ID‚÷ÜË9IIÄS.EÂ7œÉU{oî÷Î çLC1fënŵlÀ}öî!{eU—Úæ¬ißl$& ÖÀû;õN¢Œsáb¦þƒneüj@‘–%„|éÜ!¤Ìy 7OHø ™ãÝ4fOn/qy¿¦DÊM£ßÕTLÀ;€XHRç”ZQpuÂsi@;¢±Óˆa56cZc}lì5 îú²7×Hxrn–3H"âß…3"ÄP§$v!Ø`hr¹¦‚»Wg Y}Ó“Èâ0Ê¢ÊuUªO M^º‡¤±…{¤êÊkÌÝ u3ë Põ°Ô¢šXT»üªaBÂ89ª‘ A=Õ“ÈâPÊ2BUx\å@ÊW癋õÚ:ÃCÏoÜ‚"àDð§1ŸOãGÃxYŒCYF0b ùÛ¾Uˆš¶S¸2ËVOz 9øÈˆÇ<ÎO£a(`N$Æð¢8‡¢ü  ­rXý80¡ºNÃå#XR8e'2Ã(N æIdqhe+\¶×Ú‘ ƒŸ)` ¼/ðã¥ÍÔ©]̴퀽šÚÕ¸|Èý—ÊfáÑ¡ ÚÜöŽ$<Åz†Ÿ&eã”ÉØNÃÝ¢?äþ‹ëߦWqŸ^A|(Õ°ÐÅ6 ¹wš?Œš#Kñ¸éñ¥åo=n`ÜdŒ9âÚèb7sôt7aÂÔz3ÕJì›æ­…Aˆ®KIDm‘ÀÃÑ*Qÿ d  endstream endobj 1349 0 obj << /Length 2300 /Filter /FlateDecode >> stream xÚ­ZKoãȾûWsY µûÝÍÜæ™ÌbàÉ®Áìh±mq–"µ$eïùñ©~I¤LKZXK"›]U_Uu}U4Nîœüý‡Ï·7—‰HFÎHrs—h´V‰ÂI’%7Eò5ý nªböíæç‹7\x'$!$C™b°š!ÅI²X]|ý†“nþœ`Ä2<º¥+»\"*²¤J®/~™’L¨D„gcÑ…ém¹î˦~*^Q'YpzH2c jŸyV´R`†NdFP&‰—Œ:³˜­–Z!¬ùy¬ÉîLÂgsÆxÚÎHjfs’.š¶èì5–ÞΨN„ïe?£*},;ã/¼¹zï¿<–ýÒïŸnû½[Ïæð´Y”έ…_Œí¬µÉœSDMæAyG§ ¢0ÒTœ %Âô4Tš;ÿÙ/ËpÅ!ãTVÑüß°ÀuÓ°Ó;¸N¶Æ+®öŒ_Ty×0Zp$¤<“Ñ"Cœ³¡Ñ]°Ú0—§^Iwñ3Ac,ö̬óÕ!×2Vg²’3D {Nrm å¡s£®—™UeBP+`¾° N‘RÒ `ˆ"‚ýrŠ‡Ë¹‚pµ–ºu‹?6fc:TÔZ4õ]yïŸÛ0Í)F" 6Ü,mˆA¤õm^”6›òÊ_x{m3¯­n}™÷[åkóè3*Ž0ÞpáÅx'ÙgLëÃr'¸ï^ûÌëÂß'Ù¥eab”†/÷¡:†tëBÂ&ïÚ·îï´k›ªòêÙÍcÊïAÑCv”Ê]Ø=ÉØŒ#®x"¸DRëƒÕq&·µq°ƒ†ÈÖ}—ñ{nÇaæµá:Vßt}kp9×H=¾2=d:² ÿÂÆ¸ÈV>šÎtþRn? ¾¹¸°‡ˆƒÒÝ)–¦Ýþêÿù4íÕî‡÷Eþçk»¿ GÏöˆtŸ(sZ 2N—qGV½ik¼[Lðy¾õýje56^l7™8ØfP•|â¸dùúm ;¥ÇoB<æ>‘! Vùz½ 28zD¾Cð†$qÄ  EÅÂåŽ[{o³²oýAÔú3H‹¼²™ì€s3›SâòöƒTÖ4áBŠ/e @^0G.)â”žÆ ­dvX2Jà 0HAˆë„sà+2`óîŸSµ”"­„[™I~8±(ÝB ±¥T¥~¸zóöó‡HsìþyJ¾Ë«pºÝæ. ~GÅÃWs4Û[í¦^äÎ7îNaz8_#)µ$¦0wù¦êw¼EC"h2æ-ÓHC0‹4Í€CŠ—!M’iØKàÔ_®>Ï4KÿskÊ‘¤úLXe7uøÿ¦ ‰ý^–Bm„C6™g là§ ôH’p8Ú8—/ŒQþân/°÷Ÿ®)ô€Lfgn(ºˆÇYèætÔ²µ<"ðüâ#ø~ýpýåó ðJÿ5a ³eš% €'‡3î8Œ \‘IØK¢ Ž"'ýÝ—«ÏcÈ2 ¼ä<ŽÄîu¡y’Ëøɵե^oB;²+±XËðßKé«ëëºy÷)X5Ð5€BƒžT¿ VNÍ2·—¢ÙI°BKjëþypÊÆõº©ò6vx‘‚LñŒgÞgs a¥[R!Ǥõ?Ö¶IÄÍí÷xæ¡¿š~ÓÖ#Þ ,Ì“;ŒÆ‹×µÖÝ¿ Jß ÙÛS¾ãüß';¨”ÅE±- ¼9Ê tÛ’w™~ß¶–} ðÐEMÉÇÛ†°.«IÙ Á±²Uìnm{=ÉÞæ²GÈ)‡ÊÈáºq9¶-è%¨Hï .Ú²xÌ¡÷|S[{œù¥ØÛõ„M,ùØ á—…d7;m4›s΃‹»=¯[óP6›§O½Þú6´In 3Ó<=ݢОÁb>\ lk ÀOGO½]ò70N*ÿ!ácbO$.¶Ñ6Ýñk8±žŠüqÜYµ' Žhv‘·ö#‚XÇ˺„ö´*ÿkBS™/줴½ãý˜Hm»Ë©Yß‘}ÇÍ囡–šSwà¡æ@%ËŠú¥³-Á¡õ— …k\†ÙÖ±‰"ê ÖžGÌrå±üc­Ì æ†"«N.èÉ)æƒA[gÚ8ax|‹£/eëȃfc|”< úy{ÐGr«²ë‡Lž~ ¼5/   !|ÙëA7qÆí§_v™ëö7#·Oç%]ä …Úcn¨3yëkèôCXê3¡o‹ÓÐ?‹à€þPîSô‹f•—¡|w8 䟑zD œ¯Ã=λ¢?\±›Æv;>g¹êïʇæ÷ÍúàB"³3. ’ZžøYÀ‡rგˆxÑ:ÃOeBõTiÑýÏ&?~P;*u[ÖÅ«ˆ4ÔÍýžÅÙâó(S…0>Ba¡ÛÔPðŽ£Ìp5uÊgPÊ]äuS— 7]šQy(ýp»·£@7`€;þà°LÄ%ŶgŽã¢ñSÝL¥¨/W¦Ùôñ^Þ÷fµ·áwÛôyoââÖ,6m?'†ç¨[åm¿?,Fýb=oO)FöÚsäÕ<àJF~ÿyBÜr~"a3±{óê‡|!N+“×Üz%‚[‡:˜zGe>¼¿ºÆ¡¹ØØaþÛö;ü0!ûø9Ï>Ÿ”C¿>à¤Àï3¨˜!맆¥ÏÂw> stream xÚÕYÛrÜ6}×W°ö%œ*LÜxqžlÇÞuJ+{­©ÝMɪ 5Äh¸áeÂKdåë· Î5‰ãÉVí‹l4€ƒÓîx·^àýõ,p¿¯VgÏßríñ€%AÂ½ÕÆ‹‹íEAÌBžx«Ì»òëòÒÔ}·¸^}öfuöó‡ñÇ=®Êô%‹÷ÖåÙÕuàeÐù½0™ÄÞU-Q=dB'^á]žýcnm®"P§‹W û}‰oKøkšqÂ’PžF2®¦(–RÊG‡˜î"ò©¨ççÞ4÷ÔlL×ä¦E¬Þ’sÅx"¡Ak;ZÓ®3å®kŸæ5Œ#¦bq^ÃD0þZOƒ‚hƒ(ÓÏyé€ij•e³ÞÐïž1«¾ÈéÀà´4­i~Yìw|°õ ~H|SwigŽÐq&„üZÚCs =‚œÖ¦mÕô·0iu„õ“€p¬14i•Õeþ«!N¼E{¾[S˜u—×Õ@cÂÂè1fÝ7í1µ‚­¨ñ¨+ñå<ž„ãqŒarÚ‘‰œ|¯¸'‡Ì«¶3iöÀƒï¦Žè¼ÜÎm×ßPgcÚºxàÈ!0*Z -ÓæHX…fNþÑ­ Ø‘ßæ_I¦cùåüŸ„ãŒaSƒ·*Íý‹ËgÔøû¿]ãòã‚sîÿ¿üÄÀv^¤nجn\š¢5ŽŽ¤±ä°¯÷éXV·u—4/Ì`õh–ÀÏ7Ô]ÕÖ&°­%¼Ê™+¯ÖEŸ™lê·E˘Áe¨Ó®„¼Ê»<-œÏTíÝÔ)¦EøÐ)ºõîˆK@Z‹“èD¶Eú¤”“ÀpN1FÑÇÜêõ‡ÇË ARvH ’‰HBëÉÕ0Çzºü›‹—¯Îß<;Aò@‡' EÀ§Þ_œ/béÿpDÌ! '§5c¢ÅÄwï.‘ˆÁ‰Áó·r2.TJc˜ÙX×Õ&¿½º&Íé KjØçÃú)·×™f“®$! G(ã¯t5Ím5¨“ˆiîê—J¼êöi'?ÍÊää“…ß}p1$Ë V¹@ÒÕ®<Í+bÉÝ– TE) 1[ù§@†Ý2ˆZJùWàò0fœñë\Hý|ÎrK.bZjPŽÄØ€Ì&1£&Ö,I’ÁÚ§"';……¡Êt*/ ´†ý„ð33'™Òjp;ë:ÖÚ[ÂIщ"Í¦ë› hS!ÇDŒ?ÿSÀÕ-}ÕÁ´öÿE \†¢)#%ŸÃxJÞØš$o+¹Á{jušYà%;¦0¿,t!Þú´›ó ˆ’Ú®0í·€KhßZm&Å<7Ýú9`hç&䊺«¶½Ë»5®³µJÎþ+ŒéO¸s©z‡5 ¥yà µ•¡•è¢F9M0ç62Öc;‡©ÌÝœá4d˜h¯ËfÝP)a’ T(z2mSSþ¿ðC©¬I%F~ˆÂ‰b÷`(~ÅÚÃVʹã~>*HmõB¢ƒ_¢ÉÃø‘Éa‘Élrŧ&‡ÇLîÜSŸÊâN‡ ®Æ³¾ÀƒµÉ ƒA ¯žÑQiï«.ý|}ˆeü!±I>àVR¼Ô,OÜÞ§$¡sHíƒuÐ $o€â(þ°¾=ÒéçoH!3íºÉ]‰ÔŽ×رL;¬Cé»mNÇ”úÚmݵÝpúøøæòý¹+x%#h„òÉ)¿*Õk¨ÚàÔÃ\‚ÅÒQöúýÅ[ªG• m÷ØæhŽÌlÒ¾èЮ(©Ý=óâòò_ïV¯ÿ6“C.ZEF Sñ×U(˜Bã0²s‰x›=™ÝPý)/ۥݷ…¿_èeŽ”óáÈØ˜i'] !þ o†×¦n’Ó!u“bq°'NßÌn Z"øP÷1J$ŠÜC”êP˜Z-ñÓ;*½)Ü8¨˜ú5Lh0I¸§™Ù°! 6Làbó»2EØÇ©`t)´hè‡NÛî€/∲ÛÒµ Û‡6ï®° Ÿ‘fèlÍlž™F³ÿ]ž‰ChÇÓ ör·³o"tKH ™AV6€ü‘AQ´èL¶þŠF‡ƒIq¢!*¡x›¢«î“Îx½6-hïÔaïÁ ž„E7óOÉ*ÉO4ÜZ&Y%awdTwDcu¸ûÆ‚ï}Jj$(«Z¶­[zÌ}d7fb•Å|áw ”„CÖEÁ«Ëï¨aë5šÕ~’TNÖ Ó욺ƒš(½w£$§JÕjcJûla•²Þ†%…{&¡™¬ó̸£Ú‡6ptl½TÌ¢YÚ /\u߬]¤Û qÿ¾ñ‰ã5wõW@¬ú§~G‚Òt[u_òRŒh|”=¨4Á’\$‘OµöÉc4¤ ôº/Á¦Ÿ©»òj¿ˆÐH÷vÕÕ‡ËãCoQÉø˜ÃþÓƒØw7»u]îèí­¥Ä )1¨áNꮇ[sø¢Û š8•5о@±üîÿ;hˆ7p,B¾‡-¢‰ pþ_ײW† endstream endobj 1314 0 obj << /Type /ObjStm /N 100 /First 907 /Length 1263 /Filter /FlateDecode >> stream xÚ͘Ïj$7ÆïýzTÿT³‡dñ-°löÄìÁl†Xì`{aóöùªíOO{[Ìx!·ºç§*Ué“T2wK%U®%I§¥J OD5I)‰ÔñÔD=ž=1wÀœ¸Å³%!tÖê$¤I á½'NœÔÏ– ¿ ¼˜Y8ÐÔJC½/hPê0+¢©+LJO½—Iã) û ¼4 G©Ö@ ýªÂ¼u4:ì7ŽÂA„Amö áon…„ƒWtàÞ'|ëô@™ÐûÑË8YÃX[Åýš‚é0ßðG:`gAØ€½Í‘àS¯ãL‰ÊìU„¨‘I¸A££ü+"!B‚c$jžÙ¤¢bÀˆ¦ XàA‘P¡IÑÄc®ÒyÖ8ÇD2æ¯Í“€2®H+y‰L¹ÀKîp‡ÄR‰¥n}R$– D¡H,-Æ'd§²”„Y Ù¡Y(ÒÎA(RÇŒ$(RÇ9)RÇ1uŠÔ±tŸYc…ˆ©Ãl”dŒ AÆÇf” ¾¹•9&tk)ÄvG,4bŸºC[5´Im2¤XŠ9ÔfP'rlÚS ˜4φ0šaÁ7¶Æ¬=dÌ(Æ`p'*2YüÙ¬9x°X‚FFåQÑÈ«8ͦ È˜…bŒgík)’BUZ0'ÖB:nÓÅÅ´{›® ÍŠ%ù>í~ùõ·µiƤ¦›/Ÿ?œÞ¼™ÁËÛ›‡tq‘v—X:*˜{\r¼è¿/óR(óºíÞÝÝ~úyÿ®ÒîÝÛË´û°ÿúþ³øáï¿öøáúý´ûÖ÷7÷Ëý§Ýûýýí—»OûûÇ¥;ûiÿûŸ×?Ü~MWáÄ*¥Öé#]ß¡w¬¾>ƒOƒ³gÁUÄW;†`•(cÁlrØ`rµYFª69é=»ð(5+Tµ VÍŒÍfÄÎî¶ RçÛÄ6h-÷!‹Es¡`ªj¶Ú_RæeZñò¤òKìÿýI¥¯¤Yª+šícše²#ÍRÐìô Í.8kœ«ÄŽ˜qJlƒj%{•°Öçú&ˆó6÷n`EÐîÛ ¢néáÊGÊ&‡#1sðL±ˆ iö¹— F­ât¦Lû±L©!S‘©ªJ{î…@ÈOV7®¨JtÉr€› `·6¶b±æÛ IϵÔŽÔï»q1+‚ëéŠ`PÄW€ÊK;ÂD¹š£\ßQ:e]⤚˪ *Ë\WOùH]±µPP_˜€¥äö¢¼¾!¢—OÌW‘×J-Çz†¼Fj9¬å\lL†´n‚Ú±;4¥¡ŽÖmPÜ󺶗%eö!⺇£Û·Áн“씂êà¤zI`' GV *>£ ’‘‚J *-¨d´ ’Ñ‚jJoÙÕ@ñ,Ò@”â^ÆHÞ°5 DM$ÙˀŊ¢Ï—ÇóÿA‹z¬Eá3´(#Z”A-ʨ E™KÛ`œ§­Ú6(qÛÉ9“ÊHX/Ë}ñA~’35°R9Ëhå¬õX#•³, bê¹­^§ V,M¡m ×ip{8fb›#3Üu|¬Ì+J^.UŽçRéô¹T˜ËèñÎâ«5åÇuiõ*²q¹é«5åd“\žÿåBÜF¼ endstream endobj 1359 0 obj << /Length 1454 /Filter /FlateDecode >> stream xÚµXYÛ6~÷¯ú²2sIв¤öi‹6EŠ"M›}K,mÑk%:»Ý ?¾3œ‘#o´­Ó¢/áßZ· ~ZHþ~½¸|Å’"“™ ®wA¢ƒDfB˜åÁ›pßt}'ú‡ƒ{+cÙlÞÃG-ß]ÿ|ù\”ÁJKg1ûÝõC[/WQ…ýÞáÀ„]ßõ--¾U‘ÉëŽ&^Ê7DT,U¸# Q4• âÇ ÉK]æôP©Ht4,Ào/‡VšÍr¯Ã÷nÛ?ã•vNj–“Æ#¿º(g…‘: mÀàö¾èœ`†'´+f"Žð+Re¦0uïÚCÓy¬k[9Ð,Öq¸êm_4õ?aÏÌ®òühAZ/u"ïåJ'! u;AŸV¶¥í ÊõûèÓ0Ë•1†}Ê”=¸iôë¡uwE3|y‘-vìÐ# –© g¡‘ J<¥v÷Ç™¶Ot ¯bXÿZ(eQˆÕ1KÃVã‚Nvûf(s:±ñÕÐ1çš¾/^ÝyÂXY3 ±¢ Ýš' šwA90È›ÊBŸd©Éø¾¢K$2X|Q£ÂÐÜsAsKR’ wS:[ÿÙ· 0’ 8¥ÃØ_­ðÝÚº©‹­-™Û(ÆÒQ¨5Þ¼ fñ%Tû€%ŠáÏŠÀeB%m$Ñø² •¬î‡á0Ž ”YëÑÙH73‘ÐBi"ÔrJh‘j5^àÛƒ } ?{¸”»¹èYñ™“ºö.Ñêó•[{`´ xùš¶ÊbÓZ¿êQDúêPºŠs×W.X´¼i‰öqáÒêäð·´ _6´UÙ¾ÇkÇ÷{‡= ±Ùb#¡ÌíÐNäÌÄHÕ\ péÑý¤³õc=pkZ@1Þ¤A™5Ñ3#­Æ!Zíy›ªè½½HXô´ vCÙ3džqϰb¯ˆk¹ííÆv¬ÄnTÐ'†—CwÁLä` ƒžé:„¤ã  ³Ú-¼0‹-Âj-#¨|ÄC‚£Šæ‚;Þ#P Ùrâ;tƒ-ËÚÞø$p,qχ|<ƒýxŸù¸I¡ØfäÜÙï—h·ÄÄ[4ãÆ‰}Ï0BPÛ–Ùfû˶©h„šÌ`¥×BÊt.?¥]q;ÛËZñäs¿59ƛ̷1Ð^˜DbžxÁbÊhÆ|ìXm’§å¬ïiúâê厬t‰lx ‰cKÉéIòÂòâÇëÅÇ(Af„^Ë IÔ–mµxóN9lÁqeipï « &Zè ^/~›2H¸ñ«̼͔TPqH—±¤õ ?ªf`R±–ǧ ´åvÒšÃ/xcÛT³Ã‹YÇ”­°Sa€A«_dµ£Ýñ ‡$E»?x¶_ÿŠØ^ÑÖ'ô1äïÍp]3 ¨Y8×õ'ú”U’ £ÓG$Vãÿÿ9:)3þýôÄ{Ô¨ûgI×"Vësž¤zþIzü €Þú'ð¶N$Ç”NOˆ þ-»33 endstream endobj 1363 0 obj << /Length 1939 /Filter /FlateDecode >> stream xÚÅXM“Û6½ûW¨æ2TÕ!Rus6›”sH²ñÜW™!‰6?‚ôdRùñÛnR¤ÌdíÚ­ÍE_‡×¯› W§U¸úîEÈϯ_|õ­ŽW2i˜ÊÕãq•¨U¦"Œà-_½ ÎEÝ9?¶½4ÎþÆaUöa½‰UûúÐM f¹~ûøýWßÊÙjáj£B§1-ö2Ï×­uдøŒ‚¢^+àÚëJØ€ì ;s¿#ë¡Ìœ£žÊvçæ˜ ëMEÁ϶ëÛšGvgKã.­ýX4ý§³ÈRyC¿ÑóÚD ƒèOb„‘Ñ•Ú>!oZëM}|ûŸPàÉ£§Q¬ñ¤Q°5ÎÅZàLâO#š=`½ ÞÛCG5a¼ã ×›Ä÷4ªp4B³.x/Y¹tµÕÐ6à—÷çðko{ëD^;˧¥CÈЈP'äÁ+ð –Á¡£}ŸÎÅ¡<“;»ƒ‹®6:‘"âÕFj3-Ž=‚fdPexögzÙû»âžÞÙœZ]ƒO‹]‘•Åï<"ϺlŸ9;›|ÎÐ A èÅ& þÑ·­%^•¼OÑÝ;Z²÷¸$…û©sBb‚†6‘év@#oÖ1wª nº%£H¤fœÌÜ”M–,‘J…4Š`‰R2²#Žxêlû=´-õ”…ë>Ÿ°®ë÷_ÄØø†±Àd,>&±‰¯–’áz99Žô“Ðj7MÉÇÂhV2xu¤®KÛ >ù@4l< ZÂg²×¢cY˃ºìƒß¢fwÚ¦ââæ‚ÓËøÜ(ÙŠd›Î¢$FTÐõÎ‹Ž¹_à¡ã$øO4WœéúÒ¶i¼¶‹DW1hÏVœsÛÒ…Ä[š‘ÞJÜ‹7MÆ‚€ÌÍñƧiV‚\ c¡âÑ›Ç_r*ž8¼+j¸Ïªß›z¨O²<‡åܼæ6_ZHE÷ü¹û•_£OzÞáÃgllLƒ^9µ´P›„Á;téžú1¿ãP4}ˆƒ9oª “¥_šäZ”M̦Z£jzkZÞ¬¦¿þw}I/‚'Ü\§>ÖÅJŠRTxI2‹Ä"ð0åb¶׺ÌÏ(xÇáí÷¬ï\JsÙ¥9SõŽY§>]‹àñ~ݘ!üÓf#‡á CA „ƒ Ój2O(•F„6ˆQ ˰+HØáaÇÆ (XÉùކÂÏÇ’_‹Cì†aÎÐŒ(݆°‘>BÞ¨0”»8Ô»}¦ín§v:|»“Jß-©÷Ÿ)S#")B½ÝÅúnÐ1("¡þAôXeR‘HþÔÒB ©i  §£D%—êuÊT@þnxÚ,…>žýÅB<Éd(ªKi+_d]ÍàÁ1kíÙÖ>W&Ïúæ‡×Ôð®ô$þ: ƒ¬<5X¨ݹBò‡xm—l?l‡ϧµ¿™D(ô£¦çžíy lªnÏdj;~‹Ló¹ÿîã£Z÷p3'ë×ø=Qá4P"xñÑ „‚æ3¸Ûç‘îcT…sl‚SÙó^PN@|¸EŠSa’YA߇3˜Ä¾¦öô†–ë/#“1Œg0ê;×ÁÁ¼Ohð~S“µ¨¥)#PW'°¢N·Á\ÍÈè!w’Uv¹àÊ WÁÒçÕãÉ/g}䇳c:21ò!ìí}òÒ@ŠUÊ‘8 ƒÆ'›unyºÿT)ÊZÕt–ŒWÆø>‡ Ô5ú¤Téd,ûXòþ(:2VÙoÅféŒo8¤P/%ðJŒn±¿¢¬˜c8â_¯¾¡ÆP€{öF<¯ F‰ç‡¥¨“T˜Ôü•ЏÅ4 V‰æQ±°‰ÃPÈd Èì?›lˆŠ)᱃yLÇ,i”¿Tœ‘åø;ƒ(‰­}_~˜E$Ðøô9ÃñD÷|ñÿ`5û÷_ôÅÁÞ‡ðîšýPìóÚ}zÊ;þ‡ëÈê¦[ü3åZY¼_ÞˆDƒ Þ#c±»tö…ö0þ˶´w˜Šm8þ‘Såâæ ¤¯ñÒ›áËtn¨(µH€"˨ÿM~J¹ÿãÿ‡ã_¬+˜Äø‡—N…Ž9Õ«t6èŸ/þ ª­µ± endstream endobj 1367 0 obj << /Length 2352 /Filter /FlateDecode >> stream xÚÕXYoä6~÷¯è·¨7CŠÔÁÙ'ofÌë$3À`dµÚ­D-µuŒÇÁüøÔA]mÙñû²/â!²X¬úê¢\Ý®äê‡3éÚ_}û½VJ +­Z]íV‘¿Š¤ÒÀh»ºöꬩŠOY-Êìþ7Èk˜H«r÷áú|_5mƒm^B ?ÕúÃÕ¾ý^ÍÊÕÆ—"°Ó{—µ]]6ëF^ æ¶öc/«y¦ºYo‚Ðû=KÛs˜‰¥‡ÿ&•¹íêl먤iUoóò–‡måÚ}Ææˆ;½,Íqc¿«§ãÈÒe\?)ݸ2Ó6˜iÞ/§´ŒåË}tÂYo¢Øûf½ÑÎIJîÜ0<Èr`¯Æ~ìõ ˆ|ÐßU+íU5ÿI¸i“›"ã=M—ãh­<7±ëד¦!YÐŽŠU2×±Vˆ‚¸'®‘ãü¼¤¿ >€Â­ëÑ¡÷‘ÄånHâÂÎÇA^ßœ“x6:ÒBúÑ\J9ÈGkí•yqν¤Æ[hãåeÓ&L#OZTýŒ‹PÁ8M ÆÎ¡"Ñn³/u–vu“3„x5É5mwó¥ÚlM×åU%È‘Àà¶ŒŠKòCHÇ"VæÄDð·ÞÌlʘ^„;·DO—(%T|SgŽ \„!%esßg’8ÞfM)ã9râržÞ%]Ñò^ºL^pƒ†#?KEÖ@ †mo/ybº„HÇÞ ®,ÝL=޳™ÖyHp€±ö–q«… ¢Aûl“Kª2J„cnFÈ |‡ÜýQ›<—ɱ!kÂùüñ4[-tÈctèšÑ‚€òzrvôê°’(wÎÖb)t4•WIÛf‡c»ˆ5cE¤ìßW 3µgı1~$”9±É±`Hð~ìª?­!ÁK tÄLÉ׫HØH!‹ÝjÁѰ‹Çñ²o„ë“ÀhBL¯6œqø&OB"^ÿ5]šf V ÆZöQ°ÒoÇ&šø¥fn¶0‘ÕuåŽJûT›‡p ´ ‰'ö¼KòÂ'U ‘wE TjÆ<Ž€ÑÃ)ßÃD²ê'})1 TVb¨ŽÆPM´ho»ç 3èf±mûã..ßÿòæÝ{4åªüŽ€ˆ¸uÌQE2ÝõOúîÇ×o–R²3ÀÐ寯üïÅÛË¡„Ш}‚¯öý²К—}ã&û$#?ûœâ¾NŽhp‹3Ø:˜ÄIN È’z@ ñ¾énù"„b 0--8±4Œ„Õ]Ö¦ûE¢òÍ¿ÉOâHh°ªåü„ù}A‚òâ×…&Ãj]ÛØ):w—ÉÊÃÒã'¡ÎlÝÎ$pÊ $®!\Ünè•‚éaÖ,†CO’a wÝ9²äjˆ*Z YŸrå(¬Û%Esz>=ØÈÙÛòœeâ×½&¸4”Ýš™Úœ1Oap[1Ê–dp˜Ër/Н)z¨Áªè§š]Ê:Ö§W h gú”W]ƒæŽcÆ=àÐI]ó„œ<$‰%4C õ€ùç]l(¡°<¾ Z>…ÌŒõ Õ’rš%wJ¼”Û¾Ãí¨Nb-åÿ3­~階¼á’C€»j9˜î›‹À1-&»‡ž<çàA=PÌ(š; è—‹N†áS&†÷p™¼W8¯i¼IQß?è Õ¼ió´9)І—žI-Ê®€fùÁ)ÍØ‡ëÒRM ˜žqJ@¹v<{suvw†i´\©• 5Äý˜Z"Mg×äj ?Ápôêž–py(|H&ŠÕû³ŸùÙõ$‡ˆÀT íN†Œ5­sª—Ÿ…Qòáþó‡¬EãkßÓ§K%"AÝ5{ùhqyÖChWÀYJ›*Jø˜‘>ÖMd…ÓËç ®^Zü¼DJX¾HÍ ¢…I«¤7t–eG`œ#0þr ‚š ºÁ ôí£xÞNƒüT:â3µ> stream xÚÕYÉrÛF½ë+P9Uæx6l¾Å‰ŠOY”ÊAÖ‡âØ @ åëÓ==€jhË6/9˜z¶î~¯—‘ytñè— îŸ¯//^¾I$8+x!¢Ëu”+¦ee“­IÁr-¿Ì€•š%RY9T_¡©ÊYÆ“3YªKrõLK/ ¡ºXŠ˜Udž\ò„Á3{Ÿƒ¬”LëôLöÊŒ)©¿ÖÞχiÂ4œöÔÊgÇ)‡Ðʳ3Y(ãð<¢ACæXBZ)è£Òqò¶ì>Ò)›ÒkÑ€‘í@ï>o@†wÉNåÀŽLÌÕÊ–³‚àSYŠ ¨4sO/s4P‚«‹‡iÆt÷¶74Éù_*(»nI;†¡»ÇmKë4|¡Ü,¯leÛ: PNÞí¬óîòžñ¾:Yœ · <Žô‰÷KÕn]Ì»©GØ¡M¤&¿‚Œq›9z©… –`p#É3à³i¾p´õîê²B;u êÍ #Ú³I -ˆ‘ôjQÍigø®h·Ûåk’OkÃ…ÚõÁù¸ÃÜ Èx˜ß nqô>Ho¬á…žè!ñ÷9ŽC¦L ~úˆ„V3v…¬Æ²åõň#ÂkÎà.3÷áÊöUÙ­õ“ê–C©çe±gÃÃΕÅöæÃ—*£_û‡ö]CÕœíÖÐQ,€ð½PRû؛̨SwÎEcšþ„€fÀÉ.l2S£Ë@Ç~šm9M²cQ»†ùùêI™p½Ç¬T;ITòT®' ÉBЈˆG­G„ƒÀ@üƒIv HWÏH m¬¯¯^lÚ~èñix>´×fãsÁØ<Ž·»…Ìà]CÑy¬bWT!Gs¦Ž iààÑÈÆÙX]´öœñ\I‡Q"> H€(ói‚(q…)àFX¨â”#ûaã×éÙŸd84öSk¦¡€)“^÷ g»G ÀžÀHL¹á6ØÚX‘Í{›?qò…Š;Á1—ù—_Ђ{‹m5äO—·;ªBÔìa‹e©¬í¿.åÀl2pÀÇ9‡Þñ=º¡qŽ• ¿oiö®ìÊí2؈BÛÑûȺßXÊ䞀›v_¯è}âh›ƒ(V˜^ö¸fÌ6{È“]3‚ןÑ©ï§D¦³„Ë WÁf#ÌÞ²Ì2aÅc³Pc]# Õ1 »¶þ?,ÔÚ!€¸}í»ÞŽØèO(yBGqOG6å|xù!ñ{FG 1ÐÝK«C:âçDÇ9¿–‡ylf9kÆC»‹ß~eûÛ¸¨DÂTª¿•‹Å¸¨ì®NdÄWpX÷€Å¥Ç\W™ÄØ\U &L_ØBSø}%G–¸Ùž(9ø*ž”§qèèyø À2Í?•Ù þ¼’mÙ<®R õw‚À:ÄySÌú ØÙ6U½ïíD¹†Szš¢Å¸)‰z¿öor²ñ²@\‘Ä ×èÉ îPE6» ”§Š&åjux s¨tWŒôñ‡bÛ_}n?®ºŽRÓÿeD9¤Ôiúº³9o./þóðSõ endstream endobj 1376 0 obj << /Length 2052 /Filter /FlateDecode >> stream xÚ•X[Û6~ϯ0ö¥2P+EÝò–b“mív±˜b±hTcÓc¶²èŠÒLØ¿çFYr”mòb’G‡ä¹‡N6›d󷉌ßݽxù6Ë7i×InRmʤŽ «Ãæ—¨7Þµ¦÷¯Ìð>É“_{6n>À"Ý~¸ûûË·éâ„d³SIœ×9ð/3Œ}·ÝeY58h9s«ªÈôüáØ»3φ“a¦Ëv ~Ëȵñv§u½;òY6¿1-“öÂÍt£¸&›Nc]äÑz¾Ó|¼XéÛ  ,Ÿ:ÛŠèÝ'oîÞýøæ¯?ý|‹(‹ v*Mã²®`RÅUªoxɈðg6Ôsú5ÝQ£|Ò¨'£ááÛ]ZG rF^3ErNï›=úãw6ÁàÎÈfΨ‚3tôºÃ-ϼå çl÷À[ö®'ØÎˆ%›ÞÌ8Ýïäþ.1Ý€Ýêݳ/y1zsàû¦ˆHO‘w 92¶ƒ½´ ‡A1‚Ò,ÎuÍÊý0¢¼U .íMsð²p<6¨Øž $àÞ~w0ï“Tw$Ð §.[´+Rýܵ–]Ê<Þe6W¸ª–aʸ rb&êÃÏú5e\g°,€É¹<`ˆá²sOG2‰BY• 8·³ƒë™zi†Á`uÂ/(û¡}ÞVY„NSöÉ0‰g2{È 'þÐ3yc0yI…cúœ²=ÀM!6ƒr…ó€@!Ê2úÞMîÓ÷XªtIN‚D<Žm˳IP\<Ù@ýØ´í3/®q…_ºÎì÷M/_Ïã"}a¦Æ‘lŒ“~ìxbe¼4=\bÚxÍe?²`·µàìz3ƒ¨·$ !(ûÌ$+àqéÍΜ/ƒ} q†•ŠÂy*:ìòGÛ»nJ×IHöLéì&”©Y>êOò¯µg»¼TR4›§h6¥h&)ªoÃ?MÁ!p“EÞœ›ËÉõAfr¦_šd‰0“ñð|11îþ·¯Bhƒ¿~è'³¾O3=ÝðqË:IœLH 2¬ÝŸVq©²[–æ`áláhׯެ4ÊéTËX§Wù@ÛþÉz³Žå™pÍêÏYÚvP;.Γ¹»æŒòå*‡tìöÈð…àþú aCy›QBaÀÀÙÜüxñH³±°lï%`Ìpèˆ^w-dÒ£u£g»¾\+‘ÈOõðÿÄ`~k™Î6àœ,Pé"PE%Q‘è !V~ƒŽÚg »Eúò+Êñ Ÿdýš&JCwqm ‡~4k"+Ó*p¡µ’›Î¯› –4éE¼EÛÃ$+ãïÞ"ù'Ñ£?`Ç¢µSE'Ôs„˜‚v^uôv:„c YV¾Œ·ÿà ¸3;5C˜™5«%4YåMgõ™^ROÆóÔIž ¶O=Ÿ¶§Pa‰et¢^º§Ã³V„‚ÊH-Õ ’¨î(á)Üé?ÁâÕ¬¬Mhq`AæI*ay°ÐÊd¦Ç¢ žb+§ÐMê¤\Z™ ¸+‹ŠÁBk59êåÕØZ§N ƒæ1’±°á®³óS"«yþtByñ;ÂD°E@ &dkx˜5DEŒÀÉ1\öÎÄî ”vÿ€Á$­©)£1G^@$x†°…£7-äÐHuŒ*[N–}ÌËö š[Ê#|Æb¨ÞÔºîÁx9ÞÉW8LÔæìÆšÄu\ÓŸy_¯ž@6ªKkøQ§q‘MAü œQ”<0¬\“*@±©|¶ÌíÒDÇY‘-1÷ßXT©ÙÖi‘cÒRŒ“Y½ÃåÍs¢€:¨A MË,Mÿ0Î^(L’Óž­iä”T¼,w­h•€ç™þ’¤×q¡ÕÄèøîÞµ”öxÅ‘‘ž[I§e±ôŸ¼)Ûb^UôOz‚@ÿGGqΣåt5Ôúê¤àÀÇOæcƒm–¡ånÓ¿V×PGVêË€h¨`ˆÓL” H’%©–’0—g¤”Ç2Æx8…..ñâ•X½Š1kCºHÅ–:~Ï  npÍ*ìÏB‚kuáOŠi×UÞ2È»Ú_ˆ-‚?šQ'€)Ö<çJ€sVÁO‹E)®ñ +ÿÐ>9`®I/‚½˜JyCÖcÓzÙ5ëÕv€û,V Îmc;òSM^ªTòïŽ9ð õÃÎ~æÙ•¥ñacs˜? ß ßøëlÓ°~{ËýÙ <›¹e‘§¨Zt5°\¾=èî[2Ui‡œð,¶¦öjx*šš£œš#&ˆÐøBÄ%kSqP¶é #> stream xڵ˒۸ñî¯PùbN­/‚œ[’ʦ¼§­õTå`»* ÒÐK‘ ;žÊϧ ð¡fœJrÐ/ôJ7ÇMºùÛ›Ôÿ|÷æÃÏRoxÊŠ´à›»ÃƈI –*˜í7Ÿ“]Ûì«¡j›Û¾:6eý%Õéçæ+|øÍ×»_>üÌWÇÓÍV¤LšNrgn¶R«¤Äž¾‡y–&7"OÊßñ×6Us¤]mcéTÛàÔv–FtPÐ^• -÷=»Ùê´H>ÚŸo¶ˆsW}I¹²{O®¤ÕSù½:Ý“Œ'Oç@ØÿѼ[aE!Q*.™VIU:V¤DVLÒzÞ߬l÷Xõ~KY£ørÆGÐ(F•|Ã9+´A£ÓZmÉãmér£2,öÓ½ýs´£íÙ¹kOÈLä®¶þÄêÆî*àQw ܵ%@u:×öddvp›$Hå=‡q»+‡Áv ÖV¥&ù8БSÛ´¾«ÛÞÖO4éloOˆú¾¶—ÈÿòÓOœÓ°ö·· ¡žÝM³rEA7ªÈ“?ýú±§¥®Ä;¢ñðP6adiðK‰lüq£³¤ü´ëªó@ð_=U7lìåíu¹Ã…ß=Ñ]Ûµ#­íÁP´âI߯øŸ°é›T*ïiFöƒ–vƒÙ®n˽Ýü±h´kñÚ¾Óä06;ô½²®<ùDàŠîÔ6þ¼N†–VNàgÇrðÔœŠp×tŠz‘€$hÀy*úÑürIŸsW!uÊ5X¡¥Eâò\í0Š `ìË£_C—%¤o{=Ag6œõ¼ó`rÉ£`ä¶Ž‰rªº®í¼Ï>8]?o¶O“çI;Ö{Z¸| ‡ß5s_ûícïT #P:¹ûÊ)WLi}áÆ1÷U)K‹°ïá$éþUWQˆ‹YF`Àßµµsü´0ÁñZUP§×1ȦøÍîZû à’S<§)ùAW•÷u¸Â–V–~< |´õ0Yð^83í«½iMFmxnát4ò…ý«Û†TA ;d¦.1mq&XQL—Âbø¶&„@Zä,Óf…– Og‹¶½ÿöZ†Utò7 6ßôáúý•÷CGé€_¸Tÿ[oyϘ\ ªËÕÄ<°#ÏsfĤè*ä3"ëÉ„LØÅÈøžÒTSÕQ*†)>Q™Rj\¡"Ë—Y\£}t%K”tµéj1äÓ|rÆÏï£zröÜÞlyfè“Á'fQTh¯Wk©-‡Inò+®´@½+-’æ“ï"Ôùn–|»á x²X”ÿ ¢€,.Ôpq4%–·î¨7žÐ™r ACŽ¡5ô~„vcãõÈË0ÕìÐùº¶iÇâc®°• bëCNˆr– bÜOÀsa_¼ôã}o!bR:ƒC²€XtE , Îeá8®#Y*ôkZ‘éZ+€ƒ=Æx=ºaÄÀôR‘OQÏÄ( Eƒeûm¨LûG†Š³ ‡Ò¡Æ¤áÖÚC<ªxüUú°KÍÞŽzsº‡Ë¸Z‡OR&¹þ¾ê cPÎSxr³tƒXû+U¾T¹N¶X ¸í‡r{ ÿiPU!~RVo°pxK`¢îë/A&g0éÉýpƒ?ÿä2Þ@“±¥­ÿp0ìI”Î…ÃXcŸ‚¦ãˆ»HJþžÚÛÐt‰>VÒ bTL(ñ£Y.cFMÆóX¹GÉÉ qìKEé ´GT>¦•QW§H·Åê‰ ›PÜråæBµ®Œ…yYÕT¬Ê«2‰»£â¿” j*_ù‘íó‚e¹\Å „höËE3j¤©·Îì™Å/X¡ “íäþÐ&Æ<‚çÿßu=C¥+T¶n!›MÐÙ¨ &Œu‰ë%pØ.Öæ­–u'´Fìë#’ ¬rs©¢‹8±TéEœŽKœfž¡£-³0,›ÝXoˆÝ™HÈgYH=ÏBÊ[˜ò=VëTœC}(_{-y–Ë* Šx<…4Îçþÿ5c‡ÖßL⺔Õá ŠÉ³+šÖÈŽzUÑf½ˆ™¸ºìÕ€BUþȺðpܹ=TLÂ[ùG1G×ד°!äa¯Š ™³{¬ØX”Pæ´{ƒõ¶ 2pLbHmèI«œ¬ w|mpØÙ|“á#0¿š, èåU½Ì‹·Î̺Éý97ôRîU9tV{x溩‡ t®¾Ø€Bè0ÙZC䦋Î}V…/!ñ xhu}!ÚTÍ/Ù‹UˆyþTrÕ rH-9ÿŸ:Aa´XWݳH#É p°tœ»—z¤5‹ºØ­ú¦×9x4#…ÇðÞpaxá3ã/[F^Lí÷3˜¸ûëî~~‹y•«j!¥ý\²LdëvÖõ-KSP` 4¯Ûæh}Nñÿt4ûùÍ=ŸQÀÕ«þ,µžlö-á ¹iål¡Z_ý¥»r‘-UwýAgaAÎ0§¡Ð´{d÷‚¯ÿ£C®6ýõîÍ¿›é² endstream endobj 1385 0 obj << /Length 2829 /Filter /FlateDecode >> stream xÚÝَܸñÝ_ÑØ«iZ"u./Ä^Ç;AlѨÙÓ‚u´EÉãÙ¯O]T–½ö"‹yèY,’ÅbÝ Ww«põÓ£P¾¾yôä™IVQ¨Š°ˆV7»U¦WYX¨0†Þvõ&8 }[;ûã¡ošÝöm˜„ð‹ÖïnþúäYt63\mt¨’"ቯí8 [oLš%ª¾ÛÖcÝw؃놆º¼m,CÜT¾—»~àÆa½ÑyÔÝ#Þïëj­³`ÏÝZö™œÝrkìùëê»®l¸=X×7Óqÿ~'¸{ÙP‹§ÃãDF%qÁÇ¡õŒ Ên ¤<`¨Àv '"²`tܶÜ:ÅüÑf•©" ‰=°*-²•QE"kF!ã™FœhDÜxLXQ¥£¥U”ɲá)zœ©\G@.áU&;Y§ÊéSSß.]ÓF&ðeÅ<í†8Ì«›º¸ÛöÄùíÔȸý„w‘½³Ž!®oe¬Zo¢ 'þ|Ä?ÛÕ¶«d°&øh‡]YYwÀİj¦-sïl©sl†Ñ Àwo›·îëqzW"yåáÐÔU)÷œ™yµ»áH²•i2lm)òZšZ¦zxÿÀí[:»Ý—(z×QPûÉã¾¹µ/]÷XÚĆñ8zVö¯pl_vw$±ÇÅ«rZ–@’V±§¯ž³ô9P¸¬qHƒ VYÐ:@ó™‚æ*äÎYDTéœFTl™cNç䡊òYº>.- ²‡©Gy³´J¢2{ŒALÒŒ?)|––Ô0!óÞ}É⦄±>7<¿ÔmÝ”x/¡f¡ £àoSùØ1èvª›q2±x–L¥ÀpÙ™y³´µ‰X‡âÂÝ~ªìa”Y `ëû½í–˜RD*‹ò Öžï’ƒõðµ¿+gyeüF`@Y”¬ Œ —¤ÎÐ.‡»©µ,þ ij'-|XfôØoÃ(XšÀ%€é:“Á®ï6]Ý\¡(‡ÔrtŠJŸ¤AÙLÔ͈õžœJÇÚ­u®¼³j½‰µ žïdÞÅ0Oª,Òñu:C¶ƒPCjLX¸V“ƒÍ‹ækµÃÐõÊ4€ K’åg,ú€­j¶p¬ˆ%ƒ÷šZ¤¡è Ä—Á ìÆEyÅ5z6¾t·€4ôà:Yµâ…fÃÁ0À`Üê±oûî=‘".éPŽ"[qI=ˆyá_9ŒÞ5ý-¸Ç%ž…±*âoÔãñø€°t¡LhÎÏù¢wãÒN:œâƒ-moñ3¢‡Ç ƒÛx+a @ qG÷ÝÙØÑXxv |‘6èoç[–`C6õ¶î[ð"ƒøV¹½ƒø¤G¡¤¨ØOœÈB•Åù¹ÝŠÅÖOÀWÏt¡Fm$ÚP=¢ˆ]4Îè‡TSX£FŸ;’8î)@;¤IRœ´ÞF&¦¥Ý¼Ï=’ÝO  oe–9¾~Šã?=}þòJ7Ï_\ÿåçÜ\-:*¤Úõ«ç¯®¯¸ ñ,îC6„ô$U5 6+4ºÑQb³ì¶²b•Ò=’ˆÞ×È\ž‡¶µ«Êak· yªƒçÝ<±wH¿=šàД²!Zêù;Xáù(õÇ© Ç:ýœWµ8Ì0n9R¬ú†‡9Ç–—vDd7¼äx‹\é£CÝÕ]²h*’Ù%B$t%{L#oÐÚ¶¯µ e@£íiU}Ûö·É ð4Ñcv|:Q©ŽÎBÊÁ yGJA¤cC:Á­’˜°•àc`q‡‚êzŸ#X­yWqf›-¸á´ªe3oç™Ýx÷ßÙ!ê©ùbt…F…Åÿltg4d9ÑEtg–ZÊ r²òðqsЃ8NƒdŒ)Åd—+1Çhï4 ^¢(‹Uœœ}It ú`Ê-bºÉ(´C ’ /]‚Ž”¾ ÔcœÜæ¹{T:ž‘ J[T hfó^>ø¹d‹JŽn–¼¥¥r•šô„¨ˆ*~0Ÿý AÅ †@A2«/‚«ýY¤Äº0‡€œzo9xæSžs‡Ž’wšq<[ç1ß*Ö§²=4öjžG‡ÊV)°Ää"rª01|!o7Z²ô™G…*RRÐÍhæ5˜¼\0ʼn`\ãòD}»$éúÕõëŠ÷ùç ›*ß(ÍWV.ÔC©Ât›ÔÃüŸ«GôEÁÿö/8AŒÿQ¤ƒ— ñ€ÀLô_Ñ?&+ˆ&èÔôßпå=MÇä ÆßyJXD-ü[ú¯è¤ÿþd”Òˆ$xŒLLñ¤þNµ‹þ“j÷ÿ’åúVº—’óº‰sa £¸‘ «g±­[ýððÃÕÑÉ,*_rZðùfԿͳì»X¶ÿ‘b&q¶bØÚo+ðUý"•á™ß޳?¤Â+“E_¨ði,eôü• 6¿^á‹UZ« ½Ô\„M‹²£HNÂ>ÄsØG$HŽs­ZóÂBÓCm)qÅôÀI®ñy¤OJ.¾”//Ë5ª¢P:‹ÏÃ(çipRž¢l7ysÊ\ LNÆY.4ÜI† ݉㩺aô­’$yrXìC.ÊÒÀÎÞE`’9qç­:Ft’ó¶%f GšoæÞR"ÊL6±¯\B 1?1¼÷ŽÕ;]€AæÁß …ФSªnódòÂNp[Ê£Œ×#.Ô2ÈOOlU·ôÄ eç³ÁÃë²–‹”ÂÅr jéD\Û”þìéBƒ)o©nA…›ô¤€Cl°›áb«l稀"–8ºçò)VzÄÝÃNrlFöœ·-#Ø“Ê tåý§öEešÜÉîÒAŸÌA…׫‹gy©»ó:彗㈇0Éç²›äË9¿Ã@ž]c¸ZW5ÿºêRa3‰Uh¢ßokÔ™¿¾yôá®®"|§…^…`«öÑ›wáj pà2E¾º'¬våà<ÈÃ4«_ý|u¼B·ŸQ¨–•MI(ÖVÏÞ%ÁãçàÓÁíaò«òl  ‡^j׋Su{ é+°ÄU0O‹Y±vXÓâ—@“Ë;VîïŒB…s€±SÃ9Ü<–ÓE@oÏ hm‹Ø·÷è|Ïðm.—çGgz¬ëòqdþÝùŠAT@â%ø+R’û<ŒŸTv£‚ò‡ ޞѥø96\•À‚\Wy 4Ë?ðäô=<ÖgtÉBn=퇑!R;—0·tïdÖyòU‘Ð4±@œ.áÜøw/A2u&*”&@PK¥‰„•È:>fkÇ~ûž8»U_U/jÿ*LQüâãŠ&¾TFtC` endstream endobj 1393 0 obj << /Length 619 /Filter /FlateDecode >> stream xÚÅUÉnÛ0½ë+x« DS®"Õc¶@P(â[’m36QYr$ªNþ¾”HÉQ¡t»ô`$gÞÌ{³£=ÂèS‚ã÷ý:yû‘ D0¸ hý€$E€¹?íÐmª»§ÒnÀÕ­klµ¿Ã¯î××Þ½t+ä§ïÑd†, W£‡!Á†Ìl²BóWÅ ŒoìÑ–ºYeŒ±ÔÕý—§Ÿ;ýfEÒ6œ6-]f«¥¸„x:lŒ<YŠÎ(2åxš§­9¹¦íN§UFeZ7.F~¶¦ÜõxƒqýÐã&ÖÉcB<F1™çåJAÎs´=&·÷íüã5ÂÀ …΃é1 ²Ï´D7É×—ÊÛåÅßC ž“dŠyŒ÷XsUGã´ÿ¼ÔT¥»zR(c„ ž}qeÁs}°mðjêÎÙÊY¶º ·›ÃŒZžJ»µ®|ŽêêÛª~^'í¶ýù`vójoÕ#¶;D¸}Yot¹Ts†9ük.GKX˘À …ôÄ•7³Á87úôÚPä $°–°sDüv*¤…žÖÏSA¢Nt˜Š6\ýz$”—gÒg[ÇÚ l–2ðýG™M}1$íZÓ.òã€ñä5kLÛÍR©_¶Äù`ª?xÄþ¤—|§Îäˆ&t&ÿ¹À Ož‘UyäÆ8PBçܾÔ.&î:.§®*mÈy)EÐK.éLûîµLæ{5†%lŒv£­p´q/Vµ Ú9½,ªþÙ5uYšf`=ýA!)!/üä œûΈۊ‰™‘ß¶?=î·é endstream endobj 1397 0 obj << /Length 1037 /Filter /FlateDecode >> stream xÚÅXIoÛF¾ûW°9QE5ž} ÐK'AáC‘èVç Ør"Ä‘IF[ èoï[8ÔP"-º ’Ë›çYÞò½²¬>T²z}!›õ—ÙÅå+£*mEˆAU³û*èÊë$Œ­fwÕïµLMpõÕŸóÏ›‡Ånò~ö+½P®x/d5õV8›BM¦JiW¿™Í~ãçïÞ]3óvñåq±Û³˜Py‘‚‰(dªŒH tª•H^±(Õ¨sU‚Míñ"(ŽÒƒRºñ0™ÆP¯Æú–ø9ñ¸/n%]!úHtqÂï&Ólý3/Ûâ¤|·$Úž*k뼌ÔÁ/ÀÄK“Îp!àÓ$ñè-ÑODD÷hµù&Æ \Ìy{”Õìèq€9pßÍp€ùž‘ª?ÂbQwK7ˆŒ‹c³aÿ2"k¢«‚2|ºç«7ÒIà¤c4Mý¡ÐÔw²6Äõ…’BÂ[@?óJѦþ ölÂÊG˜¢ OÖö|òAC #%¦Gpm’=3eÀ¯9¹ úž¤:ý ŸB¿OºQç^k4‚ùMð¨6R}ç Q&äZW߃喼0rCžRB¿$º¦8UÞŒñáĶÔãƒI"†;Zo.£ è‰õ”ËÙ¨9Ñm±ÃôvvãÌ´FD¡ñBV¹ñöuÞæâ(;%! ñ€à¿'~ÁÇÝÚ1º~W_Q’Ì8on¤mî\òÒl@ØÞÆYAaÒ((õKR¢H¡ ^£ŒeÍ«ƒZÉ wå¯ë^6xMt—¥fyÒg× ?Œh"æ´‰Dj"Ðò^’–L²Ä›~š àúH“$eÒ$šÿ0ãöDçÿHtw˜qÞ&äJç_•~ÎŽªƒÖîfî(7ì`SrgKî,‰_åt8MX~8ü¥5õzç΀1?c7Õocvè7;$—;/ÆÏæžOÙ`Y¶‚Obne^.ƒU.àw|Üxþ#/[‚Óœ€yŽ%¨Pé(s6'uÆ †àý»€—G©qÏò})çw:‹#O5žpwýÑÁÛîªÕÙ¬z²`Á°âak“ñþ/Þÿ*8ïì:CÚÓ&Ïd#èŸBÂSU×ÕföÎÅï—Ä×CI<þŽ{}MÒ/*›Ã¿lR ž e¸+¦Ãcù[L§nX8?ºÓ Š¡¨ŒCƒluBªÔ5{l¦—ýS‡áïˆgÛÛ6ûsUìÚqëâèoœNúŠ[EáÓimG®ŒÁæÁÏÊlT¿ô(u?Ïͽ¥ü{d[þ’Ê¡2åg­æ HþªX·°>úŒdËÛßþÇëå+å*%E’‰þ uÞRh¡|Qã;B®fÿj’‡£ endstream endobj 1356 0 obj << /Type /ObjStm /N 100 /First 918 /Length 1280 /Filter /FlateDecode >> stream xÚµ—Án7 †ïûz­H‘" 9´o‚4‡¶AAº( q‘8@úöý9qÒÌxQ×È^†ÚùDQÔ/ 5ÌK+ùH>Fq*Ô• 1§á…¥Ã­t¡2Ot!E(Ÿ^D£X㢭¬¢àŒZQ85’2¸ã vH1xµ¦xŽb2r+æ–#pñÐ4¤D Ko…Z‡Ï.0¬á/„z"6B‹ JX3@SÀl¡S§bЧq`¡)iá­š ÃÒ@”–ïzF¢HÌàÊ:Å„É,옊!Qäº8BÈÑÐ¥nÙìÖ ÓèC¶˜ñÒ.æÌ òÅ 7Ž„qGf¼ ÌÙ11äÆIa,ÉA(,Kv0#-§„8i>8 ,ŽÃ ›§WÎpÕáÊŠwt ¬‰wÀ!V8|q$¥7Î98í´¸ (=8ÐΆ8•JÂPŒ‚i÷øgth¢ãŸXàΑ‚®ßnW]¦Zcññ°öX£`LtÌ—á9¢ïîiÀU0A p¹8Ik)B„-M$­€å™k¤XjJí K÷½/¹B”’ë z•ÀÐS‘‘€GÑ%ËJtÉrŒ"cdJR׆y–G¼A’Xq!øOžûa‰Ü(Òð6XËN´:,Ñ´Vj&×W¡Ï´°Sh,œå¶pKôpuu8>-/)óÝÊórüå×ß°g°JR¡æ·Þ¼ùýðäɃ­ÜfÀ6ª"7+ðúæím¹º*Çk,eø]ëž ýÜ@F° wž›¯}n^Ñ]ƒ[‰»7ð~|öîæõϧÛò²Ÿ=½.ǧ·åËÀ/þùû„¯þ<Ž?"ˆÓÛÛ÷¹u#ûŽÏOïo>¼{}zÿi;/ÿýtúã¯W?Ü|,/s¡ZðïèÕ;ôNp‰dÿ*Ø“|6Q¾J.$jŸÖ+Žˆ {5ìƒD5õµ Ê ú>H˜46Ý.Øfméá Úy"FœÉÕEæJ‹k•~_-Ž~_‹ƒ.×"N¸}-® ohqÃ=¬Å ¨1ê0™¹Uœ±û H¯c&ƾlƒ R£MÄH¢UâåàóLÜúŽrÖ »\GvFGúI­¹!£6¤iT‡àÎîî-ø°ŽÖ`¯Yí‚ìZ]'<2&EÀÿ?Xþâ¡[ªÐ»–dë‹¥BŸ5–…5}q¹ñ8%ßW’µË•d4¡¤„õT|z¨ïƒ½¶6öAL¡f»2Õ¼·ì‚¨f+а °E5Ÿ˜5®µi¿ xš:[V’»\㌠î™c,ÐÌ<´g…:ã‘PƒÐŒÇ6j¨îƒ÷®·g!\ƚ̀FµÅ§Qqcøüçºií endstream endobj 1434 0 obj << /Length 845 /Filter /FlateDecode >> stream xÚÍVMoÛ0 ½÷W;)Ãìê[V]´†í´Ü¶ÚÄiƒeIê¤Ë~þ(J–åØERv¡hÉ’ß#)3rOùxÁâøazqy#9᪔Ê2]+ˆe®dŠ“éœ|£ª“‚s¡é—§Õ~¹]Õ–ëûI!­¦×³‰äôaž¾ÖÍoÿ\7“ÓO—7–˜ÒYYùS .K')/‰GóðžÔÄÁ¤0þE@R1àðÕ¤¨,…ó«ŠÎпEßÏ3ç§œ hŸÐÖ7)¬Uô}šl%ß·D›V¹RôMÎüFØñt «tZŸ‚õ[óË`gh¢­Ñî=jùOÀ—~§ñp%B Çáz±Ë×lááæã»F;NÒwð ÝâÜmƒvß­Kp˜Â#•?¬gÔdÔp´uÉ>”€ìå1kõ«‚r3Ò!\G(0Jáø)ò£P+œ[¢ÝµëéÝux÷;Ó <‰L&Öhçžb•‘§EH …Y8M¡î¢ý}öŒÏÃ6¿[Dm*„#1ü"øOháÌú5ÚÆo7Ø-Ü m’l=°c…‘²…sxçœ5(f_›¨3öè#OåúÂâƒÎ¹Æœ¨ÑüŠ‹¤Ø—ãÆƒ1Ðçc0 ­—ëÊÛÄRi²‚Ù1¢,JÓ*T ŠÎp&(½ìëÍ‚'B—Pž±K˜€fñnRUD:Û)OŠ­²&sG%ä¹»Ê;Þ*ë‹è5Ú=Ø.„Óæ›o1v*c;ÍP%Kët¿ññ±«ÍTeÅÕ¦×À†ÌŽ„³>ûGê$öY¬’%VÆýý]XŽa0­†·aøÜ›<‡t§JÆìé£× ‡aFX-èBl2èûzZÈŽ¿9ÑYŸŒ»>–:¥®ƒ(ÎÄ?v³8¨kѯ—a x§€äY›ÝãÌ<%„ôqÆ´èšmÇúa¨Áì M‹Úާºc©ÝôÉÀ|é±ÇÿN±7Zr£m¯*¨ ñlSÅm½[_ðñã+&Úóo³»x—ÝÅM~{wÙ4ê|•]|›Ôº*vmqT zú—?/o¸&œþbX!li„ÿêjL&i{‡\O/þês¢› endstream endobj 1460 0 obj << /Length 1460 /Filter /FlateDecode >> stream xÚåXKoã6¾ï¯rYX+|ˆ"¹À^ 4Z½ø¶-°ÞDN¼›ØYÛIÚßy%˱·AÓC/#š"©™of¾Z×…(~z#Âó‡Ù›ó - YWºnT1[VVøJÔ²˜]Kx3™J©L9»Ù´ó«ÉT[SþÚn·óëåêzòÇìçó [4•·ÚáS©+¯ëbªdå›pŠäuÚ&Uƒ á£N4 ­¸L-× ]yIã9q^xœòªüFòd»7ÞN¦ÖÖå~l²7ù¾%ÉôVÖuyÆ¿Á;ÀP©¼1'˜`Ë Ê’’-É9É+ÔZ¿Šò>ôq}d­ØÐ¡ƒÙq=Ûõ¸ƒ­•a]Ã릀EÒÂáOð ¯Z@iKˆí±974Þ!"¤•µê½§µ¾\á¹<¥Ë{úîœä6J|±£µO´vcKk£ï6ÙøŽÆmÔ*y œ§´FÓ¤´ññi25®üã-ߨ¡vNÖøÆèÒÃ2aàÇ(ä”r󒜲N  F,A‚ ´z‡*J\ ?ÖIQ˜úÀñyRÐP€ Â9"•£¹ã׿ ƒÀÔM¹€Íš"µÆÏÁø’äŽf–4^“\ñ†°U6¸°¡— ¿LÑép›TŠ~iS~ala#QQ¡L%†€v±i$Å…CCŒPx|b7"’3ÍQv9Ê.˜éÆ”i ´{ËÙÌ9xOò‘$Â%Ñy†b$\eéºH)/ –8OA#B’|&`×4~¤1å“Å}–|`Á¨k2mMrE²‰2í+gm;e°ÎKõÿ`n„ò’ ü†‘ú;ùV“ C,ßÈÚ‡8¶"Åå¾Cèž®~Ü!VÛ>Ó&¥¥Òå{T¡ÁÏ:Â7qá=¾°) G¬Îr±¦\ÔY.RIȺc6Ôº²Âõmcý@ã*/dŒªEFA¦ §29  %ZHJß‘üL3-7=ÒXfd@£w/>2çJRÀdÈFú>ËðžH*Tä-‘è.R©§÷ â<.zÏìù”•ÈeV:Û¹‚JgìúmÈúŽvÑõu¶20¦ä^ˆœÀŸgÁ>©k­º=¯|ï¡„¯ë|] a£ëÕsç{Œ†Ù¤;(®•?X|¨g·Yåoi|Ÿö¨MÚ?PÙpWrÔ»HêOYe]f¸Ê›kÜÝ/z+ Ú²ãL¹£¯/éë޵BTZZ8iÆ=#aSç™Ò“û¤gòÀf-FyÇšÎc‚f+K'·L!}ÔÝÚÔ›x€cßÂ@&}ÇjÒ“7ÑÀþ±zÏÐ샞(:œïÇÏ÷"8nL²Í¾ãö)ä®Ò9‡+æpÆ2–òuâðŽˆ]$!߇®m}8Ú? ETl aDÇŠÜ–fwé*£uß7JÆ›£FoÚZ{R»úVá eÏ3nM#tÎbCߨ¿Í ~YýÅr#‡´;,§–a%šÇëÕ²ûáŸ!`vђܲÊÒ16v  œÕÁÔ ;Þ@kqÍ §f–QÿNá[¡Ž£ýÆ K ×Lpûò’ä#É+vuVLá2rj šŽµ Ç¸®dØ£p‘súôÖ—q¾v @uy"MøjôWw;ld¸LßÄ(‹p|Éøj›iêSÚÇ=µ<@äÃ>“ce‘Lù•/D;Òá¦×é]Cˆj} wu‰åï³Ënž+ý‹¯ÞG2dÕl"Ï8Ê£¹_8¢ÆB²c1`ÑmöoG{Úz€˜6G­ËÿôØd·ÏMÿZÿb‰eV1 ñu«bnÝfÿ*lhœºù4³éï÷=ʆì/¹Âë—g¯¶Ê‹ßoT3Žz¦zÁ6.^¡zi?~¼ê•„`×ë¥?¦‡Ïó i ) • ¼(”²US#O6¢‚–8Øåz‡ü8{ó7±åúM endstream endobj 1431 0 obj << /Type /ObjStm /N 100 /First 970 /Length 1898 /Filter /FlateDecode >> stream xÚíXM5¼¿_á_ൻÝþ"€rCBÀXåÁ !¡%A‚OU{v3;»Ã³@H8„/ÂÍKØ-Ÿñ’U¥g^ @QÄçEæ…ú(n¾|÷ö‡¯ï>„Ûpóåç/ÃÍ7w¿ìßüñën¼þéîró,ݽùðž% pþåæ«»÷o{÷ÃÝûY|ì‹»~ýéÛßÃmÂ@EeoC^ÁÐëw˜Í"׸Û(KéÇfìõ·=Êy„²¥Ôà@´lQH}MȲBMÈmIçôí ’ ÏéÛ $AÓ #$AË #$AÇ£AøtI(ÏéÛ $Ád!0I°¶ÂI¨y…’€(.óˆm%yPzc{Nߎ@<Ä6²>Œ½¬0¶{_a´‡Ê@jzªFÖ×ÔÈ’§FÏñ‚2Ú £Ö˜VŽ~‘S]8Õ%KÌy…1¥˜m/yQÒ#Þ×”¯œQN^\£®(”–u!ÉTz,+ ¥¹Æ²?hIÄëëu  ‰¶¢PÒS¬+ %µÇº¢Pb5Ö…’Rb;*Ô¿Wlo¥C–ÂlŠQZàéQŸŠÔBkÉÛÆV¡Q¡ã„a¶¦(ȻЊº!«qþðö }äÝŽ¹Å‹] Q¡ Ä+´Žƒr?7'Ž€¿z‰èPëÁ>Úì}ÌÁ.Ö„ k~o¾ç·GmNN°ýñÜb/ð,¿}p%ýÁÏ…iŽÝÛä¶$Î7ó†¹>¢¨ªüb„‘‘è«ÊƒûGꋲ„ó\é©ÒÈ¥P´<+Ϋ˭ÕAìßi1[|³mÞòËÏÇžibÌ cÖ“Œ,‚GÒ*bˆ'œîžFáPȽkV¦GC¦½û8#’Jž*°P ¦<:æ8dŠE§Wk' óFáH®Àœ‹w«‹×xÔæÄ¯zlJ¦OQA+â>TgFºTãxçJùEϨnPdÎŽßü:-ã…³BQYv¸KêyXq>Æ€¯3GªG¦yôÙ<††ˆ™ç€2bШÿùƒ—˜ <åâÁ|ìç¦æÂ<1ª~7»×OÚ-'+s¹n©[»!<Ó¨q­Á/>u³`Ù3/ûù¨Ô‹û¾ÛwLI̼ÙNÆý]5âïÙþj¥+íÆ¹k§­=fŽ´Bÿµäš;÷lP(5Uü¼Sk'N»ûCèîƒ:‰ó‹ÅÜt·­XWÜÑN¾Ç) ¹Î>›…E\úåžgΛ̳>á ödß$’j¯ýû¡êä{7<í?rˆ·¸y'bï–÷9€Ü»»?,ë#rÞ/‰OæÆÌŸ¡z¸370ûv—1óqÃE‘‰ü·ÚW—?ÔÙµ endstream endobj 1520 0 obj << /Length1 1927 /Length2 14382 /Length3 0 /Length 15563 /Filter /FlateDecode >> stream xÚõPÙÒ€ #ÁÝ-ÀÆÝÝÝ%¸Û6îNð ÁÝ5¸»wBpw‡àÜ/3gÎÌœïÿ«î-ªà}ÚVw¯î‰’*ƒˆ©½1HÒÞÎ……‘™ ¦ ªÅ `ffcdff…§ P³t±ýWO¡rr¶´·ãý—…˜èò.º¼*ØÛd]m,lN^.^ff+33Ï íxâ@7KS€#@ÖÞä O!fïàédináò~Î?Ô&4.ú?Ý"¶ 'K @èb²}?ÑhPµ7±¹xþOj~ ^&&wwwF ­3£½“¹ =ÀÝÒÅ r9¹L” PÚ‚þ*ž faéü…ª½™‹;Ð xØXš€ìœß]\íLAN€÷Óª2ò€O »ÿËÿÇ€ðWs,Œ,‡ûËû@–v:MLìm€vž–væ3Kà“¤<£‹‡ =hgú‡!ÐÆÙþÝè´´¿ü™: )¢ ¾WøW}Î&N–.ΌΖ6ÔÈôG˜÷6KØ™ŠÙÛÚ‚ì\œáÿÈOÜÒ dòÞwO¦¿.×ÚÎÞÝÎë¿dfigjöG¦®Lêv–Ž® ñ¿lÞEðÿÈÌA.fff.NnÈò0±`úã5OПJ–?Äï5øx9Ø;ÌÞËùXšÞÿÀ{9Ý@'W׿ÿKð,,SK€1ÈÜÒþŸèïbÙøýþ,=ºÌïãÇ`þãçï/ý÷ 3µ·³ñüÇüÏ+f•ÑVÔ–¡û«ä¿•¢¢ö/N+3€……“Àõþáó¿qþîÀ«ÿSª´ü+»E”±3³ðü§ˆ÷îý··¿&ƒú¯µ¡üï Šöïó Pÿ3þzÌÌ&ï¿Xþ?/ÁŸ.ÿÿfÿ(ÿ¯ãÿ3’tµ±ùSOýƒÿ=ÐÖÒÆó/‹÷yvuyß û÷ ±û¿¦š ÿ,´¨½éÿÕɸß7DÄÎÜæï6Z:KZz€L•,]L,þ3Dÿ½…÷à6–v %{gË? 3óÿѽõû£âü~Wª@ï+õ¿GJؙ؛þ±{¬ï÷trz¿_ý;q¼XÞ—Ôäñçl˜íì]Þ]ïÅùÌìàÿ¸QNÎ÷qúCôâ0‰ýCÜ&ñˆÀ$ñ7q1˜$ÿ!6“Ì?ôSþz÷ûô7q¿[ªþCì&µˆÀ¤þ7ñ¼çü‡Þ£˜üMtŠÉô_È`ý YLf#Çdïêô/ý{æÿÂ÷,,þ…ïiXþ ßó°þ¾7Åæ_øž—í?ø¾ŽLvÿÂ÷¼ìÿFöwÛ÷ÿR¿gæð/|OËñ_øžÖ¿’fyOËùŸ¢Þ•ÎïÇ?ê÷¦»ü£~?ØÅ ôÏaïÄäânÿ/‡÷º\ÿ…ïɹÿƒ¬ïæÿ‰þ?ãfâêäôþÿù ¼ÏâùÏwò™À/ÍÛ›ðYÕµß׈¸3ìŽ ÌPìj¦Ò0x-9u¸>"Ã$ÑTg~ÙpºIêE]Ý‘ ¾^&~ñ:ni€ùÚš Üöäýl§2µÛ¿8‰ýc¢àX¤~€î#ƒšðž÷‹£·F€5d x—,Å7GWnd¥<Œ{÷~)ú²•Ñù]å½jN9„ç²i†(õH½€âYŠ\ã¬9\RhBXZô ”Ù›Ûôœ‰7bÙ8:xŸ“(¶B/MÖ臹Ïkj¬ÎÝxäx:¸„7è£S”^¢ɲ8 ^%E‘ Ù\a1?—ƒúãçä4ðH}•p‚J2üå»0È»sÜM2œl4L&”TÄõ°^Á%Rf UdC%†Y-ý^ÈUãµÀMé:zˆºOÎÔ»h½ãcpyY¢öÇm‰n0¶;³|9Ÿ ™ÊSRòPIf`ëdËŠŸ°§ÇTs}$T†œ£ ¼tÞr‹î¬ ­¾z¹Øê­óö#xpã(?+5$œŸ¾vÅZ é6Ö`Þ`N¢vò„îLº°¾²%4q]Ñ˰âˆ"Ÿ–gqÿ”MÝ`žÃ´uþ¨¡9©ühªuBòqj@yæ%«ó´LhEù ý„ý¤!… ?Θæàg89öQ(3à÷Y6¯ý!ïwu|×ñ]Û4¦ŸÀÆ™Õ=¿yöØF›köŸñsØ».òü¾”ÄšéÁîò@st%ØG‹-eÿôñoØÝVnUS4ÁçúεŽ*¤/“ÕƒÁdϱ ³nÂïI/P:r~JÒqN’üw²yuOé|›SŒŠ¾Ç¥þ35ްò²t†S°V3 Ûð.¯ÚnrKyÄ =WØ4ù eÇh~x‘ù`ü£`;Œá:呤øw)2¶Âa7ëð!—fF‰ÞŸË»tò,3ø²k?É3q?â‘^ÀN¡–J‹,suÎ ’–gçgḜ$¹óÑNí†Úp t!HJ"‰jþzd V%"y€(`¥m¬Ÿ¦àM!`Ы*ÿBf-jË{ª ¤*±«ÄÓ3ªÓWŽ ä=@Þ^kéå´Á³c6æ ±Õo¿¸¤?fZ.)ÔÓ&çÄ×¥ P—'ûÑ©ûé„i¢ŽQüyK¾z» Wï†vKøþ3UqådgQÐkÊ*txuÒuºµ¿Q®øÆ+SÝOˆ$kjô¡øèëTT‡jd£[ƒCѾǯ¸ÛsµzÒ¾GÚ†·¥#ñ ¯X°O&Ý(¾Ô˜$Ëð²n ÎÈ(>©ƒ<Žóî{“›^;ß8CЫÆÇЂÒàî)•ú¾ìS…Q†§)PÖq5 µ %»èl<}Ðs"ôóæHR(næ ­ÿBÛYÒEx²|Ä_9ž$YOß&efE—w2ÜÙÑ5~{œ3”EêÁÜ{gAY¾áª+¼ì³u°D:?d*'A G°êÚÓßpcìqñhÅ*…;·–èѶɫ.ò1ýÊÈ’Eد¼‹M‚ñ+ .•ÛÁnG²²«–!õÄÔúJ€¸*Þä}OÙžÚwc-x`™ÕÞ»t‚ †‰áQylÜâÚ+DP2Uÿ@Ÿv®Ñâœõ¹;éyWÄɯ IŠh1￉Méó‰&¢ÏªbÒßM!kå£J8?d%º tëA¬êOj}ûic> 9Üa¯¯M3ÿÁi銛ê¼Upc“K:ç[›x4L»U š|ëvÓtt}ŽÔÐ|5Da¤Žo†Ø~ |„:ã$‰±¶-¶(Ÿy ]rñ¶F\ÉC€Ø³®Çè˜G•]¯˜#Y#™še+#êµå½æÓ¥å.m œÑòPvn?êÒ Á›Ï¡9ßxW\JE‚pAG¤à¼tµEÏñÓÃy銭Ï(KÚ#Ã×»ÝoOX¥º¨ñà§H}ˆû.V\°l kžÛŸ‹‚öè·„t.¾ ï=ª`*‰Z ^ýš<.ѦlcnÿÉ–)-¤ù4…ÒšŽ†ÈJôÅâmtKx‡èÍÔšA§óRÄo¾¿ÖX®ÒzbÒaøùüLé« f8 wͲ蕺ÆK¼/ª¬ÛU¸[¹¯‰-B¡C›jAƒ® úÌ,Åé¼Âm5èYFÕº¢’_;×éå)fmÕ?f¶`4ˆ‚üÛiÉkˆíì=¶Øä¨7½Wäºyº=‘/Á©fê|xì¯QuÙxÊÐV-WñAõqáÐæm_‰ÍS6ì×W‘H(Õñ1»Û- KÛV©g³¬7- 9õ]µyÅô€­†Í´ŽO¸º“¡ñã†zBÑš.#ü?ƒ™øZ¢I÷T>VÚ®õVådÐTÝ–bfHDë9ýÔdAì™™Š·O,œÄóÜ™[¢ ¿Ðë^68Œœ²H8:.%]s³ÌO o£tM‰ç4}>óp!î4wOzUg —ë418-Û,ÅО·9‚ÉÀÍþõq©u1<×ñƒ¼¸ù4<¯±,\è£|k¿c ~â Ó¾‡A?d™¢ŠF½,Ê~%ków¬]ÅÉÖD †ö$ן.ó~ÉD+BqØ4Gìð;’jºˆêñ劔ÜCé?ççCÕݘá¹{zzKcæz…¨ûbóÛt_. ¡RÁ:ýÑΜWZ7Q §ÚûŸÆç%Ç#úSÂÝâ•¿Y¡ä¼¢áCÄ?VeqbQd›øC’ ú6Æ"¤„|ÚƒB‚AL‡Å}¥.©²¤ÿž}ìõJ"¸"9X’â{6¸ÇÒ'6ú‰]Æûn«üˆ7j§'x±" ¤[úY!g…¤Ì–9,Xf5ò,ri¬Fs†Rò4*¤^°ÑýpÍí©~Ë›‡‘ý(VxÆYhÞò>t¬Áx`[Ï .ŠY¤¼oS‡ÿÁ®lºZ^ìW[Ò8] á 7D fòÇÏF±é¥9”_ºÔK’|–ôdvñCˆ³†‡qdÍ ³º·x÷VÄ…pñK‡ñ0Ô¿ñÑz“O9¢´T˜V;J¼`xŒª¸7±Pb"¶û—%ÑÅ)忌zν2U|‹wmªî¦ðq©SŒZóAîÈ#ŠŠ¹XøÀIôî¢4úë…J« ý#•L;:ÕB­ùê½ê9qî껫ŒfQnÝ寮3Sð ë]N³u<‹û²$ѯ— #ü mNûÙ`1ŽáTq‰Ú'•Ða.ñ*NÓ² X0D îX,ô±ßw4S’½pvÖx®ˆ:&èÁ·Ž<_ìšiÂôMú`¢œŽ(z”BÔö°‰k;[5õü%„ör@"qÐÞ‘ˆ¯+t®m‚i-º•óH·Quþ Õ¾¹‡à»âïUØÒ\ɰÄ:AvÔËEpòØìÓÝJ2fâ)ûûDW݆å±Ñ' õA’¸#zÄë±ÎîoW÷ÕK'œ­åš Q!ÊÜpH§õüˆ6”Åðóו¨g¾¢Û›’ y¤¬z-úô¿üfj…@Ô%È6’û´áÚ\ÏKQË‹FµªBÍ–Þ#.Þ`º°wØÆü\ ™Ó\8ÎC“vø_&Q›¦ÊÜYÙ×Q‚»M™ŒÏÄ}uiª`ÌÙ‹sÙ^Ö¤(Á˜{)P’—ø*Û]öÀSxÁ#qŽ0z}ÜұИÀ¼à '*üó™KSv¬Htv¢…ù# ö¾Ü¾~öœ·™¥y{!­¬\ÜÈåbLìv¯©ÛÚO§Á.±€™@¤øÐøgíìNØäÅãG«½;¿Y ’| †ûfß{1ˆZšýë@JÅsXm™ë7¿ÄÊùök·c˜¦¯XCÞƒ­6Þ•À>h‡Ñ”âqYð­ˆo£+æûwò_—ãßéµ3 %ŸV ÀÓs(À¥úpZ1­:é)ÁΰÕ)ÇU+ʘQŽÒ"ËSU+ÇÌ;õvš{ŸÂӎצ>ÐYp3~Å$*nŠ{c÷¢ºÎM=7Ïå»=&’êï¬É÷/ø1 C&Ò›[Z> r”hDèR‘/ƒûòê/·(Lî½$®Y™1sͤ¤Xm0™ÄFË,wó+à°ÿeõÇwŸÈ*ÙCsÒirŽGîKÃNór1gV±F.z¹¬”¹l!Øñ¶ñ šo{I¡ìëˆÈ«,dà½ö­åÉ-ßq°dˆµL²”î}þ³òa¿¿ü2(`Áâ$œI¤±"üT´ Äô‘ ì=_¢ ¾•OSò`l¤„ÝÀî–Säuñó»o—X %ŽC®¼Z W(y´¾ÅöcÏ—|= qö¡Õzé[”+²*N»2qÐ÷óMãæïÞ‰íIekÊ·jÙeÍúú…" œ‡Kqü‚ü³o¬AoO8„ ­|Ÿ¼âÏgØq}^È ,¸@=A¬þL#Î(466ÏÓžUmÆ®rËZv=¥ÕÒ2ŠëNF„b¨ò™VgÔ•ÆqkÓ ðˆq‹|Ëf±£z²‘±¯„µ‰&><ïMà ;sÒ›@1ŸÒ»«¬Ï?kß?j6á;o–!à˜ÙñS¶¿¼BœDg¥sNNœ`hÚ¤çîˆz—?O w0—1ë€xRú°‰M;t,¸T6æ¦R‰ë>>‰l„(F…®ÙD}«œPMm œ:›†¸éYBÎÀt…¢¡›ô÷«j£Ü@Y¥7—ãW÷9©¼ÅŒÇlòM°)ÅÜÎÅWÛiNGV=™¿Í”ÈÖU°±®“ÒPa M?ü7pE®Y®zâ³[,\¥»ór Ÿi%øp7†ÇpÑŒì)œ‰0†T„()°q<{pBË´4S4–j|ÄJLjÇF²Ì»JÍ(éê-Ο_mòë1õŸø¯4håaU qÆÛOœõmI¾®û{ž·½H ¼­m;Q´!¡žYÊJ>WyšŸkÏÖgø ~÷KéÊŒ†êÄí™=|¢¼GBVÃo\—§Ë¶"ôÄ)Ù ‚$ t2vÂ×àa0}¸ª˜:švXgÅ¥YtD–Q?=“¸0\?4Ø*ª!Eí·iù G÷—L±¦¿póÁN†@ ç­P5Æ¡9Z$9’Ú°Té1zn³¢³<ÇÍØøaœ<ñ•k8C48ç¼¹¡Û#¼üž}¡Duó<_š96­Ëq÷t0yå»]t¯»žT—qP"#Á­É=Øb‘‘Ô*06”! ﵺ5PA©Ãu­3†·Kê“'­9SâyÉ2WÅ‘*‘ßS×êg¢Iÿ‹¦›\!ÞÏ•J-­d2öÁ\ÑÝ·«hs[%²&Û¼CtªD]mÐÔ€”`ñÆÒ“ŸÎú”)Õ+Ûú™ÖW§½ª òçž'놯V÷#©* Müç;€ß£-s°NLßË{p2ÌazV= wÖåòé±b{í19T¥‘ÈärPpt)*p÷®M¿áAýæãÏ0m!FýÐW·±ýCd‰êî#·°§§²ê±ë0Úyâ„Öç×lÛÃ%6…ë0K9’¨Jg׈ÉxÓˆú%'\±Z-¡+›öÀ¤Q ?MSϵY”Øú©ëÕcæã©{ÕÖ6 Õ5¹–_Q4ÍXPþDþ eøaI6ÍÆ[Î5¯^àǼ2^ F”U‡×¹YÄ:ð¼Ô¸*íuóbHAžÒ»É1êªÓ˜I oþ–û¦2ñ2JKàPXäQ.]È<º÷ƒbœcÈQÊv°:nTšþµÂ1¸°nôµ’TÕ– ¹á1Þcköü+ÌUUØdŸ%>6þä•£áFþ£€ªºÄxDŒ½Ã5jÛ)‡°H\ÞÞ¥±#æ¸þÇÛNÍyZO1D •.Ëóôª憰†úߘƒÃ«Ž¤†µæî?×ÐIyÇÃæšÆ;éKHÅ­lW2£Íc‹máæôìòßR`ݨkÄ똮—åËÑü#L·Ï—T*ùV¤2„„€²}j([¯ž+•oßÍóm}7ÓêtX#¶&C½# &½~‰Èš¤(«Ü :Ú™R¯£¬dz¦ýþ~k/>f¤V&+óaõJúëgàêõà ¡ÒsÝKþB"êÂQذÇL1á¶×ȇq~ݘ(æ ¡’Ð „ŽÚÑu ]p©Šnžõ½ùÀDqÔ&VàN"*¸‹´ÌÙ Öxþ¢¦scé'íγÞë‰ÓÝÉÒ¥w/ïwT (ÅXÑO=ꟑ‡V¥/€RÕâÒÈÕ§äœäì.òí¨Õ²Æàðí2gŠ2¸bó™Ž@F¹44ú•ˆ‚þbñžð6I°\TÙ¶f ” öÃ,[¥¼Á{–¯ÓÁ¥h»®„uH¶=k Å ðœ4a>€¨èÓ Ö™uî,[7¸ YØ/X£²k[÷'OÈÅŠºÍ.ýyfXGû-Ù…ÓA@ÚõÒù&û¯5 1QÑ€Rùý'1gÃÒ I}T!ªÔ”A"&UÑæº(Ö|m[›9šÿQsvž¶ÃL'<^?§¶¦¨6(tY°rl“-÷ðômáÆûéêИçÔ*Äq^ƒ÷F®Kãü¢hÊŸE]=¹y¨^®0KÍ=‰D”f,Ñ¥|,¾ êÂØkƹ2ŸÛ§Ž9ºÎV†'Ì7hõóEÅÛѹúUâOÌ:Cj .V²cº#)Ë=õÒ÷2:ÄêuÝU¤k‚ŠÆ‰5g®5Ö–„ž·åÕUy ³Œ.™÷n¸„Y*+³;tëàS~­"o³ñ\kP`£$ÛjvQ¶!5w6/¯‘‡§†9ANÜô‡µpsÉÙ¦ýÕŒ/—ÆÒʺ•˜“¥D—$uöþk¤`Ôì¶—È(X|+W»‚ÊV$¾kð}8¼ c"Ê6«ÌöæçO¾}!¤µ ¶Ý9 OÇìóRŸÏª¤Õ# DeAb³õ­à‚2ñÕžLô¾S†™ï çÍUÞ†w˜Mr‘´føcS„•ò›¯Ž):?P‡$8Y×nÎdõ)_Så°Ä9íø³,L•»¯0(ìØ/“,NÛöþ,A@bŸ›òMýÀ´ê –G[ÔI¨YU‚{µ…© ,·!Dßw¹8­@LFqóÀ!u3}Ú(r÷Œ„æ[ç¹ìû™GÉ7ÏëªÀ?]ë/v½½ÄØ0 hùAÎÚÝÎãøU4c€#¡j !=(¨ºNßù|Ë.m8Tß³= %12YOL'äxù9OACKÜ"2Ûg©³!›àdõê±OorrP8¿‚+nqN”Ú«×-ÄBciNôqD?$×øØ ö• ¹MªºVj5.r¡Õ’‹£µƒ¯(›â^­»•îgRØ[ðåCôç|òoûµð/-=ëûBiÚ€8µõŒÜ¤ß§9/¡—:›0ŠÊ=”.ëȉúŠ„t½žGUfb ŒýTI¿ï„£('Pä ]×íæU/Ñ–âÄ[Ä›’žx§£q11γ>b ý›½¡Ž±‹Òh?,ì5‚m[5Wl¢I?4ÚÁ£u" e^\µìj;ùÌ1²~Ï#/7‹Üø¾°¬EYÊÚ….O#‹†í‹€‚3€Ð¤¦’£÷ñ{¢2ó¡¹&DEßé¸îqZÉÙaÀÃg^[¦Š–QHÐeª;]¼WåoØ(õÍØÛ‰ÅfC@f›¦™[CCxäTæ§‘ÜC5ÓCp~KCUtË›Hc2T§%¿%CZVº#a5\&RͰª9åSØ^Ñ·ì»,ःóñ+ŠXI úä¾òù]BÚ[á'©û:(©¼\Æ£‹&ÿƒï Ñè}<ô0ü¼ôßçÄ0ºv}Ê$%¹¹M÷{›ì†gªM+o‘óì9VùuèÕ©.»è1 œÁç ㊌"¹=‡• ~kTµBKÞN*ÈÜÀ^µƒVppÿ½~tµ<×k …_‡,߈æZ:út{Ö`ëÕW_ÇZ}^ã‘//üõ‹Þ$Àp©¿)oõå5w½XÆð…&Ì1†áŽQ;Å7vF õð9ã³÷¡ôìYb¡Š¥»È²f–“O%Q² ò›»¥”LÑ@÷o(Ç÷SÈyfê’;çÌÕÝ“ÖsÊ_µfì=mÁÉ'¢ƒ¦äU£4ðÕ~‡7Z=£¢X)&±Š2™ÁaÕCÒÅ=^¦£ô‘=Û’Yxë©”Ôé ¾Fxz±¦PSwY"—Љ¬KG8ïP$Õ=ìé7¼?T; êNÕs-hgxêõÔxŸe[û n‚ãÝá7$;Å-^÷ŒúDõŸ4meÖZÕN§’‚œ¿ËGØÉf +% O¯[í-úÄødPð¢õ-áž li.‡>ÎQÌŠÓ‚¸Ù;à+]èÒGGãºЂ®n¨Ç~'wçÂ5eNfa®Îä¶àÆœt‘©m‚«±fSßžòEHê”-¤#¹¢„IK³?Ì«GÑ´©çÆòê DgúÛol|keï¿U’=“QÙ¹R¼@ó}DIAÌ ª4æ í¢¢Tzú­¦w#CQÃÁ3e3ᜈ$_â‘÷”é.q–SlÀ`Y¸´ÊïHœ"„txŒmº'sƒ_¿¤0¼Ô(C3åô#Ä“ô„·C‰5?,žŸvøÁ|S¸ˆ'Â$”&%1¦öƒ˜£8Ã1÷] m•œòÈjÚÏ=T0ð*ÈNX&ßέÁðJÄ‹6®¦®BÆ)†Üº¼ågKsAâ–•”&±‡3óꡚªq ä9Q¯r¥!N¦MOÏÉú¤ÇîÜCŽ/‚ÃÚQZ™†XäŸSIæ¾É85é>ó©:FÊY"ó‡´}¥Wª âƒ(ÙµºpH=ô;.‘¬)‡®/¨@ÑÎCÎ@¢+ºƒô!ƒÒ„X¤ŠK“þ÷Á§±œí\¶Dƒ Ò„d¡Ê'lÖÎ>œ×–f Ê©!Mm‘HTØŸ×M´ÓmœºJàç°ˣÇBÕþöìÞ룮­[ɽïy]&ö8Òù¬¶â¦Ôè­-y¾äQñ¨AÍ^y_[m¾Ë÷Ñ,ƒ{·„÷fÌ4¯ÅÍæÀ=Ç ÊÁC 54 Ä4¼ÿ-~Üf3 )FQ kº×˜àiPôÔ~‡Ù¥ã¥`¯‰ÄÎLjyG-PàbƒÏßÎ^?sU%¹¤|=:0ö¡'aѨü…lYYý½²6´>ža8—„­± ü¦.‘+ybùÈ‹\?”6€ëm÷2(¸f­VʼS‹d¢y9ë €…–±ï™ä±çQ¼Á’ò¯WáÎ=c4é…]n¨°¦È€W«%¾ê@)ùœ·^µSÊ™Šƒœ$ //ÅçFøT8Þqvï\—±6QxL·xû ßÁ uŽNa!M ]MM<8òö/9A8Í „%ß§ë™3¤mĤGÑ& æCÒ ³á¿0]ªdP»ÔµW]xÜ!=7¹¤Â§R¬x$,ó3£ Ñ¿=üØøé¶+¡¤Eq€÷lf Å内ôIQ§ƒ–ªÛ­Lc}©¿Ïªàžå0›ÅmdZÒö03ËÓÐC Ñiü$m»×g7«UroLRøSØF¥zâe„ï s½ÊRtReê$F/´ß·Ý%2çò\Q;ªn´1Zß " ±ªÆÒ©²L`¶ˆu‡9‘˜X·¸jŠCžxÂb¥§)–p§–‰Þ¥KŸ°;àA­ë¦I÷uÌ5-©Ýâ~€»–=B&7J5ïË‚â¾ëGØ(É*Б¢q<%PÌà ¾Id߯©9gAwyÒ¼Ž\²ƒÑ`ÃLƲTÍÕɬóQ-áéȰvÚ¼¶Oò˜·I㎠fœéIoÿ˜$$eeJy¢v­gŹÏÆ©²õR?xx¥Ó,¨IDž½kËåD®Íß~±†îEqMÚ”á ™ú¶V|æ³l눺j¾¦0ó(´ð;~6Ûó°âp¬¹Ï›Íôdß`¨ÍÄY«à®±û §«–òfNEöp±¦=îd&¾xà×^[•o»uâ®En<Ø»JŠöÿ`º™j&…§¥“ý·‰]“î>;ѯ¡Óéª+I0µ!g¦~µî©¥²m4ÿ¶è§oH?ÐÚ÷<~ÙÇ×—wQÇ&•æ}Ïb§y ÿô,‰0ý1âé:ºà޹·fQ§­„ 9© éн­CˆkÐjiE¸ËXYà©ØbÔ óñÍþÔ5—€Ï’€ò)´[Ìç Ú‹Ì ­ÙèšÚ.)òI«3fY¯2\Vÿ»ügüpŒT×dƒÍ×|¾RGIuÒ´ðZüÁ[ö ;/cXâ›wŒo3>ÐË5h Zp±811ãr$-Ƨ/†´‹ãà,i¶‰/l±)ÐE7ª^Ƶ',TóŸiü_ðkú·=c Ûô ®Íkˆ´Ò°‘[I‡R¢‘4­gtuªÛ0i]Þz ¦9øíÇlcåÕâlaEß3d“VÑæG[£¢,Çäf[)ÐÈ[™–—¸b‘")´'…½17àBÞzÏ2.`f1}·“*´t²íT"(Nd7ã…asì[›OØÃ ?ôذάw~m’ƒ07Ã.^“D¼§¡¢¬‚[è+‚ŽöIJŽøÂA¤Ôü›ÔVTäC}F‚~xg¹X;6’°ŒŽ:>‡Äæ!EŒÞi›ŽÃ2’@Ve<%c?«£Åô,•±ã]©,ÚKØÓd@ßåbxvßÏöOÏ^%ÕWFK1(s£2š~b¥à=ÊÔqyP×|:l›š¬©¤ƒj",Äá3 RÔâ-gžÍ ̓Ìßcף£©¿ˆå 5ŒM踽8æ4à¬õGC7ÊuZ3ãl'3I¶`–BEx˜á8X.Ë̱ gÇõt?ÝU-q±¸¶cÚµ@2:º, i®y B’4à¦I™„1ÿp ½ ͺjò—\ûAͼ>ò[Ô„•Æ^ ,EËÏ’-üRQ\ç ƒ‰¸ )¬ùQ+ê;b°ætÑ5FىǮ×| ë›~ÿ:«†ëÜ0Åœ¿ÄÂôÞBêP)òݯœnR?4¡;ŧªZZGÙpE³µúÒxü€*åá—M*Ã’hÞ$1<~9xpÓ¨„‚Îo¿¾Ok!Ç*š÷[7šº›]Š2n›ÉŸŸAB…G\J½xùKNѤQG«ºOÝ] ã»ݽçëUµþ…t‘¡@¦‡ôERL»öãÏ2dÈ Âõë‰cøK…÷LV—di2uhTÙ7§pY#ònˆ *=EÂËQ”œŠn›ëO\WœödÌ좛ŒÏw»ñ[¼ªxêß_c •4©ed)Û–Æ^šE‡ª//Cø €2ýÙÁ¥0{º$TâÖx_÷jÔp¯}ßSÆÞ'èÊzWÝQns*þ¶_[æÓQGhTø5j\#²k›“DëŸÅnmFžƒDÔýf!ñ¨ã#©|N‘¦ô=Ö rÃr°“å>éɇ <5åËUMºFÈÍ\bÿ~ôäa(»}:¥Æ,»¦Œë”O4<:`ñåúWPPãWzÄQ¹'~7ѽé§!‡>I½´S[mZ²…„ S|Õ20oœ›Á˜_ "Ö?ꮘ´•ìU.~%¢…Gêáȥ &%\Jä3A²³á{饨¸$Ç×jÐÀ2 µ´8šÁRXLlŒSé’ˆ]n¤ÅÐu j‘ëþ6õ0d$p„óf&l©¦ª~0;»oØ#øŠø…ÏL¹¼žÈ…wôøˆœ§Ó-€dúB»=‰´äÂýfÈ„Ú:+æLþ ¡G²"ˆØØqpš÷©¿ã;c·aÂRªÔW<ÝÊ7g; *Å·ø^c–‹tq£øÊ\†¹ J(xha§êÚˆ”9NØJwìÌ}‹‚^.¤é§BPdâÅ¡„2°ñ6žš 8˜=-Ǭp3ArË0‚ jÝk„#ºàkŸly|‹Ó‘”J¿ˆLLSÂÅzŠGëV–+U ÍF_o7Ý vøM=FšN²üŒå Á!ë—A>¨ªT.H”Z•Ò¨IXDqTÑŽ{ÑfO) D÷Ÿ÷RÜã(äÊÎ^°ÄâÄU·ªÍ²'7˨öñÑav`:E9êÃ7Æy‘žZž)oŠK^ 2VF¯¤×™ôÏjR²‚aÁN-3ÌÕÀÝö¬Ä¹RúCQÊNð”öV‹]›a/zCÖøVÑ~ÕÄZ·é®l rÁ Ï/ú=yÄÅ.‚äa}ìØ¯ç SOöÔóâÕLêlÄîø„3p&„»‡ˆ8€›Ý’D±¡IÒ*{tqŸp5ZžÒðX–Þ|A·Ó$ÇpìÑ ÓKÊé{oºè¸Üz¦•×—Ä;—Ï]ìsoºì&B8QoŒ¹Vl +è`Êu{v¨ÚUö­ÖÛRŠÒ–ÒØ=àeZöÕpâ‚&¢x(¯Ë¹Ž•ÆŒJDE¹-Wí.dùÓܧg„wù ³ûã~\­ê™j‹ H ŒÀí\Ûg³ ™¹×HÖͼo7ç¿5Ž›!à¦Ø]ioÇÂp]Š[-õÅ(%«ÂKÔO/·¹¸T¬ÄßòÎám¨2zÙ›ÙݯãÏ2KbyÀn4Ô›e"È׌ô6#N«Äë¼÷‘ѨŽí§žº¸¯ Ø^ vgfRÚ„kŽCö À’9èáÝ8ÐÈü—E$x«S;MÒÑÙ2ô`˜BrH¸nõõðáw‡ó[~&ßöî‘|¢×=ì(~@¤ÍÂÁÇÒÚg?³ ½â¦ökN ƒ{AÛy‰¢ƒ@ Òi–­t9lí‡È fµçÐ ÕA4¿í]oôDãÃižÊÉ~eYíñœ8AwB&^f#ƲÌ|\µŠ5â,KKPVA Î|4 Yj +d®)íI‚7 ´1S¥ßüСa|ÔäJcPøJU~×Xçºn³… ^jà?g%m@˜ò3@©>&ê¥öÒ­‰3Â9äPR&ädr„ŠÝqÒ,ŽJpÈ]˜r£¬ÚÜ’Ï÷/ߨ˜GžÌÕ®Š<«J ì8%l~ü^ÿ#Ï^ó‰õÆŸFŸð¥y’V_CA»N«Fš'§¼ð EïS[Ùž“nª¦CÄYÙ=Щ ߪEòYúÇE±¡2,ý\'Æ)·J‡´¿mט;Aù0OØò‘fM™ô|…_ ˜[N,Þk’ŠK[W<#´f²uO¦äŽ _Jñàç"ùÔe”u{_\þ3NÊ8>§¢é¥Òyìr_|dÛ=Y«. Žº ùš³Mè|ŸcFðaƒl­ÝëÍŽþÆn§_«\ʼòÈGxWFäIÕBd—ì¹CD\¦R_œë«>£ŒÂMchÁ“m¬P¼ãXÌ—µ‡ ¤¬œ+Í=Ƕ”Ž–]1ž;§1ȹgddãGñö¼[~/𬔂Éë Ä”ôJ7B<ìn!|hgÖ]¡•8½ÿð6=8¬KªÃ’ KÅ7!< ‰JÃ`Ìý@½j¿"UÒƒ§•))ÏhD;p¨« º|Íé‡ÇüÑÕÔµÖ‡ húÛñ\ʽ"ÜD–Æ®¿ìÒ qÂí÷ÇvñºÃ&º^´‘E¿ûh¥* èbš7œ‘O:¸fKÍóŽÎ{o£¡êb2%éj.Ï‘8ý^¥-#}„½ãý«#Ãò BþÆž¿ ×Ó¥&&?LÅÏ÷l.œ—«úF€Åæ1ÂîÓ–ôôÆâ‰ç k¨é€³t‘v·¨ø¶D–ï$ýp¸êvŠ6ÆXõîÍ­ÎYŠ0å·ž¥õPóiœYÙÉFvÕõß­ˆù«UÀØk&ù”e,ÕĶ¡Ò 0MB§”4f¿HLJ5Ây™ i)☋f7¥62¸IXºŠc3:¹jcrÅ¿x"`YÍâ)´¢@±8ƒw\ËÑ7/z=Ã1³Ñ9ô`o͈K‰àÀ…žöð›Œì—ˆ?%~W] œÐ€ßVÄpÁ ØKV˜d µÕaO1á°Ì:ÍöŽ{—ôòÖžévT’X^çcÉcÎׂª YšûwŸUðÂQE±É§fãM‹Û·9ÿÔÀ*¬3ïìÀ={¾ŸÃbþrªt$`ei¢ßëÒJ?ôŸ ™÷·í½MÕSŸ9;™u’1gK’êe?Y)²ö¢Ù5¬qz ¢>iñ÷8ÖÉ‹’AJð"¡sté-Úh©r ¤gRLj÷ž±þ”Æ’ßó’2M:vƒ •uceŒ×'ƒƒ‹Œÿ<¸ìuyÃ*ÿÖõÉ ÃFUÈ"py[ “1ÈÁM8€@@‚´2 ú,âÙ‰z{Û§‚EƒjFZ·BìðœÇª{ÛXFÏ™à9þÊ %Òµ.˜ß‘?Ô„Wƒ 4³ÇÑ[ùÃ.â“UúøÙX*ƒÈ‰ñ¬ÌR™²jXÑ‘F®Ù-µ˜%…º4€µ¹¥–¢Œ~ÊÏ)Mjr-|C•és_¨«¼œ5°ËXcH7ìn/Î4%áˆ~ÀaˆDh;»H\ otšÁ‡ü¶ë2—]ÌÞ¸mÕ>'G×ç—à„õo  WkL âá¦P©ñj·i£‚5Ì)Ó$DPM]ñ}gî¾FGÌõŠ[ÿä¡}Ã4Ò$‰aºÂ”–Û*'ü‰ç…;œ©œÃ<˧£\¡º-a#iØç¦¢j§,SiÃ/º;5SK–d]Îv³ cÚ:ZÇÃýóc¢"Þ·uj€’ÓA#L2[˜²ÀëW=¥‰„V`K‰@c&Ѿٳ°þª¢ä#æ4¬Þf1‚Qtv’#‚ËQùõv%V`²¹rs/ƒ>6ÓôÞm}MÈ…)ÂôYiç îYáɯoŒIJb[NY/$Ë4* ͘,Ã>¸Î¤©i2Ñj6Õ´vžïÚh‡êé‰eDq ƒBJ˜®Q² }D¡ÓÀEŒ”T½Øß‡7dV`©b¦CèlØlzo•ŒÂÂó93þ6áO»]6dž£_v1Éc¼R¹\ÜhD#ÆÄÂèË´Ÿò~‘ÃÜ=U·¾4X²y$>M*ã BwÔö9„¨£ËÇh~6bƒ¿L‘Ñò}G€ÚÍMˆbÌL*ïñ eå)±ÒU8|•÷—=xp”qñ7˜LÌè¹ÿ)*|F¤-9Z¸G4e—Ißçì5¬ž sö]¼îâüý­ å‹ä¼>â‡úz0ÏýµÄ–­ÉVzªÁá®tFpy|¿k%^èr7^”üŸ‡F}Q  ʺüLw…qm«=‹NÌÛ×­Ü6ì[³['Ë®%Á<,eù]]2”Ù­5ÅÉ×HÅ^~Ô Üâdá3ýt{¿6Ö ôÓa°mpØ,ªŽ¤ÈÙùt È}½·¿ŒžóI¥ üî}Q‹la2 [ä‹VúÖljX;’¸1¡D²Vk„íáìv3+óKX~ÉãDŒc˜;pÜPZËò ßVt#b&Ó¸Ñ çòXk N;ì 0#ùl?ß!¯ x[Ñ®ÎJ…[&_I¤míÎûA€;LþÖå‰l|ü[‡{þdzbJe<ã§9Ý&‘@ldÔÀý%:í4I:ý¬çÖ²æúÞ´[œà8jÖo-T#o—mW”ù‰\µ¢'¢au9 E»oç ŸFÚQXu™|Îyµz>V|Øùžr†±iÀÖ;©…ñ¥ ]±£8<ÆctMü œN$³ƒÎù*! ŽÑFŽñj[}*Q &Ûa®Q þ=‘D>í‘›¢jñyÕ®\¸ó^s±‹·a¨úÞU?ëGåzWðáç¹_åÝKy:€»/RJhÅXÖ뙄*„ò¤ý?·òX÷䙀ʴŒƒ½YüF,:âtwO—Ñâ­¢îä0üâ(eÞ{⪬E¨/†÷yTÓYÁ³©wÐþ%ÔäÍgM²…¨DÒ ¸¿Ëïݧ©_ÙŸ)¡ ¿®ž¼îHªÙF?ò@w û¬HWšOqŽÓ¥1?Uò*“}d\Ž­’¥}ÄÕúéÞa°‹X…‰@Ýdmñ,æ‹”—:§ }ÙÝÿ&ß<‹üX±/eqõž|C÷ý9tÖKìºJødvºÑo†9=G5+xH·U’ƒ¸œ‘‡Ñ:›åĪ5[Œ’áËñÛLœ…V(ùÊk¿%7ŸÎ¯›'Ýòœ8žYâ­<WÓЕˆ¨òÂ,è+Q(‘[þ µúe EUÍÔʸû.k$£H*Y, —øSk.A~úÕÑ…arËÐ~¨bNb‹â–Ît¢b{qËN™²Uöù ¥.Yj:Ž¡ÿÇÜàAÂÉÔ/V§ 2FÝ,F¶Áÿ@¿$ endstream endobj 1522 0 obj << /Length1 2179 /Length2 15416 /Length3 0 /Length 16707 /Filter /FlateDecode >> stream xÚõp%ÚÖ 'ÛÖŽmÛIǶmÛ6;FǶí¤cÛN'éØêà國>÷ûÿª÷*U;{¬‰1Ç\s®MJ(¯D+hlgh"fgëLËHÇÀ–Rgd000Ó100A“’*[8[›üçšTÕÄÑÉÂΖëÂŽ&Οg"ΟŽ2v¶Ik#3€‘‹‘‹ÀÄÀÀùG;G.€ˆ«…1@† igkâM*lgïáhafîüÉóŸ¯ #J#'';ÍßáAG #[€Œ³¹‰Í'£‘5@ÉÎÈÂÄÙãRPð˜;;ÛsÑÓ»¹¹ÑØ8ÑÙ9šñQÒÜ,œÍŠ&N&Ž®&Æ€¿$d lLþ-š lnáô/ƒ’©³›£ àóÀÚÂÈÄÖé3ÄÅÖØÄðÉP’ÈÙ›ØþËYú_4€7ÀHÇøßtÿŽþ+‘…íßÁFFv6ö¶¶fS k€œ˜4³»3 ÀÀÖø/Gk'»ÏxW kÃO‡¿K7ˆ * >þ[Ÿ“‘£…½³“…õ_éÿJóÙfQ[ca;[g'è¿ê±p41úì»ý¿/×ÊÖÎÍÖë?ÈÔÂÖØô/Æ.öô*¶.&"ÿöù<‚þsffâ `e```gã˜8LÜÌéÿ"Pö°7ùÛÈø×ñ§/{;{€é§  S“ÏÐ^N®&gG¯þA32Œ-Œœ†&f¶Ð²›˜þ Þ¿£…;@‹ásü ýý÷›Îç„ÛÙZ{üqÿûŠé%……¥¾ Qÿ[òBBvî/ZVf-+#€‘‘™ÀÎÊðùß<ÿíÀÔÿ}*o`ñïêþd”°5µpþKÄg÷þ#Äõß“Añﵡü/ƒ¬Ýç<›(þŒ¿6+ƒÑçãÿç%ø;äÿßìÿ•åÿuüÿoEb.ÖÖÛ)þåðÿc7°±°öø·Çç<»8Ýç†Øþ_W5“-´µñÿµI8|nˆ ­™õÛhá$fánb,oáldþ¯!úÏ-|&·¶°5‘·s²øëÁÐ220üÛçÎY}>*NŸwõ·Éäs¥þ—RÔÖÈÎø¯Ýcbe8:x@3|++À‹ñsIMÜÿžm=­ógàSœÀÔÎú¯ecÐ ÿuô/Ä ùƒ8ô¢ÿEìŒzñ?ˆ @ÿõbÐKüA,Ÿ#ú±è¥ÿ O>™?è“Oöúä“û/â`ÐËÿAŸ|ŠÐ'ŸÒôɧü±èUþ O>µÿ"ÎOdð}²þAŸìFÿE,Ÿ9?6›?Þݽñ?àg?Lþ ?›Oÿ¯ÁøãðY²é‡Ï’M-\ÿñ—ÙÎÅñŸ.fÿ€Ÿ%˜ÿ~ê²øü”bõø©ÅúðSŒÍÈøYºíæÏPÛÏéú‡ýS‹ÝíŸÁvÿcþ,ÕþùSªýçæÚý£Ÿ¿ôÿ€Ÿ¥ÿCãgéNèÿB&®&ÿ(èÓÝéóüðIñ§“Ÿ¯½³¹£É?z÷©ÇÙÍ\þ?»áúø)ÈídúŒþÓgz?ê>C=Mÿ•ûVÍÈÅÑñógèïÇðsÿƒÿþÍ31q71‚^]²3ⶬî|ªÄq£ý9Å;OúS-’ÖkÕ±Ëå"™²æ{à¶ãƒ`òh?âÆ¾(ŽÀÁ›×i[#DX{¢BÇoïW½oг?; WfЇ¦ Oñ pi•½ß¼U¬@Ú€{$Is\8àåóQžÜ~ˆ»7 –¯O„.ýT8¬a“‚y-Ÿ£Q‰Ö(Y Í3ÌZÄ$w¦Åƒ¤B¾rGX¸˜GΙþ üF ísÃ\䥹Ãû¼è¹Y©ÌäÔ‹E‚¥‰‰r<1Kæ%tœ"‰±ìUZ'UlЧ»ÔÂ×…`àÀRGسÊS>>½;ŽùK/@¸a64ÎHaThô—µRN7&rn{ÉÜ£^´“B÷ѺgTÕÂwÇ þ!+çsy¦°ôküà{ ~ðîã°ã”e¶¬ü¡ANß9òÆ„?o+L†yÎZ}^îpçÝ_ØŸ®}¦§Åm!³µî”5Îù”g€pG‰ ÆÍ s¸ÁõϳÕF†ª‘?¬ø€qÊbf¿HYNÔmKöx ÈfŸñ¯éò> À~ÙYvPþé¼b\®e2›aÉ%Ùg :.õ;’˜I)¿V+ét0KÈ™¢R8Ð.¸ž½¦ÔÀ 0%"Ÿ»5Ìì®äQkªq¶l.æ—qq‚÷¬DùÌEÈW]·p„êŠib‘KƬ fµÄ‚Þº‰™ÛI:t¶åê<ž4e,™Ñ3¢&¾`GL ¬JÙÀa´ìؽ âL¾M˾óüÑ&ê4•w·šë}è×F’øƒ™=í]5Y:á" Õ9íKÙÃCÜÜ®þVf$x°Aþk=D§¾2™Pù–Çr˜îXS%ÒݥäÉ}N<ûØ¡ù½‘øZYÇ%Ji^ó²ØÅÀ¹‘©Õ¼ö>ù…(ÝÌ–V°ÞÞèÚÒ)OªÉu|æÊ#߇ý¯EnAž?¸¾,ŽBtÀ/íNÒ%Í¢¹·—£¸b¦–cSd"óhòD²­d°J­/d4ÏS{¾"‰wå1Söú9jT&Fr-× Žå£ Á•å±K¨.¼-ýÌUûhËTÔkúÎùLS3ÏÆÛÃ.8¬.>`4PBÛ&T(Ô×6=éäç9ìC.ƒ6Ö›©4l¦#èÊhrg€Ü_ȵÍÉóØÌª^`nà`Š1òµC‚€Ô>Óº ¸Wú"#µÔ ‘´á7¬Rà ±Å! m„Òàö)”|Ыs=¯$öW(‰„3Dx·œ ÑŠ^vÍŸ²ïñ:Ô§ž&!˜ªZécPØà6M´ÝTÛû½;,Ũ$«Iª"š´5)}Õé jkR)öM5°ù­Íy¾Ê.EWKr§ Œhß* Ÿp} 2ÉØY ƒŽÀ¢mÌõ BÐð¶S—«Ša22׎c sX ˜Ÿéƒ[w  ÚBÆodül lyí†[èáºÊ+ÑJ'™Ì1æš±0B[J·|þ-Ãt ?ÁêX-wËU€yÐP8ò²SÕ=é»ón”‹¼,£UºBhÊ©Ç'Rã)VXöPŶr˜†’Üœñ’%±ÃÓä‹ $µ$æ9Ø3–,ÍWßjÝŠ`¾_$ä-_JPNŒñZT½`êé…0ðT¦§Îx› ‹Ãç©,F Ǻ䲳»A>ÍŠ‹‰-þéØIŽ=yM`›ãxpbù»ÙŒÒp¶vœoIϜŮ¼æ¢½zêCñ¡²OiœØ%GƬK<ù[ê,å`¬(„qW°ê™ൢø’±Ý-ˆÚ+ :êy¨˜í)øÕ™¯«ÖT­ 8\¸M´|¸ö£ìÉ­Òí¿‡W4³‡…}†­ïŸï+#‘;ŒÓ '¹è^f„4¨ÃÐ]>1yð SÔWœÌª^€îd…ë¥Sñê¦æ=8v Bn‚/ÀÔ»‡×ps’É~‚§`àP‡®–ôŽ3‹k߃4ñä)YjM—¨åìÕõÂ}¬)ZqŠš$<æ¹Â[ni]׿YäQ’ÜÌ {ü¾>3[ˆú6i!Ùœy>&+¢=‘àÍ¿‚h‰æ²x¡„ü°÷T¤W{þO ÕPj 'vØ”hcžXnbzCÞÙê¤åÙRI—µ$Ìà‚«’ÿˆwÖEVþsFpÊ–ˆš¥d«ˆo®­0—¸ÚD@ŠJíK§hD3¨Þ(­dWÒ0Œ!ÞTAXÁ­¦qÜU™yXJ|}TÜ>wÙó²ü-ûý8'â¬6qiÿU¹"s¾Ûœy»˜»ùÓÜÒ,ì^ 5Iî+Ijmè)\I¬ßl˜cýVX(±K6œDರ}‘¹>­®+À±9››— ‚ïVµ«Oæ‚Ô•ÑSSGWzñˆAöDÑ!ãpŠ}ÔEZ®sŠ×ÐoÒ÷âUoSêM8P‚Ÿ‡õº FÖÓ©¤ñÜÇEZ?EfÅ¿¼›ˆ=[ª{äâñ¨ãæ t“ÃCåus_èëªO‰ßO'Š‹Ëœ·z˜,E’Œ…Zp •Þ  aö«ÏH™ÛtÈÁèØ™WüˆH,êW†köŸ0ªƒ¨`:ûDðê!¢-Óˆ¤ÄùuåJ!¦s1ËÑôN¼D÷U›•5,™xÕM+Dcº‘í]ê@©H·Ñ×¶-½J[ý7 Y»Ü1É{êf2Œ®3½rÀŠÇt_ÏÌÈÛJ–V)/ÊUÞI 5‘Nc¾;l““5ÍLûÕdl‹`ZùéÃ8ïÊb{0"¶©xiyõñmOCøâÍP£¹GŸ€sJR é)S9&„Íꀽ…ìB£J| m£ÉG—£_Nô ¬bE›G´²•5:_;ùNý×óyØ-ÎÙ@Ú$Êçžß.uн.Ô¨ÖÆ÷,M¿Z=ËŒ¤ÂÑŸ”%Áµ¸À*]§°_C£ùÕ"dŸ¢dШÚájeÁ,./¤MoMÅèKøa €&’ì§&ô & s7àö„Nœuî=µ»02Á*·’éÓY OOÑe^JSÐ2—ÅÜüF.MXÉcunü5{å'ñíG€c——›‹køGäã ‚G¦¤ÉiAp¡§ÐÊ£6™Å}Š”±ŽÈ3Öòâl{‰Gtù¾ÄÍå.‘çÒf¶‚úoùoˆ=u»ÉÈ)Ç׈8I¦+à´7¾ôyA@ýèRÞên¹cªVÅúV-ë´=ûûÁ¯›T(hÍ<úfw™f÷–/ç0 ûÖî—TêÑ׬éø„\@kÓ”b©Ê$;¹dEóeÞpæìM•£¼Äfüî#u®Â°å¨©eN©|#4ãE‡§!Óhušÿ– µTKpÛа) Ñ=³oÁîß)ìùôì}U¼ãv´GФþ‘‡;¶)¸’B͸¶‡Àl‚ ¿úÍ{QÅ8ks”—`½â)òÉÒ®H¶Câý¸ÆïÌ{°?îãd>,{ Js`WpÎHãž`¨¬…‹ï¸¶‚óë+qíœrIšØ+M4"<ùíqfVEçŽoòh¦Pó¾¢ ½¡ˆÓ€S•eŸ ωÑì°ä´Ò!Í*™7%ô,š ÉØSÓ¹ùUë²:ã@̤]ÉU`®ÀâW³ÛÑœñ›qÆÇ—þñËPDR§±mßLm“爧yŠØË5h®Ÿ7Lí…ü³(^!¹?÷Ås–²!깆ò¬+_ 9ŽW»ÅÛçT#M;î$‘:ó6ºO‹&$€yôÛ.biù/îu¸ioOÑÁÅ}”Hð ¼awí`—›­\È/Ý2ßjR=€ÄNÒ‚·ì™ðQÛøHPm5b OóyðgÈéƒu²¤"¿rí%׊åªê36Ï©^=Ò…IÙ‰1Æ`·Ÿa}Läl™²ˆëNTZ%Px Lrm@C¦7½oéКH¶¬b',ò£Õå¼N˜>:Q#OåûE¾ é59ef)ÕcdãÑ8—zX×]ÏN8¾·‘6Þˆ1c½š!åmE‘åËø¬¬—Ž0çˆX˜ëÇhç8ºóm_s?Z´ âèu_D AÛ‰éͦYÝËf.D=Øê翸.£-R2e$¯§'ŸÃ±=Ïz½_B¾VíU7k‹¹Þ«œ©áÁôø[_@“ºƒi™5. ž‡X²ù! Ã, m%$èZY ×_z ´oâ¾PÎÚ6«-Š}—£B•AL÷¨òE]8ò½Õtè:/[#°È )ÎWΤr*Ôù]~”ByG`‘ ìÌ¢™™¢ ´ có3"@uèÛšÜïÄÿêûNJ3 ‹÷6èRÚ/’*C¹Z¦µÊêì ôtØ!FaèQ* á4æì—2ù,Ó'¸PžÇœ\û »–ï6Ž®÷™òw±Äp| ¹†þî7ûzÅãÒ­?r`<*B˃jï¦Yfè½LO„¯}*ŒÇz!G–|Wö~Ö€°t¼åÌÆßwD›Êصœû÷ÈÌSë’Ó¤Á_°éѯ«y™¯LŽ^ŽÒGÌfœLX5(ù̽ŸK†yÂÌ:…F.6Dî.Yb@•n5øzfJ{9j)Ι“PFqyÊ*IJozàZêfÿHNÀkŽÂŧœSžñ‘ÒD{‘g‹ —”G÷¥àóåÜ#(ÚríÛÓý ‚ ë¦ì Sg=Âá5‘ 4E\©/#aÛ[rgÜ0så?ògak`øúð ý”ðý¶Iöî­Ï2Oޝ´ uµÛpóãga[ °sžØÇÍaÃÏûk²K¯[7-jÍHÉ„|‡Vhê¶]˜Ë}[šmK[QS X˜REq¼ЪF øŽþŒ'ٌ҂,)ÄH.…B„僲üþ’h‘—GäË‚tV½^<ò#ý©ü?Iè ó÷IBwm; A©‚öo2ò8QQ9" 6d\ô”³N2›a È.¨ÀL euÉJ\$¼ïŸƒ©?–Êšˆ`¸`€ã1” ®ª*úÙªUÜîåˆD—ûáÍ\ÝœòŠÅ ð ä"Ìj…¸3 !í¿Á¤ßkzyÆ ÜmÐcžsŒ“7kB5öMêšÃùvøÅîK©-Tœªäz†Þ?äGÉf…æ?ÐHfL., Ž¾Xó5¿Uíí„ö‚QÓ[ åÎú¢‚x†<ÒæÂtË‚}WCCQ3êpŒ›AùÍïׯJcì¬MÂ]\錫¬ØKŠ*ÃÑ'ræ×:#ã¤á!ÒHÌó$âeP¦mÉÝBb[„h<–C>9)% ›BnŸøÖ`ÿaCO ˜qÉþuìÕ@T~ƒ…¨§˜Gëd´ÒŒÝŸt¥=ýÈߊžI¢Ù*‚¼ªÄ ÄgB(ùÁ{˜[cïLuu¯˜×J9 ÚÃ÷Ã"Nhr¾ ëSÜMZ}ôLTá¨Ú x6¬5ï¬ãz-¦á2¯§Ö‰Ÿ‡^wz ¸‘ŸÃmÕ©ÕKqŸ¶!Z¹ÿ¬QŠŽÁùeeQ<¢éÕýâÂ-{2x¬õ¬m†ŒÛOužï0Þ£p=Oy­W[n˜LÄc\Þã3l[yË,_Ÿk‹¬¯}%‚Dc‹lî®3\«"j iúµ2ÜËvG5BZÒvq¥1ZzÉ_˜Ç¹ô‹Ù…ã|Õ©õÓº‰ÅU8ëDŠlÃu7B-1øïQ¸lƒ­{Þ`Ô¦ÕâœV8jÌž×ËFÖæ4“ÞÎb™AÝŒ`«¨“™O†³Å¯Èuݾì,¨M1Õé®a>ÅÃ{Š×œ¿,dsç¦*ÞÞˆVù~Ó™é?` ±‰A¿œ %㿎«_Ä7¡ÞǶ©‹úÕ}? 94QCîÐ;α¶V(Ca{·°+ HãiˆÇf^HÕyßÜ“%EßR×u›ÂÜ ŠðÊ$­ŠæÊ·â Dü¹ÎÀ*­k‘ð.J†Îñ]\ˬ1•*³°r/Ä]xöÙ4µ`æ™_V|mέ  h‹üZö;¾Ö:¥¬E¬½j–ü¬,§ÕyïÓÁÈç!lF3§œ xgšj}E+·Þe^ÓÚ󂤀—\i¼•t¤Ðφißö°ÙŤ×,¬ô·a‹ös‰/j!ΡÀ›6–xø³«kåÂ"FCü2FV#gë,ý-~/I Câ+ŒKØ¸ç“ ·ï*#Õ#Ø¢Éõ‹ÕÝ÷í›êDµ˜BÞW”R4zb‹¼¹úÔáškÛ[†JE0GæÚe#›ŽîcÒ•ÄÄ@geb BU†"OßöÍ^3Ñ%È©ïTú$YÕïÐÕ&e¨$ iYÎq_^jƒ©.ƒ<ú—åЊFèè±ùGÎróÖé‘C¼½Û®9R)_žq÷´=Yâk# ŽÛg’-½âÀþªöw·Ð­>ã£óS…îô÷-ÓÆˆ™&7®ÍMâqx2c™vîLª¶o÷¯{º2ýs¿ÅÛî¥qKØëIñ¥¶ìw¾ÑdìÛÚ÷¡ãKûª‰P{;«‰³U×{0BV B¨³€I‰Õÿ¸`Ÿ=b^¤uDGDüÆBd 7%˜"y»ŠŒž¼K€ÆMç '"µ<¾a¨Y<Ã]àfè~ÛyŠDøܯFo“Ü?Ö3„“*ºÓÉè|‰Ž*ŒáªÍ%³xE­Ëö¸2ýhž=ØMبä'±#­ÜZŒøÂäHk‰Q¾PC×®cXíêÈݵËqW·×îÓþKîà{S› bçÌ)s޵Lé]tpúòåi¦êï'¸”(Ö4P`JiCë€ ]-k´VÿÉ„ƒônPð‚>ô‡ëê£nÏÖûôDBÕ>`0IÍJ®Î‡É7ƒºª‚6åÇ?I«/ujÚhiudCFP—.›ãáÔ8íÍtl¤sÉÞ*ÏŠfýOô tsØUȳ„#ìÃÄ|ŸÍý˜OÆwç¯È½á‰@¸û>½g ®K\~8 ChV‘Ê”Ìyb9Š ÅŸ’Þ­~hª~ÑÙˆ* Ì# QÏ%M‘‰2 Õ¦ÏÀL¨¢jPŒõ Áu1m@—À4ô Ö8¿ŒóQÎ&,dl¦”Z–§ƒå“Aµ9Mstíļޫ{|î ö"½:sÑà E´¯_ÕÂC„nêDc-ðÕg>eä³ß>78­²üml§<5|’N¶¢³Ó)©5ò^»©uì/0ÜCF,Æa±¥Ë™ÙÌ“ ì6 aBG[bÝÒ%”gü=Óå’ï@¨ "žÉ~_‹ýÓrÙ`¨&­~mÚÄÉ*è…¥Š?á®JyÙ.»yö›´î¢ID:R(Œ\Ð̪AͰÙÑÉ}…[ '-ë»ûp‰ÀpÏ×é-÷«ò•ršèû²¢.Ïf¨à‚¯àÑ;Ω:¸‹Gp x|®—WD¼m•ÅÜÁߥýžk0®» L7;0Mmë“áqžqé±IUç.r5Ö ¥®çô–›={‹Fåáx–t â}> íŽmÜâ#íGÀMŽd#$µú éä;éÛ,—BæßGu3n p¦Ñ-þ4•ý0:ä;ïÆ2øÚKØK¥’©çvܽ”\ÔÉ k9ÇvØÎÂÜwuþÒàŽã,ã5ì²#î(‘î¹’¼+ð&&ÅN.˜÷~Ìn²åˆ˜òiœ‡ƒ¯†¡º÷=ÔÀltѨcÈîjSca¨à½t)ÅIÙ¿µ]¯3侘“K€?_ ‰"uSg0Ü:Å[ƒ¥} GÄÆ<.¶Ab² k"*ÈlJ÷±¹AiœO–Ü„ØźäîE#‰#E̓M•§” =²$!¤"/"ÇðU×%À’ÿR˜Ý‡ø ¾I'G¢…_–;¬ŒÛm3b»Ÿ‚gÖA­:5ôXgºØRv:ò+/œÃшZŒ‚²r„Ré[ ·7Á FÛSs¢øÇB9ߣ±ï4­pÎ/pëa‰Î-bB .˜æÐ® à99¼hÛ%›p>p×îaöÇŽÖ ˆ%Þ»¿’×dR1Õ_ZÎN¶JðÄÂ/¯=‡6Ø5aÕ"à–àBý">€QeÜ8˙٫S™Ú¹é©ªLII2f=÷λaÒ2ï`/œ*eMNGÙ9LûÐÉÂ-3°ÂE aчJôE¨@¶yK=?ŒÍm%/háæÓÔÞ%ÙF²T)`'?YŠT†2„ AKlƒFrvô…<Õ‘4Çå—2ºûj#CÔf¦2ºŽJ`ý ƒêPÃ¤Ý ÄK6=ÎIŽí‹Ã*å­°ã¾þRMÓ†¦9¹c¾BwYX?Ÿ§>ã€ûÁ\™Î…'0C0v¦-×(wNÂK¶ºѪ*+^›` ()£¯Ó–M1D)zµäR<­pÑëmú*ç7 ½¤2T]ÑS³fû‘/ÌßS¹ßú¶k§a#¬ÁR0ŸÊ˜¾Úƒì‘ãsÛ‚Ñ¡¥6E¾CŠ©ãÀž÷/ö°y0Ø‚ˆò}­&ò>óµäil~êÙ³Wµqfð‚¬'½MVnJ q×(÷€¨Ö  µñ¡yIØ yŒø=:Y”œGíš "íŒÄ€þ¬fºØrš¢ê%ëÕH-oXkÄ^2L1+w·fzÁà£Ã}k ”‰U+WVÜîðýju·©…£‚oe‘/›Æf˵m”[ÄE«{‹Îžƒ~AõŒ¬1`æ½(8ÎæšötXÆñ ±$ì«§9åìrlW ä¾:n/¬ÃØ5’pPþuDä1Æà¢n{CFw=m$œ@k“ïOЬcbŽæº©‹Õ#uâÔ JþÖþ’¹§t’ %˜?ÆGÍñ6;†À¶±í;ä£Zk>áq×UlÿbEÄ0 O¥XÔ~çQIò ¹súd`ú¢Ï>Óìöwþ’<2Ú»P/–ȇ’šÕ¿·&½ŠHÄ$º7ú'ð]e1 ÍJ†ÿáΙ7–º+έ—e™— ãÕîÄuψMǃš‚ý$¾ç«ÞƒD‚kg¯é9Ôp‡ùeâÏ’ÄõÃ'Šq ‰îî_¾ð¨í,W©]}a§ >¥wÊÄ,¡»AixnGx#<˜—¹fz€ÁtRá\4œ“MŽ@An³ÝÖñïp.¦óÎÒ¹°ež/g™œÎ9`og!¡Ìaó±G„Tüf±ª) ÊukÇzñ¶L¿T|®~·ËõíDC4b¸, †|vBi…QÈkÒU¥Œ5â%KÖ-ÛC8D§Õ)½ᦩ~ê=̈ùÃY¡«ÜÀƒsÁaa¹’ÀkÆ1\ï@Šfp ¦óŠU×Z.¼-¼¥«ï,)‰P‚ _møcV‘˜­… –” y—¼Í|tF)QP0õü,'ŽFÏïm·ÿÑrÈ}!ZÜ&|›Ûxæ  Ì#jqÏ[¥¤" |‹HŽ.»Já|ƒFG¡38Î×S®Â±n;6MÉ@§žªÞàB}C·¥éŒÌ–_\®w¨Í²Ež7¼Yà íI–Í<9êÛb@åV8ZåÑ’`5‰B¨!B€ êw?Ô®Ú‘}ûýp¸F6Å•Ûn`Ñ£N’S‰F³«»¾™‹ã.Ïe½J´)­…¾ÝÂP¤ž „(¿d’l:tC?ëË“<Çä`ÒíSð÷ C/H¬ÑÂB”ËPýÔ£¼E·‡NÐv~Ò_ ‚@'½p·_ÞÏæÁÑÏÆH¾ËX tÈð#L³ªdÑFy@ôkTÙd¤}±æÔç†ÂLë*Äjü~61{‚lËëUeïÑVmœ>¢#à²ñ¥[‰QlІÚ+áaôû\=X\˜EWIŽØuÁHhhôbåú\CĈSð7±”/Ìò§}ìd‰öK†ÂáïNîJ>‹ßGÄ–q=XÌiØÇy/P¬ hL7ˆ˜º¾|°a—aAÏæ)¸EGªÈF?ªzõaÍʘe`ª=×£4˜s+ÏýÜñ§Q’X.C}¾f"²¥To|?9ùÞCÕÎ'€8 Õ&Á½Q›^jÄfvIûî®_(þÍðš»h&Îê2ÍaUârZTA_˳:`GìkrMÆ ‡lÍ£NõBô”o$ôX©¼§¬ æVòn#\Ààe2Å xTÇ—‡êrkŽ&µù_›³Ãñ[†¨n`.ýŒ±Ý„UPùvØÁª©BðÇ&9†,iÛÁ9‹Üƒ´’iknáÚ¿výâfft½õbL)"^+¸}@ bYŸ–±Ž&Oâëéâ-w`’Ûss¬ÙØRÌ)G"×Á ÉÞ|N¢íÆ–‘Û$|îû‘=æ)æ”ᆵ§èˆ½[f)(k—Ç —»»ÖDáfe`'‚„¢'r¡PN&v,u@2,}ÄŸï[äIdYÌÙ 6œ¥šuZG/ª>F'$Üç˜áoÏt(Ñ¡¸¶ÕZ+±9¸?Â{%äÌŽ¦ÝLL×õƒ®pßÀ×›s W½ÐãáÄ ¸QŸx ª+½þ Îo’´}WI+tÏó#I¾LâØ²qEº¹P÷K‹—£’¿ ¶ïK¼-#¢;Æ>ÖÈì²Î4-!C80MO^íÙI\’aîÉç=‰Þ”‰Äçö—2LjåÏ’Ëâ» H>MàÑ£goêÃ'hê4Gç¾9%ýE‘þkÌx¬8¼@ÖG;+¨š­Kät¬é픓svÁäRØÆûAEd䀻:;ð›”‹@¹b<â2£goÐô–ËBeN7³qSeÿðK ¾ÙäІh¯K¬) iò)ˆæŠoÖ®WdF|©š«OIƒðŒ¥X,- :gá¬üÕû…¼w㻢wËü-Ôº0ô \2'¡&+5#„®x±OÖ¢f©¾Ð•WUJž¯[aÃ^Íož9b’J+LXÿbM—$t\ „hO–Á•VÖod0µ5Xçß¼%†_kß®•MG~gÕ yåUmùºw¸L*(¯…1‚˜÷ðÐïhÞï€Í=ÓK‹\¹YLÁ^Èá­þØc-J=ÝZ"B5úˆ朅"— ý IÔ}ÒNtLËC ‰Y‚vÍ}%-=àC4Ò}jDžðSäçcÁ€m%•õpbí¦¸f`άº¤@tƒ±ó }Ʊ­Êì[áI¬€ídìÂÁÆ××öâêh¤ƪŸ¾7.Ö¦ýÐ¥âö-ßè†ÄßpbÞüet1Pé­Zä/cdñ¢c§§i_ o,~Kô)ÏAYy\™H¯=RƬ§Æ,ô{@‡Za‹§R‘f ²¸f„:+R;‹˜lÁzŸ|[ƒ´;ÿmwSèí´‘`(<‹s¯ps·šxP¨Ä…ÂûÛßU¾½ÌÞDô È Yä ^Ø| 91.ÝUð¢'(ß 43¼ÍËj£t÷Ñ&èipŽgôž©Ðª®ŒnÌ/Šö‚…Qn¼ë¹!!Ráî.<–)8Îéz„Û½?†¾„qåLTÄ!çÒûdpÒaÖÂ:·mûErUý¬Æ[þç¶FPfÝï1´›þ ó;Ž ²;ßÄE±‹öÄC®2àÝ.Þnk$A Ç#ƒnÒ )xÅß”VÃZɤ·v*¾òŒ¬ž§;²Ä¨¹ðUúé**ß¼Ë{R:Ì•‡{¶Í-F7I™)Ç“Çf?7öQ&‚àÛÊ ƒ 0à\¼æN—èk† ÔyŒ[ƒ_$˜ƒpÛ;KšAZ )WÃÌpþ¬¼Î;hóêb@Y€ˆúÊe¬Örã8´Ìi*ñhðIrÖGó**¤2³¸b鵂šËÈ&ASxÄérV¡°-JQl$–3L”K5S¡oJ¹oJÐ=Ön¥sj¡…YÅÏW•ÖP´Ü-Ù÷ý øcnlÇ´6ºïùB‚úZÎdM0e BüàÄáã„VàZ6XN ˽èVDíg8üÛà}+6“81.5‰s'««‚t7eî¨Ó¬ïÌY›°e¾g%t¡œÆÑï/A*ò»ÍÙ£Áhþoy#Œ¼CŠ8NËT¼Š·}‘}ºP/*­þ;€-ÞùSc˜p½iQ,ÏošâûbÓÅÝ=M<ÞQ ÝΤQMñTeÉmüT*Mï;_ØEoWÐ1‰ÊuÔæ1Ž—óï•oBîbÆÃ#iéŽÄ¤gñ)g·óMñ9~™}]¿ÞëÄrGˆ7ÄÅø ái!½¦ˆmùîÇGH¬àÒ"³t.­C"øîïÌÀûhqØ'Õ“TKz]f!dðþՃΖùOS2±¬ÀÇnl’¤ÎëR•’?:ôb0O Ó™‡ùr0nø 6}´œÂ*4mÁÐA H7_³þmûÈ0q¿ù^Ȳ‚é:³Œ¤àßJ¿µQî”—<: `~(™(y!¹pXŸü¦n­H FtäMºPð¶ã±1´jÄI¼¶å$²Œ J^â ®«^ñ¢…skã‹üÒÚ(U-P”(±-iÍz½K§ýÃ>g°ýwkOŸÂK\ŸR'Y»ó™ØžÌÊ æ<´¦†V0˜\T|ÄÚƒÃEÈ2Y¤–³ÐÞA£ª¨`eDP B4S/'nZ§Ñ=Žå8„Š“9¦üķ](¢‰`èÒ`l)dO\†› Äm(­SÂÉ*:BÎ1‰[÷DÖ¼pœhqò×áÅÂ$]Mu~1Zj˜ã^rOlYäR¿äB ˜¡;°£/¡ôß"=ôé“g;š”YhgáVàÕ¸øÄrÌqŽK«=õ_/'qºH"äG¾éZ³&¼òô¢_0CeÑ!‚Ýr¨â7cã_)óOªân¿%™€ëÑ·P>E„Ùð?*iK¼ëi–-‚ïðÕ·@﫨[ÊØ4ZòéÐ<š¦Æ_:€Ãé™Dj…*,Z¾¡Í¿,F„c Ÿ_R¶Dö¡µ?JUi?½3ÚŒÖA5óÚ¥"/Œê‡ð´¸ˆKyŠ…y¦ 8wdÅû°†!—m)‘ç*s£2 ¥‚Œ|[›w4¿èU’ν"O$î q>'B¼tT¤å2 @-[áŠÚ=¹¡Ö…n#ñתªäãv1ž†× ?åN>ËÈòGDH§.ŒxQõ:ò¨RÚKñ’m”ôÜ…²¡]’A^íPpÞ*k ê»ÑOãÉ>Læc×÷ÜÛ„C±ÛvÇQ¯?«C´¶¦ˆ†€î}0â}©„HÙe EÝ0yõžÁïÙ²4èL“àèîÝ&¡Aïij£:Hï?(ƒ]Û¬Þîk''6v½¬H·äÎæHÚ=Ðí•_€î½¡]AS²´©>ûã• Ø’ Šçj€Ž¦CǤ¤(H99z{‹üµdº²XÅ8=Î 6Št GùÓn窊ƃqši, ë4­\{Xíãqã¸è†ì^;øêÇ4SºC4œ*P²D@ŒË29b)õi'yçª9ÕìÁ.Ø™_ÿcøý‰F£ÝJ؇ô£ïnHï’ÿˆ’¦ž$"¨ü sÅð‹R H±ËÕâÖ:;*3†W=¡iK÷YÜ1Ù2€³d?«ov l_æ+BŒSá¯Öd¤Ezí¦Ä’·™hB¯mè#h†ü–¡6Yòë.ŸMeèã×Ç:ÌhAÓôRýòi—bÕ¤ò…âTj¥»ó•¯yPðU?¤¤ùåµ½ "'ˆmˆàyD¾ ”r™\Ã8bæ¿ Ä‘î#®eZ•åùµ‡(³Û—5Ö…Á¡,"·2õàfšú¶ôV<õŠ"´™ÈŒŸ§9óØÏŠDñZp"%ôñå©|ô¥u k©öZ7%D(·§[ÎX³ÐsK(Y&®µY´5£•³tKü¨5·–tëâ!.GnC<É´ý2&`ið| º¶ŠR›…¯G3k5Р v÷ûù: GפÕïŒÉ ÜÑ{Cœ›ˆ˜¯T@xbT |N;âtž'û,{à®Ëœ”ªù6%¨›ß‚nÊî.³ùÕEÝ:’öô'ÕáÉé¥K’7 Ô `…#ˆþÝ-u¿ø)ÇG!#}yJþCT{úõz<9;_zéf«ÓbV ÿ¨°ögáòOʵ=´wÞ6¼ Q§ðmIQìCÒš|oW„&…üGŸïK§ˆé "¦Öu ]nZ]ÑYDOçÝ~ùœ°3Òc§Ç³ö"‰Dy!ç|^|I³ÇP¥§µu¦¢\ËßmPI˜ðnðÖP×g»hœ™|²ÅÁSÓË_‚»ÅðJ?ÐÙ¶ÊgÊ ¨•Õ‚ÅÛÿˆÓ°i…‘ŒK`Dv—¸ˆiÝ-Þ! á’bã*‹Ê¶G~Õ1ÚøñÖ¡X9Är¯w¬_«»ñºo~|ì´h§Tþ(_Õ6‹ÚäëOTÞ¹8EØUêlÕ|†Ò $\ŸÍ?FÕ<½®ÁÕ8Õ–:gú­³”Æì"Dlmƒ‰ÛCÄ”ÆT0˜¢¶±Æ®e·7{ª`ââÔ#=SGÿZÐü&ªü×üª×ÀUý6r"bê˜yž¦ñhwÖrhýls <Ôñ|ÿ 4Ñ/6tbûy©CÛ™)ï‘©ÑÞohkÈ"£ÔM2ÜÊ5Ž¢–…‰¬líøZVÞáq¡Âyë^Óødmt¥ØêŠÆ‘Qì$üMÅ[kÍ—Ì|ÖÝzAB^Ø4¸ *àHœ˜?o¾g!ÁD¦R×̳Ñ-ÕØ-Im7´RQuT½0ü%]9’•;ú!5SÔç2g{‡_|ø»{·þ4KÅk@Õe6Y X -O¬òR”©þˆn¯eò¦©‡_ýKl;žìX]¢¬ë9¡~£´øãÙ-:ïÅx˺VÞD«|?ûÊÏÆCœ\©+eƒz+ú„e#Ææõj¦8VèqÜ‹”ÞXóêvìŽûþY’Y¼plæ—j, _àêâÃE1âܰó¸êë߇\ÅD9*k_AXîyŠÙPšNà€Q¼[¼®~ZŸ6ó$YAµ +×<Ù–Y†Ã¨‘g§‡{EhÆòj$åBô÷¼õR½ÐÅ0•¿zÄuPA·u•â¼YÓ7¸3IifåCàx\´›Üæ’¼O£¢¦ÀœJ‚xÔæSs‡C¡¥átÕ¦¾wN—b¸»yn Zk‡ïã¼èØxÅV ^¿4üà?(”*V²_º<ÙdBÓg£}ãÃþJ¿›&Ä;Y;Vi‰~?Àf÷æÎÞq Ãäã#,8ÏíWgÙµòBSxº¸å¦—ëX‚»²ë?BêÉ5”­Û\“œ!j-§ÞQ bXnIi5”É´6ñ=æZ5zå.MF3mì£S jòõZoƒè~^‰¶¤¤#ù©=é ­,1C¿E ôñÛ‰æžÒÝ!ú“(Vmž>^ DX÷Ì#>p:$MÔðöè¢ã\ÂYK— Ãf7õwò«=ôn(%@Øú%RݪFYãjI¾×ýÍl´ ¿¾ÿÒOÒÙŽ;GÁ™Ï÷ÈâÎ_êûöìßU"b±ý´øä8_”oáš{¶.‘3®ªšý$æ ÐJ¾èâãj·«Nœ²;¨>öé‚Q1¸bN+'¯¾Ð§ÀÆÜ!¡@ø…ùOd®¶ çÔ•Óº…e€4SY.µŽ·î(ì^Ä,½ÌטCy À®Á̱‘‚"ã—C‘)&jn(è܂șxÀ•÷DmZËz‰žîÁ7CBɹP:8˜(⥅‹´ Öq8úQBÁnaUY;£ü*LÝû‹RÆðÉb,b⤫XŠ{Î3E·}½]8Ø«a%0岤Æ4%C÷¾óÉâÛÓ£JÎl½EDÒDêžiwa–è{CðÓ5 Þ³ÚôÙ»\‚¥œ"+H^‚ãr bWñ ¥Þ1¿ÝM”DË… À )â[Mëï$p»ܹ{Xì‰'<Í‚@ºMMª­Òã]+Xljxgó¯I¨X:ÝŒ>O¶'…0€9Õ^íò#ÅpCL}š%¨r¤°Mrųž®*»5a¥Å¨9Ûò _o†{dC¡}0®…w5Ÿër:ÑbGœ¸oÊïÐþ95 D(• êÒ'7(7V³‚T^[ßi ¼žu]ZtR°ð×§@V·ïHWs*Ì©BéÏÏVƒJ°Œ1¹n†cþ/i³Á],NmWcOê=Càs„_¢V´Æ‹æÅ†5)FÌUV d¦4¾‰v=ôµnŽs%RÄiJÕ2Ñ:–nšfzáÛÏPÇIk0ÃìÀìÜøßåVXrûáSÖy¶PFcÈÚˆP½ª$í ×WNûkG|v…ÕÛðc,É["Þí=rg rr>—Iš½qFyÑDc ÏžùW ¥ÃÙÑ]g©–¦ß¾«iîw LdÀóv’= €§þ$õ"ø ¼VílHÌ‹ú:äÞŽ!èkõÊûÊl‹èRcª´­0ÒͼðYíds<õ’…Ÿ0/QV¯…ŒkËÍ®¥4ùt«êƒ»^QŒ[aS×:” „bÏNˆJý™úÅúïUÚx¯`3Ù,ð‰rö-1W'?ÔG´Ä¦ žh¤šõí¯}–Cü]„Úé/¨°Ð_è:RÊ·hª®Ý(¥ ·Å k('©¦dü”™jh„uÜ騥àî ?FÊç”±Èìíñ´ Y›øûEf,{ë~òOê’6àzüÖ® yÿÀƒYˆ•X3¹ÿ:t^תˆ èËüY«¹‰ó[W%Të4§ÛfÀ–tþ€cˆo×çÆ¥9õ>7ò¡Ñv™a̳y²«ß7øõ¬üéþI×¹ÀïþÌ볇ïq3Øl(ظ‹¬ ÁhþDÑæ"Š <=^"+òÎìµ"(_¥½p·Ó\-ïîÀe>);n¼?&FÂìü5ŸH:uUâGýkÔiAzðF¦ FçGvÙûc[¨å"ûQ!PĘéìàò_ íxÕ°O—N ãØ8FbX‹0©BüÁ6¿²é ’BI¸DŠ¥|v56×GÚ€@J%î‹óâ¸12Ñ\íâÆºo‰î¥á%7Ïih ~5õ`©Øí®}9@©X7rϯ=´:È$i³ÉóJç½ø6»wüh:B‹Tc­7 t93]ÿ¾ÁŸM’>ÀbˆÃ‚ÎÉÄõß¹ k¤1PΙ~ëÕ;iÉ¡µŸ†ËÍÜ®l!Jý-®£åú;"ë@Ë“CpqdøÚ KãP”£Së–N íl­”xQúBÑ»“ø£ÿÞ$N€NB{vdïU‹ \¸@X­xY<ÜÅëe¨=«ŠZù\…‘:è¡L=„¹7ŠÉ,% {;4èÇQÕÕã8Ñi`¯U3¬WtëX€=í7e~å<Ã+Èù(Z1\yDjg, :S4©Å·ø¯£s"QôElΠ¡/¹8«zjb $´GýY|¥Tb6Ę¥îùçû•ÔÔ¿¢ $VôR÷ #œ¯×Zu‘òãtøÂÝÑ…€Crh¶¿€{ ì«’É UÚÁTˆ}âE¤xÇH}®ß8ˆlB¹@ªÐßêSЧ(R›NëëÎKVòv'\J°rqHþm[Џ‰¿Ìp\ð+©maá¦gâGaQi5QºKÀ&ëò˜žžïìUàò‰uRíUàà)1¯Æåj_˜‡PGzà¾Wæm*æTÃä‡:•°_œß+òLW“1(>Í¡½7òvëÍ ßGåÿV\‘) endstream endobj 1524 0 obj << /Length1 1747 /Length2 10232 /Length3 0 /Length 11338 /Filter /FlateDecode >> stream xÚ·P›ë.L‘âN) (îîîîP,@€` ÁÝ¡hqw/îŠ{BñâNq Å)péÞûœÓsþæÞÉLò=KŸõ®µÞùBC©¦É,n1É@œ™ÙYØ’ÊÊòìl66N664-°³è_r4Ì qøÃB:?ɤ€ÎO†Ê€‚‹€ÀÎ#ÀÎ+ÀÆà`cãÿ—!&º‚-Ê,ˆÈ Fõ€­¬Ÿòüë@gN`çççeúË n‚Íe ³5Èþ)£9Р 1ƒœ=þ+µ³3T€•ÕÍÍhïÄY‰Ð3ÜÀÎÖ æ ²ü. ´ýS  @Ëìô·Bbéì„O;°9ÈÁéÉÅÅÁ^N>nÈr7·fý@Ë úKÉþ[üTƒX>•ò[‚ž~мœ€® €3Ìäãõ§â¿;;Àlî 0YÐþýI ²ü?õv¼e{?vÛïÏ¿ŸŒž&Ìâ`çñó¿Z̪¢§¦$%ÉøOÉÿVJH@Ü^Ìœfn6;€÷éÁç¿ãüûþUý_R5 øvD”w°„~§ø]ÅÓñý«×FƒîŸ½¡üw ÈÓ@ƒtÿ™C6n6ó§/öÿç-øËåÿoøGù¿Îÿÿ2’q±³ûKO÷·ÁÿG´Ûyücñ4Ð.ÎOË¡ yZ‡ÿ5Õý½ÑÊ °‹ýÿjåOK"î`õ4èÌì\,l\ËÁN2`w…ØÙÜúïaúW7žrØ@j'ðï‹çÉ‹ítO»gnût¹8=õìoÐéiÿjïo zZµÿæ!í`±ø½“Ü< ô@{‰'Ä ðbZ^ û_3`eq€8?¹žjöXB`h¿ÍÏ `þýø¬fÿAüVó£ßlY-þ€ìVÐÀjùä°Zÿ¹¬à? €ÕæøDÂöøÄÂîøDÃþ?ðiLYþ€O4 À'ÐCî§HЧكüÁ›ý‰ìøÄÌéøÄÌùøÄÌåøÏõøÄÌí?㉊Ç_ð¿Zdîƒ=õð¯Ýzêß¿ð_w(ä2G›Ÿ…˜ ÛÔ·]׈“¸1o !¤^ëq0£8÷KOš¬Çkfe.(–ÉÌ÷±ËÛtªH8^g¯ÌýôÚª§hðàºd¦Ùµ¢0‹}¼|6àuEJ5‹Ý —§›,A.Pë‡S#‹ÄéBµ°êפÁö~S×û-Á– T†W!¤M¥£«¦4S‰”_çpCùÅlHk›ûœèMΆýû Í(Ãð•t*E¡Ž´x·jœµÁá¡%Ü>²îÈ×Àíԡθ҂C¤R\ÈÕ:KùX¿rJ\Òñ¼ÌdñÅ,úËW铯ëTά·ÎŒ(é»x­M-£Ø=oÈp¡K©°Ü‚‹Åì0 „ƒ" WN:½i`¹ž•Ê 2[…°E…éÞ‰µ_.Š)òoÇ‚阔ã…*¶}iÚ£]-Cá[ßÔ$ãnI3fÒïûXåPÛe-]Ò…Lër¼åKµnÓ)^ânõ×ÃåäÄt§Vic±6~D.Ò¿ôòäzÈÿÙÑ!A»àXéå¹V¿ä^˜w{ÂÎú¹Á<­6JÒÓÍ~ýÒ»Ú}ÝHiÐRäSéÙfÏ}´6‡PdØïŒŽ&bi §ª\Üì{wA^kWª’[~{@øeÒljæZu{àyŸPAƒ6x"•©½Ô™ì´âÌïG‡™gz<iMQõWà²g½×àlšma òê`Ì'lÝÂãÇ]»l+'n©Ša[û0ã/ÒÅ`¨íÅ¿j_;oɉ¤Ÿª%™>Âh+@aáÿð=óàœÓŠäÕÏLa,Š·&f“?$Hu WoG›Ît©(Žˆá·š²2zÖ€g{'Õ8úàÄBš ޳·(•O•Sr"R>?P³âúõ“°ÚŽjÙm ª‚¬J±žw²›u´d*”½M;¬£KÌüè»ÝôƒNÌßÒ²Ù$þ¨Óƒãõ¸¶BÃOµ&Öü’O‹äçƒ}`žÜ·œ5ïfMÐúIì¸òÒÇ ¦eH„jƬ:qŽ¿o›ËdeŸþÃs¤ˆ>Âe"ñ|s oén`ì–ý„qbwêö¡ncÇ0’)u—YŽÐá.2l9'Wû–ÂHKú²Û&]c(—•ñ}k‹pžŠ³lÇ·å©·­;ÏÉÈ’£Ô¹Z °h¢lò´àÊ/Ð*žç`Ú¼u%xm®øžaiVÑsü²^T5ž'¦-àe¡úÔª[¿ÐqÀŒg›=Zþ¶eL‡6Dôuæ*NøÝ¾eK“ÐÅ_1SÔ¶Ív—+4&çöÔF8iü›Ì}A¹Û/mJ#~¯–nRµÆÕؽ¾à¿"pÈ ™É–Yð2Ójm³|ºq”[é®4y@û®« mX/‹s¸xíöê¸qUD4a«A+÷ÛäXÃ-}j]-Œ<Ôr‚Æz†ÞºKFÚMDèqwÔŒåœæ¼³ —ÔJÅëæè¼fyëÓ…·3‘H_ʇ¨©1¥—äñO(Ó©Ã&IÕ¸m=¨‡¦ ã½vVkÈš—Èt4knEõI{Èê6ÛM…Otñ¥Åw/kB.‘¼dc7©âb õs ¬>„} ØBÂxM¬³QŠæ$­ èo'ÝÝ‘ž_` LPU*šï혠¡‹üúüK 2!\È@ŠßüyäÖ£Ðþ5ÿ9ušö)°Ø¿Î?‹[ÀS€(ë#ɾì„ûõ£¹ŽË íç´ GKÓ³HÛœûð‹äºXd ¤ŸgÐ]IêK§xjD“ß}¿J1ÙW €RäQ¦å€û‡]rà¨CnÝÂlæ"x)ºl|âC”fï|[tÐÆ- l¥ü§ôÛÌ÷/°vƒJ<Û“·ßê°÷×&©‹$ÊЕàY#b¿ 4K^fWæhýeI²'ÇØ]xØŒœLÐâÏ“¼Ó[èj ¹rVKèö~HÔÝ3ãÚ¶}jÉH¶-Z?¿û¤H^e¹,¶bR(þfg®Ë uª …‰Z„j?-õ¡2º+ê;ò£êG8ús½¥ñ{>{qÏù‹ï™ˆ*‚T[i¾DÏ'âK; «m—)/š‰%e‹±üT¾æÔÄô­z ‘ypºÄW¯¦MM·:lXÑá_ÂÔ¿÷|Ð.¦a¸å!1üö³Ibg=a!Nhx[-ŠŠ`Séß5°çÖëÂ.]W*”Mgü5 h×.êùá´«ßšeâ“fØã,'¹|2|CžÆŠ&µ¾ ø4Œêõmb¤!TåU“tìß{÷°x¥˜×„Ù]³Å¼£+!\ð“DIe<þBÚL:oÐü¥ОâK°¨¥Ëâeä¢íáèW_®r"U8¯jlƒ3ì¨ÙhÜm)ï%Óüù÷¾›Œ1ò±õUšû~Tу9"+Üg\nýmŸÞ¶:£ŒÊg6w-ædüòÚ]ö‘%.û€Hõ*4Ý òئzS†@k©í+&5ð$è•ÆbÍ­“ebCAá7C{s[vH-ãŽJØèÎ*¢·ô~˜ÙI$“d•ãÏ^ÚÐ{=üù‚ŒéTDX»ábSOàçÇ; ÄÔ5~½fñ*z8>]Ui9#9öâyí‡Ù]¤KhèôOÛçùAcCâ¹8tóFÊ[Äs¸Ø¨8—K±Æ•3ªIµvËã_=„IÏK=¹¥£ˆ=©GÌVØFl5# ïÛÅÀ[—ÀœÐCÚß™")|Ix°lk••¶ž¦Ý}(À_«"1MÊÚã¤L…Š)%µRŒ4œÑ±=;«ß¼bè¾Ò e–žxµ’ù¤e¶±ÜɽUý±óW;Gb‡Hù¡¢»#goÑÚgç²ÊŒ½Ø¯Þºs4ïØ F…w¤ãĪ[YN8Œ×`õE9tdr" ÝµÑåúiÁê«oƒsyÁ>öº™{̧ÊëâcžN¦Nf>±ÙQó÷½NÍb& Nm¬ð)Ù›2ï:ƒ±Uã¥{ÊSìŽ#W¬ eMÍž]Éù Ó?RÓ­œ ksâW;¦7L_œlþ”D¢*tíŒl¾°yÀ…‹M͹]'›ú¸ÃÍRê$Ða4H"(ÇQÒ^'j×ÙPgN€·õ§VtuN B)XÜgvfùÆúŠu0ÎL(4)aéÞöŽuÛkõùÞD"$c,?øý—„ÍÃ">YÜpèXÛçƒÊ§SI·÷=Ôæšþ$Ü”eñÙ­lI b!”ÉM¾[R¨S¯ù1üòÎ:!’åï×[IþÈkX½Ù=˜óDŒ61Jhƒù>ým–K¨guþ…Þûs—Gò¯¬ULKêÍgì a~ê äÓÁ"ê †˜­ÇòwGËÄU¯1‹ÂÓöûߢ֤ ÍÄ›»Ü{´hkØ®!Ä>½‘4‡“” è önÓS›ÎÕiï§Ûó\™Ø·÷rméUqfÈ=M7Ç“J«}n"èl;½Âœ‘µÏEFSRÒ„$Szs’FŒq½|T÷ꂸj]U¨ÓgËZº¥;è˜Ô¢-R*34]ñÄ·&Ülóœ“RØEú§ú^b Ý”`½MMßðÇÛÕyùí'²ü¦Õ;ñ«PÙ„/*šƒæÓ¿Zç`x,¡Ë›¼3’1Æ’¹ë‘»5Â衯 Óä3‚L;ÇJ:£åÎ4I–ân"[=Ù¡hÉ$”=ùêÝ8:õ ‹à®©š¯æ#…°âò£¨ôÍŽ"IFm=ãÏð˜!5*õÛ¹(V÷Y(gÜÁ†¾v^…cR#¼i. ðáJ½2A›o`°Ëu ½Á”.º´³-1sPgü9*¨åÎá1ZM<ßäCþiWqŽVÐër¾WëQÙIPi·á¬ì Bis<ô„œÞŠ^aù‹½ð…AÓŸõè5ÜOVâ÷búÜQ¹'X¬K_£´JÉïç̳ Á›7úv·*al†6Ÿ 8" Y{(.7<È é?æ| ùåÛë}Vþquÿ\‹Öy_LQ}"Þg¶ß6XÐp·eWAcçyΖôuúž ¥&©Â¥hØ´|¶#*ýúTÃaò*‰jÝöõrÎy¼˜fî­·:ý㹜!"És¦ÄIWmQ)!zÿ‘ä£ÂŒûÂã¤O?â+6Vöd¿âv' Á}L×¢Þ•W¾\÷ˆ„Kç#äbU&ó®ùØù:GØ”çÕó‘DéöU(ïtQ¥,£A×HŽDTíjDç¾p3 íh€o9Ÿ¯_XìëÔɵN»¤>ŠMuýíÍÖ w‹»sIóél¢Ͷ…½£H˜Ì'ØðúÙì>žÔ4|qÒiuƒ«dŸL¼Ê£` x/Ct–Â\Ú»Xƒëç á*Z*¯·¨@À‘öK¸¦í%üö| ¤ðÚO©…E”­a™yfsAÓT.פ•^HX"dV„…:×}\™¬qÖÒU5 %üÁÅ¢êNíïþ÷4æZ¯™8·bÄŠOQ–À䘭ž2`rîY¬ôct,0ø\·ñ 1E±¾­ó׬¢Û÷NŽaZÖ<°…ý3¥Z]ýˆ¾¶´8À¦UYa£’…HTx¼"r¦ØçŒá±¦Š_^á+ĽâUmÑú­i 3š‰»HÔGÿÔ]‘Ì;A_úTTÕx´òƒznÜ_X©FÖ½mÓJé@ÄÔ¬D+ å²èÍk äƒÝÙ¤lÊ×ÖÐ ­=š¾³íg̹t"E c·¨“¥‡CÜï¬ÛvOZ½åY¸]ºMÕÕ ®féMO?¤¨«6íÑýB¾âJà&Jª7. Z $P^­†¼#Cá–e0ý¢!£\йÖëÿ03jµ³í²\X…w@x€ëà`Ƕ¨(l‰;Rì;À{«„æÑìˆÚ ÑÕDÝ+ö†øÝ0Ï¥îÁÚXì­ô‹¦ù–žk¦;y­nê°è‡1Œù°ã„grÉPpËÎ£Ž¹u¶‘íâjžLï²@KŽjáõë¡J)R¸H\¸zxd Ñf†²{ÂýÐ •‰RÊ­(ií@Ø,Û ‡¯;Ýa+½J¯”Ñ•›,ÛiÄLè"¦ÞHI=”í„áºIñ(iŽñ£˜ˆ…yR6õ~÷A’¸Š19oÀßnçd`ÂЗXƒñµk/$Yª(mÁ*tåÁ#àf?wˆ¿èí“@/žœWCþ¡ÙäÆÛÚ5RŠ»Žê…šóë7g¡Û-á,Ïdó¥Q4ÆuG’FË^©`Š#lLÀBn66)?CŽš  ©r‚ôîe&{™¯Ïès©õ±Ök½ÁïĹ…qU­ö¿H:ù<*8ÆA<…KÈFÓä™ÐŸæo4 åÞuLsSŽ“/”1ý®#«häò´<ùHø":…;ÔulvJI!ˇgéÞ…ì¾€~y‰Þcì éùë2Ø0Jùy¢Øx £ Ý’Ñ×s· \×àÏ ±tÔÖI‹oç âKߤÝuÜÓÂõØúªx ühª4DœßLa´í¬K ÑbŒ‰¼vÒæ<ÖÀ$-Þ0‹1ùâ7|+®¡¯áùœ†GgÔxV&Gyý`'n ¸DL{¾]ÝÆàÑ×v ûœºÄ3úÕàöØ4!BCPfñË5+¾Z[×" _gõ§}.eÅ5²åQ ³Ãà'Dü¸yk¶<)_­U lÐ=aVÜÁ+ † ¹nŒˆVÑx_àMDå\ì/©ÊnYªgä*¢4ƒK_{ý}o Äøe–ß°.Îv¿‡[–nÎ]c’Ó¶lÎêòܵÅÍÇtœw ›žÁ§+#šÚPÐEQ<“D*<+lbò*W2n9:Ó'0Ö?™E7U 8‘ItG0h¼ab”fׂO`åv[50ñÖþøÌOJqxÄqêÛ¯Þ©S¸8Uö’HØ&ØÿµŒ.j󯆅hJŠûô Dó5yß*ÿ„´<‹€RÈLt‹V}­~E8ìEØ–Î $Sª÷yˆ·vê% †Q¦ù~ÄF0“‡õ[µÑÀCÂyÀÉp7îCŒI^lF([×™ÀáP6Ê)òh)™Q]–ÏÂ1uFƒ€W€+ö› ±•à 9@2Ñ¯Ë #g÷æ4$ ßc;î‚ ÅM:'¿9Ëy¯Z>R’‚Žâ™á™L††Æ1Çne5ÃK^”` ¢—a÷„Hz†i.sŸè:À sE—Ë#LÜÆ}¶óñ9wÔhç‹5ë‹’•à"ý@úÐèf€a—žjM cU»J†Lƒx®_ aê»Çãä½FbrŸÓú‘XÚAà{ìJ>Ú7îðg(ówÛúÊ—kû3¨GÕ­e”®XÄfúß¶h£v¦m–²iÈž¹Z…ˆp¢ šˆ”‘&¼øºï­UGráøïX{šz#ØG£úÑIôÕÏ>ŒzræÈŠÎŸ[$höðžÊ†ô'¼›³plìà‡¢t0¥¤^°Œï^ÕÑtW‘\Üà çs´F#Ú¾G.A^1øßùCÉT”s>rZÌKLš¼=pÚâˆóªÛè ûªQÂQ(b õC ¬C k O±7çør>^êNÒa¨}¦ÊyÆ¡7¹YxöÅÔMBXúüæ½n]£B #wbÃË<ÿ‘A¡}ÜÚ‹(nsõ¦ Èúf »Ä·lŒ·ù#ë ®ì¼Á ½ ÛÎ"þ›¢;Ù êª8ÔÀýµ…ÜÔäÜsŒ™Ýûyv YWÛAÉÐh^Öò9›[ˆmLt`…/ë³ÍZY»â/ŸŸÛ;ä›'òmXàǪƤ¿|㪴ü’+0ë‹o`¼ºüµ@f$šL)Ÿßš®_pæ*Í #Bi€V]&²PdñxSâô›|Ù–¶$ý…Ågè·5Bn~ºïŒ¥0xZO¦é5§· È™*X8h½Q¼ì#Ùõ|ÉÒ(<ßi*¨ózÅäá‹í§…³*²Âw+I]fÓöKØ_x[cI\dBvµ,¬(å{OjNvó1â¬‚Ò ƒ:ôÚÜZ*c]dœíƶ›Þ¡Y4œ&|_rm"7å7Ñß%¼¾TX‘†¢º³ßä«nÐî݇¡i=h\TèÐBº(·š…"箕Á­ô¶/Y¯V ¬©†…ÑüŸ¹“ô`1ð½G~8uOîo÷§ Ø/—–&4C¯‡Rû‘fXV½(5ýÔ"É™ààJðý;pÈe]m+ªyD’L¹ÓÀ;èT>á|zÕa±)"•ß§\þêX9Öq…ûÆ “Ÿ³>Äõ%í»dæ”m£°Mââˆ>‘÷ . ±ôäDáFe)7°ÓdfCPu&û­J!hÍs×vžod(làðxãy>2fØt¸ k°ÈwƬﱈ"ÛÔÇÎ]é§3J!݈K?…„¨5S¡k’½…ÂJ> åE6¬Z¾ H%fR7²A»hDô1ÆÊñ r°›¸ˆ-&3¥lcK!s¢7T¼ŽŽöœ9º‚’*ÙÆàþè]K££¡Š¦‘v»L’½Ý÷AÕO¿«šE¹‹—å¬ÊÄ#8Fã zÝg¤v5$›;flc˜é…Ü·õ©Ñ®ïœ#í‹yÜéõd ˆÚG[¿¤'¿CŸíI± ¼a͘…}Ÿ­‘íÿìÊ€ _Èš´ìƒ–CO–ýóøÞ/®ºmýÈÁàÞ¬ÃáT®¸¦œ#Õ ,­ÞiNõè¸Pð¹½GÀixñqÚ$ m"d‘ùµ°ØÇí‘„4Vö´Y‰Cl®¯FuIþ9=vK ?0™P‚/Ý.PÞ;²D%›¬ç\…–¹öÁ/ñªý´ ÿ±©aSë’ˆ&ßÛÚÁE8wÇ|«üè}w7–ÊSãtÏJDͤQ.¦Z+‘Ø*yjë7Þ¢©ó†Nä6½kÌYË oßXe”›ú®âdÝfR›ô*±î‹öEµÀÏ„ÈDIAm¸uõOÈxjÕŠîIèÕ)mØÚ[³®Ù fýƒß\æ! Ìí´'e¾5Ú,­^Î[ß\§ ‰‰ë7"yæÐ­ZÉ’jF;¥Ó¤È}2„Å_5ŸðÀß’³ŽöüÜïF‹&©§”.= ;˜5r—‡Vq2¿· ÷ÄÛ1.ã–^gܧÝNõÃ" ¾LÓö»¦%Õ,ŒEÒ ½©óñâóx·%Ÿy‘792žÏù^âÑEXmžÛnWhF •á*à 9vH ²Tf½§Aµ†j¹í쌖¹i'¾ ½ðÌŸLùèh$⸹~Zoã­*þî%ÁdOèEF´†xé+{A²F…_õ:ç£máÁW–¡§}|~roûBï*Bî\á­°0Àx:’åÜC¾÷ ¼U†ȱ‹|ÙW}wºôï#±4Àò厜<žÂošÚq'$Ñ'Šhå d>#ÊfPÐôA¨æŒ<îyš°ïߨw´õP‘–Ž>ZúÌâžWzÐ PGàL ê£mw{râš­þ`ñ‹á6¨¿7Ùm8=Î,=¶ûŽDÌ®±ðù ýŠlüR`…6±óËs§} ‹.чµ/ˆ¶™±³ ȼŒ{ßçd'~±ã¨.ç"aIÉ"ø ¯þ(6F¹Ëï‹ÝÍ²ßØÍÎ|vW‚䬌¢ÆÑ$ßq¹K6Fzöé;¦nÏÍòõíĉ¦ ͵}Å–ªÆ¬.ñÀ7sïm‚.çëþg½§¼Ê¢a¨¯NÃt ýô ’Sú¦“*Ì˰ânT¾S‘ØÍÔŽL8¤ûIæø~Ÿ Ñéïç¥ZG93FÄê%™Ì{ƒüûn¨†9Úâºc NS&ø9Ý.ÞôFb’¸¿zfCgÈOYý9n ±ñn…HI\¬­Ýh‘Ѹ†tGËʵžñ‘pýÓÕ¶I(i…rH`›´øîg£°hÝó%‡ S­ QxQææ^àvüªà5zÚhâ³ÔóoõÉŸÌ!‚¦Qq86RÇ&=~v㥮±!·¸1å ~#iJ=¥D7ƒ$¢úr3ë/J-!Æ©¦Ä&Ã8MZnÏBfCÂ÷løVkU¨Ê‰… bF2Ñ42 ‘çþÄœ >—²WÞX[ÞXÏñïÐA)ÑâÄlS%¯Û?ŸykðWy¦|‹;›t%ú28Êë8õ}n¯§÷n,ϾX(5ÀéðÛ]®MÆrž4IAbvØbÓÑõuÎaÑâáQÁ3rè—Ê-Â!.QÔY­è›»›‡„µïÝi±:×{yaä×±(1xSV¦Vº¢i;øÈý—ºa€7ó…Ö Ç ˜}¸Á[£Ô4²Þ Ñ&çi{&Ã?(¬8_£HVQ¸•# ‹Ž–ë• <;j¾ÁKy”Á^¡{´Ð7PDŽþ,0$c/shÑÒÎtí~Vl †ßµƒg©,ÿ¡jûš^!Ü)Œ™n'x•S5þ‚¢KõãµÏkÇÅשñŸ×ØáïA|.)S³µZ êv¦rœz¢ª[h¨)]éza68÷JI‡ÂåôÑ?M¥ó¼­‡Éré›° "ב¡™’ÛdöÇR»Ðø³5+Œg‰ý2ŒÉÀNô1Èc÷ÁN!F y™GÔv±ó1¸`‘ͰÄTuB²?$(/áü¨°1f3‡aoâøÆþLðUJ)û0)‚ûübù ß»zº ŽcŠd ˆm|äQ;#’±ˆºÉ¯¥À_WJ&µJ«_Š•ˆÙ^ÃütÈ—¢Tð>\Ž i•ÉyÛ ¿ NYiïΠI¯Ì,Ö¬4D_©%ÊùÚf=«°Ý}|'ˆ¥H5í­1Ûô]J„3þ –·['¸GUbÖll¯JYê-È¡P´ðê%Æ·!¯Å†…Å)'ŽFs»(K£r?'tÞ{ùYœèÑ•×ù„h¢0èr03 ËêM^¤vM|U úJZ‹æŒZf§þà6Oñ€S;ÇAø2ò“CPpãŠlÿ'QM¶æÙÁÙê@·œqƒ0çíµ®5%0Ö;kSGÌDZä¹@ULf˲Ë1yÇŒóC/‘,¤svRR꘳¢¡^=5%•«3gæB¯> stream xÚ´T”í6L· Hƒ8 Hwww7Ã0À3ÀÒ%H‡Ò© ÝÒ%)RÒ5"ÒÒñ¾ï9ç=çÿ×ú¾5k=ó\»îûÚûÚ½¶‡Œ ̬ƒºsðpr‹ä44TDÜÜ|œÜܼxLLúw'ðßf<&C°ƒŠþ#@Î tGÚäîÈ8  êáàáðŠò‰rsx¹¹EþsÈ=!6 N€* †ã1ÉÁ\|Ü vöîÈcþõ `±xDD„Øÿ¤dœÁn кۃ‘'‚€N=v÷ù¯,âöîî.¢\\^^^œ@g8'ÌÍN’•àq·è‚á`7O° à7a€&Ðü3N<&€¾=þ—]fëît' …#3< 6`7òp€žŠ:@Ë ý+Xý¯vÀß½ðpòü»ÜßÙ¿ A ’ ÌÙõ@í¶'0@KQÓÝÛ„Úü:ÁaÈ| 'â´Fü¹9 (£" þMrƒ¸¸Ã9á§ß¹~—AvYj#svCÝáx¿ï'qƒm÷áúk²ŽP˜Ô÷o` ÚØþ&aãáÂe…¸z€UäÿAšðþc³»¸……ø„ù`WØdÏõ»¼¾ ø“ç·ÉÀß׿°E’ûClÁÈ?<_8Ð pwóûûþÓñ߇`¹¬Áv(Þª#Í`Û¿0røno€7R{<îß¿¿Y åeƒ:ùü'üÏ|¹t4µõ´5ÙþbüoŸ¬,ÌàËÁ+àààðp „/þÿ]æß øù?Vm äïËqÿ§¢ Ô†¬ó d÷þEÄóo]°ü½3¬€ÿ>B†3Àòí›s pƒžÿç ø“òÿ'üßUþoÚÿß )z89ýq³üñÿÜ@gˆ“ÏßH-{¸#÷B†Üèÿ†ÿÚe ° ÄÃù½*î@ä~È@íçàáçäþ»»¸"Äl£ qÙÿ¥¤Íy† Ö†Á!¿?9È,nîÿñ!×äˆü¬À‘ûË„#wÐýÏpc0rËþû PÌæ÷:ò €nn@<¤ Hà˃Ü[°÷Á¸8¡0wd ÉÙ` sÃû=f!ð·éâAÊ‘ ü(à‚üЏœÿ‘šç‚ýò¸\þ „‘9t˜Í?"åàÿ€‚.÷@äU<þÀÿbòpsC¶à0‘ôÿ…ÿ|}À`o0o~ s¨ k¿¨–¡ñâØÇt3ÆüuªfƒšxÇ%^’WÕõÁá¡%â¾ÇÝÑÁªž![‡Œ¯Ü‰Ćhåù±« —ÞÞäyþÈ ñµVz$ýæ¿l•5å¢VóØqüÂþ¹k_NeÎ^¡ºRR@F~Üp’ôXbõI¢ùlyt…Ô›g¼SÊ1?È»ŠjT?*îòUáp¸,ŸO;‚ûºšœñÇú|å>\ ³ɹš÷Ø» ´¼ÓØNpÅ>íhÀNÏœUñ«óà‹;´z¬¬ìyÑ!_$põÎsBµ–+gV¾«ãæ `ÑF'hËìâZ—ÛbM—P( h#RŸ—¬5Î…\cò³ÙÀ#~z¬A;w[ Š9öù¶þý7o’_í-_U5V#Ø×ÏPMVªÇ¤¢™)¨ÙE䱄LÇß "„×õb.P'¼à^E“r”¸3$™ ÔÙ‚¤6†ÒÃnF!x;´,ÄZMÖ‰1^cѨ]‰ä)g ª_ ø"¥Œ~# ÌÒEѨEå'Šï$8ŽÝúò°xuš\ÙŠfòú#ÒVÇ%¬‚?a›çÈ&DÞƒ¿lÁ Y(•ûîðŸ7È$fŠ_F{Ç"’cŒ}J"ÕªÐè¢Þ®É”Ó|—Þé= ©ð!zvÙ¨ù@JšIô%;=›ŒfeÔ‚«ýÁBeÊÐs  …­b—É´vÆ”«ÖA涆Ô•ØdäÙ g@ÉnLè4ÌBD Œ®½ŸÝË"õæ>ó|9.ýŽã£l6j¯ÄÈ L—˜1.`aš•!˜(cÿ5ª½¯VZ ‚Ç}bA4qØ‹R–ó™Ô]ÒDZz~ÛO*~t Üñ|D=LAúí>£HqëuÆmÇòæÕðXܨ¶Í•ýg6† ÔÈGôš¯×ÀüÉË͵?¥gcôFkŽ3ãùR%,gØÆk6=Ÿ|¼' U©™“±‹æ>.Ø*ëWÅ®}Û/Гç¹În«7å«›dÓb’:î7{±³â<.GA,7̹µ7u­8á¾Ú(èÅÿ¼œÑ:˳xTKÅ$EÔ.Y2œü [g>³©Õgªûö!´-JIdøuù6ñÓ‚Óóx©”'ƒ•ª(´=8GÏÍÖÛòâŒ^š¯—pÔ!ÊË¢ª«ÊaŽ4 ¦c ôïb`ù.ñÒ3,Ø€ÒåØx%deÍÒ†Ô-Ï&z£­ö¶9/lÐ(ôàév;~x¾ç²yᜠ¸¼ó‘ˆ±#¼þ^œ9&Ɉ¾~4Võ ½Õ‡O¿NY›Îø‘Kfr(G_*+·޽ &ïa’emÃ4ó+ü[¡Ž(õ6j"êRZ(¨lïhåa’aò+Ìi„âÀDJΙ”¨[ïZÿ½øÆnË•¸Ãëlý™_ŸV}§¨0ZYúËîWœß\u°í„g%tv3 ø„;Ýá¸ò_ð/PS¹+ÊuÉ¥gíV Ʋ1í¦“ù4‹YÈpô\UWlv0 6]ýËÒ‰¨_Â7¤·§q5ˆ%ÒN+E{m˜mñë ¨j«vÛa³Tãyi5¥²þK¹j$â[™ø÷mcíQ/§ŠÄõ¬X–Bíúé_0ͽ`O¶dF jLµ•kTÙÊFŠr”oîØw#>‘`¹ÈjYÄÂðYéʉžöĶ[¹0±Rì{ßÜ‹àæA[t²‘bÜ«›V®ðìM¦^îµ]É ÐF)Ũô‡ê¤ÞÆ–v>9?E„ý w87¾ä¾:B×a 'æ¤ÙaÆ;rÍÅm’"ÁG ùn—ýÔlƒdK´ýЖi H<{š˜4'÷Å3’¨ÍS—‚Š-tÕ3(mE@¾± ®f÷™£­l&%ÑÝkíä(ÑOcÅ 4c>‡ã¸¡‘)ÑS9ã'ü“œ ö›iÔw‰vi|–Œäº7+M{TtÄ%ÒÍž'šÂ­ò®Îî^ͼ´>ñ€¥s_òWŠröƉ•ˆª|à¦Üõ“S\°æ°òó¯èKX1“enüô¼9¥I ^éñz¬¢}½‡x9î@3ˆÿ½iPGpnø×»ÍÚ:¢©Ò÷:Q(/ùÁØõfùìÍ+ÆÞMv^Õ´wƼõ3¼‡\Œéä²Døº:väçû‹½žû¸!‘ì˜c.è8牀{ÍØ ÈëRe-CâæéMÎÜǪ_ØÅxáçÒ­¼"Þh¡¡nÀÁº4ÑÌ'Ääú!ŒX?4®¾dÎúè§}–Ö8zq!I‹Aõåæep × ~è´k1äBp*íþe[DW”Í’­—:![tÝ×]M¢ônz3ºw„ÙJÛbA\Ž\þû.oMÔGK[T††Ãuœ4ïn‚ô|6H1׆T \ܲÌ:צ¥G¾»•Ö°Ú[½J…YËKšôV^Ü Õ!aO½/~âm„tâ+ØûúÔq¨>¢Oñêy$O'—²XßóÀ#vÜ$œÏ¿¿ !%¸Od[ï¾7Å5Éy$¸‰>0}ý“™µ·ežLmîçIë/+µÜ= [ñ<ÇU€DRmÀ'‰13N>îÔ’IÞݦåÄO0ÊÒª4K°|¹ag°<À@²TxütÊ’Œƒì~FÌVÈ…5¨Z{]ÃI¾ì‹QuÍÍI÷ø¥š(ö6åDzgyh†ƒ`[c\;Èh°%ÞÎT1¯¶gÔ0]<÷D M8I¤5hsÒgá­Uá€]NrA4þéF8W§¦æÌ”ÔÖÌ$QéÒQ*²ªâKWµØ+RÕm&aýqÕÚ(™™½¥á ÉOéÇòû~ÌÍÜm±KK\ˆéûËsû€iéMÑ+£ÓXÕH×ñxQ.L6åä_Ä?G™"í ›cWÛ•Mû)Ÿ[àŽ9JÞ¤u0Ú*¹©1bgµWt‘ÆU> }..úµðJ}T¦õ†bÑ5’Œ¶Z<¥éLiÑ­óC³Kû*éX 'nY½8þ>Nx¼§ÙRÑ+OÔ[ŽK£]³n!ÒÆqa˜*_òS"­YŽ›š²ôŽÛKû¶õa²Ì@ަUýLñ¦î¼Ë|EZ݇÷Ûï˜J ü˜DV2ï kêpk0&ô?Ä:¤~¯‰ë¡§MYÅŸñÕÇ2æ7 sÙM°YUHÌünƒÃÕ*_jíç›DFµ²¸l$LÞà¡\ªæÇQÇ{Ïàøõ3€YÒæ ½­92Ë~n®Ð{w/æ*hÝ‹(¸Æj‹0ò>u³Î(åÅkÉýà†éòk8¡á£’íÅÏJu>ùëÕ0K}ýç–kŒz\S’" gOªÌù!VÙPt¢gJÏC¡ V^Îø3?T½3‡üò…J¶sE™–©¶Â ¯ÈV|uäšB³ë‚ÂuÏ¡ËÎ]$¦$Št8.c¢â©ÓYÍÁh ?:îüî)=¦,‹3#Ëå Å«¤]òŒ×1kû l²WyØè«LÌ}x©‘ñlªß:ÊÖq=‰¾VC*'3a›Žët¤¬ŸÄ vcQùþ#™\éôžž‹ÞÁÀþO‰Ð cÂWKúf®*>4ÚÁ¼bÙÔ‚ „Ò tM‰Z^±Œ:=G£›)¡6buçVw,RÑSøÑ Â?Ró%n¤¡20w•4¸À‘uæFá¦é[Ð/ùö7…|2¦%Êl÷5^‹Øöм—‡ˆ>iL¿è©6¤c<2=Cd¼ygã[¬CÐö#Qª•t/ä6‚#2fFYsÈSUBòœãÅ ŒÚ™ÎGŠ!45 Ü®Ÿx\+1@< ÆK/*!xKÝŸs„Ë0i§¢óíÇ MˆÃŒTáá·Fé’dV\›25U•‚€!)£Wd´U2ên¤§8W¾o ñc߯ìÉ”j !T!^£ç•kÎ@R“xgO¸ hçÖ¹s¶BA„E‡«±ÃŠšÏúè*FTÆÜ”­µÊ𦱉xüÄð9ýÑ¥?p_HäóØaÿpI8?G*ýÔ%±k¤îUÿ ì5ÀùÁg|Ç~¡Ž ®anLeƒOoJîøæ:ÆâW¯Ic!¥oམú\´6ðWÚ)Ô2`IÝËMÞ.éEW'Jê÷fEy7säʉ  »1t¿{œ¯?yÕ{iV6“ŒŽ¦Vº . iOî¹ý"’ZU' ½w ‘*Ž÷&pŠAS¦úü˜®‘2< AõU"%Oº–7ùiKq´ÒFga*áƒ`ûsðÔòGÕw¶¢XS@¥»mµJ<ÆxXðâî­³þÀò6 è–Æù°£0´î¦Áî ØY¶Ÿ×~'z«Z.ÙŠ•(ô´Çq[%Õ×u`9da·\ŠÊq*ßÖ`Fžl~ZS¨&P­'$ÁEÐטŒ ‘`æÐͨ§ÝÃÇÞHýÒ €ˆ¿G\ô¡¶´3h«²%ñK_X'\Ñ¡û W„ e–„¦°ÌÏ6xŽŒ5vH´8’¹Ó±ï)‰¨¢õ)Ìw=‚Îï%ƒDÓ¢Š{ÈÔ˜.q Ë*û¯.*eVÌ«Ž¿ûbzn€ø>‚>s‘÷|ü¢ 棫CjÀù©E'­Èð+â©Ö(Þ¡q ™v½·Ý˜’Øh]Þ¶ 2 ÿ™Jæúh(>³þÑ#¡ìºÒí´Èó¶rû1ÇžÇí–QXk¬Lô†¸:up &>ñÉ6}Qê­=m?]µ“ÂhÀê³ù`¶ãRï4BöËü³©¸!+NÄ£ ü4 oàñ»O¯lü0 üšU×äïÞ̹±+ù‹¦Wk_ç@hI«r  &þB\IOM§EZž Â\O2Ü–“ƒ×>RAëöö·[Eë£~˜Æødº†?E*ù5Pm*<‹½ñqøƒçˆ. >n±¼ÒC¹Îø[Ñjúß#Ñ8“øÝH5äY¡Þê_&0¯?ùj#2 ŒN+aYô¥›ZíÏÇá“^ c9bFLî²O$°”T~ÚP˜`n“´]yÆw;,–Ú[H¯¨Õ¤²L(¹K@ñEÝåÕÆôö1«‡U°Ñ!ÆÇ7†N# ²aßsù`cS3)VòrÜû‹*o˜¾zÃòqU5,|in_ô5ZKÉûaÉs9¦ëîܳ>çŽo‚ªTŠóã”?›)ʺÎàÃLñŒn.g³qÕÝ3èûÂ<þtÙ•›÷â%¼÷è*=ÕÊW÷(L‹˜7È>,S2™§…Øä0Žt¦f1q2…x”èAR•=¡ÑS@ý•=Æ-RF+H¸#Ú‚ÉÜúÂÃÏÝôüeñrv(JÝm“ X¿Ï™¹$ME·3¥9}å#ÀM¤«a ¿Ï)ãX°Î´1ÞjÚSe07¹îÎ8Œ-¬± ¬yÃÈ|¬æ‡~ûe¬#tiѸ¸Õ‘q´C<=0F‡¡zã%.\¢ñS‹¥YÊm¸ÏA ßIv~@á>3ù|»—~³H‹óSÇß«yûZk­x§Fw2Iƒ7èá=ž>˜t+dù¦áþÍnýÑ×èDø&îE_øOÛ3̧ɭš¯^C×>Óû†¼Þ^EÄyËZpËíEÓ·på ûz6Ž„“¤W3—¤(Ø*”œ[4¨¯RÝ–˜Ìv…ceHz«íM|¿ßo²dÚe=S6^Ÿ)c•ß!žÇÚ\é÷´<;Êc.U’oó©g+ZºyšêC@ë%˜4 “ \cÎüAA›Z}¹,¦s¶Œj“ÉR0è¨d‘ 2À g¿ï \ÿRÇ‹Ð{Gnuý؈Ûuؼ—Y9 þˆÒ¼ã{DZ¶<êQÄIËÒBó/éJhçcèd©ál}å Á—Öõï«gN;µ!ËÏ%?ͤÔJiüÀ}÷¦¥m9D¼é«ˆ}÷­@²È‘úWDu þZÁiN½®\$Îs', ·Û¸BTæ§Ät@“4j2nƒñûàÄ—þÔNÖBîIUîÅ©L¥§:%ê1´Ð…,`)XÎ÷w…wÞ(#i´Æ]\j< ÕŠnIÏP“& T¢¶èŸrŠ©"ð—ÔW#ïÛ{SŸ´·w=}¶zJ}iH•IçÈÙWÌ5˜aÈÙ l"iò›[y„²ÐŠ‘ i}Pû:(ï,øÒVàfbM&'?©p©P°*>qö¼<.JP;Ï:*E^}àôÙÄ’3YŠôžÖ6õµ×â©KTåjçÇ÷å´Ã¼¤ú†®GlØwžˆ^ˆ§°0g0Uò¹žËë[j•¢aâÏïâEGÍÍ mF2¯3Kï5L”pˆ£ïñó‡yJ½ê|oêô“Q]½)›Üï²9îjdúv…õä#Ñ=âçNû/Þ¼êóEa߆пË{ùÖg÷䵩Í6{¡¬e‰p~«•5l1˜”C‹aæ]ËF}LÜ¡™g,*[Ù·©ìºº’ðv*¯ÿÚÿDëikþp~vªŠßzH’ô4vvÆC}ibýÆ¶Žž8çítÏxŸ‚/¬ÙÒo|ŠIsØ'ÞLo–¹>zèK¤NQ¡;w¸Ý´‡Ãèö½çmî3‘ÈJÙ·“ %ž•¼˜_8š• }[´¶ÑB•ê^v—]Ĩ.%õ*Y>TF©6VGÃ~lÊÚpæ¯þ¼ÏöA—;j>Ëu7X+\cÊwËQê“bŒžr÷ðÞ\~5ö4•CϸC¦•Ò†÷–Lûƒ~ÃJÝPRu¢£OnêcnÔƒHÅÉøŸšóÔ þDû[óÚ”þ¹-¿êú†ú ´¼J_=¼ùt8”Á±ØûþóðþAÏjJîSó±õ«¼9å9e;”çô²· ÔCEdðr••9˜®ØvÈåtÅjp†<9ýÉÄò‰›ðØÕ°ëG/“¥a×xõ÷ÔŸ È]®qÖìñå·ív}Möúq0pbö˜{jÄÐ@à=X¨6ÿQû]4‘Êð¢¥ÒtÏ "ke48ÙÃßäÇ•Ôa3[ÚpNŽì³Ô6OCºÀF2XÚ+·œŽœ¼í|´d¦›B¹Û7>» 9»¯ÚÛ&ýŠ›ï—•Qf¹!É–Èä‡"·b¬Ž6c•ßZ s'7Ðv´†$ŒÍª;i$R«•|‹FTWm-‡• Ö´q¢šàt{»gEþJxžìˆr`o~åëýO!£„ÔÀÅ癳š¸™{¸/ÎB¦—´Ø]á蟻8!ÞXYüMvU£)R añ5 éiNŠTƸ gBl?­XYë'b7>¢c3V¢e‹®¿!ÉE÷§â%áÂè/ÎI]‡Øq¼[ãWåÆhÍjèÞîNÓ¥ˆ'WZfƒu;e1¡&rø„3â;åÊ7^„óNL[ZÎ’OŠºÓ™W•ñ_™™‰ÊÙKŸ*i÷ú­Ô]l͹×Rª¸W?»Œ}ÿHÃäõª ƒÉ7"¬#šõµ­Š.^U™>ÝíGôÛž ê->1\¯“I]CózlØM½KÄ÷÷bl«"ÜÖƒÅ,:Ÿ§„Å{ÖO1¦}µ¤ÛZi²3°¬6¾ÆªWT€€×Ç|^¥É2÷7b¡^£©Þs Ç•ÅK£ÂZáÝ'¶øÉ#Ý9ÒøW¾Ü yžäÍ’¾GFï1Ù'Òü…2VBÅ@ì Ž_­D¦Àuc TÙOvWxO&;Ãýš‚iµÒó®ÆA.OÐÚ»è6†$]Ý,-”‡Õ-–-ný8 j+—s>ï¿bá±ð†…s"Ná¨×u܇ÒQ;ŒûØÞ^•Dœ~¸Œ®$?¥á i®ÞS)6‹zóif›ö¥ õQV…ažÖσUƆ w SÙukskÇ ©|*EŽmü÷r7° AÔ!»Å/ÓNö”¾­OÇ MÒ?7©²F•”¤8n;;aUÉ8Ñêišcž$ÜsüäZ`Ñ¥ýÙú'ŽHÏ9©ŽSÇÂQæ´«/5ºEÛâË\á-yFÃúÇ]/1Í|£äzºA0ÞåÊû¤ÔË#š˜F g•5œ"Á¤‚y_WrÀyxÒYï4…‰L†U9t*Ï©ëmÐ\‡í¾"97d £»OQß·ï0M®4º“2h hFØ-VªÚ„>“–µ¦( —xK€õ‘W5”äWÝe$q*ÞÛød©¾8®¥zÂï¤1ÅX½9Jk©,²‰3îs2I@(2uaœaÒ 3täw=ó;¹þ¦)Äì÷¥î!üÛ¤<¤œgq\ãìÅçsTÒ§;1ùn)âš¼h61ƒ³%Z¾¬ ľß1Qez•OŒ“™Ø„£!ƒ3pë —{ÌEt(­ÂŒ§”DC±eë2$y¤ú‚fãƒy›-&DìÌŸ­f]-¥.R¿¢¦4ÃÇä=Ù—¼Ž¡…ªâdqþ.–Ã;jr̓¤¨ê™àƒðåóF¬S‚ì|MÊüGÀˆ­ý áƒË_5©Ïò¥wË´"ü?çIÕlÈHÒ]À®{0¯HG±gM½§£¦óÑ[¬¢ÑPªñæ…˜7^»3p¹O¤ÌJHä,):Sô} !PWòø*}Ø^’mÜ©LÂlè{ÑÅž²œºÚ?:לÆëüuÂýúÖ"bf½CˆØŸ­A–jM›3ƒøKg‹º±Ê;¢^%O¼1]0ÓÃÔLåܘS±ÒV渿¼Ææ>vâH|˜¸¢Jî]L1Vîý“£ü›Únê8¹/C8¥*‚…¬ WÿÇQ#Æ1%JHSœçú{Ç9HÚ!•È( Æ£\¬ÅÌó7öÍgz+í™™Ï÷ƒSh ¨‰mÏ”޼‹ìr%>‘ÅïXòš T98óéÔ5²áxÅüÆ¥:öuiÓÆgH{:Q³Qú+M\©ÝÌ Õ‘^ö#CØGÊÅgTþv3~é­9ZKø¤/i[X'‚¨N¸cÌ!âðõäaxZ“¯ö$Å7èz]åàOGýâ7ƒoÄ+ZH}VŸ×н€ë¦æë óT?÷ÎrÒ­¼ a¸"1ã%&Ωì Å×ý~(òûñY°¶°>„ø1ÿ¡o|‰ULÎŒ“h%úg¹Ÿ2!³fs“eUMAÕ¹éN ¥ƒ\5V„ú–íœ!EýúšBTØñÓ¥Ïçh'Ö«ŸÊ$$YÕ0Óïý„Åe‹°:Ó‘ƒ}¬0Úš-ß[<,×ÐËÃV;¢`›Î”sع`U“uþ?öð\ endstream endobj 1528 0 obj << /Length1 2788 /Length2 24204 /Length3 0 /Length 25745 /Filter /FlateDecode >> stream xÚŒöPèÖŠBÐàîÒ¸»»»»;»{ ¸ \ƒœàÜ-¸»»ÜÎÌœIæ¯êÞ¢ zm]Ûúƒ‚DYAÄÜÑ(éèàÆÀÂÈÌ SPea03³123³ÂQP¨[»ÙÿÃQh]\­xÿ0sš¸dâ&n ;G€¬»€… ÀÂÉËÂÅËÌ `efæùŸ¡£ /@ÜÄÃÚ ÀutºÂQˆ9:y»X[Z¹Òüï#€ÚŒÀÂÃÃEÿ—;@ÄèbmfâP0q³Úƒ2š™ØÔͬnÞÿ AÍoåææÄËÄäééÉhbïÊèèb)HCð´v³¨].@sÀ¯‚Š&öÀ¿+c„£¨[Y»þ-Ws´pó4q@;k3 ƒ+ÈÃÝÁè%¨ÉÈ”œ€Ëÿm@ø§7F–Ãýãý+µÃ_Î&ffŽöN&ÞÖ– k; @IRžÑÍË`â`þËÐÄÎÕäoâabmgb 2ø‹¹ @RD`*ðŸò\Í\¬Ü\]­í~•Èô+ ¨ËæbŽöö@7W¸_üÄ­]€f ¶{3ý=Y[GO߀…µƒ¹Å¯"Ìݘ4¬Ý2âÿ˜€Dp¿e–@7333è z™Y1ý ¯îíüKÉòK ªÀß×ÉÑ `*èomýóu5ñÜ\Üþ¾*þ‹àXXæÖfnS ¥µÜïè 1Ðâo ¾‹µ@´{,æ_?ÿ~2­—¹£ƒ÷oó¿æË$*­)"!O÷wÅÿêDE½¾ ìÌVf˯%ã}ðÿo˜ð¿âÿ’*›XÿCîˆ2Žž¿k5ïuxü³Ôÿœ à¿A» Pÿ^}}ff3Ð/–ÿÏð—Ëÿ¿½ÿåÿmõÿ/!Iw;»¿ÔÔéÿÔ&öÖvÞÿ€VÙÝ t Ž ãpø¿¦ZÀ¿OYhnínÿµ2n& óq°´û·Ö®’Ö^@sek73«¿wèS…·³v*;ºZÿú²0€öt ƒ3³}¡¸‚fõ— º§ÿ¦”p0s4ÿux¬œo8ÐèAˆà˺Ps ×_« `btpt¹@åù,]à~M”“À$òKô7âíÖoÄ`û¸Lâ¿€Iâ_ÄÅ `’üXLR¿+€Iú7b0ÉüFì&ÙßÄEî7q‘ÿ@\~#ÅßÄEé_Ä â¢ü¸¨üF .ª¿ˆ‹Úoâ¢þ¸hüF .š¿ˆ‹Öoâ¢ý¸èü‹x@\t#ŸÉ¿ˆÄÅÄ´iÖ®¶¿Û "aâöÛÛô7Õbêbbf ½_X±ý+ÿûÚþU€È˜ý‹8@ÁÌí@›ö/ö_{ûߤ~­ “ù”ø;¨ Àÿd`a…Ú››¸Zý!ñ-êŸ2Î_qœÝAçý;4¨~‹ßd`ñdÿ­ÿˆ ¿!Û/èñ›Ë/Áïà¿ÌÝ]þÈ2°ü‚âÿfǶ•·“Ðá ìüÌ Òmþ€ QÚþA­ý³4Pßíÿ( ÔÓß‘9@® ÛþCªÝñ7³ãÔ bœ~«AÁœ@¹ÃV€åé€ ”Ë èz°ÿ0åüKfíø{Ìì æ8Ù¹»þ‘$qþ½G ÎîŽn@sS»ÿ¤ecÿ­ø?«Çùæ¿ö<ÿHÿkÌòkÜ ‹Ôûßd8@N®@{ëÿî.Ç/ Ç#ãq=¥ÿV*ÞÕî?›Ébõ;-è5br³rþ±à æºy:þáŠáþÍÙãbæùÇŽ‚¼½þ€ ðÞ@P}~“Eòºüê?ßèfî. 9ºýõæ‚®èø¯ÿ«€@/ ܬ£_ˆMMHëÝW|O†íQ)Šm­4ß—6÷$˜4U™ïW]nD>v¡,oJP_ /?û6ÕÁ„7'©´<ú=%¨Nl·ÀÍcõŠÔö¾%`PÞñ{vöÓ ²…hÿ.K‘ëìΤœ~çÙ#åUÛ[¶46»­²SÅ)ÿT6É«£Tp¨lE3ŒìÕ ˆ‡öðܘïmÜ”—‹bî;jÞŸUxFˆÆ­«s…äE¶«æt¸*}’‰~óùvs¦û­)Ù±º¨‰}C<²–ºÀÃæHÇç'Oú2q ª¢…^¹}þp¿ƒyñ¢nܯÂX‘× ¬5mJwÔ`™(?ã(2w– ÈŒÚ4Ï_ðDñÕÔïòÉ|ÖÀºç&G6I~Ll’ûÚUè³÷4ø8b>AØ%P’Ìm¦_® PÇD½XD5®hxŠÔ-TgL÷W†!EÉ`ÚØ˜1G¤…¥T™+òEt¯³IÙ6>YÂÄñ•®ú5+×ü¡åú:àä«âBoÇwž8?æLzIå©8ô—ã§GánYWQÌ&g“ÌMÀɳ‚aWëe§£^m½£!‰±Õ~k¶‡ ¶É?C…1PŸr®Š®Ì&‘GÂÀ[J…ºö2.‚$‹\ŵÐS#&; &ëVFÙíËý³Ž,Hs¼{¼ãpfVz?é2š©tIrÎÒ“T’#9Þó\-‚ܯê5ÈQ…tw4~Â8à s©ì§Wþ–}ûÓº†ûùÌ•A«³•ÜÕŠý@Øúú¯_3„q….¹R±à†s¡ô>¶_r{)ó³³Ì^ ¸ÝIƒ-"#òq~jjôw‘sgÙg{L׃²NǪ“v«¨™˜ò¿ñÆ!š} 0#¢Ð_u> ÔonÞaWb÷ ÑË0º•iÎÿzZ[õ„Ù·cǼJX~¡ {¯£Ãüe8Þ×s'yVÿ®ðÆ%Çy0” Wà"9K}RGŽqYç`¦“[jHÎ×$BLWT*» ŽˆÐ{‰)JŽ*¬›U‰yJ3µ’SF™œÜîqÿáK#Cöš^Îá4pd0ú®WnxŽ’•Â2Ù´Ê'üVv÷½»c€ÂÖŸ÷ÄO¹áÓœ´é-´Ÿ{»Ë'^y¾–¬ õÈ%c™Ñàü»]×?³Ìq« *ÝŒë¹zt€V?ôlKÚi%Ï5WÔkÌ(]Tº™.Òbõ"Þãv&‘7Ÿ¬ÜÄw< ØbÑdŠ®¾z8&aNìÔÉsô¹'?¾¿í¤è©ø>1ˆé¹¾²ÎjàXÖ½ ¨4z’±úì¾—'j" Í[Ï‚;Ÿb[:àÍyû¾cq@(‰X‚#ïÀ¡váIYÊp8õµ*[Ê箄çG`€*q‹zø¹UæÉJñ¡nÖwK €”Æ!ïa6c e]ÿ¹ƒr€TáèÀ¾G¸Ak&¬§°Â~Ù”‚y! ŠöÁ8ý¦'/i5‰Éâº;Ë®BY‚5—HS„FMTži‡¿ 넽L,[¤`õ6ËŽ¯Ò÷G<5Yó¼6”ƒ$t")l4XX·¯ê¼c"jg:6õ–‘ÖÙS‘—wr~Ž ðFè³S©Z¨«/ mˆÞ[ {¯œå¹Õ†m·&nÙİÂJ,‡yLY`’çÄÉߨ¼‰È>mš!+(ÌKõê>ÿÔƒ°ÿòÚì3#ùú-š]Ù}9i—¬`%r^S4^K”ÀW“æä²ÚÀ’5.}ÞiÐJñ©*V#VQ[‹dl?DY›rã^›<äD ×äg#½QmÍTìþË+•Ü*Duå×kàÜ{8à}¢ÓË"^°Ë;«¤‡Æ$w¬ó!1Ætv¾x°·@•ÛÙFÂØœlxÒ«Ê&>¸#˜F_sF0útËYˆh˶úcѪâèúMÃø|– ¿ ªPaÃÚÓÔä›Ïk‰˜BBD«TbTÊ^³¦oåæ.Ÿ% >ê µÌÐX&•\{“¼SÏ3}LKh× Š³£¶³¬ìÌZ4¬ˆýIÒ¸W6“2óCýùÞ„]…ûëp œgÃY³¨±x¸èjh{œïj—á€ä“Ð SQ°[È;K Ÿíƒk‡zVæU”_ì¡(Þ­ ÅÅC.#oýB™$2o‰¿î`Çé .û6^R©þ‚_îíö¡òH¡RœˆÎGXÒ»•I»|ÊÝú;í¹:ª…Q0­Åñí`ÊŽ|æ 9À .f:“õ[ë…I÷·¦Ùx]bpÜý=>"ÒE39làpÞŒz¹^M?ÝXDÜ+ʲœDÆõØ ÔæYM=“éÌn¥…'Š@<+ñ᚟𽤟Hˆ¶Êù踜ô =½pÑt6¤à rHTŽ,ï%a¢ä-7¥HÕß®ŸíR‰ Ø5 Oœ°/s»3^ºÓl.>\ òѿ󬛠€sl©ZÍõ‚`Ns}:;€‹¦Å"é5v2½—º °|±Oˆ­÷Òô¾®_DÈ"5 ó ÀI-vò¸èDÌ" Køt3ß[Âè"ÊIŽéШ#V!ÆòÐãVf.Lv[ 6n«>-÷Fñ2ê38ßÚz‘ùË—®ÇcðÄ ”§UÚÖ(þ¾Ïy7{ÑCÐË \;>óð×KÅ#ŠI‡jîç[­µ öP`ª$J9*·õlL ¿#/ûÐF:ÁÆÖz«Ì3Çs¤ëÊNe¼YA„x䲨uÏ«˜\t4à:uÝŸÜ@, mŒiH¨õ3”’•Ý­ÔÎ~‡|šUë½Æóghß‘ékÁ6©Òçô¡­nZI4ƒŸLŸ½ËbTªßF &ë«|ì—¤-[¶VÇ:Ý3Tܘ(†©Q¨×*6ËqÅWÁÒ©~š=Òs)ÊxйîE?Öqú·VÏ EßÎë„+?\ÁJ‚kHä]¾ÄœMÉwÌ KÇPÕÔ ‚ëª"Ë,¬¹Ql½¦dvè•ÐpõP¶?åZ:d!TãîëPM/™t}Sï>§Òvy7½ú‰ÉÁÈá#„¬|<,uŠ0\Ínîñt¸ŠzEB_9ÉúÕ¥3CÛ Ó•.‡ƒ¬ccùªØ%“B<ð¦^ß§4SùÕåæ|ö¤+–4òwè.ö"¯'®1ºú./tW!h?µ”Þ¹^Ÿ9f`˜ û&xÏÂÃ’ˆ‰Gûb›Á2j½ÓývtJ´ÃŒí¿fêj骜fNÎ0¡S5:A)Ïïô­ÙÁ¯) £Ž{ïq°'åÅh¿w‰G0ñºQ¨OåPxNØ«Çt+,ô›ƒËÖë¹ðnmp“nÜ™B{¸”¥pKÏ8Ø;®mÉ·:ÂÞjç¡9o>UÜÛ9ŠÎîIzKœ’åAÂ|¿×%™üáçêÃI©Äª‰^Ä{‹;e/ö@éÐ\L¿­§:¾%)¢Y tR6Ñ+Ž…Ë§»ui}Ò©r=è;–”ÑOÇfŠw9§Ô"(ÛšUÑn"‡–^-ÞEv“)¬h}ûn`†qA‡à\xÞ³Ü%ÅèêÈa9¨ ±]éiÓMDŠAÑ!%:UÊhËÐ(tÉ#±> «/ž/Ö˜èëøq‰œhøÊ'°ç[ç2qUB#œ`òžÝw§„0¼òüwS4aß²îë‹.˜bù`Ôp© ›®y·Ü e«m„0¼Êkƒ%¨™¥†øF›1—TœßÄ•ö'ÛªS5e£ðeópÆw˜ßrr³“cIÒîpŒà¥Â9'Ìõ8O mEû°ˆ©Ñu(³tߨ±ŽÀ³•˦(,…|´£>Ÿª'‡%jªl3Ù;)rÈqk=£ühº—æWL¢¤S*Öt›¦ªØÝc5ü¿÷öìv_•@ch&H~¤è|<:»'™—ÖÙ.™ÊÁ•£Ø3+’¨j<"zÌ]–1ÀTƒf¹]N5£Z! ë©„É[•é0l¿Psyñ6á((ëH•ÎŒóñ@Æ@–N`ë€aE‡ s&þ\ Æƒ»ËL„Ïên—¶ ÏGÙtÅtÁ|ﵜ·ÃØ%¸8>[U•7f›Z¹h×ÃG™REÆ¿»˜þõøÆbbKÒ'ø¡¿A(SÆ LXMš2…ÿª5ñèáÆ”%ùž@S³ºŒ­÷Ò¤B„ÄÛ»u“÷|Š#ÛŒ¤Àë *å½é˜ÂËlä:“…?ÛjJÆáÏý›¤R¥)¨»T ®0ÓÄÚZÏþO7õ :=õ±†ðÓs6#ý$Òí§ÑJÒ¯°ƒ#qÄF÷ߢùöÂkF/Þ%lÈ6zä!Õ,“Á‘/À”!/Q‰Y q|âQÃy&l9þñµó\8©'ëmºÉ—º@ŒO"ïÌ¢ÂöíµÀë¨/â¡]ÀŠÉ–ŒçÆt|m–ŒÙüìÞxV7SÉØå>¿[«&Ù®{Ê»žRî:òM·–¨J;u2Ù3„š·:T²…0Îj8ÉØtº{@¨àv÷^&w¹HùyùÑÎÂß̹›Ž£Ó;€äù§ÁLÖþ]Ç’}x»[.ÍŽ5¯ôEn»RhÏu¥P)hŸPR³ýÅyKð‹ûÞZƒoÄúyOpvh†´®>E¼Æ±Ç|â¸Ï¦!ÎÚÑXoŠ@EõÆol8•Ú‡kB­ö²‰wX Sl!·„Ö›sÒÂË‚,Æ‚ŸmU3®Ÿ=¨ûíѨFÔ6»Ä[†켟ˆq¸’°òt®gÅj+zí÷ë ¿UI¹sIeÀÜÕ/°|~DvcŠ™†Y‹Þ8ƒ[c¬f©pˆ0zôw‚¦Û±(³ä±"'|(é7>U¸7ž7œl’’»áã(‡.ôì?lÜ’qq>)a³y¿ªƒjíþ%°ëçV÷EÛÄéÑ$šŽ Ê>¡øõ o|ì¢9ŒjÄO@’ˆ*Â!CÙz šf“²ÂÁf«1½éb÷WXµ]¹DÜÔu¹Rmò-OËÁ¤oJÁzo¼¢ñûƒ ¼!e¨S^[mP8ä‹Ëd:Ìêýý¬ˆ:È|Ä7Ð5ƺ>x¶èÄÒµ=Ñå=Ýí+ìÍdù~Qo÷nØßnì~'üÏòË1æìrÄ^]zŸá"'þ S¶÷Ä£±ÉYÚçÄ(½ªÑ(ãÉöa‰ì‚.O¼N¨#"|„<Ÿë y“l4›¡¯»ƒÂQÛþ½ï¼wNµ¾AÁ|ÚvwKcv EòEª²Ò-õ¹Œþ8„-ظË}LìÄ ?A9Pðq*g,0®+¡è2NÉ?ùù}еL¨ÙãGKŸ¡þS$m"æì³ÊÐz‰¢©E(ÝRkûJH¶#éÛØ…b·­>—––#&[qÜë$s'>ñw£k?xÿl,û2þæ¶õÍ€åÍ+»6«A×öéô‘üŒ¹‹tÃÎqgy cMfšÆñeÂmQbâ8—àM_áå'i‡ÙošK*D¸H÷}ÅÃTù™XÝžzû¯î]·?>ÚòÈh_%Z”fN@l¼Ž=?Ò¡/|Ö–8*NA`Z|²+”6FustÛ}ÿNÛ\SJ~–žnËó‹ÔKáš!Sc©s]7äà ¸UL/ò—û{]¢/>!â¥nÂl·@´ôŒœ=…yaA~-+ã-…ÖÁ×¹üÁ×€uàÚ±¦¸ìŒ_js³6²ÿD±˜KÔvŸú€ÈÀ¼Îó›´‚Äde>¨(Ò©V¹›)²ÑõÜ$ÉÛý]¼ç[Ãé$B<x¾Oo;üTâú=ï¥Ý§9_UzŸÇÓ¨²TØLù¼ÈãˆÙ.‰Õ†yËlLQ—(Ýd`˜‡ò”^O2 ]4W‚ºp½ ¾6A· ÒlœætÖÚeÝCp8¶£èíæKò¡Ð+R–P¡[—FéW+ªtÊ\HO\w©ó ÏKžbï*¾Âèiwþd®æÿBÓbµ¦5 ÔÆnÑ$)Þ`¿Ä8_«þ†ÆP j‚(ÔÁÓÿ™>BW±Qs\ üªê^>V XïÓ5}sÆÖ:©÷mÞíøåEgÃ8[2À(ë36B#Û/šJ;±¾?}çƒî[’™à …¡×Ê«%C4"¸‘ß?ie>xÒTžY¢GýÀtžæ]3·!+jªZÆŒ°‚qî‡/ÊluuDdhN„“ƒâŠß$­ðS³Sö° нðqõ }ï_Ð#?S§†UÄhQ6cßZÜ%Âv?{¦ÏÚ#€›0«WkÞ0Ÿô$›©¿\‹ETApev´ˆøtìýí݆e¢/Cd×å% ³²c¢òwXÖ£‹^ŽØüíE]èØGÍ•2áÒž2TBêô–Wø5:µy ß/. ™ÑÝP¬U¿=VlYUãà » î¬-Ë&ížíɬµÜSßoý¹ehZªÕ ½D]CKˆñ„þr·Gƒ“´jˆ¡HâRÑ­ãSÄ_F‰í’Ê'b.#†oÌäÇ`çôÑÿaw<ü•)ÇŽG;$Ç€;÷S«Hm[9z¤R¤LŠ*¸«gù»v|rXâ”u~\Z™«»ŠèXgKk¡FrMB!aÎ }@4r#‡ Ìàœ•‚_¥ig#`ÂÊphsøè뻂~²Å~_óÏNž!„`ÖÖÄŽ°<.I#¾>?ü9Õ .”6±‚"ëÕ­*êN*EŠªzÅQ·JŽ…ÏZJœeŒæ}ëxÂ$»s©{"†x•Ø»rð•Ëb |nËó¡-LP;jtZˆzèyÄCÛQiQ2Ó)f8â©{x ¾¸2¢OŠ©“’­˜W[æŽw÷V½âóÞàb Òl*ÛpÃ[ôÃHVzXb”ˆo¿õæ€Yçø½¼\÷ëÜVƒ#ÉØ¢É*#Ö‰ª"6ÊfEcN2e[’‚ÄöSbÈ ¤mNóÏNêg†'n§»¡Sð£Õ7…m ŒíTb”¹µ¾B‹ô{Ä4¤½Ñ­¥nk«ÿÌšúõ½²oWLÈ9q€Ÿ¥ÛòrE¾ UOé‰Jý¾!H8&¡u I¶áÚ»]X.߃þ{V.å²Ï4@n¸9ñöæ>¶td맨¨Ó™k¬àa“!B$âGõ±¬ïÃ)õ˜úîi³s¨&ë¬=øj|oÀýû=CR&ŠQÓ hã|XÙKÍnoúåÙÉÏY—$«¡Ë=ùç7JÖ¸h¾”u/ 0çbÈŒ_žÎë·ØY4¥Uýäh?]Óß;v; Ð@j¥\Æ/xÁبaÓÌxÅœÕy½é!±Ãc¶ÏåµúúÏöt×;e¡NÐ?»§ØÓÖS±K9»ìpöPlc÷mÞ=¾-ÀÓ™˜ÊE=€<¼/Žn³.à¤Í³û®ùjÃÍb2ƒÜ%U1¨÷5gUU¬ý0·¡E…PË%ĵ,§ÔÈxTqFl»›»Ú}öTÃZ  Mn’s7™Íl)¼À2^+Õ !,ŒŽÁM&{*ÅTC\„i#B«&ž\ü6Û½Ö²\Ö=E\‰éÎ™ê£Æ'ÖœY–×…úÅœGÿà([Ÿ¹ìX¡ažþºÊ¶¢‰1äÖv7«”Á¸¯ßSp™ïÏ%æüoU²á,rÁfd<íiôIës(I \¸¶ç0iM ¢ÿÄôR½DûNTiƒ öMšã³àF7;á9ÚÏb¯êíès r¶C}eéö›‡M†) GI¦³6$â Ùûs«3賩þÍ{G5™¢W.½œ?vòPÂD ÃK¸ªð.?Œsj½œ†úÄ¥Šsê&b¥ïiO¥’ª¶ ‘ eÌ÷NYwÅd~ $^¢Øá>Íp&lS#4œ¿mr÷VÀ’”klÜA#:TiÁ ÛÝcÞ”&ÞÄÌÒ—¥wêAaçÜ¢L<&¢åE·Sbú籬v‹Å!‡A™,:~ÄøŽ)®Å‰ˆpܸP·bX•®w‰vp‚UÅ¡4än}<ì–ò V³ŠºúMˆUP a/»ÑCº¿ðÄGhBâ€pÿ¸&vêÇø‰Ù[KmèpîÃúò®_JÍ—ByõOçUj_¤å5åúáîXÝ„51“.iuWÏ'ˆŒvzXŽÊr­r xpSËbÍ\8g¿´n÷¾CιÂG!RO׌üƒi}üj|êÞÏé|±ÚÁGA?T˜Ô7ž¿³Çh%œ¡JÞ;bI^Xìø<ÿ#v®Øg;9o‚‘ÈWg%çUµy·Ã|Û: u*D<ó Äøa¤ÅE9(ŒÓÇP¼x _ã¹È=¹µ [ŠÔ¸F|i¸‘ŽDŒ6ÓúGŸU•ÐiåB¯¸µ#?§x‹–Š=Ôoר†Z§@/ýdD!HïOJ|ëm™ªÈÆSçŸX¤ƒÖ4Â>Ü<ŸùaA·VgÞ›h/”¼C«~!´5VLJ7¸ŒåŽÊ*¾%³ϱÐÿ^Ê‚b›÷±Y%Aÿ´xÞ´ÌZRæ‡ü)8`ËvRáh,¯¥€îômeoõ« áfaú:ïšfí)&ÐÅpÍ7U»¡¦×ž†oü«&ͤ»>tóRà4˜ù(¾¿Ò®2®4ýÁS¹íŠ^ »SâhÑ Î‚ÉF¤«kMÚêæð½ŽÆW˜ç}¨±|÷…k7¡o™àˆ„ßD/Ó¦ðžrPºXŠûÏt/9JJQb)˪ëeË&Õ^ÙâtjN°Ö(‘ë 5ßÜrò²¯cÀIÿž–ô ¼½š2£óÞØÜs‡Ö†OˆŒŽ{ºyÕMæß£LoÄ!GØœó9e[ÇæSx‘HÿÞÝ¥~Ÿ¯âO>Ÿá»W/ÑÅB’3èâ¯ãÒ8~FM8ù9GÖâÍÚ¥¹~ÓpZMWŠIM¦o^Órk¸í÷Ê ŒëG0Å ‡ ĉXz×À“o¥èc "Ýç{ö1GêsXa I9¼,œúW|)ƒ‰ºz¹¾†z®n4”;`ô“âPF`F8ϼZQ¹D§ˆ# °¾XDáÅiؼ\†%±Å;fªæ“û_v¹u"‰yå’|°)NîäbÊK¢'¯¶Œ¾MäÖ4á‚+#ÕÂÅdÔk¼ÿd(µ&/p#ËZ60¡hÚ(xä&’–™7R9ŒüH_¯%2 ÷äR7b£ëh©ÀæãRœ„&±2|+Êjó¶‹ê '›UJ-Öm×;Y¬Zß¾ÑRØÎG*›>û!ÓU'ÙϬ—aÈ¿|ïË„Gr že¹R…`¹±ñ¥_Éjcä!cäÆTɇ–\ãXó‘E#õ<M(ð‚t:+Á¦–µö8‘‡ $š4Ï÷tclJï¡Ðà´éÚÁ K¨Ú„ø÷¼…ŒüöyçÒHäuzM/nyû<ßmZ,i9mÆ,0Šëߨ̛l¾gÂImŸõz]j’à' ¦`ˆ Š÷‚ÿ6‹÷eÏ©±¸Bøª­e|üzºÊÒõÌBKµÑ×¹‘;\tXñ‡–Wß Êó±Œ`‘Ý£ã†z¾‰J}¯„½l¾ Â3–{]\‘Wƒ‘m1~Ónõðyê«¥®€²];É©Ae$Ù¸Õ ‹Yî® `tÜñhYVI<½QÒùûHÊr,¥ëã„Õ˜ CÛÍÀ=ÎOì–Êé”dY‰£K”›nMùøœbD[þe’É›öýX¶*‚Rá·ˆõ‘ãPp;.2ü±„Fiòä.'2Šãˆó: eü†ŠêoKü&ì¥øHh²)ꡱºã[öêß7Q_8ßU…¬ÛÅ¥7Üæ”êb'Ì †mbî!‹pÔ}jY¶˜jy×Î`ývté|Ä›ÛC¶G÷I´ô¯(ÒˆÏz†ÁDÙüÜ?¡#ÎõIAÆP“x Yžü¦H&ÉË‘µ¦;$ÚîýÍ; ™õ|™÷ÕHá IVþ{2…,h‡â‘“½“Ö:ûa^ j 0šdÙP®©yµ…é¬lÌ.oÈ•t%[U„ü‰Œ×<Íö#Fw7ŒÆ~&ÉçãnÙíHhBYwæ¸ÕNE¬DYE{,bL_€>p;ÃPÙÈØÃÉ+ý0ÛO®»«9^IHâ¹Ä;<,üŸá*- ²¹è¡ïÄ´Íi—ãj_km6mEkšÉ£†å ñ`A7¬þzˆ°\ü²sÌ´Ã;ô;¾e'ôd)„ ë,ð~tc¾ðÚô¼PãRsø¹„? Cãt«šR¦‘OØ¢<'¥ ëÕnº8„‚Ì¿£ÃGß(LqϹ¢I/ ›¸iùò4hd¨G,b¿¦Ý•Ø ûôò•„€ Ä6÷ŽÊ1AlÙCrj'8›ˆÅÁƶ9[8yy*憗zÈð­Í~_€CKœãbî²,¸i˜»ù¨$f–”^5×÷}‚9Ú^+"ÚuÓ“-§y嬼ÞCÔ§0~x,:_´¡c>·Âö£µ‡ð1vƒKßk2x&µUö`çcõoƒ7\?Jņj;m¿Ÿ ã¢Þó7Ú¾Õd‘B_2XlÏ2s÷£TÌ`} höHF„ò‡âYŽbm}ù¾„¥Œ„IÛœ§÷­afå¤b+XÈ$‹ŠTÆ‘ŸXǼ²&Š8 zíŒÀŸW— #êÖˆé6øŒ ùÙË8ˆÍò(Á¶€¡¤úàØf†m©ŽÛÊÉœ¿„O·yÝ®ªoap¸7; ¿·w÷i£ó’Îû4F®}¨#熆ÂQShWëSÊœŽDúVæDò “rŒƒDé-NUh鉑Ö,Ý{hß]¾mˆs IÕûÛ¢ 8E‰R_.쨟ªi•ècÑP2©Ãeˆ=u!?1ëïœÏúõÖÄC|G•ß]jwž}ÕàhôßfCé lírC`ãgÊwn/‹„6@%~`½ÆwWWª¯ÏÿŠ[Û㔚õkøbÌúaù4¢£“ÝwÈ­aÆå YqÈx#Í,¸eð¥%RMÊÿA÷Îæ]œ“žc¢ Šž£ó攎jíç³,–¾p‡:QÌÙÉ#–Ký÷3YØý;«£ çR‘S=xu«›¡8'FûëìAbû€PŽ\\Fà÷†ò“ÃÈË´úqÔI‘X«±}y½ýbr=„-p#kûaTü©´{o,ÔK¬íŠ…eNqÞ¡gή=ÑÁ¡öÞR½qarÌh¢×#ªŸy¾˜\ b9b¯Ãb¿.¹ì|ßË:Ê"y ÝoSD¬¤º<Ð(Ç“>$^D‡éÛ¿r±×(í°ÕÆ;2ê¬K=ONw¬}1“™ÎzìÍ‚VˆöÛ{õ–•NcŸ›Õ~³¼îTMs[Ð~HЍ°\sd”}iLÓ!`'…¢=>âõÔ!{{[GË“à÷yk„®ñ«—K–…1ÿF>l8Õ´Ù›­t‘¼Çð`ðÎVøx+{…SKaR šŠeέÅ_ÏiZ'üTwH»Ò—½û 1‘é2Ò5uRvJ[S‹'®Á «“¥èÜ$ ìgZöå¡;ÒÆe«E1IÖ|ÛÿâúiËl‚äœyV#žq ›³çºðôùl15¡âÚWpš"_ã¡ø*ùhˆaW7°)ºÍWÇ)\pÕ¡¹þ…šÙA.ö”K^ãxöeÓŠ>dO/Y]BÚ»øÐqô j¹8¿†a¯\—Ó¶ ¿ÄEfÄ“ßõBؤǪS(0s›EŸPr™œûª/ýË㢞¸Ï¹eï¨UŒÚzI&ªcíº3Ž]¬ÈknúV 9~˜FC÷NWM­éÅSìd©rN†Àæç†è ïE<"—k‘Ëz~cx£ò¾g4`º@· Ú5Á”õ8H…mždDVK—™’W¢l’ôã|^þý¡è Ê.…³àçÚk,„W“Eé¯õ³Eüý õ?iÆ$[¡yÍ:ÑŽ¶µ:Šfò]Y·ÞW<=Aôzž‡Nu¸ô¢Hó@µÍÛ2¯·½tµËUË6žðÙB™M¹³9€!}å3&±Úš…gƒ»•€ç w^«½Ft® Õ@–Rž2Ó3óã,Â.bÁ¼>¾$±…›†%Ôшd޳?7ßEß1þðÈí5;Y Ñαk­R­™Ýw?mÆôUá)§Ô1ÞøS«XIwa{QWj¾N†QcQTž`ªx+ž‘€£¨©Üý­m2ÄÚJÖp“CÂCK~Ïgþ¾ á!oì7ïÆc})†ä}.ÖI¬>y(À÷q³,{éí-) !hjR\åuóÁ=àÅÃÌ¿c{I‡L ÔÎ~öVL9uA @ä b(÷(œ%—R˜4E¹ŸbFÌfìß# ÆP :¸¢ »íÑ<Ÿ´D&'™% ? ö18º-¿YáœgŸóê‡ñæßÒK›¤Ý¶ÐMC“ðŠ_Sì²í¥o³èóÖ¢µcËÑ’]Í“Æ3©yí7‘:þ»ÿ"£ˆûÁfôƒt‡(òû¦ YB·]3e™M,ôorï7M3´øÂݹ »µô\RB×v9©R'—ëÛ&óR¡ëzЇ_?)´nÁÊU¾gì×íÐÖßö¡ kÖÄV^ó¤Wı–Øâ›[ëú×»rϧØêuAŸ`¥úþ‚í³IÜÖâ,F„ñ†)º¢*|$+w•dÏ™Áy¤pIŠRµ/ÑL`œô4ë|R™âé’yì*éJê9s¯;ÎÄ¡Èh&öE¾ÒÖSÜò(Œ³Ù¬N<ÖVeM %z˜½‰Wr{|qËñV'|Ò æ6\…”Ú`»žf¦RÄXÃð¬0œ€C)f ö—úH(ñf…Z‘‰¦†ç¸Â(ÿõÜd \: ñ÷³_cÒ…ÏŠaÚ¸'{«÷†gëŒe§ÇõƒrŒ›8‰ÃrÛfj´Þ´e}ÃPIËÐAêšÇIæ SsöP¯z5ù¹iÏLÉ>ƒy&¹62í§E…bÖà ,ivN~p¸>زïÈF¬}ˉßð|4¼ÁŒ—4âÕ­jt¹íBÿ"’<Ø¿~TøHdÓš5=“[øÔÜ/ºû­ÔÇf%2(õVåÃ.ŸÄmT']ß,k§³‡Dr¾£Ê,†ÕµuPìVч«¦™¨÷¸LêŒYK|Ó4h}yBsÞì,­ z±D9/yæa~¢ekNZF‘¸Œ7µÞxMÌØQR†é%mÊ›pFãád𴤣ߓ¿Ô°ž%mìöbFð™‡æ¶P'¯¢_ÈdÃÖ!,0¡Z¿ÎóË&[DRèÞ¼tœÌ—ŸÊŽ=ÚJ“óª0Ur2µFV×'Æ7h“˜»ÒŒ,ƒ­æø(p՘Ĩ0º]Åæ!ø4ã˜M  skWM޽Qöšt‹½øý†Ä‰»pÈç…y{eöd$ŒôHÏ‹Em¨Ñr¶õ#Ñ:¬®Çùjn(4ÇlPÄL¶¥ÓµP6íÈC[£ö…þJÁµšé0¹d…îÀ9m)å% Ür‹ö‘ùŠ~Ëz~½ [µÿó±ÒsLQ' ¤ïr'õ¨øãÔ±f<·åÉNAÂÖDq4Zþ$ð2Rx@xÿòdF,ÄF¢~í4ç¸ ‘?b)û€9¸~µïÛï%,=³­öS[tô…?8ËÙí¼„p)½ßû¯³»FUÄHvr?F>†7cR|‡sÇŠù¹ËQð6à~Ò AÃÐÿžˆSâ³t=×›µ”¸·b„ÍJ8ù.sòO˜^ÆLcŸîÒ{™$£´6–êo¿hÎÄ@±`‰¬¨mDÿµ¸à}ßw1¾Æa>Á5q ÿf½1Ñ÷rƒÐ ærù‡ºs¿±M)ãÞyù(±H¦²7Úç@­ØÃ>èÞt•fÁ‡û )~ÞJ:GEUN,S8¯EZ©1.}¼ EgØB1F‡³3qg…ª{a§CÑ^¢x(¹w €†æ72WTDQŒ÷P°Ésö˜uÊdûŸ"ðZî8ïŽÐ•è£ð«®BЧ4‘ IÙc¢% íV¹Î°Á6ÉŽë>¾K§škØe—¿?q+½IÕéòòÚÔÓª;piЧ°%®6á)²e53–;ò(A;;•ˆán·þF2-[XG’Õ!Ì)½ 4"ð[w5Üž„eñÁ’¥ö‰‰ ËÔÝ ©|¥ºÂ¶¯ ]Œºy=ë/1zp“]^ãHK¬dÊœ¼ç¬‡XuŒ×ɵ¸ŸfVÜà6X&@Anð¼ð{õ°"b} N¥¾Ýû£HÁ©Ù\KUH|wèŽÖœÅ£Ra$1O,6Ç·’’°h¥îߟW7-¬‰q'D“ãZ[>äƒ »ÐQÛ0á™ÌöžÈÜ0òø–ý‡ó)j]%D)š.߇QtÂi3}²ä¾rÀsZ†ø…¯8Á˨`y¦»&J?¦¦ÏŸ¼z…:Å‘hw&Õ¡ ¥.Ãúξá@¢¶oÏ>w'Z ‰ZK<»Dú>Ã÷·4 ª€Á¿Ÿä½xÌC²Íšûª#«zìåjYwE{²å¿µÝ{³€,µK.UÆ}L1F„õfóAȬû½ÊT¶26߈¢Š6"b2£í-â@œ›¸8¶,ƒÜYo ðãÍÕLu¸†BƒtôáªßÃ>ŒbujÖ¹rÿ2ð>"DWBé‘n©FÚþƒŒm,Zç$¡íf¨¸,Â4 `Ñ1´CÊÕâ›À‘MÞM!’Ñ[`qn¢_¢ýû€ª„¸¤„g·7S…<É࣠8®ùÔ9BdWôÕ—K±Å|nœâòŸZ>C Å0`Šöšô\”àÔQz/2þ+ë¡/r£!/ ~òüæí bÙÕú-Cñzô¥­eM®n‡(^ä†lÕL-6~âÔ(S_Qà»ÌV™ü5ÅÊ>úÚUd7ýœÈSÇbð(¾„„—EN˜õç_^úèñ8±Þ㎨רN{^"Úg('LP2·¥œÚ÷øÂ¨ž8yÌBDÉ:S#¶ôa£’ŒØaÃo]‚Ü2—YgNáŠÆ.AÇøkÇïÂ9tœfF­úÁŠm™ŽÆëan×xQ Ÿ^ÈŸ$)\—ìY¢ô‘™é þ[)T´Ÿ©§î¤$‚þÄ J¢åÀ!öòÞC]x3 Ö"Ù<¼Ž'8:IýEÚtµÍeJ*þÛÒ ”Ýór{ŽÈzÎ7©R¢7òŽ„A‡,¶f[_LîdO¥Ô,&-|,l7ÊHOޤID2?žXtpVÝ.¡Z&\L¿ÕóöxÆZ¶ðÎâî>¾HwDô,港 Ž°EJ£25.ÓX` <:1Ú 2Ñí«íX:CR^&ÄWeSýku¢¦¨v,Ž÷g:iÛýÎØI“^9Û,òù¹j|–ã£u_ˆ¥æ¿¿q/a¸¹,=#:ͺö,©îi”!ˆŒz!NÎ4·®ä&Ñ;_=–©Ù„ö¦öeÊl D_k‘±3ZÙùê/ɉ‚Ê¡>}ìef3e ç ”Ï„‡QK“Ò;àÇÚçgê–ÒóBHÆSÅ×TR¦˲ʤ Œâþð (Haà|6,øÆñ“õ{ýý‡jVãŸàÄb7½!-Z|·¦?‰ØH¨c¯÷"<’}¯õWææ¿ËŠ„,}è´Ãb¾k˜:'>æ»Aê‰#~ ¸&î øJ6³3KæCÉâàMyŸ.ÝD¸wl+•x ÆI15ŸZ·³¢† йòg2ø‘Ñžÿjg΃*û¢º"—ãþ~Ä×Hf§ák 3¢ªX‰.’Ÿ`íªø^ÀV>€,²–Ôpd®¡)~ZwNð†º&øçÚ¤uxé3JH%…ѾÁÌ ­•»DìœdB #F¤7ÿÙÇßÏÃŒMà=p#3òöE€É~‹Å2©-— %,Ã!·¥©†ÔìÏ1«õŒØNnØ$×HÍ]§}qc·£)R£žÅ„¡vÕ¸hüBrCš~ìe|Ë.ÆTL‡ŸÌÄ¿ôIP ÿ\X3 x9|ÂòSDF«UgÚ§“~ß¹_¼v9ì:‹7¨ÏÚ>ÐúòsÞXަ1 B?AÔ¼ï9±P©m©^‘…oóP¼ë·|˜´Å&ünÇA‰Ï!bï…È1Êp‘;³¡sRÈ@´/(% ÏžMìè}\üiKbr<«ÁÕ<ýññá4¸¤bŸ5@ýcTã‹ê¼ø‘. *ªnœ|ÿ’Œ°œZc–ò¥¥÷µ`Œ`ßú”5KQL1Ò&ŸDqW01Ñ‹. K6õ§]¾ «W¯J’ÉY,6½)ˆÂæ^DÇè¡.]ÛÊúÅõ­¬2á&òâL™|þm §$ÖqÚwsZHs†4TßTU^-¡²ºƒLôÛQ¤áŸî²SùùY};Ò)¦Í{[ª?còNx%½+ãÞÉ®=-©L“âÚîÕwSšf)ÃL'ŒäV X~ÂXôÃ^{_FÅc_²Csÿ%éê–¬R µô:¾ôÝEû –÷NÞìÿæ°Ù3ÁýÍ*¸Ï[n¡¿¼ÒǬRÖ$JÏz½äö.e²fNßaë:ntX!>1O¥µÂhå³Äè&MxÐý &&aI½¸Æº,²jWZQšKê°íqü`UêÎ êD3óu¥DGîÇ…ghÃHídtßcŠpSCw,\§»Mè×@nû¾/ç,ö¡ËÆ“‹âÛ¢yô{]/Õ¼\¤ÖÈPQ5b8¬ã1刞t÷ŸšœT Fm×-úÁP1v9ŒåG¹ƒ…Ò2H¡«1\é[ý:÷˜…†¦nÏÑ~”LÕ\7.H¯ $åµí¹×"²`‘Vˆ *ûÊ ½‹&3ù|ª?ñ9:v³¿t¥M"‡æ8;ç¢Ä@·"8GPXîà2‘i@ŽÔR)óbثйºÀ¯ùÕ"ÆoäÊGÝøp0þaWþ‘J.ûÛÎŒƒEíGcÖ]'¡õ’¶Ã” ï–k%ÔlkDWû“]Uâka×Óu³C?ÍÞ—pwÜħðsŠª[5}T øõPîí¤"">ŠÏ—|O:P‘f›ÒtË:ïœ2§l [ý©ç ƒÑåKŠ~TÛ˜²óóMPÎŵ1êM¨Çgò#xRçd›)‹=r96•&\B½S?®Š!;ÁG_Ó©¤‚9±-?Õ¸~;ßLÉ[°— Ë6‡,zÚKl(!5øT–ºJ·CK,çã•UÇÖæiX_ù懬jf°½q­ü´umÁ`ëØþ.†©î„!²ƒêÛ|Ý|rT錓b=g¨MtÓs²z.¾Eæ>¥²—DR2@{5A–Rãº>„ò)Šüý¼û~êwy¼PR¹rCèï5UDÓý$ŒÒó?p| ]X¾î”øa)xQõàÜ¡>À#|L%Ý7ëó)@8'€'^öX°ùb‰f"Cî”–œbŠ}^|dFóŸ²`„j–¾F,§$ ý†EÖÒêw÷µ'ûtý[|È //‹C˜<Óg*°öÀ¬WVVô1§<ŠÏȸaž¼ =9áöÉ!:oó£Öˆ”sœ»£ øQÛОhðq€vì‚Àša#ݳ§ êûÚ* âÄ›åhÅòpS5Ò 50ØïqSj)Ck®§¦FUU¨‹“ïèà ˜"ÝJú|‚ñ£ÉÌ=Õ<Ëk~Ä/’ßÃhzöab.à+.ìtk¦>7U^¦Äc1w¤7ßþ„uŸ»µF/) VåVÈ:¬'¬ò=aÉÓ#òvÖÎß”f´¶…¼ÆžØ9Ö0fò[wÅlù UЉ\½ŒM‡K¤À|΋þFêÑ/Å”SŽúlÉѯ‹…X; êp^njŠáç2í!û« 9†3·¨Kß$³£Y_G9—¡ð›~>þ”ûgS¨0îûO`ü+Oè—˜[ºBÁ| Rª¨¶úVmKùn˜9Ì®5"‰b{ã$øQ7üE5û6jÉ~x¦X†'¼b3`\W÷LãòÒ›^EkYrpÂ9HhD#ö)%ÜoNÌÈ žøRtÈ’äý¤æ‚òBÓ(0}²<øïB[•¸¾“#Ò¢vW]«1wfº!)“}@ÃÂ?ýH½ŒnC 7™¡§]òØLÔ”y Ñšóü#c5©ôf„?€yžú¢¿•Y{! ôëÔë üQñDMj¼ ãå~O gt-ýMü´B¤bTǪ²ë˜xûûä½áÖþ¯ïîÐGyÝÐw­V\^\´7jeD\ù‡ŽÀJñn%¿c^WçÉÆ¨7Þ¤«ƒÂÊFX˜=ç?b—“piÁ£Ã;ñ*¹éï¸N4¿’ý?¬"SÝ¥BÉy0µÇñö@Ñ„6äZgM\m‚U«A?=;»$’ÐŽY´TG íoXgZ:írÓ6›X—o”CL·/†XTýaó€j0ý‚E ð7'õœ¨ìÈIÑCɱº¶Ò¢E{ì}&*>”$ψ”U°–ýT¡:©~ü‘`ùOþ1>,ˆr¡&|®cF×\[Mãl¥»ºëŽ©wn{ç9zÃ%gݔּ“·ºmJ:kH±ͼ];îjŸÿÅÇ*Êîo«9¹Yµµ=–™qËÝèíuò±ÏûAI¢¸B|“žµdR¬!Ö-´“ˆÄú]múʪ˜sPV]@u&CkÁn?˜yaˆ¿Ò5 TÕ…wš8í{§1îTé²ËK‘Ä:­Œ÷(`(µë‰ ‹le‰¸¼gB…‡_I®à·—Y…é Žæ[V¯W—(D—œuÆ¢„ a¨ù+ÕuÕÈ](JpkÉÚpô·$–À"»”*ÿ6ÿn®çjŸVcwÒÞ–ÂðÖ ·‡‹Ÿ ¸p2>?/ÂÕˆÌ)“€° QFÊŒÎu8.rAži(‡`¼»$±¸•ÇÕ71x@B$Š>M2ŒØ% ÀEýŒo`I€rî¦ ú6k€Ÿ'f Æ kšÌŒ«eâ™^»†€N n° ÄxÛelÞ¯jQ…<¸²F´oh¶TñGzñƬÜ*é’Q´{ÊJ'F†u–VŒ0ã,y`¨ðƽÞ3cñ¯=øt|óÚ$I•Žíny6)¯îÐ=~Èx¥½4^rOnIÃã·¹ªzqÍ2Ù©ƒ2oO?ŠPƒhà\ AÉ «ïÇ9#2î¢û™ß9WnûF‘ÇS‡·~Ì[®Rràl•´Qw/ï2¦`Ê m§’Î~‹…Å‹©½{ÝÛ©CÌŠFމ¤!ë•pêgˆn 77ØØ7wà@±æÆG‡ž%އþ¤7ö©¿êÚôïËþMDáâÌCö¯¢>d *pG¡ùøŽ³¾˜^àÁªš]ÝiM4E'Ï;·ŒÊ¶¯Ý?C‚úÎÈ1þÐs‹«å¡MZWë©Òmf†µ‡öM ï•¥ÔKôÐÑRŸ^ub–­Þ’ÞdI.DÒ9½fž šrÆÚ/|‚F :vÕèα÷û»ÒËøMùeY¶)¼ ™9­@ª ·a«w°zúÈé0;]€•Çšòw醧ø‚ç67“I7š?±myƒé(åiÍòæ'ù÷ðÉøö2hi>R-ñXÁÏHÚ« ¼ÃÅÅõ­jÝœË:ºÚÕÕ¤:Ù{-Vø’[O«øfW)â$­‰r–.õö3Ûú úkHí=N†v)èEÇ>äû&71#{¨Ô¡„ýøŠiÛ˜¤f§‰4ÕߨÆvÎA$ÏïZ[ê±bÀÆÐФ½oÏ£%=D™ùEeÐB}(+ö«"ˆ¬÷L~ßY †š®Øô×íêƒÞïìûÔä ãÖDÔ¥ÈP@ƶ£/b–½A’âä©U,’‹Iö¤ÄÀ§’ K‰_'y°dMó†Ú0Nar:9DƒËv’†cRàÇ¿/v °“,sAØJõ´"Gº$?¢È¦.Ú/ûÙñÌ0§[“!5Øð“7n$©%†QŽg±#Z¥ñ|ñªá=O"òC7 vŒv™s&•åwèžtѾHe…Þ˜Ÿ¹ªœ @ÐÚ`@ÿ£ÚP%ŒúckÄÂœŠ °€6Ò/z¹š’!Ýø,¥:€äÌ6^×ÏeI¿ã³oãÿ̼#¶Ò·(l¢ò 7xÓà„à6ŠÚ~Ñw/|ò ZôÅž°VHR–Ö óð!@­“ÕAÈóÊ8E¢×ŠRÀÞx;o™(Y ‘ój-)š ..a«ýx:—Ó:†¨YAÌïZLtõÑM§Üˆž~fœ¬‹‹Š×ë݃Ç:ŠJ+A¿%Šë;ÑÅ&x Qô[1»Ëj>‡ažÇ´4Íêï˜Ã©‘^"=;žmÚÞ|…–U ©¸}O·e½Y~ TöšÈE²ÀÅ I]ÿ³:yÁ’`ß—z»Lí0‰W!“>ŸI«õ;„ô/I3ïÿ·ëC|ö9®‚:ÿÍ触ÞΉy¬„’<„©t|Q Ž Üg"#8¯†(?ýî„È ;²· h¥6¢Ã8†?ë.TU77ÆnP@è„1ßK޶Ya¼¯+Jðj¾#]/W3ÒC"ºÿ™ Kø|`a.޼Ö8˜þéѹ`Œ¿¯XÿP¡2fÐVˆ¦•Ÿ×ßg$=KQeØñšÍEZ ±®½–—u¿äí‡LÞ®m”ÑšñmZ@ï?xMÎב|8Œg™uµjSø?/€¨öÈ…|÷©7ùœ ³izªº;m½Ù\‹ûŒK€É "VDŽ!ŸPöÓ}=J*-¤d[iŒÇJ€oœºõ©‰Z §Wm3`S=Z? £›)<ˆð1äã(j€D5Ô›jKˆ>}~Ì|¡säеf„ÿþçLZHy¾ŒZYw¦Ì*›âfkÀtcXçKsýÆIßÔÎMSÊ pÐù|Ý.DyΘŒø"‡ <‘5F¾E]é„HM6hµxº?’Þ6Ÿ»ÔÖËh1˶£;hÙ<Ƭ5ë.I;¶~ÙpwUŠwI:G FVòÉMpRp3Cï8ï·¡`šöI!Œë§û<»P—BÔF~%†$n±WÏt–ž(™ç¡/zÝ ¯†ý ð!mB§>ÿm»hÖª®3Îkƒq’Xˆ wƒ”(¡@¸Y@¯N]·Àú@î®5.¿oq¥ ‚ðÚÍ–ªUÈâŒúWH`W:½Í™¦D ÞLË7¿UÑ cÔxÛÙÑù ó;3§ï¶¤Ž«÷$2=YÀÌ\8tÔCÏ—¼3 ­¥y-þEHdst°å|¬½&Á¡q5r‘™w¤­êÄÀͯ6eÜ_j¹-§7B"Þ³YúQÄ‚"z_JQ3q&ËLøIky°4–+böúLd—³ê¬Ü ÷à&~@¶EÚ—Û7TÆ ƒ˜ÓÉ…š¥…zLÙèû9ÝÛG¨yÏÞW™ƒ‹þmœàfó;÷»¦H^£P3¯T/pY/?òc‰û€(ó&§Ú„:£Ûî¾ãvœV@s‡ÏT7ò.”O§Ôf-øƒ•r³-7ÂÛÂܸã+ª,^c8„¡<Ö¼>êQ$DÚ<ñŠ‚Sàűj–°$êb`l 4;›&KP³{1ÇŽZiØ)îM•¥/ôþ! ‹˜ºÀ b¯Ø:¬¶[‰ÃÎôÛxñ'ûÇÂêàOEY+Qdž}°eê ejõ¶¥/åÒB×ýV×ÿü­Ót1:çbvìgÚÈ|ÒÙBÛjÈS"Ì)Ë@¢ÜÙ#ùn¹£ÍžËÝ0zt å.:¼*i»&™þº†;\âœ/Ù Ùb:D—/4/‚;Šn=9oø&ºoú­jå‡\Oõäl H$…}š#²J<{x±×ÂßGõʘ-ýã"MAÀpwÄ•íÖgoÉýIóQUÒ¼®š1äN€R)Ôò3dË÷š§óõÙ4£2< í •íOÛH•¸I’K7B¾·'î†ü%]h,‚ìµGªìXwìåˆ2… „P+ùù~¾rê4  .<飕žP†ׄö{] l”ØÓÜô— O„ê–ËHÂ÷^mÛ¥:éæNÊþã×{µØl22`Aòs…ÍLƒåŸxzׇȸ~/ÈMžLàÕ:ŸR¸ŠŽ¦>0À}ØÞYKBƒ%“ï*BVªQ 4£(Hǰý‘¬‚s !Új&ÍÚR,ød­å|§òH]ñS¬^ Ås ­7™±vŽ%xp‘ù(#³è{Ï& /x¦ÜáIÚR¬Ce-5¼,À¬?ŽÊ3ÞÉñ+Iîy¿Ñf<—%¾ÑKÑj§n–ÚËa2õ†@ÐÑ3´mROj#bÝC˜Í1“Ú*@Y½/Ð5ê*_*ÞÙhTD‘·Ùø>G)œÐUž]£û¤ ä.Ãä*is¨‹5IX^”$]žÖ/¶ÃÔÑâO¯ÿGõ .z[0Ïzx…úÈh%Ðf=ÚÅÒp*y­Mc0)"ò÷¨F’2À4w÷Vä·dãœÐuo°Ä'd –2/…9E°2ûÝZy 9ïž*Mƒ‰æŽ”·g¾cïi`Æœ@îPF§P«ôÔN€ é'm{NáA.öøÀo°Îàº8>¨ôåu«Ëû§³ÿ&kÃÆnWtèüP(äý2ÂV¾òÒë«‚äÏ"@JûbÊ+±e‚ÈÞ2­?M°²³ îðî([£Iâ3–‡I[3H˜gó ͽù¹ÝêüœÞ¦ùgÕSž°¹¯vfynXM£ t+´0iÅÉ”rÒJ»=|üî#/‹CêÈsÛˆe¬<Ætýî^ül ¦vÒllŸávH«:XSžû–u;§ ÂÑ€¥ßv€°õÃQ€ÿ|ØÛáÑÑù²JÆL£tÒâBJ<¡ñXÞÔ”€É›2‘Ǹö†ö.2õ¯Éû$gâ*µ‹Dfæõ‡ÃbÃ0¦øüA«ç<Ø&@Kn¦°£· 9Ã`·ƒ£¶³Þ ¸Ú1¥"¹² Ñ|cBêÜZ5gmJ®^)j†Õþx (ü×/Q9z˜6ÄZ-*yd‰‘ÝunÿÀ\C ´Ëþ .:mì¶Ѭ‡¦¥WG£vZ~y©‹\§ŽÄà)<8[šb:ˆb(+è+Jc¯Ú2<Óš–‹È —z¿¥ÿÔYó0Nê׳ƒ"¶OÉÉøäù›òÀ–h¹¡l€ Á1ÅÛ©(êŽ/vLÔt¼‡0L¡ Ü¡÷vSk„Ñ„×õíÍrvÖ :<ö¡­z™OØûOeV1‚ŸÔeîo£]°#[ËaÜÍ 7}©Ò7Ñ3LþWžãr˜žT—‡”®[Rf•v4+™¶¤v¸¼n “¢3YSåÅH¦’eÕy®‡Ÿ•Ç”añ6Žü>ê‰1ûD×±.kÑzÉ;há ZÚ‹Ïô•”tg¥ÖÓ1$h6 ìâ›XdÙ¢ûJ™©èÊËSU‚3*ú­:;ôÕ,ã¶[áž¿¤Åÿ5²~Je!¼ùÕ®÷Ï~Šà}m’ÇÍs®—J/ÙðönÂŒ¸Ù­!”s0@²Jl2[\={"d÷wüðG¼6ÒíZÃJLˆ½­‘Iûì¦iÓ‚”é`>@Qº^0c¿ñ±&_DUhQ»ÉÐNEþ­ƒ Å!K‰íb™<Á™EF­ëïRàêGןѺ=®¸d…ä.Êü*-犴ÒòážÒŽ€ ¯ûõ“„9WÐï<ØU"Ô>yÕ ƒÓŒÝ->^€±‘ò]˜#ƒ ÿ¿:þìzUa.e$ojgÌŸ%aÿ`^_6-O áý­—ÕcºM¼Rá…5]êŸ €cäkMÕì»^”!¦6N_øºï¬+×H¿í\ ‘Fr;Z<Ò«‰N}Ì×öbä¼yhí´&À|LPÖ`Dèî¹5ß«ë0¶˜ÝCþŠãfž_†«lÖ˜(EQP&ûµJD emQçL €z»h™bͤ8d̓xÛȧPÈcÅ,´|Ó¡Hrf=riœ!¾®ã»˜ªiq,ü B좭osLÂgp hs`z"“D–$Üʱ'iPn8˜ž¼UÌ+ïO`A6eö“Ê“Úf¯“þ_öEâ"ä± òØ= h¥ìiÖ´M¦Á:ZdÈÒšû^= ‹ú7n97²Ç±cÈÿ“GlÌUç¢Ö{-盼ž­ÂJ"_Ü4—&ç¤AïžzÚ4Sׯj2¡*'v¡›ÊqТoMµì…åƒ 6s Ðo•¬_B|e@ô¡ÒXºÉ\„ôì'Ä´F¥–œ"œU®è"¨!`ËC/çzx³H]sèÙ×VÏçİ=í…+˜¬зÃÉðûf9ê‚"àÿÙí3lÛ5jÐznÜÈc)!µ]\Zä½´ ‰ãð¯jns³‹ézlhË eJ™*ˆúéÝ×p$‹ÓX›«`V‘<§çù£aé.‡Ë ÇŒM™o®r1ym.Uh{“™'w².YƒÄæ Í„N3gÞM¸êFøá@å–fàHÑ·R4Þ`]íò˜QA…Ñ1u%Ä( …H7<)˜¦ŒnÃÙW£q(å[±k«W»ÁšãéLµ²zae²c,XH—æ½,n|ÍVw­‹›[p!X#y‹+ÿúp~å°[%.«3|7ÁÄKºa«,LJ#q ¬³¤‡‹F|‚½E1ýñ³†jÐB×øÜzµ®¡J&ÁLÌBŸÒÒ¿¹»ðîÇ(ŸÕÀ/omÄsÉvFç ø›Xµ\d&½” à¾I*eçCe;q-T}j "=„—wÂL0 &¸Õ‡³§îÃo·—iõe"‹%»ûµšóã‹þ ›Ä ÂÊ ƒL_ "_6ʶ ç=#óîí®‚Ú –ëbÒßÛÂæ°õYc©àgF—ð™ç„±~ ³<9ÙY2_U8."êÊ6øñ¾‹çñš’‡ÿŒËö'ÿ•*í/Ž9°E8äméM;›éöÃ/6Q½ª(–  ÐìŽ àp¿odŽs2ÔjåUßšœímÖ«\ï}ÂÇ –ûE=ëô»›§òº=з¨{zZ誓õÒÍ Ë4¯ö,¦6>Ô›øgª8GÃGeƒ¢çݯnw€ð ¦‰ºÖÄ4 åzßÓŠ`&A¹\p%Qô¾µÜÁú¡hÇ·™ÁÿÁt¹¶©(Å«êükRJ;‹ò—ŸŒÍ¾‹xÐ!¨®è!˜ø¾8â HbŽÞ2Þsê*›"}Û,÷³¸VÎ ”òÂ? â(½|Sébîã*m<ªráòwÜßËéµ7@Ö÷ ^Ö½i×ÂP¹uŸÉ v‚JóÉœœÕ4eÚÈz¬_@­ßÙ¥¥óOü`¾5<“®8úYLNžûnPìù}!š8.—a[åqï3$ÇFXíÝ–Þä¹â­3×ý„{ª§ænj‡;{÷ÖÃTû­iJƒÑ(o©²g‚ {˜~z”§¯=©µÇHmkF¯¾è\ åÙñ(’mAm5a„ðní”Ô„A²–[¢Çi5"ã?·-{ÖïxT&ĉ÷n>z—4âÿ>¬âF<Gµß{Õ»¸ }Á§i Á¡þíÌmob3RcÝËf9á²Ok˜É[}CµM9ɼÊ)8Y“¶M‰3í„£ìV,Æ#ŸbLˆ:~  Yó{gíáš«Úe£Ì_ß’[$w§¿çS¶ášÕ³Õ¤ÿhoê—r‚§èžx“2;Šö/g«¾Hæð›·òFSÚö­CÑ ­UÙ2ê"tÊl]›®¹rN½ÉßtôÑèw\"—`ÿ×°éÊ¿‘î­=–¾3-¥À‡U:ä/MGm×DéK]E¹â=L¸£‚4°ÓI9PÏÙBÎy;I']ÄÜ#Üær°³á9…'zô¤Ahˆ«ß{gR+«úyïWÙ<9çÔÊÈ’e]Ðó¬.÷SYd´3ô]“gA„B¿¸òöI v­1ùL”-ÂÞfjî§J{ô_nWéD;×… ~tî…X‡þ£õ‘,¿LlÎøÂùøÁ›®ÿ×:ýê™›3†=/ù÷˜G[asB”‰úËÖxº»Ï³÷§dv•L¦ñד5Ãe<žé¾³–Éz$ v®Üò¾:Û ÕÙ¯†Ó½†]s.‡û…øH0lT‡>ö7¡Œºm:Fã¹ì0õ/Ü©ˆ¯‹Ó©œM¸Ÿ¼œÓ¥†ŒïýfùÔ_oà47ìéƒO)/Ç•{´ªF24+ecª¾G X ‚·;?Ä…Ãl©n çŸp‹gÄTÁ*µíŠ,z0?糈hû «ÿ04‚‡ÉÃú|H¨ À æ2|>æ«ÀL}9^iÄ2U¬M?7 ¥p¦~ö5@±Ê×ÿ´%–œ‘2UÄ5 Ðã‚`@¾O-©@~®öÑ(ù™6ÂØaÚ€Êd) ÐDÍïÄ5#ë¦Ö½ÿÔ‘.šú=øõj•nü#”BÌå¬þö¡Ï¿¤²xFP†„ý=!t{ewüæ$ï$4Ö.¤p‡”®KÈÆ•°š'w h‡&6çϨ(JÚäÇ|EÄì&¤¼2áj‚—“NÄålõp \aF¶^yÝ™yÒ†P)ƒTua$xjä×ÚO­;u ª7#Ÿ˜R=>7|tÇú.y/òˇ8ÄñÙw„ w§tMZ¨IUÚyiš¦äCá¾Û"³PôšÍ…&{íŒí}œæ<-á€.þÏ,ShP³6ŒIEúxçɨ"]Á\wµ9BŒL¹%Bòϲs«‹ëðæõ—ÝG2ÁÞSó¾pÇÐéBÚ(øÚú°Õ»Ò.t‹Ú~žGç=¶+ˉ«s¬î5¸(›kÙ\1oak'ãbƒÏWæMTšDšæL[õcy´lLC›t§Ûw×%ŠØÁ°ÉÈ‚[^(©ñ±TúÕuI-µ¿ƒ¬ã”¶¥'ðvŽ®nM¡•šG½Ç­Ê-‘Ç×+´ÉGÈøýz-ô²Ǻi¦A endstream endobj 1530 0 obj << /Length1 1629 /Length2 8795 /Length3 0 /Length 9860 /Filter /FlateDecode >> stream xÚ·TœÙ-Œ< Ú÷ÆÝݼÆénw×àwww ®A‚ \‚„GfæÎ̽ÿ¿Ö{«×êþvÕ®ªSçì:ëkzMv)+¨X qeçâ dÔ´¹¸@ ÈIO¯kçêþËŒI¯†»ØA!Âÿ"ÈÀÁ ×G›,Èõ‘§…”Ý\<.~a.a À  ý‡… dAîvV5€2vÁ¤—¼àv6¶®eþó`²dp °ýrÃí,A€ÈÕìôXÑäÐZÚ]½þ+“¨­«+L˜“ÓÃÃääÂ…Ûˆ3³<ì\mÚ`0ÜløÝ0@äþ³3Lz€®­ËŸv¨µ«<í,Á—Ç7ˆx,ÐQRhÀÀ?ɪØí €‹ƒëïtEÿNdù#di u‚ ^v€µ# !¯ÊáêéÊA¬~AŽ.ÐÇx;ÈÎdñHøcå €¼”ôØà_í¹XÂí`®..vŽ¿[äüæq—å V2P''0ÄÕó÷údíà`ËÇm÷âüód PˆÏ_ÀÚbeý» +7§ÄÎÙ ¬$ûåÑ„ùÍì àB¼°3ìiiËù;½® ü‡“ë·ù±?°~lìgg ~üÁôq¹ƒ®p7°ŸÏ¿ÿ0¹¸Vv–® °óŸìf°õŸøñðávž#à£ö¸Àߟ¿ŸLåe…8zýCÿã|9u”Õu”YÿìøoŸ´4ÔàÃÎà `çæã ø€¿ÿÎòwÿÿéý«&ÈÿI¨±†„þláqïþÓ†û_ª`úkb˜ÿ]Aú(e0€éåù€–_\ÿÏúÿ#äÿOö¿³üß”ÿ¿ ’wstüÃÍô‡ÿÿã9Ù9zýExT²›ëãT¨Agò¿T📬¶²ssú_¯’+èq:¤ 6Žo£‹¼'ØJÓÎÕÒöO ýçÓ;ÚAÀšP»ßw € üßã¼Y:<Þ'.gõ‡ ü8Nÿ]Rb µú=wÜ|üòÂ>Ê‹›àÃõ8 V`Ï?” àä€@]Cíù¬¡pÌß'ÊÏà”úmú ð8•ÿF‚Nƒ¿‘Ð#ýx™Cïô…ëQ³œàÁG†í¿àc!»AA§ã¿ €ÓéÈpBþ©õè|¼Uÿå~L ÿò="—Ç ú?»z@ÿE\¹Û?Ù+{ƒáúÿk/-ÝàðÇ;æµ?nôðì ¶Ä\Y„ZŠ„Ú7†v_×K‘{°ïM‹ÍÓï¤3³û¬À{ÜnqÑS˜ë²ƒ7áWR)ãO?îÈ1]J®Rßû|îhFèLÖêúé{g–¨=»×…¹üžxd¦ø³TÓ0%»®ä¾ï½³¯~rb¯2}¾³› ®f!ÁµÇ‚gÓpåÚdøâžÖ~¿ Ö]å{¬^ŒqPÙ}EÎÒh®ì”OXžzâ-\^Í?Ë›y VNdÅôûËSâcø‰;îæƒ÷zµ.·Kßsºç†¤”È—Ï&g|¤S•I–|ÊK6á“iˆ{¦¶œÞp92yï©k_ÃÎLèg„™ž“r"*½ŽÙìTLr*£IBãÍ9ý¨¼é©t&Ðaᦑd÷ á(Ðħ%´×›×ä}ˆÎDÔø’%BÛ–(¥‰2ÈA‰˜×Ä€Y´g3zK²a±NØÒ.„k.GÙѸm“Ýèt´®K3þÑsLiỲ÷\¤ûÑ‹#áé…et¤wÊÅÊé¹)b }KK°Øa}gJž,+ùkâ¦J·+¡rT²XÚ”ìÌüžM¬Q3“ƽÏXÌYu‡]ØבÃÇ;„•z•Ru5ù‚ÙøÅŠèñq7mH[SnÄ ï·„e7žEl)GÆýb /<7¨8Ÿz{þͤˆ(ÊQi£#‡­îø†À0W#AõÛ|A¹©Pó á/á2•NT߯ò˜Á¡>ƒQ€ÕˆÀÊE9¶Íç¹?»‘HÄ{ì¦ižŸSµ†1¾<: 8 ‹ê¶{Ž=.òÜËp\óÒµe§‰Xü¡aù#쀊+HG¿Ñ/~T¸´o£tn´Zò uBûÀíq2™—zNfÃ7âœ:ö ó&„‚\H!Ø60Ææä|Y”Gg–'̪· Iya‘D•Ê™åõe/=yØŽ¨#W¾Kй©º¬{QkLîÓl…¡fH·§á·¯ÅIÄÞóY?9rt^'«]P3‡¯‰Ž·Å|u¿×œ· ¤åR¢Õ0ÛsY/î“ÿaãV?¸sBlñÉ~Õ-üú!9©àô'Nž“f³'ãíN<7¿9ÌzÑ/ŠäÇÌÝSôX;”{›ï”jU3Æ]êèÕç¸ß©$ô3ËçH<ç²^‚¿Z’ÃìJ'T°û3ðŽÒ>Hh¥3gñ" -0§ý*f%‹rÒb$âßZV@+ î1Êf#; ~ ,ú>ÑžyÃz\Í2Ê](ôôJ¢vaãC²ü½Á'«ÑÃEWâƒ;ʹ1MRy̹ÓéUFCh]Õ„™”zQƒb÷¨^ßbIù.‰`oNÛµóô~´Ÿ0Œ»;Ë ‚SÙ?eU4 ¤Ìº*=Hy²LÑmÑ|´¡ÖF&U ÔA ¨Ñî×G8ÑûøE‚Ʊ4Vm¥z–Û†@ãpƒ0LwH ™ô¶Õc&9€Y¢¹DæÉf~ª¾¡€é¶CVnÅ¥²Hµ%¦zß–sœ•XоÒâO+¾YI ƒT‘zÅ–Sµ›…‡¸ãåŽ [Éüs¶™ü¨FÜ+›-ïó¨¨ ?¹ë]dn;vkúÒ3ë¶ã:v.UåP>{&ÆÐ Â<¨&¾PŽtI5£,šlLÊ@õŠ X!óõÑ.ŒåjÎH“N‹°WúA†D8–ÎFvß"Œº+£Ç~H—àÅ’|@ :æeJ¢èæCü ˜¢6ϳe„UÏí°z¦¸O½ô o”x=‰¯°Èƒ/K)œlÚßR0Ÿ—ÀÂÐÁû¶ ßöŒ½Ûâ#SåÇ›Úâe9H~×1›ŒÛ£–q45E›ô[mew’ìœÝ?l`H6tý*EÏoóöP‹†'[ mj1íæŠ:v¡×‰¤T Œ*r&µñ¦i¯*’vñ½­™D[ÜÓk!Zb“§ðbùŠLP ¢ÞƒEá’†¨ë`¿7¢a¨A%H Ø¦ ðýðÉI\·gÆ¡~ëÉÜ-¬þ•¤$ºÍmÚÀœ^‚ OKòÂCdž2ð—H7–U ZûGÇ (ÆY÷=v²ÞÖù¥[ÿvºÌâkz²áWº—ŒÚ1£dösyÐE.JµJˆŸê¯xV„Óda_-œ“æ?$CæóæãöˆˆÆyÛãš«”1qC¥±_OŽÑU½}¨W$ÂJ °+i½y~.º!‚–öÓÝWô„}AaeWÒ5ÖùexŒøÖxOvxCÒq{#ÃdÊë­ ØWRîCø£ è,é0VŽ+{GÃ|8•­+yü´0w‰ëÊ÷/rù Ñ+gêo¸'õ]òjó¸>¤["™R@È­ER­„ûÁ+õiVå´‘:‚a~†|º/Y?D±¼žÓÂð~ÌÔ#CÈB.ìë9twS°.#Hªð#÷ò¼†s ‘B}“ /LIjnµŸxFøÒ˜ã½õ-Y7Ì}o³szÈŽåå%ç´¨UøÂH–q…¬²6À³×ƒÿýì¨!¨JÃvFYj4#³©[Ü´ì™{Þ|‚ 9:£’“kû|­°•:í·´à¢7ªóäû mÏüAš°;+šm,Ä7Y‘™-,ßóÑ‚zw…öóItj‡e­–É’,èêoõ¿†Wšº¦òúÙò7Õ#áCŠ5J%lmèüŒŸJžoÍà;6Q‡ga†>„«k“®Òz‚0&4tsŸèï±w1µÑ(·´¾õz¹‰ÑSNcRç½ôQr‰£5°â†›ï$ a†=>YVµ™Ï ¾¢¸wTãl\VÇI?( ~zh3U§IŸüÚPmuÜ›Ú癿 uoùy8}¢s¦ ðî•Õ:S(‹pî54ÖÊ<²°PJíVÓ"4&œRœqT犒dBÛR5âhÃ0>.O«Á;Lµà³"˜eÑÃàKÀiÐÓµ½}¾îèÞ+ÔEnÆŒx³ð|’&“•9ò™ò)ë$S»ZƇîUÍŸú¯“4`£®–2æò·YqCµ‚|Õ¯¤˜†’"¹”3±˜±ÉêÞÌ‹M@$ä`qq[ güÂh㺛cFæwf¼+”oýT^Ô±¶X-Š›Znè¿æy”3qe»'$xÎ\mw… hP7ÕÌçñAðKÉí¥ù~±ì„¦y~‚ôHR÷ËýD’Õ|ïÌÓ*]®}1m‡¬ô°^UŠ]:Ð~ªõè‡ n5?L%Þo¹¶äS®õјqºç‡¬à>%wVØI;lÉs’g&¬Ë­‰=Ñ2kxÞ—|”# ö½Ú^•ï1l ‚VÈ©ëÉFj>î¾/äR•®¯šyF«¼ÛˆØrÚwþkð{;%Œ„H4 D˜~¦ü ?QEÀPUö¨(åÂz_‰«œBbÁÞuq\P˜“)F)W¡ o a¢…?zåyD:ŽhkAcü pí™»lôN,²9 ®K¨wÏ{Pé- JéH+¬ ÞûR&•Ê‹¦çbb!b“ˆHË®*|󃓨zµvFZN}‚'¼óžËófôÍÙØ®y"š~_Êw99¤¼ ‰V˧¤¸‹×„Ø2ki£ìæ£+t(u5jî›èǔǾMa .C1ÓØµü/i†lh!V3FÉø»¡A¦P«•¸4è”úG™YØ—7PnÜÑõ®J ¤ÈŠQœ~<$ªOµ¥^SÌž‹#:+YÝcÃŧ7ØsƒöCmŸ‚“í|58ä„<,t:]†¥Ñ½¶3ÇšiZ2¹mN‘ŠïùËŸ~Z›²@·„§Swœ­%qáH½Ø„¼,œüÎÕ‚ØÅI R¡ì×a'V—ªØ"±á;10õœšdBõ jzXÍ•  ªï_u$yê§žw±¨';ú&)=ª·*³„º—&(ti5ð…‰b«šq]p¸¿$$`ø<‚„u$nPXJB-QJ½ŽéjY÷sv&^aDÿ¤~¾ù-+L£³íË—,ÿ^$v1BÈ#|©`ŸñƒÙß!%srEQêuÙš¶¸ E禚©ß­I¸•ª´[;“GúØ0àƒ‚2 Þ·…·:±@_ÂCÏho‰zL Š¤ºdzÌgÂôÖuC»ë7!ÜõÅv‹ðYÔpzŠBÏèÙ)Þ×'‚ïÚNB"Æ ùü?ÿêÉ‘õ€½¨±ÿ^/…r[ƒ1m®£mÜú ·cÎCýɽLC¢ÃžA•ÿ†›"í“ @Ào“S‰„ ËFä0¨fM泜ôSž‡#96DÇCó“mž ß~¾ðºÜY‡–gKñHÈ]Åqh?Í“ÆEÜDÝ JCµœÑóÕ8+gíУ¤k†ÏŠ­DD%Ô’Cî›øÌ'SŠØ>mq.œšÂEø$È„&–Oñĵ²Õ— íÐCª7d'Ú¤…ÃÄ|ÂçÍðÆ2€ ŠSâLïšÝ zæK¼}³t'›wô•Ôvê¬Y6‹BùˆÐ'-jWMEÓ²µù¦ÜÙ=jYöÔð–ã#oÎS¹‚õ K±í"$v ƒ•¥­ÃòŸç|¦Ô.EB·óÙU¤mæµÅb CÌ'·öïÆ|˾·tÖ3E(yÍä÷%‹ô.jŸX7MK--;T³¦!Øül!²ÀÜ;ÆçcVv¤'vÔ7"ÖÌ$Ìw$¢_E]—’„Ô3Wú'7•)ó×]rž¹€ñê'·'QH®й”§++Xb—ê{+T2Ú†b]X,m.%—ý¬ÝËe×'F BÃqp„ta†’ñ¦ø!“#Èe¡ùv°¶*îÖ”`-Õª&ÆbXN±•ä³¶Iÿƒ­Äª‘SL‚ö‹gQ Åc_êÝt–Õöñ‰Ò¶›º´"ãĦÓ8’a}ƒJ¢š—ü àÌ—]çœ~.×^S9™'Lk~B\Q'=À¾]—•ôÈ!´n¨Qû$õ.£E 8^‘ÆÅ$2gÊùÌA ›}NjQôä(;ù“½è™Æ(R½ýï'Ï`2ÕùEªeÏJ”¥;€—Wø—YÚþ7ENü"!q5îšÛÌ uw;{š¤ôxÖ&ôñ®Añ{;¤ŽÖÝRèÛÏÄÇó¶Z¨ýZ%#ÍfÚwP+Î Ê™Qio¦—ë3c²Ð'pïÛ™eTHçx»£|3§0‘95±d= šé mFe ™¸i©ÝS#vt‡ÿ(X2žû…¡^;;wêÔǬ`+Ñ5ê>G+ZY…~c†Ç­¾]i›cÍç8r5k™cc ,Ðd^=_– g™Çßâ5ƒ-ÑåcQèùP²Ç´–ÏKG‘ŸòbÂÉœWó3 1A擾Î;QVé¨[Ó/IJÆ}¸¦–*Z”ÏYvhæ´0¾Ü9ltWZÿ±?ŽÔ†…\ õlãÙy¢lÃÂñùO‡k—ÔB[ñÖLó°Ä¿ØvGçÇ»L2úÆW0=o¨®,KN¹3p@mKJSäKýìTå@œoî5“ÊÖ-Ѽ8+zx+o—×Ér¢ Ôœ¸÷Ò¾½¢nï~s‰QMÉn~‡“õ&ñûT"MI»oÅ¥¶¾œÊß>öG•G-˜,¯³&ö„‰žÃœãê ǵ/ÓœôûX@dîáUs<„kÏ€ñž]kç| ¤…Ü8ínhÔ#ýZ 'õðÐpcQ½Æu@ð´ŒÎÉxÐ/ZèªVp¬ç/žÁçù^8}v¶ð!ë¢0„#H'uŒD’¯':ÑxhÖ¤/ÇÂ`³€¦P}£¶Bü L²ŽïvŒ_‡tÍcÊÉÆb7ÃÐeàÂßëÏ)ñ÷¾h=E+ÚEH<$ÜžÌ1h!G&ô¡Jt’Ñ[niÓrýE¾“cTm¸Yù ß~$³Ù÷²/õؾŒ(‘!xfù+ twïV6ŠŽ5+©<“P ­Y¾·{³ü²ñm ÍRÓ' ì/¯w|† ²•ˆv2øTòw^âQF&Ü·Ÿd91Ñ·b•é  â¬ŠÕ-mAûžÀœÿ(Ú ÐFÏ»9 ¯RµZSI~’BÉ!†¼5ú}).—ý.é\ ÷Ëwmž§"RÑ‚ìÑa†“1±Û@_ü Ô¯çOö[b1°'‹û·Œ%Zk„+Ž>,N‹J§/$ÒNЩB*—gY§)2wõtZ–`2á——o`‚˜ií /¤×µ‘*#§÷àãA,>ÜqG¹v Hôö#Æq)¼Ë½è¸ó-[Bžƒà”ÇÔ³ÃJ“CÁ's,ýÈfF• ÀÀ{•¤É­F7òQÆ“»Ÿ€FÎ}–¼bØàbvÑñÅNÍ»ìŽ7¥€âÒ,G… ’œÕ%(ݼµUûìqT‘jƒd¾ºxÏÚ¹~–%ˆ6õÇ’ø"]uvû"ઠC z›þ)ü™§•6%«{ÈL玘™©66?FêYå;h¾usnõ•©˜Öµ•sµÞ>S&n ˜©}‰R'ìøâòWâÊÞÆè/£®ñP»p#­,$ÒÆñ­‹8”­§Å;]¦_‰ø^êâ M´øä ÷¦ãî~B¹ÞAÛTa¹£ò¥ÈíA+Ç8'œc')‘ëüž”nîoŽ:Œ÷€½ûeA¦¬Ž5²Rý*ðÊ!½ž ~}žsÃ([u¤y’ç;HA{û%qç%¡Ãáéš²/¸°Á¡„oÍÖ9i.ðƒÈ®æ9u¹·¿Þ‡ÕÝT$œá ÝȱÞTQ-÷]¬3dìX‹åz`g—|©Ž?ˆë_Õ}UΈ±%jdlb¶iêQ*Nôš¯po M_§Ü»}\:ÇæT2¼&¢ýÓ2¢p]¿¼f d]SÂ<4QâÖJÑno=Ç¥V%Ô¯£•¹»ö Bè0%üðâFਹŒ¨]>œ¸æ›ŽFÙÝêØ(•D‚±­IÔŠs¯ò—¥†;y•õöM-ôñmQ(›`Vg{ÝãI¬¼À‚,$\•”BŠ‚Q*d WÄæ^(_¬ŒB£®V5cq•Ö‰ù«XM›ë êÙÙkþeÖ­z]%d(nµí‚nµè{¨jBz‰VíôºU€ ŒªâÑÅ¡l&Ϩ¾W{¢ŠÐ§›ë¿6P«Ö¦åÞ#興ìSêÇE­Ñßmùz—~%œF·¿ýyTÒÞSMW¸¬r½ë÷%´¼€{»Åô’wqß>àÌSI$gîºÈu…±4±(}˜)€];HTiN¾‰7LâF¥¦fXòdá¡Âöi_"iÁ;ÕxšüØ;.q1{sÖ¸g§è€wXÕØg7 2ßiå;¡Ó4ô ±°’¨ñ‚VVµÎöÆ·FªÕ›Ê>A¥ZÀº{Ô†¼™UÏ'y>y|Ü?˜#‘˜êrb‡æšñÊ™°üÐß½…ˆáXçúÛŸt¼?pH ?° þXÏ™°fõ¥ÝäÙ#û:ê›HiË‚ßÀâNÍËé6×xÙ¨÷/„µ5ÝD̵$¯YU™”R …JhblÓ/¢Kf‹²W§œ ¢Çö#EÌË=°mµí¯$R†§\‰°w¦¸r—þå !%á/;/-ÓÀ]~;D»ôr'Þ1èI·^ ªŽâff‘Ñé}yt bÝ?û&Í7€)@‡â`^¾sÔ|o¹Q2˜Ö²Ëåy™›^ôÚÛÞªäê«Z•üñÖ§hRÇÄ1̹T¶J‘ò­y¨Æs·{é³ìi‡æŒº+:kfYý™ÞNÜÏ»–ÊD›è31.ƒ¤Â1”/ôU63J›2Ûk-ý&Èôt¨NS<­?}{[sÃ[¿Ãk|2†Â…¿Úh$Bîy7ø6ºr‰ê!M-´Ò%Ñó›?ÍW$Cw›YýTÆMCºdj#;M§j¶]Oa'øM@}mÝ·Û4–’äð3ƒWNùD³÷) ÝÆDW1ìo³½i\®W(ðÕâöñ^,-¼Œ€5ƒ<’_~C—Œ¶tc‚¦â¾ÂËÑÞ aξï¥=îd/E Î#nófEpyÐùæð|{<¸>²=˜³Âj¥£ÁÝ|>£ŸÑÉŒËB?9™Ð…¶SHçÐ8JL>+G°6#s€c„»Ó v8 Ü;ºÝñfý†Ò˜ ï]úL—¬Ñ—rWˆN~8âozq#×2Ò¼uU”Íx$n ’LôskèhCå¹ #ûb"‡H¿ß¾>RX±æc(oñ|âøsí¾t†âyÿZµlvϹÔz'3ýÓiÿ+Ÿé/6/~¸í©Ý«z Tè•¡1óÞ÷SKßt¯‘v××s°Q)Û{²â"åþPfW‘t‘SÉè9òøU4ØŽ¼ßŸýbðýy<Þžì*®"š±PTt•ŽByç ûå®÷Œˆ”ê{/:*Ô¿)Qì=Tt¢}íeÛxÚ7]XªcR« +È<ÿtP6Ž¿"­ÐÂÈãE’D)ŒÕÿêÿ%s°Õwá_!éÂæsÆ…å)%JÄö`%æÆþZ&]Òzö¼-éd0skQu†{lÐäù$Áç'IÕªòÙѹ@!‹!íˆrO<ƒŸ›ÉOŒvå 96ÐÊ^ÛB"£ VØtÖ’7jü¦;ëíôVàå×cÒ¦Ž"·Ï»Ç8f÷’/*ÎÝ–bbËé·C aX}DÈeèƒb[¾Š¦«i„ÄCZ:üô*2M+s6Eú§ÍÉa‡øõŠ(–É€06[[L6éÑ¥ª|ûre<¥´Õ;|°€´}X°Ø§|‚/ÑoÒd›sœÇG›ìwŒs‰mB(¥|½“ðÛRã”ñmyÈÆP8§&˜Ó§q‰P§FÑT¶;U¸“3¼Eå§d[ÿ<«¨#¶lHõþ•ûr*1æì•mûlÐ)[ÀãûÏ*ßò½l‚’e¶bç'/+~º 4±Y¹¥ª$e …ÊÏYQÔØ/ú*‡/E™B5EÔïÚ|T{]XJI'²øíL÷\O߬Õða%Vè­lÍߥö}ô¾Îá_ãID!1m£^|(Ÿ—¢,B™He"—Š5ö¼WµVSoOdš 6Š%m.Má×"10níçhx¡M"ŽÖA< %<»»†;”xFÈ«E+5H©i8’HÇ’H:PŠè@²@*8`lÍwÉYNýC6QðÓ—6~«—ä83¦{‰¬êÍ1 o:ŠÙ õ殚\f_)—‘P·?f&™g’·µäÙÜâêÍ ˜“U„d¾Ýë¹ç¶µ`Ìàjá­5†WÅF誓ÁSoÆé*»“ëf¤õš–ÁHª8„BɼñBy,àF/EZîãRÓÌÁtsËÐG£—5òêƒ(Ÿ”‰˜·ÖÑ›ßU½Mº¼íí©Ÿ]ºM—ßÀCo£Ã)µZËØ—N×ÄVM ,]”% OÕD(h„§mBðêF¼SÞ*µË¯<1ñ7^£za&ç»fìükt}ù£Sd'ÿYk‚©?meá1‡ä9ãíƒt[QÅ­áÃx§¯6õ‹*ˆÌX\ã®—žkÖ‡œA(fÆ^yf.J"Ä‹«H›0 é_žù‰?É31¼ 8Ìôä^¼ÓŸûùBSj¤ˆ\ÿѽ>NÄCl{¹³QÙ¦h86ü?× endstream endobj 1532 0 obj << /Length1 1533 /Length2 7479 /Length3 0 /Length 8501 /Filter /FlateDecode >> stream xÚ·P^6N§‚H¨°ÄJÃÒ ÒÝ%%,°À»°,Ý)-]‚4H§”´HJwc€tHH|«þâ}ßÿæûfgvï}NÜûœóœ;³@:M)+¸DCrps‚D2jÚˆ—â!u¡HÈ”¨A¸@á0‘ÿ°Ë `$ “#Qnjp@ÙÕÀÍ àá< ðߎp„@쵨q”á0ˆ Pîä‰€ÚØ"Q§ü½0[²¸……Ù‡¤!¨%P#m!ލ-Á¸%‚ôü¯Ìb¶H¤“—»»;'ØÑ…ް‘`a¸C‘¶mˆ á±ü¢ P;B~ã$tm¡.`¸5ÒŒ€P€ÔsA¸Â¬ êl€Ž’*@à ûã¬úÇðWiÜœÜÿ¤û+úW"(ìw0ØÒîè†yBa6k¨ !¯Ê‰ô@²À0«_Ž`8*ì†:€-P¿/ÈKiÀ(~±s±D@.œ.P‡_ ¹~¥AYf%wt„À.¿î' E@,QU÷äúÝV{Üæýgm …YYÿ¢`åêÄ¥ƒ:»B”dÿò@Aÿb6$€  Έ‡¥-ׯ亞NßFî_0êþ¾ÞNp'€5ŠÄj Aýx»€Ý $ÂâëýŸ†ÿÞps¬ –H€Ä #ø7; †XÿÙ£:€zŒA(áq@¿>ÿ¬LQÚ²‚Ã<ÿuÿÝ\.y=M¶ß„ÿ1IKÃ=Þ< ?ÀÍ-Ì D-|ÿ;Ë?üÿæþÕCÿºèߌJ0k8@øTíþ¦áö—&˜ÿÀŸ Gé`þWö& ~%ê‹ûÿYü¿Cþÿ4ÿ+ËÿEöÿ{yW‡ßVæ_æÿìuðüËŽR±+5jpÔ\Àþ×ÕògˆÕ VPWÇÿµ*!Á¨É‚Ù8üSD¨‹<Ôb¥ EZÚþÐß=@¥w€Â špè¯gÀÁ ý 5k–ö¨§ÄÕ©ß&j”þûH9˜%Üê×Ìñð ÀØ“ÕxÔŽàÍN+ˆÇo]¸8ap$*€¢ç °†#~õÄõ‡ þƒð¸¬¡(iÿð ¸+â_@ÀCÝî€OÀÿ=êl.ˆê%ùáC!(ÿ³çp!mÿˆ¡wø¿9…\^Äà¿([º"¨gà·$Qõø{ÿûÍ@< –ó3pKÑ»ÚÖój©‡îë#â“Àuƒ4ïyD›ë%n2KUfÐ*âL*y ûÞÒ9æÓ§ ´×Þ;Íõ¸/ZµÞýô¹2‹×_G07FÑ;Z°#U÷á1þ#ݧ>×Î>úö˜ÍèÊÀgW!"Í<Òs÷º¥‹ÃfÖµ6ªT¯J'8bô¢M‹§€¹YÓ”ô8HŽÇx¬÷=ˆ§NÏ&ïgÞÒ*dzøîÆðz­ñÄ^L{-—ëò¸tR1RQ>Æ<½ÿqü‰·ôfŠòƒYï7…«ˆ©Ómß“¸6™½ÖÕµÏ[ߟ?ŽŠ0SQra’)EE¯¶(&8Ó%àðe.)¯z(}l¶pa×H€Þj8Öb•OÑmX=Ú§x¾³º1^?±-'AÄ[ŒUÀ[u8y^¨¡“|/ï“«†ä{áÖœÑÕùOk©'Õ÷³Ï£iÇqܼÀþY‚sîð´¡BSu1DÎ4É%:+V; $†^õ­l²;¯m/F6';ÇU‡‘á—;’Éš ¶4kØs(˜-›|Ö"­öTȳd²:ÀOñ›0V QÊ‹±´×Õ¥Ð7áagoâÙþÍOSb6̌ĎHm²ŽŒ:®w†‡ï×—¦ApV³³ýý§³ƒœ“ùüp옌帗ñ¸Û°÷«T$Ág$Ô„êE«%€H™gd˜hâ^è“Ø““ÙÌqp…”Ÿlk? ÷q8²@“ äjõ ªVÉ¥1Ã*­Ä€²3Ípj¥ >Oäm‘k>Ÿ,=Øõz,̈ÙiôtÚÊÓÔI‹¸—C"žM™‘1(MJ¦ü8¾ÇÝf²ÏŒ$þBÁ)GãõÒ€9œ!HMýc|ƒÀ‚묜èLÇí˜L/U^ø9÷Oþâå&ÛfOk‚’{ œB£Õü•–PFþ!Њ®Ã ¦ÓÕìÓy Sª[ëÏóßBµ^*èï$­•à îù e‹›boÅ™G“äí'†±n`ø nÒ¿&ÎÁ{¹m¹1÷Ãý1¨)£8òZÏÛ0 Æ9hæ¡Ô¡™SQlX¤eJmþ6¾«WMn '‘â´ëÓ3÷-‘Ã:mÑ0dEÆef"›©M|õ6©ÑÙ@èÍÝk¸‹{i⦖^ Kf£PTe?8ò³ÔspŸ›„GoLÏbP«lpÞ-0=Ê—ê€övÉ?J™1¨)X~gB¥•R0œ§Ý:«Bæõ¤@îÈí*ü`š/âIŠéη…´*«á[ù€%'ÇmRÜ&½ûUĶM9“µÙ]TŒî…&UÊV¸Bl:9râÅÏ"naCúñÀÉEɉª™l…Ac¾àè÷²£,‹{/qâ}·ƒÅbÞ²ìnãdÎûqø÷gÌOpâÛû,±"ÉãÄžOµh‹Ê+aä6jˤS¹äÀ°>µk=ÝO]òÁ}ט;>F³ÈÜÙ½²ýí¶ƚH¥Ç‘YE»JlÒsÍü¨K\ÖØŠåéÕ  pšÂÂðf>!Bm>UJü‚_ñ %·Vª}±mÙFÏ}ŸGUþm,Fý–Kä³­Qâ¾qÉ£Ï1>º0A\§¬±«î:ÑypžtâÒ»Ð]Ìè¼qš«uSGÃëOõJö;f¼~:FAf¶ç8%+Ô׎ت¨³r7p5ëú2-ÐmLÑ Þ@«ßgúȰ¬‰GØJPÑòa›%’KE;©Ùß"¾¿8Œ]Š™anrÛÒÙ Láo‰ ååžëpðØH¹3V¯‘uIB^ÝIóŒÎøKÐü\V¬ùÅ€–õwßOÚ—qø|ôQ8~,$8®ä{nwVåõÊÒÛM¢ÞˆîD¿¦žPUÜxÇ8SX¼W¡i/ùaØóçm8_h ©öÙòÊáP‚³¸Œ#àðAJÓS«ù®ìF¿ã)n-¡ )ŸÕ ns¯Š;4@}\ÊbÑ•‘ɽ<Î>ÄAÉ¿¯}.éôo-¶˜j{¦§§?AôË‚îÙªfèL³l—{ ¨mˆN%½¡gjÖÐØL¹éEÕß›‡-™ñÞä‰oµØìÚ»„ïƒægµSïñ¸kÅ™ã2Ùg–mKÞ¶ï}9¿#c-—È)o9噳õyôÑ ï.Îs‡ß“›ošÕ…ÒÂoen=¢… .V¶ñ_)?ý`ÎÆE'ÕÀSÙÜS¥L[Âr{D¦ÈM&1zýˆ] > ƒžM¶·éa«*fŠ+Å&°¡Ü­üVøÛúbGÜ>¹?Ï;1:.ž²üÔî¯ÛüÔ½É56·úè }øÖYñÉV2…”^«ŒZšœdîjÐ^e‡MÆùúê¦lÿ _þI*œõHv‚ykÈcRø 7Í2Kšå¾§ýBHp™®v¤ï’ÐLiLºã'§^ I{Þu¨øÌ¦—Aº8BN/tV ýœÔœ´{reS¸xW!))^ Iðæwª˜6ýL “5¬‚ƒ'SÂÒØ·‘/eÐëÈ+öÃýurÃ…¬Åµ{e„ÿhO(èViXcT™A(þ˜ºüF©?´/×þRæjï@¨:è©„–/E¨|®1Ö6éq‰×1“SÎ64V½zFšº¹š|´Ø °¹ö¸’øôk«=·$3Ú9šEƒ¬’Òk-§5f¤mü Å`ÆéG«O¾ëm_7<ƒV~ŽWWeé:Ò8t»¯”jª¯ôfµ2ÌDgŸ^)¶gÃÈ?Û'ÁÙÐ)gTÓô–ßçÓ¡±€ni¹% "?ŽQÑ÷™ÍÙ ãû±’Û9}.Úq’êÝ|—•úèËðöÛÛ\C >ŽÝÉÕúõowꆫz1 „Ëûòú5ÞÀ^̇rØfw*2YÍÇT5Þëíô%©tT( Î{¤h#Ñòê µ ­Ø¶¢·cêLëÇI!èÌæ¹¬G\_Z•j½Ýù i`úŸ·=1&¤ÙkÍ@oŽs¶ÜKó2¡åOOEß[åúQ÷Ó`<\yè‘}lÀD¹vh#…¿ÆˆéÖé<Ä M§xZFÐô$3s/Û*nðŽ,ovêÈ\ß½!RÅÈSK¹vŒSc¸&Ð&Ý2|å_ßšY6Î8–³[pv‚õ°×§H 9݇v==®ÿ¥ÂK2¥ÔFók{ç‡`ß§È&l£'YLñÝâ_ñx C¦,°”Šmé½æÇ †Tô÷^—HµU¡Íñ½Ï~Uüå…” ¯×òÐÖHÄ¡c«j"莘|†à›ÐqU‡IîN—>g]æK¸‰ý³\zkÎûJ,¶ûù½ÃÔî…Ò P™.'W„5÷‰å»RÕ=dM‡³Åht³áæŽáÌRÂÏ䣻ݘágjþn–< oxËAßvjŒ`üÛ»JŽn¯Æ¾_jl·Ó¿a' aÎÊì´¬”¨AŸ ›œf×àœ,ÆD “'ÖÈÅ7côÔŽ`ÆÒfäý<)f˜1DOa®i’ªìä²zdo¢§<šÐ¨Ä]}“’OzHÝ}ìñ[Ëè' ÿåN«÷ƒÑÈéF@[ߘÕÐ3µ´í«-°¯j – xδG8¹ÑØë.|œÕÕf~Dۿ£­]¢‹¨Y ´>¯—'Ø(¬¬¡eivFñHêI},€Õ_n®SóvÕŸ {E$(å4 ••”±o ŒbK·n¥9èÐü<['¨œ¦?FN”£É _›©²Ú»Kâŵ]p¹Ê[°fG_ÀÚÉüX]óÕF¦ª‚ ©¯ºç ‡J=è>œX¤ñùñ™>#šnÙHhÿñ8M¸RXW¬m*{ð•Ê6f~h¶0…±’wšæ¥D0u0?‘¶«åæýRгIÐR©[IíQN¶è¡÷ïµ–ï;3 g;–E6Ä gŸ¤4¢ã±+‹$D“_ö¾›¾ùÉ{ñ]çVãf¦/N‡;ŒÝ@ó0‡¬ÒëFJt¦è ›v82TøÙÑñaB“ ×¢édÜ{ÇzÌ2n½˜î’гøvÈ[?4ÙìJäÞådpËe¤œf‚q¿1ZÙ$WLmH.OcKå8ÛZ}c:q6Ñø`Í€GèG¾¸Æ%DqJŠ6Õp¹¦ys°Z‹‡=&Ul-1v®,¸ˆJU|í·õ3nVdñ]sˆØ·(…á¸ØåûÝÃÐ:ŠûÌ6žü:QG/š~z·÷áµÐjEÜne½µ:uŠ2°áP³(Ìvúñ“ÊÓdÑ ‡ÙF»Xo rÐ7ÒY®óöFáA‘¸°ÏrñƧ`Šºf™êë"šã¸iEÂÏ]p§XÓÅ–¼ÎÎÛ]½¶¡%Æó†¾‘R*Âf[Ñyü1%ô\?ÜÀ‘”‚ ¸ìy¿^[¥\Ù…|“'\´pðøPreqbÑQsL‚š~ˆ¡/¹÷²ú.Y¬.¿2+õ¹ÿúƒ O‹Õ6„žÌìr_äxÂ.{„_­œ\$Nð7V+„Ôè®LO „èûãžÜ'øQ–ºÇf€V£cL­±íÈK¯éh±ÅNRÆ–¤0FÙu]|¤»›fîyzO[Œ‘|øërþ'ž[0Þ“‰³&ŠìÀ9ñB™©œ³ƒêOê#w…)Jk?û öªEžf.éÖtJÖK{‘ŸÕnÎÉÓ"«õ*+™5½OêUª»sx2©ùT3$?T£o -¬Ö°îU±~°ÎÂK—±¯Öz†[¨Xñ×ãa÷­>nB!'ó¤Kõ¥èÚϪކnöôkÜg;Âæ4 Ø!w®5 æ †ÁG‹ãâÖrÀ© jªX'+ÙÂeǯ~ì’™ÓOÍäaµog ±½:_¨$±ay(‘ëÛ¸ãÖˆœœ¨µàæ8ŽÏò ]•ê·3ä^|niÊhV»Ê;4-:gíË›ÍþBŽ…¼ äg@8.|f’›…ÁG2òŠ“—'ؼ„`\â’0»ñ:Ê×ã̸³=CÌìE<î„[b¹ò0Ënºu©f¯‡úcÚ/É{”ÒßmÏÕ²éÚwÂW¦-Н˜\„»ÅqÞÃð â'OÚèí¶ùŽfm*i'ÀrÛëñªÔs‘Ný%^” û¤OVTFø¾|L+ ð›ë´œù~åÚ¤ô¦‡¿û<æåª°Œ'õDÏ3އƒ²‚È¢Ô‹NõG7k_ŒJõKßµg{‚Ñýt€it<-Ì¥;8¬%ÌL´7½¼‘,Òù”Ù¬äº)Kd¯•s^„e?ˆ‚ÆxŸZËâùSý°ǧsJsÞ±×·»“+ܵjAPŠ^¯ì=KCˆ®É¬‘ÇŽâ]Üγø,{¼gÌuávÔ(Ëk¯—|bÜ91ËF;SV;󞆒’}Oƒp¸ú‚›T‘éêC  ×ýú›†¨x¬;ÑWy]Ó>ìynâ;[a™´2ᥙ™žæ†Dš¬M'/1+B¸@2o8Ÿe÷F‹†Î‘t•.žÀN…©ЦÓr]7ç{æJvC‰Ö­„Z8OWósu;ÿ}¢.cõé§É9¬FoÏoˆ2ØÚ.©~cà­’YnÈ úYÃs×]æþ˪ØxúIÚ^ãzOÙ2å~v†a ÿî£p}VA€ÉjšÊWÑ×Q±cIžCnÉÂJ,JôøŒtñ=Ó\o.Þ µŸÂ˜5ó<µ·»êI‚õtò͉ˆÅh/È輩"³ŠÆº ­lAÁÌ2Au®RÕÝÎXTc›º–®Æ)„çÉv3â/å2pަ³©xÎeø€CÇõþåÕä± ùÐqÃýS©㑆o :ÿ§ý'wƒèÄjf‰ÕG2ãó¯¾P`“Ϫ3õÅÅÖñ%I¸±³©­qoëtrŠ0ûR»[:„jÜ ''Dµ–½ˆk²)l7â)ê·eNpˆŽü"nï\ ®*?äìä&þš9·Â$"Ê2³bÊG{Ⱦ#ðÀÕÃò›ê¼T 8xþ9]ɉ*ZmD}Ž+ÅkÚWŸ 2ýÄ GeÆxCŒÛœÜB×èKvJ—ºsZw ´‚oÉi_'y.1Ê‘³vþ”è(gZ}±Ì~—Æö®ˆ~@ê²9ûæ©‚W÷q¬î¤÷êm/¹FûâõA–YCL¬ØÎHȤì.~ñ-úŠ-ù uD-/ é`©Ø§u(çö¡»"µHõäc+,s`ãu‘xYÅ~Ù$ ¼<Ëž1«ÿ~q«h„úØp"€Á@žð uɆ‰Ÿu›$”˜|1D¾y^#É Ç –G§9÷F_ÑöÖ±Áë ÑN?ü¢3N¼âßîª0×RÊAN€aÖó:ëîVn>©ÊíYwJ /I¯4à2ÝWì -d1Úw&ˆÞ‡Þ¦Gã•R%WÍ ±ôZ9ÍRVÚÙÕ'^¶ùIJ­ÊáËäœqgƒÁ–TÔ²}FGŸ Bšo ÷F¶k~¬&~°Íø™c,F£Û ©õ„,Ä貌ÐbKm·Í}â6`UXóAêï™Ð˜fç'g¡·J¾ËR{´¿wŠCsîºþQÃ5´R8‚j!çëÖÖUÙ÷›M¡ñmU?¦»Å³Å¼ý›^¬WN:×þ÷ô»\E$Ú¬ÍG‰èͤ³à°$:wõöTÂQO.þüQ„…äÚKÁŸ#Oó%ÙBõù£¦iFwÎUVäHG‡ ´ žíÁ ß9û÷<¯àO2r4"Ø—¤<ÿ,‚©þA‹¬³>­ðt,(5¹ZŒˆŸ.ö•ãËð’ªØ\!2x‰øøh>£8ïdÔòÃI|À;îÿž³ÓûSéi«Éf¬nÍÚ÷÷ɰŒƒlŽ•¼ß[©J©'ôúãûZ`>‚ì†qÉKÞ•j2µpÍšqè ,+p^NŒ3æåS“Ö- w7®!ÔÛŒ+@¡«W¤|&‹›UaÒðÞ¦_2äË _¾Á@˺^<”Hbú€kU óhUOc€Þ˱7“ZyÕ0]\ç ںàÞ6ŽÎS8y2'ûÜþæ¤ g»$¹*Ý¿‡a‡ë—Ò›¡÷³7ý*Äÿų†ƒÍFú}‚üí]o#Ö;þLS¤O/&Ò|øÏc«ÔÂÔƒŽœ¾¯-àk¯ÝŠçï•V½úl¡nò9¬±|VM€ÎŽ]Jš÷>Äms½3eŠHxϪ0I9ÜŠW”ц–ž!pÉ“å6­á0¼˜2ûµ%\ bØÃf K;ƒõ¾¿ g–õɉáé* g4ÀæÍÉ¥Õ+ÅäË£cs­B{S¯©¹˜ÄÎ~ƒ%vY}µ#³/Ì+᱈­oî•h´/]ð1g³çGçÈ’‰H}òÛg¾>,jpR5Òì<)Ÿ)-mXaUÜJÜüæ§WêyÔ±¢Ô/W=‰»7§lã>Ý1Yáx˜ÒköSnƒKÑYüÝ€ˆµú%¸çC ˆÎcE^ÕŒè»Õã!qmâà@=ºós'äÏòèwTÝÛ5E&ѲQ”ös•zF ·m9ËïÄÙF |üöž:ÚvÎÒ÷;Ñù,äjÈ„+ŠáDB·ó¡lµ$@GÍòMçDÇàÊ8|*é<°  &¯‹Èòié5Fô½·¬gÇ{Þ–TníªÚÿ‘Þ§„wÄ|.¸ƒ¹R`{YMw›ø f´!Ì&Eä¨Ò@¹Ý¥bø=ž÷5ÁóõÇ=üýˆŸ Ãh©ý“jØ;¹U†/y,®¬oû…ò6@õÀÉw3i_+µEšÖÀ¡R‚Îä”sId(›z…¤¦aϳýhK•ȲÉþ÷"U kìQ™£ý“GF.A¾wª.¤Õ¯èt£†×nD±cȾÎÝ v&ÍPŒ¼†;¾Yvù1) LT\êV|Wß–7˜]ßþÁ£“õ%8›ÜhùÍ7¬] ;j~Ir_IÌYŸ¢Ú®fŸ¹² —hµÈ™÷µ?™1jÆÏýëüyŽBüv“,k²îí¼^lµÛÝ覔>ÓÀÜÍ›KüT)7óêÆC±ºkm(ê“9Þ|sEI…>¶XV¬?›ðµUS”ã7V´Ë j¢c·¸À¶’7À»Öœ™ÝÃÉ›]ÅFM9c[|bÂíèÚà :ÌÓ2ŽO…ÜáH}Éîùí%ç Y,¼ô~ÃcÑã–ªë0ù™‡ôÕ'C9dZ>OÂìôr5ÞñJ«yÉú‚_Jñ)£Ì ïâÝ:ZÄ≸¼A½Æ"Kª0Ïw<'£b/Ðu¾ÑtökNŽ8v ½õwÉ—G³öm*WÇt ¾þ›ƒÍó±ŸähV§ß¥=½}z2eº¹+ÌÀc¾pΜZlˆôîLHë{S¶“r<nêBÚ‰2Ѝ†xHWJ…‘6„ˆÓDd;«cß³‘Ë¥¶«>éc­„õ–Ð^‘^›i¥ncá(ËØ±’G·íTÕ[&¾@WÃDÿyÊ(ÖÀ¾<¿ÂÄ}véq¼à{*%®R'=U%"dÙm# ¬ TŽÓ¿%~êàB û#×ÇÞæå\üOÞ Ù2tô¡põN̲ÛñІ¦¼ bÃBQI§„öÍ+BuúH-|>Eâ?½Óv»×hîk̤߾'È(,ÉøN§èÝÌÓ54ËŽí<‰re´£Þ½’ñHy¼)Û+(½Uô…mcµÕ„9òžéúçî“:?7~§çòsE ¨!‹+ŠöÎvÇÑgÒ&1Z(¦]þ5 ðäEàéqÅ’¿PjG_ÙÔQÏÀˆ7šêypvŒÈÝÒP“ÖÓU•?“<÷V¾xdµpr«`dwUoé—ó®/Cÿ#V8¿-RRs¡D*…p•Å;?¤ès¯uΔ<Û”`Xâk“xÍ+¿äF$-Á,Nqåk‡±7qÑ4[äËÙ"iáºU2K*Éx³Ðmç;ëÚIŸÝò] O5zïÉw¼j[&¡ƒ‹µ-Húàß‘DG<³ø°eIùjf˘ˆzzo ·'‚ûz!µÝíscˆuÙpÚ8ÓKýäŒþu½«æVâÿyÐsâ{•K¾BÇ*¦ \ : k> stream xÚ¶4n6®¨›ª]©ÑÚ³´öÞ{ÖŠ‚$DÌ Jk+j—šµK•Úµ)ªVÚ³FíjûÒö7Þ÷ýÿÏù¾“s’ç¹îñ<×s_÷}Â}SßHPÁeSE!1‚`!4PIÇP‰ @"nnc8Æ öp›ÂОpRú?ìJhƒÃ”!œ› Ôôr‚E` i°¤4¤þvD¡¥Êo¸PG¨‰BÂ<ÜJ(w?4ÜɃ;åï%Ê KII ü* `h8‚ê@0Î0îD(Ä h„‚Âa¿ÿJÁsσq—öññ‚ <…Ph'Y^ ã 4„yÂÐÞ0à/º@]ö›˜€hì ÷ü¡1>4 ˆÜàPÒà…t€¡¸³FÚ@=wò³öà_O ÿI÷Wô¯Dpäï`ŠB¸C~p¤Ðîê©j a|1@Òá—#ÄÍ…‹‡xCàn{œÃï‹C€ª @Žß_ì<¡h¸;ÆSÈîö‹¡ð¯4¸GVA:(¡ã øu?e8ŽºŸðﲺ"Q>H쟵#éàø‹‚ƒ—»° îáÓPþËþÅœ` 8’”a@˜/ÔYøWrc?wØo#øŒ» ÖåtÄQ€Âa¸Öâ bÐ^°@ìþ{ƒp(hs‚#ÿfÇÁ0Ç?{\åÑp_à}Nx` è×矕5N[(¤›ß¿î¿‹+¬¬c¦¬kÂÿ›ð?&EE”/+(" Á` $nøßYþáÿ7÷ߨ>þ×Ý@ÿfÔ@:¢€R(àÞîoÞi‚ç¯váþ÷ º(œŽa@žeoAq_àÿgñÿùÿÓü¯,ÿÙÿï}T½ÜÜ~[y~™ÿ?Vîæ÷—§b/ ®#tP¸¾@þ¯«ìOëÀà^ˆÿµj` ¸ÎP@:¹ýóˆpOU¸/ÌAŽ:ÿÐß5À¥wƒ#aú(Oø¯1ƒ@ÿcÃõÔ7JÎÈA„d#á£Ý÷¥;8¥Íºd×LànÇŠ`-EžûϽ4ñlcâb²dd#8 ý0r «¸‘¢Éð [\0Ÿ?fåU):Û†g­4±L*yÏžpçµ\/ÁÍ5§¥ŸÛ­dõýHµˆŠé”kçe*ÜñÅ×ÖÏ@Ù½¨w±l¥´µU­g/$•ŽÞ¤Ó/KDkrÞd–‘9¸Ä¾Žžöf—fWûÁ”Rq3L˜‚Ý€B•RçMP·£0â _.<š'‹ã&¼¤= ,N'KÞFê( iSVó˜…‰P`È:Ä&à¿ÎÀÀü‘…ô[ „«sÉ(¾‹žÏFó¬Ç6>é¾ë-Ä5[¹àô}ЉH¹ÃÂÁq³ˆDެĔñhvº6æA6‚BÒX×Δµ¨d»XËòº¤NáqF´d)|rÉ5D ÄŸ“VͺP- » çãÌï+ªìÛáwœažG-•Tð“G}b¼ª¹ ÇtÞõïŸë“›î®_ŸŒos‡»&ÔÛ¹ìï¶k˨iW)3áE‘ìn֘퓹(ê:F·Ï´Õ‡ê÷ÇL5~„Ørí}¡.§èóRv¸6)úÄ€ä0U‘ÉÒÈÑÏKŒ|#«Ø·c±°qiaD÷øÞEv x’åãr®ù’k²$èFFƒ,7ËÙFàèÀäí™UãôKÞ’ z. îÁKŧúœÕ‚:´‰C¦¾®ƒ”¾ÝÛòóoÇf2é,4%œƒ÷Ê—§eœ¦+?IS·rÆ£ì'¼ç¯`-Vçì?¨rw®täv³û6®@õ_)úc»™óñdŽjÀQ›sjÏnc;ÁŸÚ䕃üÌÑ-ÉÊz–ñÅ*}BCñ³2[‰<Ü6ÅÍÂ"¦mõ ¼õ-ÌÏ7?v÷|æYØßAì52962_¥e5|"b´›¬3ÖFü¾røÙ€î‰öÓ÷¬!þZ“:lˆ×ì¯ 2÷Ï&Iùm¾ÔOx+T¿ Tú/Ô|ÙK½Y·wÌë4|ðû¾›½É~AÜ{2.ë;­ÔQÚº%>Bšôcï¦Oî¨Í—Dòãk“]“ÞI<?FÄóC`_ߎ6å“È F®l§Ê=Q®ö€y´§tÐ<-c%©ÌÍ)&ñUÙÙr̦Å^ÏãE½]ªH ±ãaùu…NÎk‚»e‡""nËä<¬ëÑÐÓ÷£$“Œœµ©Ax±¨îfNöˆœDå&½¬ô BHtÝ3µÑr#`ô7óûYñ‚C¥à•õQîZ÷箺®Å—fÊõ·QFivñV6TÌ—ÏNù ¬³¸ Ýæ·õ•\’¦Õˆ`ü) .&ûÓÒÚOƒÃÉi{”TÙŸ¶XÕל>9žQuòË”Òøí]>ĬöÈ´NýrA–n8Œ:Í•!ý~ŸRmÎÚ7¿W\ªêYm ܽòƒø4ç(ˆã}+pšÎ¡›”ÆÖ:k–ŒÍ3N¶˜qde¶ÜO(”¸Õf.ȼ‡^‰|=˜|$û¹ŒŠ]ì‰Z·ºÀ*@"£˜2½W›æÓQȘ0Û™¦ÈSŒ‡®Ïdåˆ>n÷d¤.Þ|óR„õbÒÜèÌV¸A<ãˆõ5³QÄ+¹ý|ŽçœŽÑ¢¯× š0ë2܈3Z’Ãó¢=Æ›î"?ÇßNµìf¯¬MŽˆƒ' ߬Œ¿—™j®ŸÎéIãÝJÄÖx¾’Ù@X…< Inõ¢q³QsÖíeŸ$’?ÝZäaM÷¤ð%l:‘ Oœb}Mh¢þð6-ß&Iím‚ÒÂn‰U¹Ï«[ÈV…œlfVe:»@S:8›¡r±-4aè‹Ðcjt]X㹆çC—Ñà& Ò9¦.%hÍžY[²RÁ‹bªF.7ðß,¤¸Ÿï `vüæH™P;T²Ç€+Ø¢ ˆmê«:£Ã¶öRÀ}åô2¡”]̈M£Êhóî•~²H뇱ŽÝ'릛ã‰eó.’¦[H_Û÷A,çÎèÃZ‹àÃzÀ¡ô:«½^êÌý¯¼4»êåo‰ÇÌñ§U¶š’º.‹~š8eßs­ñ(ÂÓfê¬V,[›xÏçòr´‘Æ™rB#æ69×Õùii„P˜°G*w©»P1X˜…ÂÊñ˪—&ôFj3¥Vöêþ3ç¤Xo_k~’8ùÎ:z÷(¥®5æ®Õ=q¯ ÝÕûÒìüjJÁÒ?ªTù9t©ã̶Â-ÓL© y£¡€íálÄ ¿Le©i:‘YŽþð¢è·€SüwD:ˆ£© ÅÄAr½öS Sï}Só¬½d¡BLó+Õ÷-èñZ¿‹ œü7ŒŽÓ5ÍÃãÜô½¡GþìÕt ‚Ædžè÷ï)¹á}þ¡McìÞcñÎ]såœ)Â:oð\zwÏ%ñËJñ‚ôø p°,+'’z£ßJ\ZÊÜdè…o˜±àÄi½‡oæQ“¹gY_œ°Òü¬½9:š£Dð|cÏ#+’d«Ê…“¿g¹§×*}›8ôÀ/y½4Ùz¸™Jï¨yÆGöÔÄôôûiðUÀ™†µ¥ÃËõLPM ªeÍÁT“K¾"`/úFsC ßf1sK†ŸÏþõsjr­lª¡›ßòÆ)A[¢GÞ„©Bª¶læ )¹ª‡ÑâfŠ~6øÔ1f²c£"þzÉk„EE<*CócX8²€/Ð}A}³ì\E²žkÄó;ùDWA•òÅǽÅâ'%ÔuXæ'“ø²tü®®‡T¡†•ÞÌm‡²+ˆÈ´)Ëíæ¿mç0Œ ©´;Ùö ÕžþôR "Q†R%îÂåÌ£ì?ˆ…ÎýÄü„eµ]0âM_Öiƒ¶n%j;Xþ 3ûžA#´%Vô$Th·É†{€¯`˜­úz¿òTÀž¥\õÎ#ÉGOÌÀûWÊÉù(”ï鯀û]¬#Éü5[莒sæˆ6ŸÊ²}½¦Ìœ-§o{¹‡Få|׊JŽ×Žº -:¨cÃ;˜'e]ωF0^3"’½Ö.ø4ßܹ>–Èc§Ù_þ‰IJ(¼Ç0¿¿ØLìB\øÕ ™ª]ýÓîÄñxèzpicùDÐ@©WS”ækå!F°Ô~¯–™¤öåžš­®êwÐݶ•„c®÷Ù’<.û2²r“޼ùQØ©ÓÉX÷ÑþëÕZ¯ÒÚÓeS”qƒm™ WXQp„VÛRië¡ý&å"õ`\<#o§×,cn‘xƒï^5‚¨;…èòF`dô½–oêÀ”/%[KÖ¦® 7Ÿ'¯µRÊPKDnAƒnpÚ•¢tìê5??USŠb‚v/jÄvð8Ò\yC¦’[Nþ"ËOl¡EÌ¢dyí1wi’ç5^÷C?ç}÷/Õõ” ?9?Ò>ÅŒ Ÿ¡Y.)‘¨ lÊÏÔÅ?ëét\æö¥¤«1[ôZ§ßfœš“! ɬŽ0õ^DÚË,£ŽFž\¯/†ÆfFB.kh} Ú:6ºGúìIQXa–„ËSb/êÝÈ›5 …˜«Úœ*›Þ¡ýçõwä‚©ä‡ï}¡¹DÈ•ìu>z¶ÜQ>Új±›ÊÐ&9nc ïùªc×2e.0ÕwˆÂ÷/|FJ>£kV|1{JD©d´Ÿçn?Ñ?ÚÚ{iÕ;„8îf/äßÒ4ÜSòºEz.¬ËY kL è? ¼¡yúèËò¨ ê9c†¿Þ …qŠ’õ|¬GøäÝæ)P‹É¦¦0Ü€Ò«tž‹!º{eéE -ñ#»¢ð»ùj3¥¹þŸ®‘"ê‹ÔIñj\[šS]c‰©z5²Zü)XùUg¾ ‘qkÍîä]~2›¦¢œþ2ofy•<Ù Êj@w:nøäZaüºñ pVÍ àœìz#ô0#e'*æ®ê𠵉¢¯·Ï[ؤ«ºKòè·¯›‡!4ÞXçûz¯3‚IÂÏ=á…º\öî)ÁÝëî™Ø€8ûJÔU·ö«o÷Ì^±`ˆ{Õ;,PÔŸ›ßuý}H‡qáí;M}îÉ/¼žÉEÂx¥ºdˆÎ ¥HÆnO(Ù[ K^D"oq¹v—éæ=Gb×Ö|y'oи_«…Œß´Úicvn¹Òðc Õ}`ŽEAJêe9£ÓBè­Te›w!l†¾.h&Š}t™²TR+ÓÝ>ás(4ãÙÝa¸$˜Âán®œŸs¼×Ï+æ]äPÌ?ßÚ®J3•º½g¿Ån °(:îÝvs7›²ÍÂÄ‘¾û9ß`TwuZJû:##Ü¿¤L²ÇâqÝò´á³}r¼Q-íž2ަû·Ê¼ò¤ûøž£Ÿyí Fɤ×p •‘žNã,‡ö3œF†U%¥*þÓ÷ì«Ë dßýü§Â—Yøi«pÿq.ý‡žÍ«Š%Æoì†É4H¼D½.~ÉðíøÅ8e›ãØ€mb¼ÅV;È\½„[¬’_gFã³ÃŽi'á ±=á\>º4Ö.E–²Â IJqub1-(†¶6á& ÌžUDPð°ìúˆFX}I·2ÏZQèmÍx –a÷ÁúÕNJ@¢wØJè´¸•7Ž,ëüq¶C³¡¤j£«Òšð"xRSÜ!c–©Öù:ò„9Ô#¤"¥!˜} Ï;Ęe¡m!K·gh#@Þª™›àqLÉv[¶“ŽËüàTñ[ýmG,yA62ÛàF•¥î;4©au2»0а9—ÝAXÃgd`¡Ý¨¨o{?DÝŒP‘¶ËÀÉq h·ßÂÄBSüdû¾ï‡‚8aB|?÷@‚wœ¬^6ßÉ÷=ídϪ×p¿»MW‹|:§—Ïð=±{j«¾ÀR÷òŽÇº Ø&Ç9*ÁòŽº9‹N”¹BlvÝ5¡´ŒÅ/ÑdË«4“ÌžèF1q¦#ÁN“k}Ô ¶¾Ïð/í>¨rR.¬¶Æcñâî™Ì›xÂð±£^kuŽÌò¸ÚUŸ¼®CTJ­„‚º¯º-`Ÿ$- ˆJ†Ó¹•É1<ÜŸ µŽÅ'¿Òýæû­¨ð÷† !#t+½Øë=*©šùóÎç·3†9INJÜ…îÞ5yÍéÄI%'lÁ#M`=1¾ Îu2ÉÙ¿Ûá’8‚2¶å{ ¦l_ 0Y^ã}Ô’/×Úò‚3pnôìjìõL]f9ñîGxÕÙ Ãd¾xû÷.ªÆë÷y½œ›i²¥u¼ÇdžÐä›$F"²/Ù.SXªžIÞæ–âœz‚w›ÆaVâkÑdÜÊ£¶¯ OC¨Éš7÷é£Ñä{¶c­ï]ÍâÑ·Ih3ˆ³·æ>*ø^a¤¹ÐZÍ:}’NŸ8]´®Æú´ÖM²tÅU6b_`e}í>Gò*ŸËÀg§»Vs••O}º{¿u^«!½Ù^h}s2ÛíqÌ9§éc1ö¯<œ˜§.Û rð·œÞÊœwao±";aañÔåŽ2!³î[OÒb¾SQ?¥#ÍSP{?›åwƒ"@©ÒídÑW ¾ÏF9'1ÃDüfY>­žœ7ôÛ‹$’WþÁ6ª]Hbßy÷íÒ‘„«ág%/k»çâ”ÞQ_í¹D>ú¡Y·WÃÿ${äóg‡[Eãõ¥®Ð{ æ>£³DÇGü¡+wÀ*œ¬Ðlm’HezA×>ÖQ©Ö/dJ1\^ge9@Îß—±ÛÌ[Æãž®L ƒ}jƒQ³C/ÓBROn¬´»k—ÅãÍ:x Ò¥ëÐßÂì6j hÔn|/ã©s3ê]njvcï°¤"]½÷a>Ö-J?¡BÊÝÖûŒ–#Áññˆ²®ç³ñcÐ4¯¦‡vš½9¯§X ò§"´,\ uj­ßu Õ¸wí˜S³sLu%¢ éºOîƒöýñs.‹[ÔÏÚ=ýÉM'Š=qÓoØ/b´r¤Ó(KÈ[îYºvt@š¨ïx Ö÷\²>qú®à·Ñû/ú¿2?ŽqQCa†v*FÒƒ9kÇîhoÙ¶¼ç[¤>ÕX±•ƒ·jöë¾…‡œçÉ?ì2÷<ñyt°y$ôŠ…*êÝðuE ¹ðwý#[gÓB%ŸZf85êJw–ÒÖ¿ VížÖ™¢MLuÏç©°MrhŠ×C²ú‚ºÉW£¤­‡Þ4ç@2n¯ß•SÅóøYÌpm’gûŽáQqªuÀIÌüiÈaà;»8[¬ÑjÞ=<ÁãHÏóò‘r¥‘+–•@žá •š`†¦…¥7Ó¹ô ¦é1{&Ñ¥«‡ÔmÖïÞƒ‡@tY’¤žoß;ö*ôÔ¸`¾ˆ´Îñ&~½˜œ²V¸/mf=²ÏôN’Ü óáøƒµª€;P,̈p¥j+4¾ªU‡F-Î9ÕÚ}c_Ÿ´H\žÑñµ†!šH±ñ¹ÿ è‘ÊŠãÏ— |  M'Ö›W™|ûe™£ßÃl.:×'0Ñ” ;"« MˆHSyï "·ckÒÃÐÚ‹þ‹(­ð[Ž*d”EDB[Œ<ØTˉâ)¹ÊO£Ý_³"Ã7™#5ÉÖ êÝ0: Mr6´ [T(œŸ ôß±ù ¦†wˆM7”,å2$­)W­tõ æb·»^|·AõÎçÌúý8ƒ-v%M9x±f´èŃöÄ.–!+W3BÖÊy&3à] ¦¶/¿%Q ß !œ°' %7túYENÏa'{(’œ×^ïÂÔõŒ…®=WÌ78tÖ¬©÷ž?³îyøîRqÇ’¤1¹Ÿø«šŠX¡Âqùý ç‡åd²èœ1pðÈÉtøÙvþÞ žmÿƒçÆu±´ÍÙWòK)ãýZÜô»dv‰‰1ÎâÒK¢Ëêçv™ 1¡iÑJf¶O5çXõs'=’¯X4¤Ǻ¨óÏ܃¨·vy SkÌîµìBm$vZ¾s…Mf¶Fžžð÷ZoâyèQUéy Õ'”-¥GópØJj¼ üˆÉ¡“§pBŒlRT½0™¼¸Ž‹sô‰½ûÈ]švƒŠnP;@2ƾ9þ‹hJt–ÑE~/'<4Í”sp[JNn¥Þ±«­¾î%L|xöDßìÙ¬a«,Œ¬ÄÈcœÏÛ6a✸©e^}7—”=C¦ìE’îH ·¸ÛÌÜ-(7ϼǂ¹ r/øÿÄV{ endstream endobj 1536 0 obj << /Length1 1533 /Length2 7485 /Length3 0 /Length 8508 /Filter /FlateDecode >> stream xÚ´TÔß6Lw—¤ ’C§twˆ„4 1ÃÐ-Ò]Ò Ò!Ý ’¢¤ä;ê?î½ß·Öû®Yë7ç<;ÎyÎ~öfaÐÑç–µ…Zƒ” 87‡O ¯©' àãàáããÇaa1ÃAPCÌ …ˆÿ‡]²‚#0+8ÂM ¨y8€ °8PDœÀÏÇ'ö·#&P°òÛ4yjPȇEêêÛ;À§ü½°Ûp€bb"\¿Ã². ØÆ д‚;€\'ÚX9ô¡6`Üç¿R°K:Àá®â¼¼^^^>>1 äyÛ8ðþJnàã úmü #îàç uØ!(€Àv ÄŽŸ»•'‡y€üþÓðß; ` ¶¬Aö`οÙ0ÈîÏQyØ`ʇÀ÷ë÷ÏÊ ¡-[(ÄÙç_÷ßÅåÕRÒÒT5àüMø“œÔàÇ- àæâùDø"ˆEÀgù‡ÿßÜ£:Và¿îÆ÷oFUˆ ö‡âíþ¦áù—&ØÿjÀŸ Eè`ÿWöÏø„ølàÿ³ø‡üÿiþW–ÿ‹ìÿ÷>JÎο­ì¿Ìÿ«• ØÙç/;BÅpDGhB}ù_×§ ?M¬ ²{¸ü¯Un…è Yˆ½ó?vW{ƒluÀp‡?ú»ˆôÎ`Hêþ5fÜ@>¾ÿ±!zÍÆ 1JÜ•úm!Zé¿T„Ø@mõ¿0À ³òÁA±øÍi òþ­k/ G„ôvPίz"‚xÿPBÀ¯!í~õ€ý ˆx!ˆÛýŠx¡ÿ±GœÍëòDL’A‚Ðø?{ €îýG ñ‚þ›SÀë ‚ýþ‹² †¿%‰x¿÷¿gä ²ÁYœ‡ÚH¼p¬{ÑvY+KãŽ=.5òý4ƒƒÛoÖîqE€™ÆQ“ý| v.›6ôèÓgEö3™%ú[¿¯Í ˜-)º­×þ7IzSÛ­8')ú'оÊÖ÷ÑaÓrÈìøßºù†8¡6#wª±ä¹yˆè^zõ*{×÷U,†ÏoëîÔ«ãÞTLsÇ=‰}R:Ë’o3GɈç¦ÃzDrìM8{v>Còzâž^-‰'à N ØÏd?þçœïJ•¿{3• %êÉ諟ÜîKµ ~eÅk°Ñô9Šö± —T ó.»ï¶–Þ%lï‡9+Ë„8;%/*™jLìZ‹J²K)C2†`Îñ'µ5oÕ"ÍÖî\ÚÉà{mg‘:‹Þû©úŒvwa‰´:éÁ9é MÕpï®.UOµh^c¤ÜƒáJ3Œ8×RŸ+é3É¿ä¨lœ%T°ÅGÙ½“txu†®Sï52i~ ŒÅé_ª²¹[¯Ò´.¥Ó`øët¢º¦QhÙh“7½©–+=;{+WkØÿ¹‡‘ÃÀ!©dÂ\ž˜Ðå%%»ÞgŸ4®.wN•µ‰Ï.H=þòX%»1b”E+®UB”Íúd Kƒ4Τ'­+ÅzÀvß|À\ÝtÜrš¦¶"§ºç½ï]ÝUâpxx_#°e„“„ŽO§éy‰ZÆ¢¬uܱÐ÷€kº¤7Ðr2ê‚ñÕª:Q¢6¿€èR"Ëm¦ªüÜf«ÝNdk6¸dôÿÂuÒRa€€àîK?~yÌ 6«79ÅZ=‰¨Î òE?{«!Ú5à9/¶@4)íæS JûKÍ·ûžIbo•F^ù^‘³=¯¸$~ò¿ÔNç±> ­¡Šèøx½¿oˆ]¢Ž¿™lÞÜÔ.:Ñ*É¡YÝþY Åe -®LbôËË$ÚdÂêm[ ™‰þ8Ù—‡.:¡vc‚µ³-tšQ¯tJ†víù'ëœöq£›Jgã{YßV \ñÓ ‘Þüˆõk,•4%&§«iW<›B%1Š&1é  f>8_ %A^l¦ÄÜ5ÉEB¡-_J›8,!,â~¦Ð=å""0ÞpPþ‚ ëà'UdGaóf©!ѧÞ"Mò­k®ÂZ• Oo?×` ´‚  WÛ•iê^ò(ì&¥=M-{v}äèM@„ô,ÚA‹´D›Œ&éhᡜÖZ•r‡ÑY‚˜pŠ ÁÃ×y³¨ßöAßhYÝÔéëyl‚ý€é<»»ç„ôís*é¤ãá¡);þè¦Ü‘ï"çQLÁÞ‡Ov{Ú]ZâÛÏ,‹m†œ˜=Ä›0ô*ÈâÁŠ_ž´µÎå½–Ú:sSŸ¥ ¥ù¡gÏôRB¡íåMî$»gèȉÿâT7–rvJéîÜaöW²u'±¥ºùP²O5ñmï '·µ€W³ØÓÄK”€œ…ýeñáWß¹Âfڦ⻱™rê'>·®W]ð…ë¦ø¨ï7iü‚e_¬„§ÝË’#¾P0`Ìj¿0‚b™W럭˞¸Â˜½„RËžޏ:GæEC€E®¶¦G#‡ÎNÝè Îz[ž?‚cc-¼ÃHÚv-2Scš~»ÆHQ1¹Í½P¯¹†=ó:ËoKò`9²¨ËRÂ7ÇmA•*ÜBJÚ%K›Í —·kÏ`á^X~ßY¯÷¸m3Zeœ.s:2Ð+Ù!k¸¡\?ò-ÜÙJQ˜ Ë`'=Ôì‹Sb)N I+‡uœÙSàIŸt/íÒYŸó1†MÖºh#›i`< îÀ!“}u9U…1Ý«9–r¦Ÿ­¤í­ÑÒ5è1ôÐ,´Õbjjé >2™Ø$ÏP…ÌÌû‡Y41¼ÐKäþ\UfÓ‚OÆ\*áÑ$Å}ªKù³ñ8¾æÝ´”¿yÐYÜÐèã+ÜYä’Fit®dz.¯¨í­~…ã¨äRòƒÊh¨5Ÿvn2"F´1t€.fiï¥ßgÌü˜0›ÂGÛe# é%ëíµ$gê;ÿ‹qb™¢Þ)nâZǯ²v›bï: o¬Oóë±IDQ ®—më)ö ÛZ2¶@ug°hÏY.aÉt8š4Ž ÊÊáÓ>å"ŒŸž÷ËÎ ¤{Gý‹‘µ¦B0ˆ7,ƒcOGùùíJkÍ^qûÜÃsì^Cí¯dàñ/)K„«™ü§|\.¸Ǹ¹F¾gãÚ…){?½"ˆk¯lçêÞdÄ'ìÆðB“íˆô6¿/ai8ÐÏuÒâî=ôNU¿0íUoö%‰á=£ù¡£È¶@¶â°§!Ff&;Zù‘ú9w“tì÷¡oY3É<¶~s®Xr°8«v<ž`ã>ª«ÜEÙ¿rI)–¤cqþ–§xsHÔèÕ§+«à¹c&ùUã¹¶yðm±«gœB˜yz"Yo/&}lÖ2þ©7sÆ»CN;çÓ-ïä$ªü‘î©M* ¾ðcšcù•&M„/@p}Ïì?½pÌ(3L¾duä=é~76?Uz‹…û½‡B•ù›ôç5ì[GÅå.f÷ÕY):^m;é¬öŸ3)‹ºŠv,ÞvÙ]±‘åpÙ­anÆnTÈfã•*tö‹½H”E®"y)ÕâoµueÉm‘ÛÃçæŒÓ¾‡|Óub÷'Ÿü`q¤_È;8r!j•bøs… ºäÉø„?ž¡ê Ô‰ÎAHW:á$¿‚c':ÍžU-Ÿùˆ¾”Œ8xv„k}`ÙQߌœux ä69úhpÿÑòQÖ£øp…sÄ};­TÒt?7Zì6T™u[´Kˆ3]“ß-£z¯Âiò}_ö¼ |ÙÀ8ðXÖ½Q€ HϹÛBD&•Ø„öIoyo±¬MË£IR¤rÙîg‚tZ3~ ô‚÷‡ù7hBgU[]5Ý‹R †B<¸90dõk:Šb·ç[ú™¨Øô®¤r ‚Å;†àJÈ!چܔGTÁ‹D·YJIRß7ãé$™¼n{±ÚM¤ ]à(Æ/(pÉ2&N2fî"JÓÓGµIø¡'‹ K5Xì“8}^ÒDÔ{j^K.ŠÊzpîÑŽþú¦;)]ó±óÉØÊNbá•ÉæÜk Œ ¸ZN%¹Û@f7¦Úin~ª“Ç œOgEm9På@Ãê«õ°F< u;*3ë{èÙv?îSkúZ3Ý8âG;fˆ«CÑ6¼:SbüYâ§ØsQñ}.¿Èº:©[T5BÅP~Ó§oZüúD>µÒ‰uŸÓ°©¯æ^Ln_3O'€ŽéâSÎ3tå®…¾ ®4òmÒ g÷;¶•çJª÷*ë *´a–ÉHçYžÙi_Ë·¥Xfx¬v|Õâª}4å~BÚõ=œ|<…Þ.+øâÌ—~¢a¤ºXGz¡ëÏ:_Ö2Ø5ó¾EÒFÀ‰u¦mQ-/OÐeÿÄôc—iÊ ƒÿ¸]6©ÐÖÚ(Ž˜Zã`\óÃÁ³¹ù¾j‚¹Ï¯ílQu[àÐzA6:ýöÚteߣCPKË›•íSTU^Æša*âÿ¬%8I‚õæÜ¿ßngÀyö٠ȘÓ]²³>¯iýeV³ô:ŽæëA¼Cü=¶Úa‹Fî,V4ùgž®t—B5™óTªøÄ÷<²Z#>$¾™‚ËÜì{dìX¯ÍR ¶ËÞoŸ@ {Ý6ªºÞ(~+™àªr©Cù¬Xºß$,ö8mæ¦LD%œZ6}޲4uZí÷yòM^œ´á̈V!—«Î¹Aʪóøk\;û\ Õ»öµî4éÆÌe¹™ùŸÉ:<ÍÇ8Š+\}‚6%óÝþ{8ÔI‘èÛRW_jÉÇ¼É­ÕøoŽPÚ¢¯Í·ÌŸ^ =‘5yã­"g:RTµãBÖã/Ås|µ:@Ð0 ‘AQ"œÍRe%·õoIãD 4kóð¡­2ö}À’ñYÖÎG2Éªè¡øUÁ-Y€*·)^t¢÷É«Ò ýY˜èó¤¤E49½ úA†Ù"œÉ×W'!žX¢[+NŸÊ¿3 vR¾> ðH‰˜ü%ÈnØË5³yYò•aÞ1°"N:€•ŒÈÚ¨(£Ém¬#ð•Ô;Éåv<á絉ð\‰â©HA½ü6§¸zÒì~TÊÛŸ+J¶hZö@,åñ––sÔd¤±…²ÔwË«[}Ë¡@ŸÍú«Û•d‘£!^íòÎè‡;UosGÝ€Jݺvy@´õ¾Ö˜4±gißöe>´½úB²´ÍÞIÛh›E«H‡6YËÒô–ÏNÍ7jô³ùCÜW liõuX(Çïñõ üc áV/Ú(B ŽŒIæÃMšòOoMhCg¦ìÃl{°ŸˆÍð~öÏ?1œh7° ¡Xa¥f¶xUE&̼ö%W^PÞðØÅãE‰b¬k,©Qº8ƒ¢—ÕKr=JŠ«yðñœp‘iÁø;tA|ÍŠ5ÑQ~…2d°¦¯ØãòL½Ý²¢vÄãC<úÒ•÷<ÛãÊËJ—Û`rfx~’X‰»q›ÛŒ{æø1"<;’¢”íHÏa››0Žç¡Y™Dœ†f1´ÿc•MMáÄyŸ÷;ý1¸‹2Ú ·‰ü§Öh“ÇfÚGüó‡GgX‰ReÈ-ÂæÃÌŒ»"æ:ŒØcSÚd»Ä­ñÃùƒ—W2¦îóNHA‚Nj­Ç2¼oôµ+& ÆjÏ]©·|ý€ùõè4q©ïîг·¡¢GªÝ¨-&û£±w@:¼äI盃‚C¬(ŽíÊUÄ«<5ÆñÕ7Ö¦ÇÍj;¢¥i”Ç"Y‚ÇÇsªH.ãX™¿û)ÿ<*I¶tn'zÝSÁ+Áêï®I@Ù‚yÙÕ“ûì<—@_K±@zì´ËsÙ;¥•KÇ*c‰L4­Ÿ+v*9kxù½6|pj"†"½1Í&ûUÐ ¸=w‚wÜÜÑ~ÄH÷èì® Õ—Œ]‹=w¼çº5Óò(‹£µbÚñåcùètà#ù¡¬²5U’œD,2kø5,ÂÅ&ý©[ǘQ…?ÉyãÞâÎÉ~¬X6ä ×L9 ¬eÙfƒV&Œòøõë%ã?šfQ¨¦â¯ïx²¿E²¨ÓßAS∑ª$>Z¿’hG†mêôÍÏ2ôéÈÍBË6>°ëDÌŠ=;ø'£…Žè7²dNTß@­ŽeN)¹ (M â /»SÌ‹ÇB×w±Âtk•A²KYªj1l ~ܲÎuÌß\˜ªu› ÑÛÚòQ{pVÐÏËÛ„Tœ™ÌLÅÌú;åzÍ$v7òð "Ö¶LtĬ„´íŸø=:M|ÝÈ€©m>$´‡/QÒ%¼Ö?Û«:’›éÂÝï-s‚2³<œà>mÚÉÔ…«E› ¿hý2ƒ³¼Ôúª€s(ö²r Bì°žó:öÍqì‘£sYjªÂ;©! Ô\¬€¨ÅæO€ÑÒ›båC0¡éãšI}ŸwϬŸ ™¢€u:úMºt„}ãßëçM«| ÙǬµñz¿ñO48讀a\Ž]Ô¦­‘¶x¨•|–ÿQÒ$ìrlùC>J¸"}–@ÃÐØY{ÑvwÚ†­zON¥í\¹Ýk]¤ª=XC:¹ÂñÑ äèÄ'+BíüHÄwþî$mZ &$û“Žä|O·sAõi £þî¹9œºW„çìOY ÕÕéE©Æ=Ô( å²€…ÞeìÐÅ«i—¬$ÒI¡#Üp<Í­ÛlŒêŒ@·³?þáfæ†s‡z6 Úngµi}uû®”Ðã­gÃÅoÑå îi£Öø‡¿)1­ªgWkÜ+>’Ûs)§ýä…[°bÃ5TëÀõZ’ɮ՛åŽóᚆ`^šÏõ^§VS|ú‰¾{S|ç$æ©cK{ÿàÆüõÂnÌmûµZmNHgbÝóZÂî-‚á ªmÌ •ƒô7!–Ÿ0ˆõýÓÎØ¬ÈDLÏçMXƒyä<ÕÅBýÇjØS8o—¬­ü”öÿ Ñ'd 8UÒx%íæ´JKL¨ Ô¬Ñéh?Rd±˜C Ñõ‰"å„pÛ‘C‡VCÎ$ÆÆŸég8«+ÊT ³<Àè+ÒeœÅÜZ=9d1T8)ªŠ‰oyE>½ÓìsÈ)ÁGQêhÉSNÿ>mŸý§Xݧ͆ÒT>‰„„—¼¦È½1'™Ü ›¶ñf9Ú»0¤|¹9*ï+mé.KìŸ:ûC‚±4æê°¿VJPT™~õ,Йæž`úEòYóÆ÷oãÛ‰ן…Ÿ”ƒJún‚Ž”ßÈu@Þ'|P×ʹѦ»ŽÍÞ¬ çÏ„$}í1”v€ê ù¼^ÉôÌëm2¹“ak8âìrñÉÚ’y(îš;MÞw¼‡åÖúÄÒg ‡+ÿ.ŠUf¬A .›ww)…:^‹{œ°týÚÝiÌY¡Þ¿ü*$!Z©§æÛËûÞWÏ)8è§™/W®s˜ÀÖ\óx¼J²¸{Ÿë^Ö¦ªg8ù žœ…žFû¤-_=äù¯PUßä`¾>¥¶ÍLv—ºddwÝÖm+=ÏcÔÓíÚ+ÕwzG–p}ÙÚ Eš*?ï0óf¥K#rzÑ"¤xSƆwã¯ä’~•iVuüÉÅ Çoâ¹C&0¨×$:)ƒ½ÿa É­g6\»‹ú¡x¾Ód%Õ+Á (Õq·Î}àl^åîÛ×Λ1+õ+w#ÀAËúSU™¬èx»ã÷á§¥ŠÂ… Ä’¡ãœ+Ÿ½²ó²x-BŠ9´N Ç5öº T¥w²©<œ„éûaëöe-V©Øp7©ŸD_4¥j<¾:ˆX9*ß—{!Ï¿ /)œË¬½µýè/²×´»oAšFÒ‹,ûcà¿ûc™CØG§xVfÈöìx ¤z±Óòš,f¢ÍV]AË.,¾f+H˜¯?X©•U8å¬^PlíÍèÌÝÙØ:Ég#å7ÚOîõk CÅÊsÙ²X•b™ŽŸLlÈÆž¶wÃÐ~‚´8Vá¥YÃFc£D§¹H~tÖqg;zºœ™þõÔÑ÷ËôþTòlÚÖ·æj'¤®Ìïo7Å#ùcˆ î…ä°½ð!Ð-î6Eq¯À~HdKë·Ø"´úLσú^²’HœÒP¡‰šæJÑ2®N¥bÌ– x†×tò*àmcÍÓötiÆãQ‚:t(–ëìfçh’,œ èkv5Â[×÷þÑtLºk¾ÿ¥Bò-$(Ë%~(0êðÙŒ¯et‰ÏæG•€Ì÷“‡wÙ*!7³#õœã¡r•êÅ—¤yCý%yíÄx@­S”Ý¥ ]ik,±.EÕ|–Þ× 1&§ö¹ÞÈŽ€›¢vðÞÉÄm²¨eí{ô¥aWI¿µEÍq£lÍTǧä©ýÊ›5úÓ:+ƒ¨øˆŠçSµ•d1„­®º½Æ>|(Œ‰P¹Ä¡Ãe+—μ/ê4²´ïg+%áîåxsËÈ/¹’‡ç‰×ÙMßΘæ7áØÍÄ£¦V»þ²"ç¬.DÈ7»Pˆ3)oQ’¹j›¢›ò4]è_üô¤÷B[~£rõ=ôG\‹ó[¸‡©¥sp*ø«ó|ƒT"¯„$®èŸºƒZ4tµÅåÞåIÊJž,t§ÜMºcì|ØÞ°žÏìçüHý¢TÚ(†Êàãßcv½á®2S¤êõ'XúÏ ¼‘ˆÊ^•xψÖ}|ñããó.4Q6eßµóO=ÇÊHÌñÁ²‹ñ©¨{|‡õz`¥žS¬¾¯Gb;;’Á#é§‚Ï©W‚9Z§šªüž©ùöôD¡06§Ï{üã Æ cßÊBò‰JþoV¨;|Ø‘øµÖ#•ÈsÌLËï FX¥pÙ¾bˆ{½Î0Pþx,¢¢x·uÜÒ¸ä–öÅIo„Ó5Œ©>¡íÞ2ÅUQDCÝèü¹Ÿ1³¥jj,eP¥Ê°§µÜ9ˬGq íÔÖí³ÏS~µ±7h®wÂh8Ÿæ3ë,NÊ¿|Ôl§ ·úÚéJ‰›ÅUÃJŽíN@oàØrTœAÑ7õi…éû;…Þ"îwòd ©» ç¥>ŸøŸŒ(#(óSÖôÍ5íý¾©wE9™/ÌÃ^Z> ¼86 ©y™orf–C=LÙ‚óÞ¼øDÔÌÁF$\?Ó6tU((õe%î56‘Qž2×&z®ý½ÛÕñvÔÇP9¥@Ì:~m~¹ÛÝù„Ó_Žé{¢ÏšÑøÄ5j*áE.a?¨ª“s´×nîNo% È 2¨Uí©¯’ÏŸâá…u5ð”Þõ—(¶824–±ø²Q}œLk¦¦¦ñ½µ}úÓ‚‰=~9ÑK­úùB?ãÒqÇBÖ,8k¾2ö©¢qYø–¹þ[~a½jÛ’ð¦úr'z²W&!ø+"BÞçv O¯‘Fë‘jQ²H‘ëô·RŒ ‰™±p†NqkÂO~õ_$ç^Ê8²\j¨Êž¬u¯(`·ƒ}.(.W2-¡S@=X ÿìà ­/pÞ_M¢lV`ìÁqY2â³üÞ€¤§ã±×¤ZÃ~œ#zXÖR`3{'‰éÂÏ_’— Z.¶³I àæÛ£:Ihb{kˆA&¶ÑN¯W÷|Î’ŽÌ´Û*.­™PçìDm•¢ˆBXeCWûn6ÅvÚÀ¤…8ºpí‰Sdì9Á"ÿ˜àeó}Š“AI¶@ý¡~¸ eˆë(½(¿mÝ[ ¡y§š«{l.3Ѓiu£Ç#û™´’%´ÁäîDxDÕÑÏóô_6ãÂ?Î×d þº m3*ðήüÁƒ• HêúóÅãÉ¥gWSû3't6¹iÑï¤%ÙÚ"ê—™v,^Gê5,éÇ ¹™†ܘÕx Š9}=½Â¸Ë—묗&]7é")m ¯»ñžŠ %¾Ã8‚Õ$wJGÿ jcHižkëÔÌÙÙyÜð¶Ï ÕÈh¥)Æ çƒÌXJ)g–Z<æ´ø¢ÝÔ+}¿â¡“î@zÙ…#•7ÊB•߇œgÇÀ/ëI^\éÝÖòcñ˜î)˜ê.¸d/ú˜Õ”Aûɳ Š‚IâÙ·¹«¸ !¹€ÜkÃ[GľÒR'ò/Ø êÜ0RŒ½¶jE²6ß³Ó;ê"5zwU}ÊwiôèŒ[ìJ¼¥ªÀ())þ#/s|YE(-‘fÂ$oe.oíHÂK¾G@!¬Y]ñ¦»&L²ëízpÈsG®'o/bˉt²n Õ¨‡¥évbiZ~Á¾A³"@¶º’ÔðJÁåÉ^Hóv8»ìEÁá“oîôëº ™B«6ß=àžæaºœžh$7»è’È›Nk û!£7=WîÎ ð,,×§:HÚÑQÜüÄ IªlËú9‰um¬ØDË”¤C1km´Xµ¬ÑÀ²mûiêèºíê4–ðùQagcÊWc Mí+5ÂÚ/Ša1ìX}á˜I-½‘â Yb9Ì—0f=II”Ïhók^6U.»®0=.žŠ£ÇzEØþ£Xӆ͌‰vý„†d?45'Ù,U™ˆ½–ýÄ$·_ëÆ“˜è™Ê8¹4™3§•}àÄjÍyH]ÉX-—ÜP+Ió¦ÜX6I.~>È9\­®?õ°Bî‹Ï÷@Ó:O}ºÖ&÷肉û endstream endobj 1538 0 obj << /Length1 2207 /Length2 16432 /Length3 0 /Length 17752 /Filter /FlateDecode >> stream xÚŒ÷P]ÛÒ £ÁÝ}ãîîN‚»»l`ã®ÁÝ4¸»[p î®ÁàÁç\ɹßÿW½WTÁí£g÷\ JR%UFQ3{ ”½ #+ @\^…ÀÂÂÎÄÂÂOI©r±þK O©trÙÛñýC/î4vy—I»¼›ÉÛÛd\m¬ìV.>Vn> ï íøÆn 3€<@ÆÞè O)nïàé²°tyÏòŸG)-€•——›áow€¨-Ð djl7v±Ú¾g45¶¨Ú›‚€.žÿ‚FÀÒÅÅ™ÙÝÝÉØÖ™ÉÞÉBˆ–àr±¨Nn@3À_t ƶÀ¿‰1ÁSÔ,AÎÿ«Ú›»¸;ï)ÐÎùÝÁÕÎ èxÏ P•–(:íþe,÷/À¿[`ebýo¸{ÿd÷·³±©©½­ƒ±'ÈÎ`²¥ä˜\<\ÆvfÛ8Û¿û»ƒlŒMÞ þ.Ü %ª 0~ç÷ovΦN g&gÍ_ ™ÿ óÞdI;3q{[[ ‹3ü_õI€œ€¦ï]÷dþûX­íìÝí¼ÿõl²33ÿ‹‚™«³ºÈÑ(-ño‹wü™ÐÀÉÂÂÂÍË:€¦–ÌWótþ­dýKü^¿·ƒ½ÀüÐd|ÿïílì¸8¹}¼ÿ©ø_ÏÊ 0™ºL€ ;ø?ÑßÅ@óá÷“wytYÞÀò×ÏŸôßgËÌÞÎÆóù߇Ë,¯%/¦#Aÿ7áÿªÄÄì=ÞŒì¼F6N+ ;€ûýÁç£ü—ÿ¸ÿ-U2ý»6–?¥íÌí¼ÿ¢ðÞ»ÿÐpû÷LÐü{]hÿ›AÁþ}Žš?c¯ÇÂÉbúþ‹õÿóðÿíòÿoæÿŠòÿ2öÿ·)W›¿µ4©ÿ´Æ¶ Ïëß§ØÕå}#äíß÷ÂîÿšjÿµÄò@3«íÿÕJ»¿o†¨…Í›r–yÍ”@.¦–ÿ ÿœÁ{xPÉÞô×5`deaù?º÷]3µ~¿JœßOêoð}•þ7¥¤©½Ù_;ÇÆÉ0vr2ö„?øwÄ ðf}_N3 Çßs `f²³wyw¼Óó˜Û;Áÿuž\\f±¿DÿB<f‰?ˆÀ,ù_ÄÍ`–þƒÞýäþ wKÅÿ"³Òôî§úq˜Õþ N³úÄ `ÖüƒÞkÑú/â}×ÿAï:“?è=»éÇ{†÷«Èöõ_}e6ûd0ÿ@¶w ­™±³å?LØÌæໃù?à_JÐøNÐÜæ¿ó/­½«Ó?‚½Xü¾çû“Šã½–ž–ï×î‹wÙ?â³¼s·þ|'oóøÎÞö¥¾sýG¨¿J·ÿ“ìÝöýÕôõ{±Ôï‡êð¾‘öÿhÖ_ìÿ´úÝÞÑÕÞhfbc4wù¯‚ãâ_ ÿ_ ï¿5ÿ+fe}÷øG“Xß9;ÿi⻓3Ðdjocÿ‡ Ç{#œmþç XßKþôývav±tþaø¾7Ì.îöÿpxáúøÞL·À÷¼îÿ8èwoÀ÷ðžªywõ:ý+öÿ,¥©«“Óû‹êïKó}cÿƒÿ~+@Søå{Sþ`«ºàއQwƽ ÁYÊ=Í/´ŒÞËN®¿‘aRh«37îDSFzQ×~JÒÜŠ¬¼xŸ´6À„µ%)·?}~6LP™Þk‡_šÂœ,8­ ‚#dTÙÿüâøY#À²¼K†ò›£+²Rƃ{ÿGú²Õ±Ð…=åýj.Y„ç²Æõh½€â9Ê\“¬y\².ŒD°tè—(s·w³è9“o$2 ôð>§1ì…Þ:[l±ó^ëjlÎÝxx:¸D·ècÓTÞb‡©28‹Þ%…kƒK­…$9H ikŒ¨L‡lÕ •(»æ¾Z·±åכּßRÁÔƒ»Iµ¥Í˜†NdŠXÕí†Q.µìÖ@¢Ãïæbëw=æVi„+¼3õo€ynÒ¤Z¤ì÷ñu-¾nDöÜœ÷‹ñ‚³„/Ò²ØVÛOæxšÏ8/Òø§LÖnâ£cš†ëœµ1Å«š:·išwŠÏ¾kú¤²dÃ}CKÓN®§2s…ÑÂRã+\'2Y8Ž`BWbùÓæÈÔlßLv U+?xagA 1·ZoîwîŒ|Wª$³ºê H\ÃuÙòÑE_!ÇJÓuØOØ3D1D€9»ÊÃ9Ò†9[Ƽ#i}ržB› qõ][„bç‘’ˆ³Ñ{',xà¢PÐ5œ~.|Z µ¦Íj®‘ø}:4‚È.>™©žÚ‰AøAÂ}ôñò(‰9¾/e:2±"ŸÒ5Y•ííÆ­}}"àÑ7|ßÓånTÖŒìÇž9©NÖdˆ0ÓqÓÐPáùØ­šmcžØ‹Íþ§›Ìo£’êºÁ:Ru¥FÊ7ž´FÓ8kf@<)é´B'uÆAß7‚([‹3ŽPY­óJ¤8ïže¿Šúû¤ô¤à}î#Š×†½«/Š"òOeùÂz£hä0# vJcÓa½(s æñedž“ŒúË f|4¹Zòú¿áŠÂÆ( r)EgErµw¬%¹Î+À竞k‰ü¢Íö3¼zH¯fÒ)2“¥v#4û°òSؽQ" bsžÈsÅèó|WüÛŸ°¨këŠTT ¾bÏ*6~5“º=ª¡ùî›U‡Ûp‡LŽÜÚ]¼ŽY±Ú\¶$F3-MH#Ò‹–³ÿs|—Ä'‚qû Ÿ÷ž§(E‹-ßT`õŸ¢Ùb +½ã·þ6¡ŸÎ/ÀÔa‹m/hR†µÆuæd×ËfBFpÞ&WŸÄB`B£´'ô¤Ãl“3Q¬ÙÑð¢‚2óÔ,o…aòŒÑE’¡QðW[þ×xcS\(§JÒ®uX91DñîŽxW2ä04\Y‰ãX%TûÔéÓG/k5›®a¼Ìm}Ò§õ¯‹«BÍ­?_‡ˆc–š‚-‚“ógC¬…Yký’¥my“=éPåªç¢a©jg*ÚÑö†ëà»Y> ^j3„ùN™®öóG fƒ¹ú©X]ꉜÕåe‚ç|3PóŒ¥ës^Ä$\( Y¸PkèTÊ?kt¤u4SŒ„›(úòì}1lIw•À'NÎ$ƒ®'bÓÜ^׆Èöwowú{V°[kÇJ8°]utš„³PäT”Ï~uä[>Tü°âß;1½ÑÒ¬5އàíUÁ=A š¬ã…·ÃÒKŒ•É¿,ðS­Ø¹Zhl•¬ç?@ :çÐUpéÕùåƒUg~¸6ÚüšÚñ­Çvd GEÏ]Æ nRÅá®æjÆ7ô`͇£Žrlh®©ÛáÛßNÌM3qØ’\¦?ÃdA«55ÓzÏ*ÏÃ07”½‘m:;_z‰•a·‰‰ñ9ïõ$[±­ê&5$_)ö0™¹wàTÐ}¥SñtNK¦8á3a>«hý!û ÃÛ¼dyÓ÷ wÕ,É=Ü#ì¶P&mO)Íø".¦ò\D·QëG±É&Zq\óÌA›ˆn‹Á©ü#°è—ú~¿ŠÂ6ø!r Éo!'±Mmq©ö” ÐkJ§!l¯%•7×™4™’«º âL‘]†ÏEöQÎÎÐϲüc˜E×cØC+èÜ5Ïl”OÙñÂþd1<¢µ}¡à±Ñß7d_Ñ- j4çÖ÷Ç{×h4KAéà-ø9ÐÎq†ß³ÊÉ*˜GL~N¯a—}øy蕞}²·ÿ]#°×"ߦño•ǰúc‰•Õ¤‡¸”ZE¾&Y«aUZ’ŽˆO²fqq^ÛˆË7h^sX ‹ç÷?aœáÖe‰l‰â|ѪõIÿNäi²nëWyºýÌ3áõþSƘ_¡.n6€tÏ&6Æýù¤á×/9u‰,ÓŒLòÑvËn r °Yè äé“cªò>&˜÷óØE òe·³cÆO‹²;(P›E×Ê"ÛMoÂènJ,zŸ6:ì°}sÏYns|ë8¨÷gÖ»?¶t›Ãƒâr¼€xË+´,íË;Óñ'9”fë¼ætX§–µÎN\BXüëFÜk†úb„ËÙ™ FçLz>UÐC0(ì"Ÿ„°~Fï q®« \eaRÙÚæa)uÞüŠ Oµ>pzÍÏîg̦½â,òC–/‰ ƒ†Ín­»‘=£ Ø3ñr ì%›{PžJ•Q V<¸oÙ;…õÙ‚E§:2ø`L~8Vñ/g¿Ø„þêër"Ñ‹Ü{i(ÙFÂJ:¿à´Äœ…úF¡ºrr¦ºðq"à¬úä‹/QXÅD.Ó¼-?à‘I?@µMXf¦æÏ”›`™5k]à…¸îÞßJÿ ~"™ÆŸó«CûŽÐç@ÕV*Ò·VbqðS¦¢’<âô㊎ô¿Ó&¸§õ¢*[÷™âA,X=\Ø*Ú™ˆGŸ¨x-"‚aò„—YÇ@Ä#šÕi}ÒƒiÁõÃEl¡½)—Úüž³öØËÅmµOíV˜ ƒýÂJÍ'åª^OÃ..<Ùo¬ƒ†"XŸ¼<Pã<®kà -ê,ô¤›]"ÎI2jc£Â9mû)s€„÷ŸYκ ¡˜ýØÕ@^î§PA»gÐŽ>?R n÷‹_·ê_Á³ ¥Eï‘©cµÄ! §‘'xJ¢Oì£õ…çŸýÊ¢{îi_ilé„tû>ò6Ôœ÷˜2™pKB/‡@øT~¯ÄTýE#Y0?.ã;%ᇬbâgdbç×Hw^“R×üí„(Í^Œ¶.3™‹4Q5èx®hâuÓÈQ†÷±*¢ØÒû@*ÆšW ^޵êþ¤ÒÓžF÷cË×Å}öJòcE™"žÏ¾ÛçdÑ“ç­óC}Šuö4ýí‡;â›gÉí8|”ˆ(Tö6¸,‰?î©»‘š)ý´…~Oh´+ØŸ -Eߨ ®#ÃZe ò{\D¿öWÞdüèeGÙ£ý°LúñÚ¥®}É\F/„öö›÷-ȉ5†‡9Õ,S{`X›UÔB@ýq1yÌòHýÕçæ§SK¤£¼5}äÄ:˜ÉÏz_›ÕwN&ä®Z^cTµNØ8‘Ú‚ò>bôo¤ßQ ͨyáæë¸q‘ôü¸ßÈù$#.xÂH+kß„†[?¹:W-úQ³ó÷Å'$Žf«ì+¸ÐØÅ Á£•!#þ€ÇWDyk3BM‹²Gó0¹Dº™IÏP¤‘‰]o¹©sù˜woPålÒO§&oî„>!¯¸|ã_ Ř·d` ÏÄOmÙ®Y<Õ\ý…+mšMR2Ä!ì(´EzºÇA-®2‰XÇê@¬GsíôQ£VêG£%ûxPq5 kŠaÐ2nî|Ø|×lðͰr„³‰[;¼ Aøt_G?…ç¹à ãÎçƒÈ ‰.’µObå¯mcÄÍ£Ì̽»ë{ÐÓ¶ Pªý8RЍ’=vܳ§ê›±‘êIÇæËëy‹’È(S/•¶…í}ßCWæ ¿†BK¨Ÿ.>lÚáõLÐÍ >@@ìT»m¦l²½$žnÝ-ºaö£`;6µ>ǹZÄ8'¾Ð5{'¥]¹x4£˜`-ôÌØ@$Qi,H”‹Ž¢¯¼ê®±…Hi¿zèfæo¬sRýì~%Ò#A6.¶ˆ‡|¦@眠°„ šä_ ±õW÷(¥ýüu`O¥ÈÁŽN[j4åF·32>c< ]àÁ÷A†d h¯“=dë`ëKB7áp5I/–M-C U² µõ.ÉïsIãæëÄÏ _´ÓU±Ïx3QÓVê œ!`¢ÞL~”, KLÌÝèW?Þ¥ïÁíua§¿‰ }Æ®ã!k ó+q™á•|PÅ¥FŸÛõà³7Þ>1?„­Ž’ J3$x¥Aâ¾}ËPH•£n£œ5d}êRƒÜ®ââ…ôtŠlùÕ/´…z©·I-îé+§ÐN;3p¡±·‘|[A¬È²NöT“›~1‹' ÐK(½DñîwO?ò¼õR cO JÈ¥7Ö¾&#ƒÓ(UÿîÎñ±w1,•fb{;»~©÷–ÂL@³ Ôà`Êù ´t|BöÀaluG¬«^3¾¹¼úà´ú†Q¶RBbœñ‹.S“Þì°›Á=åÛÐŽF±í·†8ߎkÑé×9Ú³‡ÏÒ»ž™…~Íé"8ðyño¢”1é,vV@ذ&Xv }”2d‚(A"Ù˹2Z1йç½)” Õ‰õ¶ë±³ß«Å’&ô:¾¤.‰ Ž;$:ÿmÎo5)íÍ€‰ W¹pµ´ÑØÓØ{¢>R.ÈIš‡x*Žˆ™â½‰0/ š{ æmüQŠd‹" õÃY+É'Þ³ãti°‰›6Çù¸l²pl‡{‚ÍzôÐX\¯Ü¢ï + ‚U°·‡’ìíâÅ܌ֺ…*áÙ'­Ô{“^ Ud)Táµ¥Ž¡bÉ^†2yö± ^Ù8;ª#×’¬V€²yé1¸çQyÔÚß_žP”Z̦ú-÷QeB ;ã =™ám{‚°ÞÞò‡œió¾• QÏIW ùäõ!ÏUµ_³™"x·”€2«¡C˜õ@ý €XØ Õ–øJ‚¼Æk9cËÞ^už¿ÑCŒm#p,þóO!1z–8ydÂÈ+½˜hhN Ý ÍÔöL«#!™=×+š8•µ’­©´°>ù§óLþ u©š©³ÍÆÒ¥v.S—~šëîQâîúʶG>´¶–pí Gm &V§­up»DÚLåì ¿´¥†®îú‰ï'îÌÉÞÇí× Ï[µXáÒìü“±Û7Î µSû{¸Y=_{VÏÌ”÷;¬Ã‰NC Uéƒ<£ÀkŠB®„¼—.I$ 1eÙ! ½0¨ÅCâÏÂLH K‘?™kˉNû¾8,à°œ­2™§Ç²ÙÝšÚ3DUY¬sV;‡¶Ü}> ¶­fá܀ݛ±—¹qèÿ}%ÞÐlïêpF„^ðªÄꃽÂ¥RC²îÉ#½´ÖvŒý°E²<ú_ó„Û:ú!/Ù¤·’F¡°m5\¾&kg §c¶;Ãø¤ÕHÆ`ËITœT¥£Qà‰à¤\eÈÀu¿}hœIçìœã"“÷r‰ÍÖ_•¡Õ—KÊüàäM'm—Z#np L*݉^Œƒ­¬‘˜Ysù5r/Z§îÝ4’me>^5´`žV,:­ YÊäJj¾:%çÆÊN+§1Mš(JT`}›@טlÏt”ª÷[õ|ûo*N[ÚõÔW˹RxŒ5›¨»ëz*ޝà#t•Yõ#xÉ…ŒEôo;9ç]o1GíúVx)æ¯Úb+N'±‘gÁ·¿@ßqG¯9ëT£?”÷ˆ××}s䃄äåõš~ÐW™õö¯… köTlS]`Ú§ßR#7z–¡k¤+©Rt_äC`e¬ëøhŒJ&¥eÒÃöe@QŠ^ð“H)ÎsHVŒÆ8PãMž¬°¶]'q:w,æI$<|ý»"_VÝ®S­Aì>Ÿ|¯sâkHδ£H0Dâoí/†¨~³`Ò&BâW@ÝóØV‘©b}Ê@¢¢arYî(’¬¹ã߇Ã*V ™Öl¢B{T„Auë‡x*^Ó‚æhG©´V¢ëIÔa&bJfÕ? fbfIQúrÝæ÷l—dHjéªj™Uë †{!åÇx}´ˆtWUØ·‹²Ñ+™í¸IqÁIÞ&ê^0¡äNÌê((Jýì= ±ÄÅŠÎÿL‚~){ Ä9ÅÄI#»ýkVˆ&4ˆž£Ýs^¤k:+vJlþ\W›¤¥#ù®‚ˆÑè-:%"9áj<´°˜ñsé­Š†¼œšUÌ}¹{?§­2挆’Ɉéî’Kw¼îÕ°_xöá]Cœëž%ÅÎkz¨’µåOn÷h»‹¹¡XñºŠëÞë®èJ§0ô€l æa™õõM#®‡·âS¯#O ÛHÁ$ûUÞį_‹~ó7yALªéè‡qD¨P:#ôú§õ»³L47sIó6 –±ŒûÎYÍã:lâÖíÚgá%%b`Åø®j®‰ÐöæN•Q¡ówÁ1wƲ4 ±y¼‰lÅÕ—šù›•]^ ~féVEêl–¡£Y ¦7|Ö†Tæ ÞÀÒN.ãØïîý–็#fäüD-Ôº;Q•fÐûq}—†bú»e/Ɖ<Ûæc3êÊ$A¸TDòyhò&'ñ·û8’âl÷‘±ØÝ ˆ—ºH„†]c8qc#ørAx1öódº†ØQMcáâç±AL;ïO|Åã¥Äj¥?r™q“6¡`èõ¯ê¨fVfO>>ñs›*~Æä5t`¤÷U,Jó ‘H¶?äá*<²}Î|:s>YDÚæ×8œUF†îá,²4p%lK‹d¢®Â`±Ð}äMtyï!öŒ¾×Ò&IÊÞ0̋쇢á”Z ]u·º4.ê¨bxÛe ü"IÃsyȪ{êéá…f C/Þèa牽ô2¥ú2cô¬dLYÒ)ç§©<·wLµïÒ˼00a ž9‘„þ¶g¥¾‹# BpLþ ýž%ÚC3Eò†€´]Vcm}'óDʽÇú@ÛAw6ìÒ0ÌÖ—Fz¬ÇaVCa.„m¾v¨$1_¬ÓM!Gufwü±ï%&A[[®cÔ˜˜ŸZfI…¶½®¥¶~Ð4 £ÚÝf,S¾ºîµl-Òí§Ëêt3&q¨°–{‰I_3vIuPëï‹4½2’ûO½,9j›Q ÖåÎnoŸýM%ÀîÕŸC¦bã9D1 ¿Ø‘nê“;ä|õ^Ù½À|ÜGþäŠ3|ú#àŒhÒ Âeéšzó·©æ‘ëäµ`ä—Áûþ ¬X„èa%‰¢®`‘Îâ]<Ñüçú¥¨ÃÖ–û3¡‘‰±òÖÚ1o…÷2.ãN¨Ï½Ù«G=AþÁ:öÃLï$ãû¡›“VÞ«±^oø-)ìÛ«úé˰™“+øo.ÒZG†`¦½V¡”ºLKÊ>Æ~}qóñ¶NÁ´‚öôºvá»y£§ÄÌÈÙÔ»sŸçÑùeÈ­ùÈp}ý«#&¤òLS½¿àâò¼i‘‹`ùs3Tq4Zñ÷-<ù2ñ^ܵréh:‹b5ÙÚ:ØÖÇOµP3/1jÓ°X[gOXâé÷¸ª—ßûeÏ5ŽÖ@ÜF޼2Ø‚‹ºªÌ1KÄ–*îUC–vN¼U[°øzæCõº°Ÿ§&sRnóй‘úäIu¢ÐÊ”ÐfF²nñ2²µ½&dPD;©œFkž”0(ÍWoppŠXÚ¡ÈÄ}æmìýé*£œEU[a®Pa‡¤§›ç9r:Sr¦FAkƒ Ä…•$ç‹+Æ×&¼Ÿ?½bZ8ó¥ „Žƒk0j 8 ñv`©2_è rž=6.ÉOrÜðxÜ/ÚéÂH÷0¤)ˆuÔTèáã‹{o¯¯í6^³~¥9¿/ò$?<úíçyÀlè­QDŽwh~`Ø&ŸÑŸù}ôþÑùlq ‚Åaív€~Ú)Í~|á„@÷êáp2Ú¢ò÷‰ôGÅ}š8ïjj–laW¼ßnŒR Êí[0 coݬÆ„»L< ±%¹\Þ$"Ãbd£Ü`r?mrœ©Si ²Jãz~C•8‡e¼²A|Z*í O'©`ä*ýPÉéº8: ™QÀ{#d¼¯c‹‰Ý¬Â÷óV?9Áò{+8@KÍ9K¿<.@ª}·¤¾ª Niò;8W ôyFäÐo빈ëïÛ¦ Š3 ƒ[ãËN}ñæüõqêDB[g(e·FSV’ í“|^RÙ·,Ë)ÐOWŒI½ìmÁ='Çévm—¶Û_¿G–ÕÔ­¯ÃQ%}²ë{òÈb€Tó±œEÓRp«Ð•nÙ›d ¢x›%Ô ™2Ø7kV þkî|‡m†nÍ ómAn'D~¨·Íh™#áà²ü Q*f¯Yœx=Žy¬–&…Ê&pzRä¹w2Ÿds7œç†`;ù„q/å´è¢5ßdk¯Çà‰ƒËñšž'öÿ:UÊ^c蚀 HȆ°e˼^¦]l¶¨a4ûÕó“g߯iŠÖ×j˜nO¼Aœ†T¼4-˨W+3.u\-†˜(Éù픿ȗÂÞ÷VÄ:¸ÎÔ7¬öH<» iæ]g/VXU"¾QâS,¼J‚LÀ/‚o uA^ÜägÌIÁ˜÷ Ã{h#ú²@aS¼K1s¼WC‘ëƒÒ¼Ýþƒ’q€4™Ûî•ÒÉtÍEÄÖË-f¯WΉB®ö¶rp¢ý7ã ‡dî_e‹då›ÍU% ‹ºÎ‰lÄLç)à~ÝÐ=º#+Ÿfjk`K}¡#>8‹ù7£U:^ZŒ$%­}Èyémã9¸7Ç·Ùï¢ÚuPÎ42‚êUüHÞÐÞ'ž¹2:bÁ䘲Š-*T/ñƒéþâ è;BþIïRjcÊ Y)m{éZ¯Î¨ÚÓ ;ÌBœ™7=;±™ê¼Sê*4 q‚îsÙ.‚)qo}!l ‘tÎ8=gYšõ…u;íUîŠëù¡&bL“©óé=dËÇ/nðÅÓ(þgó]oŽiÊ+º´VÃÁäÇmL}¦¥½Î¤4ÂÔþº…ñhÒŽÖ ŽYüXtGÍ ›P¥Fƒ¤h±uöLô5Ó!,¼‘lì-s&†æe<äôöî§c¥ÌbÈh’/J'¾aóásoU]zÞGÊó‰µý¬9®sAóbM½ÅÔi‡Íù"P!rd8 šŒÉœ‘_K†`è3Ì'&`"tã˜5Fñ¿â}7ù92¬·ŒàÁ7¬_ÇÉðý`¹üz•ä¶;•±qo¼­ #KF2IŽÛ ûýC”K!àdù”¾Ì‡GvŒŒ½Š *Õÿ»R~.Ê¥¤‰t †CÞ—ïFϾŸº-í3*éÝ‹(1‚Ûô‡Ðú»ãªg¾ºÏ¥çÕ¾dýª ë®ö5ݤât©½ ™«{Îê“ÊPå¬ÂtD`MÛŠ€l.Ç<’\¾¾Èøhælî$¨’í[qèqü `„W,ݬá¥g£Žb0®lð$ ¤?;îh–}5gŽ)¬aÆžê–J‹D|.¤ô„fÌ!dö«ÁRR%훳žÑ“{CîÒmùçVZѶ’3;Û'åÊñž¢ôVYòLV;KZÒ\ÄÁ톎°61úîÜ%!-â—¨2"€Ø-ÊS÷‹Õ{¥$ êÅ.ÝöÅ>£Úèµmê‘øWÕèRÖv¡?ùªGÅÛrõŽîE~ÖjR6qï©÷<¹xU˜I%[KÅʬ«a’t¼òÆbÕ?íB”$â’ÀíÓ$>ûᬅ3£,)?Š·KÊqkÇéSŠ<¥)lÝg0Wïù*_÷Ãc|‰ìbj3‰“\¢Lm1b† >æ GKØIÇÁ÷MJ6d»} :8yƒtÔe1/D}á}¢™—Ì#ŒZê()2ÈÉ7αº€t‹ZVı#'$¸{ ;~]­J6ÃIë÷ÜÇtÚïÎ2±8ú¾:,b©Îâ;%œERú’Ùþ•s ºŸËÛydZVß¿·E …eíhV¨<õ[¦+f›0ØKieÃ¥B¬~b'žKójnÂÁV¡«×Öv²xz)‡Ú¹*>ã°Žºr%²êØ=©E) üÔ–*Ò>…2u²á3äÑYåG\Tí6ÔÇP„N77Ê8u÷3)’rÒ5ÁX ¨7XXÆX›N\y÷¤ÀìMϱ?¯¥-:tìFº¥–¡¢­ÉqÍ#f\)0rÎvzãF.a£BùÒ‚ã:œç·œÓOÀ‰/ëDÒ  =”¸¹­ðC¥¢¿xøMxBý,ôijƒ ,Iý,€­µD÷»BLS I"Œ®O5 gØÎì†Ôä ¦Þ8õôª–mNö ÷²KsžZ5hÛx­àÚ•Í{Kâò €ø‘€À«PCO­n¼¦¯ÔF}Þ›Êþ—qÑâ—2³½è9þ“„IJÛ/ød>ß–†˜ÂÚý!œ¶;=â3}b®»&8ù¢C dAâFàH ¼žU[äѱl¿‡DV¦iÜî2ОdÁjÀä{ÜM¾cìŸØTÐÔ‘”G7HdãS)ù99ÀÂu<+W}lX®µîuƯ7»Q?w B—TßkÀ˜K ƈœüu&j+ýIe H a?ºÖËBWû¡Å ”~üèhØm×ÑYF8¢+n'Z/Ž|Rfê"Òí.}Ìݲ¡éíŠÅÞX&òÖä ¶ãÒ=³>øwýÚ'_i.·‰Ù–Ùc?2‹î\ÊŸB%Zéÿ_vã'ˆŽIknák¦ÕW<)3Ø»Ê|„Inkéߥb⹄³_qW;s}tO»@¿:›”¨™4¬Ð9™5GÜ/Ø\Ž9EV\”Zi!fép/×°}øPj½ÚRõ£qJ’$ã*½M_$8ÐD…3Z|Åvcc¼/Ç­õFг/º-Ý <éWz#åEwa>c~+÷%,GRn~éV€ì™EEéå§@ŠüsFРП–’òÎo•5Ù$ ¢¹p6ÜEW¾ñÜóÓ©PZ˜(´¤ârlO0µã´NúªÔ"Cÿ¹ÈÞ騝Ú\î\®*è—×Y{‰jòÏÀÌŒó«—k“e˜!€ÚSYm§7øëw8kœÿS:§r—:Ô˜¢nCHN·Ñ8>~%±ÎZ݈exꋲVOÏYL_ÐŽ¼Á:PqD½£.bú¡×¿5{Á®äu¨WL>ýØóiëŒ9¨ƒ^xÖúñíλ‹˜¯°(póuÆë×Y×®ð—6„÷e¦®ì?•iĘq^MãçgËSÉÚZr^˜' =ÐJp|ïVå>L\ \Bˆ2¢•ØøY$˜„ ûë0züø %j4™¿K€TåX«év‘¯Ôé,±4¶,6zóÊØ(1ßq„Éz¡Û¿uB•U#Wë±½pÑÖLŒÔç3ÕŽbÐ…ÏÁt†i”i3ÓÐûÆë˨ú×…EÑìÞ<å”x¡ÌÀ}kc “¡ê; æ†‡›õÐ¥zS§Ì“öâBÅ" ý˜tíßTipärO—¥ÚÊjo×ïoZq«b”ùZ5x˜x©×íLϾý j“ç%Æ¡ëN@9Aÿ¶75×0þ´b]ÏÏÂcó§‚^].¹I0B½XU€ŒÉƹҚ¯$\„[’Qƒ-BÎN‘ncQ—g®‘”!ˆ1E{C$YÂ_»*ÄÚ ‰µ©‡cNdíeœá8›î¦Mëç‚°fA øØ‘zCÐ<<Š##ÕêPµ¥1GQ.x@}P‡®KÁÖÓû×—ZQn¾ÑÚh5g“KHVcïA×mîÎå­qHˆ)›Òp•êû¡Î± ¶˜É0ð±6{2|}@–¢0ÄH„¬òÕ'È©å-m#ʹ!,éâ;•r_á62ð¹–»/ÓVGŠéŽo¦…W‚04ž[ÁsŽÌHÑ—mÌ4Ý%Ö1ÄÝüäNë#9ñí´Wk5`uÛbý¥ÈZ>-¦-ÎÓÁ¥ÑK³[iàfWžì:'ø à”óèøÖS¾æÜ´XqÅyÛWBö•G±$!0dÓhù¡A£·móa”jVÛb\Ù~!}./¾q™±Ëð¼,rDrú–ؼ †°9"À”ñ§‹þ¦Â†”ފ͸ì˜]a­â°:4a‡ Ý]`ÒÙк­Õ:Õçrg¢X«ŸR¤L1|Ü ó+ÇÐƒŠª”Šbø*ŨŒ¿’¨2­~Ý©ùÚÈ[Æ}_4Œ…F)ãn‰ó²ç_)ôC$*T,cdïø&ÁúU>ñ è­G€Z4œ;Qð/BÒ$¡ëòd8îÚþ˜¾“‘ÐR€D°½L¹¯H"ê1FÔ÷ŠudH§ö¤­ÚëW¢Y*Ay+í‚Ã¥ؼuä‘p¬fæ™m|ÅTy)üÙ6¤•º¯^Oqrqsv Шi‰½ ‡ksøéŸ’´—†%¢°q²¬¶íõþïÎ þ¼tìÆ–:<‹ßêìø98v¿5rÁ;Ðßh•1¦%›¹Jé/æ61ÉF¤?' xퟔª¤pÙ%;àøãñíbô¦Î±XTÝÔ”Í*xù\hÐ^–øFV÷¬OXüVÞ»uüåª*7þ öÊðœ¨RuY0)ß;¶ñ6½j‡¾FMÙ…¦‚eÔ‘7ÀSUw’øãä}¿ÝË‹n¬y‰@ð-Æ!Æù\>.Q@ýd~dK÷!É£†£ê:xHy[äÚ—ˆãÀ;eqp½` I rþ‹b¼…ÙQfn ¹THCLÚ¯|è‹d⇆šßŠ•ýRðóeÃÉ?’_ âNoˆ›–ø¿˜ž…ÒŸt^´&é]-ÑÁò‡¥ö. kjðQ¸±ú×»}бc^O3Ö1æ’´«à{¬4dN“ž]Á¾Ü“cî¡^ð?Àyµ(ÌBâŒ3AÄ„bºëvwˆÖ{Y¢|„B4útjËqQµÉ¤K™B+ÙãŒ$òɸFVé·¯]:Ç.µíAer‚±)j†´¨4AÇ\.K_3VËô=H„9l‡ÄK}€ª´Êø™¤íCKl¼ Rê½±ï½AÙ€º:XŽpÑ*ä’ŸAÊÀˆüÌ.<A/¬Œ`ÿ˜•k{|* 3Òz©1¶!›/¥(Läê“òyPÊh:šóøPùäœòÊX̨sKÚ³˜kÏ–þMSR›]B4›ff?±_ñéLdÚßÑM¤ŸÁõ1*u¢D{®Þ3²›ÉQW~¯`[[í×ÙÎþjU<˜áÆ7Ï+m¦PìÅÅ‹QJ5ÅÙqñZ¦r@‚|;Xâû°×“=Ý}«õJ.2¨ŒÖéç©Åsxïó¤ ïfaö"¬¡Re× {Q1Ŀ쫉`—T€y`‘?ºØ¥Ïb.L9âɳ ½Ý;9÷.Ã3îD]žãÛHä3LO4á ²»3LÄÀ£Ýåo i(™>ÖdÜ*ñ?#KÅ÷ »¯ªS®—…kœø0þfQvD!6|w7”Œ$SyÝ÷ó¹˜ÅÍj»[‘«ˆj—Óba"™/?Àg’òÁ7{rn+]Y½þâFù±% U›±ä±Ì@-_µå¨ñ®V| ¾=}Íq›= „Ð-0y\ŒÁ‹‚ ¡3ƒˆý«¾Í”!‚xµ­ œ‹q7#ÇüFŠÒ¡`üt²d€R€/Yk–6ôŠÓY.lý‡ã¹kîÃEÍØP ú|£‡:ô«ÝŠDÆ%|Û^PF Vðï§Ãýj0[ýV4Ãß›X*ÉÒ׸=À:͵Pº*âÒ ”9F-:Œ;"¾§‹UÞa•·B±°ûóg|¸’½$ûµ’ªå!bK·í–^ºÙ–Ô+\‰‹Ùµ0L­†(¡‰A’R›Ÿfey÷2ÁÅ"+\>ÎÇLÁ­‹ ¨ ú2!ícä_-EBÑb7,Ïž9áˆ,Òø;Ú%ê—Û}¿ûèv´–ÓY÷Ã%ˆA„e?ù>î¾êB zoHÁxªÎ7Dö@·bID›88Œ‹#ÛpÞP¨ÓáeJÖéY¼‡¾¸],ˆÐõÎöCÒµµ,Z@˜¯Nߺ;n¤fÂ3QO• ËŠl{¾º³ ,-@éw¯j]rarÉÁ‚ k®Âh¾]F¾öê×Áîø}Ä|[ ƒŸßݳ²sØö6— ´÷Š®N“Uà¢ÙÍ…ÖY±)TEb„·2?òîîýïëp¾+p›mPÙ¯jƒÊ±„ÎV*¿wiǼŽl+Þ޶1]œ­ÞÁHï˹­‡ f¬¤©gÊÔVYê\%˜ÐV“`Z‘Ú ²31¤z7"§9±ÍÃ8Up6ú‹©sP„ÊJ¿Eï6™3câ¿Ò¢vKƒìKZÂm™FÂï/ É*®†ÑÈr½¾Ir@¿æÇû¼¥/ïÛÂ@—0ö­ZL²‹ýÈÑðo:iIì$¶ëc(q'òø‚ +”ÐJá­¬µö¦Sc®¾,t”É•~¯'jê˜þãý­0Ðn5•‚ ‘ÛBÇ ia8 ;è˜[EâáP!}-çßéÙ|‘³È§ŸædCåTè½%zÌ£µ„‚o¾B#ŠpÒ·ôuÐàœ°ÉØšˆ›r'ªëõŠ9rp0m±ÓX‹²lewW—fXrÙŽŸ í\¯g÷ÑKxxÔž0mbÒØîíêð-Ê´àȹT1Üè%r2\ô‘–ªû#¼5e£&"‡ýE¸à_ã1ئPžüÕGNõ¤$ÌBº«:“Ϭ"GyšÁO¸ÁËs»ÓDÐ88“CÎMž&šc¤AÐ ¿ºÃÿ™˜Î¸«HÒæù æ²LЧtèé‹òÏÒªSχȨxyæ.²‚üIƒcÍFÄ»©xpÖ'é˜rÄ‹¥Q±à^©ºŸfo´‰,að< @··ñ)r¦yx¦C>±íøÕuÔ|Ëh'k,_ãï¿t2+º‰Êû ì” ` ­ú‚»žñöã…ã\ °l3—t~ËsUa°ím¢ý­ÙÝ™O¡¸üb00©~•¥ª¦KIb@bÇSå– à¯ä,ÿª¯îð1®ÆÛ³wûhÐŒEdwÒ5U„:‡ IdZP lSŸ"ß±ÖR] 8ÍdÕìêÛiin¹ï`…¸&B’»KkÇÓ @ß:–Xò®ÿôˆM¬Ž2¼fÄ“èL?£¤C§-JàòÍg¦•ò@Œ¡rÏœQogÈæòwŠóE}Aø΋Òró9òX«xÎ D;šÊ¹Ç¦žÜêå² ±R™âÊñŠòTq‰ûx`’VPÞ/Kí"0î2ÃbI¨?b¶2/:›×ïss]Z¨[i¯Ø8·€ÄWMÜ‹®“¡W™() 33ç~öY_0þ2Ý ‘ ½Þ`|âYtW–îÆõ‚4ª©¾&Pý%rƾù<m»¸Â“ÒIÐíñƒ‹¸H°â' BÄØ‡ºi6‚!jDXõðà·L™P/p·/Q~âI~Qûá ’YÅÿË4üµÌy$„J;Cf¨…=s¹-=-ÖKBOàmJ­HkA#ƒ:FŒoÑ›ßæ=¢XgރЅÊëñI,è4:ÌÚë 2f­ ¸¦¥°­V á‡å2l¤w³e+¢ÂÝŒrvZ›ç°ô;JÆ¢*ŠþDή?߀ÒÑë¸ç·ì ÷‚Þ%6ï„ZUÓöôxíA¥\£çdŽ”šÏñ.põÌ-FUDiqæÒ™&dú>†?YñÕlÓ„ÿ˜Êmš<·OWij}Iš5P„‡!†Í:/ä…ñ‰îÂ}¹…b•j“Ϋr‚÷¸÷JlÐN€þ ¶@Bƒ_ßíå¿’j?D­ èO5Ô¿7gäå+¸Îyy? é™ÒeYg'‘c9ì©^ûö À­ÏkÍÉ×—‰µÇ%!yáØ³Ý?êI8EU(Ù¼H1‰ÙìêÖ"Ô@û9;ïúóÀš°Ö]D¼Z­xî† %Àþ „ð*2wW¥MÅÚ´`Ýú ü¨›$¦P5”påƒåŽ†Ñ–œì“iMF÷°1ÀÌÑ«”Eœ×< ˆu'Ú‡ûhÐêSWÜ ±©,'YdBLôîøÐ×m$è<Ä·ìÁ|«GÁí¡ô6šƒ/p‹øŒP´¯––®ÂñŽ0Rc`•ѧŸô+ AÍ8WïOD ¤ãÞõz6a"ȪqkœõtõøÔˆ—SO÷Gª¿5ßÒ'«‡pGE.7DÿéPS æˆ|ÁæÈl6¾Zý,6öWƒÂdw8N¦¬\û6I€›C­ÉîTщÁ†ºˆÑ4®ÌŽÅ}¡î°võ*¹³¹:ðih~r•ÖP>"Ý·8.Ï_XA& l:p8Ú>™²Híú$9D6e 9jh;óòÂöÑ•I4•ô¡ÂÛg7ÜY$uëQÁØóô„ROprÎîÖÔ¿k¦urôå„ê›õÓ<î<ïÒF)ªè¹rZ>ýÇ“šßÈš[o éŒ×ä" ²ÛTÌ7y7é`»OZ½4ùF0:B:(~«¦Ó¤këeIÙ…är¾¿2w¤ÆLÍkq¦f‘y €2Ù_Ž|§c»s>àÉ3nû©¿qîËEº~šgñajÎ9±—dåE€ìØÛÕTâΗª{,OßEL²Öô•®Šä'Ô{7ß_:MIl¤ªÇeúYÈ•29-1uw­ö<@vX¿ø¦‰^«3lµjÑýEiàn§n­„Á°N¬·¦Í"ù¹n¿Q¢ž!ìoêÅ7»ºmsÂÃ’´ endstream endobj 1540 0 obj << /Length1 1898 /Length2 9789 /Length3 0 /Length 10948 /Filter /FlateDecode >> stream xÚ·PjðÒ)-à"!Í.ÝÝÝݰÀ"Í"ÝÒ%!ÝÒ)ÒÝHKIH#%ñ­÷Þ÷êûþÿ3ß7;Ãîïôyžsž¨)”Õ˜DÌíMÁ’övP&3 ¦ ¦âlÌ@ +*5µ:jþ•Zìä ±·ãýÃBÌ l…ÉÄM 0C{;€¬‹ Äqò‚¸x@+ÈóC{'^€¸É[ˆ9@ kovF¥³wpw‚XZAayþó@kFñðp1þå±;AÌLì &P+°-,£™‰ @ÍÞ †ºÿWZ~+(Ô—…ÅÕÕ•ÙÄÖ™ÙÞÉRŽà ZTÁÎ`§·`sÀ¯–Š&¶àZcF¥¨[AœÿV¨Ù[@]MœÀ˜Àb¶s†¹¸Ø™ƒ°ì5y€’Øîocù¿ ÿÄ ú7Ü?Þ¿Aìþr613³·u0±s‡ØY, 6`€’¤<3Ô Ê0±3ÿehbãló7yk±11…üUº @RD`ëðŸþœÍœ PgfgˆÍ¯Y~…³„¹˜½­-ØêŒú«>qˆØ vîî,ÿ\î;{W;ÏÿÄÎÜâWæ.,vG°Œø?60êo™% àÜ@Øv3³bù•@ÝÝü—ôK ëÁÛÓÁÞ`kì ±Ã¾P=MÞ‚P'°·çŸŠÿ&T`1ƒLÁ–;ÔßÑab°Åß »'ˆ@?øëóï/Ø„™ÛÛÙ¸ÿ6ÿëŠYÔ4$4D”þiù_¥¨¨½À“‰ƒÀÄÊð°ñ¸`àýßaþ=€ÿ4ÿ—TÙòOqÀßeì,ì<÷;¼ÿôñöŸÁ ýgkèÿAÑ6Î`íïé×rÍ`@ÿÏ;ð—ËÿßèÿŠòþÿ­HÒÅÆæ/=íßÿ½‰-ÄÆý Ø8»@a«¡`[»ÿ5Õÿ½Ï `sˆ‹íÿje &°±³´ù÷ !Î’7°¹2jfõ÷ýç`ám v`e{gȯÀÿG[:³7°WÅv[©À°úï”vföæ¿–•ƒ`âäd⎠„M+ÀÛRs°Û_à `a¶³‡Â\°ö¼öN¨¿î”“À"úKô7qXÄ~7€Eü_âX$À"õ›`Qä,ŠÂo‚EQüM<¥‰› 6Í¿‰À¢þ›8,¿ Eû_âe0ù—Ø`:[ØXþzrþµe2û—Øa±a/•ío¯_§ÍbþÂ:ÿ¬‹?V©å gõÂj…ü°òÞü°úlþ@Xa¶¿+Ãî„•aÿÂÊpøayþ@X^ç?v Ð?V†ËËëúYayÝþÂÿ(3''Økû×Òæí?ü×Ó»ÍP¿ÌÙ›ñ½³®y×z]%ò•i{ôÙÚ×ö°÷:½¡PšùlO+y¤T©)GQcór¢á$å…’àÉ9¯ôG×cnú]éþ«‰ÒYP8‰ÞMѦœÁ›ó.K0¹¹=‰å8WRØ&Îq¬ –±‚Þëx­¹» ®n¶o;ù÷íÎßty¶€ÂKlŠÄÐ(‘Ìöø ª¯Ð!}ìh\'½duñ{øHô•hk$ñû¦^tÊÐÊÊ@z_oÛY®\O%gyðsb<YŠÌá¸~büØtÙFg8QŽK±RA*}ËÏ‹†98¾§ý»ÆS ’¼ˆSX1*j>W!Î-UmHý€½²ÑêüIC2~-ùû±¯î“ë´ÞR5ŽÜH)kdÝè¡\õ8©ÐC Œ¼îÆ:i7ÏbíçvÑ€n5Vܤ’õYöÅâ»à-¯„‰‹£õ×ðya”…ØÝ¨^ªÆ½†_ÛˆnèÉ%üU÷™Åâ‰Äg”éñÞqo‹!¹ø8Œ‘EÅû¸{×Ì›ˆwÝm޾%8³ÐŸÝÔŸöªB=x g-ªAZ7C’j-ÎYç¬>¥ ¹è݈\1 êhB>÷Ï9ã‘æŒfD8 ›mõµ¿š ±]“ ¾­© ¿hB ·w'5Ìw$¹P Úb{1ýMìÉÀP—T¢z‚ûÖ*Aò#eŸó]Zæ©Ê ·Ó¬Gçí Bh(ü™Ã NªÜ¤Fâ¸ôE)çQÑÐŽfiÖ®W>Ù¥äb·0Ým™W§}T„Ñþ°uÈ”qâk~A\|n;GÞCñØÙÝ­=oçÝ€áPÕçX~(÷½…šYÎbBI6€DYž ów‰¸Ÿ‹INu?]š $dÔqO{s×8¸¾}}o:HÒ'Tдíðz[ˆâÚÑ1‘8SHmͽy!FC€O#³Êæ«f€¸ T¢ÝÉóiÃ܇EZnÎ_™©Í#•¶˜Üñ*`kúá•*ŠÕ›>ñ`y <0÷ñ ‚ý\-Ü›\ŽïR`7¢¤~Q-eäpY¹O'("S˾…{ùœ|}nsUïn©ˆša”"/õë­OûlPW:5n…ÕzQ¡Öj_Ù­Úšz=bQTãbbfÜ¢ºllµ7&©ŸrF{uº…Ýâ"Z[ªøqõ˜XPÕT—¹Û?ÐqfªÓmîy× d¢«ý6iËÒõÞ3^­d_V–¢ðF]|ÙE`pFGæ-ZËd!«åЗÔüÌØßôC.cÉ=Ïü$SÉ@-µºnÄu¾TÆ@lR†£ÌB4)„VR`¡[düÛƒÔÊ:…«ßêÌêÜܤö ÍÝ#áÛ>ï¤l#¦AI='l­Â]×/ò!i¯º©§P îÙ˜s4áSÚ> ÷ڪР*ö uYV^ŽóQ¾?Q÷ÖÜôy8ÔÅ®<+…ès3ch~ƒ„¯ á'ЋÈ_¾)(ï,^¯(á.?\xÀÂÃÐîA$=õo@ïã÷³`×Wesî¯ÔŽAM+šF E4^úÚOˆÇT¸›×½=ÊÍ£77ÿ‘§|¥îæê¤]e_{i㉮ÜM¢žÚ»}´·Ð[X^«=m·g¾?‡ö‹Â:Y-¤Qr¸u–;Ãô38ñÁÛ‹JPK[´ÊÆ8yÜÙõ£æP­Äê`%^%à^ ^Ž‹ÝõFõxöéôÏ/ø+ìäBÕÏWR[öv}ì‹gžÕJŒïç*ü ÂIMazE’¤ô}Y«Ž|g"ÉèOuÛ_x¡ÍD–í¾ß$Ǭ¥lrž—:õÍ©W§-˜½838÷?¼ j:=@΋ÌhÁç-E£´A­o,)ˆæfoeÅFkï#n#imì«:V5}C(ð/§ù‘gx[À¨#÷ÙÙÊ5þ±Ø¬3ZÙ¸(x·óƯÄb•ýEÁ®;8½v QŸjwÍ5ûhés¯T;É­•Ä;kBö¾Ÿ‡c+|Øpâ)›€ŸØbFçâ5”Ü”h'mKÜzTŸ™}¸=:Â,Ì ëdv£ßïç-o6¤wäÉI~VG§\÷5þFè,ZÇcxÁ—4¤À‘‡Ñªÿɨ‰0Âb ð®ðòèR'¡“í‡úœ|&N­[öšé+É3^¶Øü.È…HÙmzÉùŒ¢oXp©™Ä×€ÓÁ©,þªöË·¶þѶ‰ïÒ/·³b™Î¹u¹Çb-"^Mð¸ÇU¸ŽDˆ$õ¸pyvÔËÅ¢&½ÉZ_˜ãFäx½2:ùÑ¡5e0¼Ýld®wXj£´òÉÌå §ýA1Ïö |!oòQ¥¢ûÛh´ì”¹4c‚Ñ—þư1ïžY'Ý—¨¸VäÄDu•¯œJŽüj¥@Ó_$Φkû»î=è·K£†§3ßt»¾N\2ª€óAÁ‹:®)BÙ|á¸Ëœ½*M‚!`ôÞvÄD¢1 Á iª”š( “vPé…‹­‡ß8íõ‡0ZÃÃmçNbŠuD¢·æOÝø…+žÙs©MâÚŒ³5LM}«[[nÏÄÞäМXë,% æ¡÷¤cè‡Y W;åÂ+Å9O*IÓ yHKrÅ´?´|2Èôö\æà>ûzØ{bèãÕû-Q|ÅÑÇåÂÜ ïõc;6õ¡ü TÀ8q"ºèà-ñŠ)=©§ÓwVq¿¯rèjªèB®í5¨r'ÙR΄ÉDèÚì)˜žrûŒâ¤¿IŒÁد=1³9Ô÷¥”QÐX}W˜]—²a]†^Àä`³˜ÅÏVë+my”l{/\ŒqF³]‡Å4‹Æ:Ÿá8ˆÓnW¦Ô~¨êL ü‰nT°a†¨±½f§—À˜¯_Ó†bIk¿/nó²²FeK“þZÑæ]B¡騤åN+ž0^ êM5ÍÁàÝÉݯ QºÙ©l%Ç—H^˜ÛMë“׫nˆd1õOV¶ÌÐßî:¬Þžvl£›‡3ñ_µÈk vl:ÅÎ:1õmNlÌg_ÍØ$]VQÍ>o¨P˜E-¶>£Å+LiXØzøÔÚˆ/€ïY9@¶ð)ÿ!à>*$ÖíºŸ¡‘h]µÒÿú–œb¯“8oNv[Û¾/.Jß‹„ÇP‰¼²acÁ‹Xìûæ_%²{×¶’Ø’uȵaz¶Fã"*»ß…UXRoº nAÖ0Ô3¿mŸ)Nqõ6‰Öö“ÄIsý‰ü¼.Õų¼—^s§ûs_?°¢o¤y’Ϙ ÊL¦[zÅ}:#,Y Èûqù!Dï3MÏsè,¢J†‹÷m³^‹o¼"¡D‘gâu‹|•@îI¯»ÞÔó4‹EKíe7+ñÓöZG³0‰ê¤ÊaÆŽY'žÔ«@ÒƒðVo;û+ݾ­¹ ¢¤È&¬M³Ò/q+X!(p£l FÅïx›‡þ g0ò$$:kȉG“û´5å°JzoŒ™¨A{3?LMàŒý5¹3ZJ“ Zg6dåB<;ù°“4æW”4•“z°øÊ›‘僃žRçñlE¦Ï&¾¢'~^³tš)yiýg*?NIl¤çâ['6Bg¡ŸUóz柯™F—u½IJÉS`³7$2Pµû#Æ×iÑ,&k…v^Šß„!îúPªÖV×QFç £p}XBý%/_éè£ÙCó‹eqݯ4©Eðå¨îÏ­MÒS¸r]cß­¤…wÞÖçlÙƒÔ‘q¨%“Ù¹I‹'#X_ø_iz0=òÉ“Úý•ù@ïyˆ [ÊE°8ìeâP{ÅŠ²\è"c•òlýû,F4&Æg„¾Þ•Qíªû!¦³5üT8â3{¹F}$oêT3%>ë1:ê âß|9¡:ÒÀZûºêäpœ¬j|Ô]ÔVC²ÿ²Ö›uö} |žÄí­¦² 1ùãTq~¾k:5à8Y|qVÆ]Ïy ¥ú†£zҳ߀ÍñOû¢!Ùáû »&æåÔøe; WÆ´Çu8w€NzÄr»Òb‘N¼ûóP>6öb,þƒ‘¼$áªÎcŸ*Éñ^<@XˆÄ¼Nö{Ý'ûïÔæ•LúüB(¾½Ê!ŸËÇÔØn©Îd…“Jm%}øZÞJ[ëHw˜5ÌêØ ›+´ËàC~¶Koñ}Ë‘7Lkp24Ï…Zb¨¯=eÿIìbÎG8œñx¡‚ b‚2ÚÓ༸¶!GE‹¨ì ›¬KnÅÖ8eë`ƒP%ê0ª‡žåSvׯÝ\¾®è¯wðâ7G\£y®Ãw²|¢½Ÿ°{Ù?•ù2ö\Î÷É ¯òi­¤E˜ôDИàŒEê;KƒÜïV>Clš¦#ÍG…˜ôj±’®‹Ò2ßøóãÉXKOÜdÚëÍË(¯f׬ÐáÍÈ’UÞ”dVŒš££ØÒg¦‡òHÖÅ9ž”«ym „L(.’fÛyý<àWÏP6`ò©fÏ—Êú’ûÆA­Þž­ÙΦö+øE€ã±á@ñfdq¬ DZG”ŽŽ¥™ÅcªïÍåËÌü ,™y˽w3‰aNt˸ÄäÚKïŽOš[Ië¹çe?ßO[÷Xu€ÐJ„¦”)IºBԺăشp,f‹TL˜ú¥‚Ò-W‘ÜÑî&ƒâs‡›ó S½œ–ìãÂÓ%|´º¤üg³™x·R-šòáZ6Pð Ú«ûÒwv¢ÓòŠSù~ݣïÜßke9ŠîH³ÆE6æaksK"t«šfyÓ“~ë3¨ùDXý–‘ßy"…×÷!±Q{—£v˜†|’w—Nâ™YQfZ£‰ÞM¾"Ô'zÍЋ›ß|/¶b|wHúã÷–Xë3ks¼PzúìE’¨¾Q²3Ò£‡PpÑs Ø-Sp¸Ž)‘¿ýö-èöndÔþµò“ pRô5šš wˆºE?¦77qõU1­f=Ae<ªÙw½ƒF§zT9Z–ÜBÅ,PÌçשØåROÇ•>¯Fïx;a¶*`´Ú#«šy˃X&Þ½sÖT仇îús"p©}»µ«pAXèÚZÜ»;Š#•ƒ“!t̤æÖi*‘(Hè…Þ"µqê´®t+ºô>¶È лNüDÚñzpozÃ]©Dÿ%H `dw?~ ‰ÜTZ>¨~¬½Ô9ì5\„|šÒ_ÜÎmÒÕÉYŽRàd iƒ4Ãn7–ºX†#¨ ¡58Ÿ³ú¨šm¨bôÄ!ÃS-žÎæ­Fm¹‹šr\8ô!^³ ~·f‰ù~ ÌÕ6(?â¯ë ÷sBö”´‚ä ì³1®Ó¬âX'x/ŽýÔ¢;Dℯœ”w3^û™¯¼_]5>uC¤Ð~Bãîzg ßPŠÑu¦ç³9%®zE±Ù¦‡Éç–…ñ×F¬Ò¢ˆ>W”Þ˜¶÷ ¹%JL<±Œ$ö ±ö•®‹t­Éû©1Œ’ y ã…5‘Ài˜OÚ²âlƒÏÉ{¢8—_W—÷ðB “Dz7=C¢B¿*º£qÙ `Ùq¡²“Έ­ß ÷Ø‚5õèA ãÞI5&é^çp·yeåQ/Æô3¾oxpßl¿@^ĬGÈwyxJ;ð='O{4þ>ïü¹ª=*5Ÿd%ò’íH÷IuÏé|åk_€ÇbS¢á«©Õv(LJâQÆžÍ%¼Ÿžú^â£ÞqÈÐz]!~”YoB—ÒÑl3Mê’ «ÜÀÖUìvz<Õ~Oï®Ó?³éAú|Ý ›oq Kîãƒ×¢Òƒùò•ÎK‚Ž^òì¥4/ªÜˆe¢^æõ%‰*öÓ“c•Þ‚Ä‚É7VFV¹qf½é†>@þ vL~ZÁ›¶ÅiZA;uïÇÊ‚S/Ó»ll²1!=Â9÷Þýî€Âœ„";Mk{Ë…/†­ƒ3bÛ 1é´•Ç·P>-iôe‹Á…E{‡—ƒïúåò3¦ÃeI±ºE5ppg1)$ñ{VGé‘Àüa8™ÜrZ®ó|Aû8áØœæ!ÍB¶FbUÄ{”#vSåï{j§ÏˆDζ¤÷€‡€l5îXw¿ñøÄâ?— øõ·jH‰LÉåÆz鎀í'ðµ” ±—ßItÏô.‘U·I7’OŠ ²TŠ>Ý%W•ò}ÎõqCoEØÃ% Ѩ6ÂX?;B˯sLSHhµŒåÚÇ4™ƒ@UŸSäžIfôm/HáÊÕJ®à¦ýŸß¢Ñ¶ºß¬(]e¦ļÄ+ÛþŠDYuZ0úŠêcæøêâÛÉýÈ8Vû 9 Ùá8 |–¶NíI Óž™òHZëÝ3¹¼¡›™ìÌ‚e¥‰ä¤LSËÌ”š…£Ô䤋bTfJ bà)½!öû0Aï™Z„DÄñ L™ªW'¤l:h2·¦V””WÔl¡xv>LZÓ Ô|‘„Rb>÷éÒØ‚ Z#Ý5/h>ÊDûb)Áz­HîmåxGŽ1jÕ,ê‹ÚÛ䇨G_½{ÙÔ»' „ø8÷÷aÛ#3œÇI2¥ m»Çº_Qé•7ü5Ó×g,òÛÍÅ|uÐe°1fN&—†™#U%«¹;ÓÜ×ÍËRÇØ§›hZl»Žz‹ªúQ‚ƒÒöÂÒüÙÒ³RûƒKQ·ñÝć!¥¯¶½F(Ý\¥|§\´R)ÒŽöˆ&Kù‰Ãúö@ èÙJ¡~+OUO¶¸Ÿ‰G¹oSö±ïœà[}\X%6°ƒ æîÕÚU0‡ß"“Z0y„ámÒN ½ø1ªŽÍÉÁ_GëFwÆÊ‹?ðÓ=—ìÁk~:¥åºÿ2:NÝš==x+H3Üt}àSÆ3‚÷´Dz+wzVV=˜Y ºàTî´/ól´§L[n½­÷“T !%^Z瘝õuú±¹— ‡â¸ß§¢Ì¶«Ëi¬cGKDä.ä }¨JÙàõy©´ó—îÔs¾{+YêÓ†~ ®Æ´"yþ®ÊŰ$Qæ[N+bšù|vr7þàffK&Ÿ¤Ç\>öIbÂÖº–,‹¾:z¾…›f^Eü.“XJ×Jœmß‹`ùíiÃ@„²È®ï&c¨›0쟂w-&‹y¸ŒÄ­.Œ€ËÎ(-Ö®-Mtäà,}©ôXŠ£Ôd6Hw:)J]|]Å>qŸ¢F–kS­xÏ”¾±Êi"ÃÝïòl&·² ¤âò2„ÝÏCØÞµVÉ1XHu ½£öõÌ¢LbvŽ·c9xArý£ w-ИÄ/ö×]Õ™”™I*‹#ž.€MÏLj£‘ Ç èø‚Gs¢óåòO¸ÐhåÊtæPÓ²1“=»xdvj‰×¶ôÛ?©IÊÒ %}×$¯€K{z4Ÿú c è#þÃð4¸ÆÈä!x-6¾„i¨^êSù–oÊ…,H,ùQ ÏzÛ8+‘¶5 õT-ÙT•mÐK.)ñvû:z%s:“Û´ò2ÎWÏÇî}¿Û׿-IÎü‚*{t9?½j² ÜìYù6rcæâ\ëWøÆ lÍHö3"e8'"kàÓ]æøLœãŸ·"NØåoë¼zjR¤öðýgưj°uFsÊ>@d">+4Å §"=ºR]Ü­ÔMFGû6S;ЫÈwoÚP’'[+õ•(>W²ÆEÜÁÓÒÎgrvr@àÎ$NÅàs˜Çü‘ =ÄŒALÍ¢—CîݱÍ}ã/SGO- Õµ§Ü|ÄÍ|±4mkVK·õÈ9é™à¡øJ.'…ÂÙ {V k>;u¢…:’6«ö2…U¹Š9ȹgÃK~:š'Ëì«k{l­‹ó2øÓå BÕ÷eã¥J,·ãåÙ= °Ös¦fÞ¦1ßïO†!oáãBü'‘LŒ(TY¿Ú?š“°ŸJ xØcËZÖû¼ËC˜N¿nð:a¾oSlê˜Á?d B‹6°?¡Ó3AépÍV<ܬ‰m¸u¾õžV]î=4?7zЪ»Rª*banI¯\­Âpž?m§Œs¨ÛõÏ#ê2s¯ úí½¾ÿø`…·£ü¾¾«[˺N8uqŸ“^0ÆïÕjêÔÀ3¢9žZp¤,iº¿²5âbÆüvZb‡%‰Õrî7ã­¼â»Á8]7EïdŒÝDà«%+¿gÛ0“Ï1øËF²1†n„uîà[~Ýf{Û6ŽHk…ó\b**l5¨~·Œè2¯ÿ«üçzEßOdtв_ 8°>GxÞ“Ò¾T¹j<ë’ÁŒ}k¯7ȕѢéŸÎèù^œòJF ínïÚL¶À¹DYu„ÿ Ç5|K×»¡=y£;;óƒÁÜ§Šž«À×ÔrØbƒfcÞ’éH2^aDQœƒÃ‘WÉùíkëd?0œÐü QÞîâ¿W¨V,eT‘É…¯è§é"ADjð‡î¤uHE², :™Ušô>Ä¥8ûh‡_2_{曼õ;žñÞ§q©D~4nh›ÚCXý"™à›GëœéjÇ®õôgµ6R¹´rÐO"”u6öi”éÈæó„p_ ‘±»F[u:Ñ7lÌA¿gÏ?»iƒ¶¬c>²¶¬í©½)ÂëÅb®œÍ¬ ÊÍÛÁPòß=^±³Þý™äX·å´r/ŠÐÚ :Å5iœýî+< ¦Æ ýYÔœó˜W.þͲ»´/JÙUÙÞßBÚêªRwW'ãmÌ”h…2 žÞ‰ÏgÈÖÍÐsÒgËÌe¤ž‡Cq½RÏÃôá"Iœw3ê9”+°é4üäÆãX©QFR”¡ílHbAž`ó¬w D¬ËtAˆ·Äc¡ã™ˆ.èzPt·Íï~ !Ÿ?Ñ…»Î÷.¤;<ŵ¦Z&œQÆ/ÔÄå.(»/GsdJ¬D´‹‚^¦’«[nŸz—†”_·ú@žÁ¬i×3pVž· 4g%½.°¼è4†ì(·T# ý (n`]É«m;s"VyS¶Žþü\éÈÔMáóð³Ø‘7G뮢úït­ô.-ŸÚS[}ËL|ß•lOü¡W-iÚŒ<6ã_Y­0?ÑÔõ; 7–ÏyÇ[0ÉL°Ðö%xåLrk㘨>Þ8ÑÉ1­lt"¦$nhïT­í°wCû…HéޒI{¦\ Æçw‹#ø †Sì‡e½n'"M˜Øc§…*ƒÛ12ïvEyà꘠þ‡™FÌ KWo$S këƒË©aé£ÑaÖ´½÷îe•M^úRc ±|ç‰Ï{j3dÍÊÙ»e²Ü–Å"õ ZÌ'¥ÖÂ]·—þx¢ü/K±ä» ¼¦Ž°RøÀž!ò´m>Ý\s|±=ÏföHR>à2i¢Ù¶Ÿ|9mó(6Ö.6¡¾Š €ß´H—½ ÏH> stream xÚxTÓíß>"]ÒŒn -ÝÝ%c Œ ØèîéA i”””ABPZZyg<Ïó{~ÿÿ9ï{vÎö½¯OÝ×ý¹>÷vÆÎ¬oį`°ƒ¨"à(~! @IÇÈBE€@a|vvc( ù Çg7…x ¡¸Ôx(y@@(4¦ B¡up€¦' $“—Â@ ä_Ž)€2È jÐh"à$>»ÂÍ×êè„B×ùëÀæIJŠóý (¸B< ` B9A\ÑÁ À†BP¾ÿJÁuÏ …r“ôöö¹"޲Ü|o(Ê `AB<¼ ö€Ÿ”º WÈjøìc'(ò·Áá€òy@hCàHtˆ'Üâ@WihôÜ ðßÎÚ¿ø $ ôwº?Ñ?Aá¿‚A`0ÂÕ ÷…ÂP §ª-€òAñ@pûŸŽ Žy 0Úá×ÖAUÍð?$Øê†B ¡°Ÿ¦A³ Ü^ áê £ø?÷§ õ€€Ñçî+ø§¹.p„7Üÿ¯•nï𓆽§›  êî ÑPþテðÿÁ!(€(PRLLTq@|ÀN‚? ûºA~Áhþn7€š$êAàû#A^ÊÃèÿŸ†¯ð…„öP0 `q„ÂñÿÉŽ†!¿×èþ{@}–@´ü„ÀŸ¯¿Ÿ¬Ð ³GÀa¾ÿ¸ÿj± ¶‚®’ïÊ>~aI¿¤ $$$þ;Ïß'ðû_¨>úgwÿ‘Qî€Hþ&>½¿ˆxýQן±áü»‚.­g€ëù?ŠÁè7¡ÿóü ùÿiÿg–ÿUþÿ½#UOì—ë·Ãÿc¹Ba¾<ÐzöD¡gCžø»šA~´ÄêéúßV =# pG´Îù…î ïþÆ¡HU¨Ä^Š;ýÖÒ_Í@×€Aá}úóÞAGÿeCØ}· Ñ-ûe‚ 'ëßuUà`„ýÏ€<<@¾øh W¢!ô¬ÚC|~I (G Ð!4Ç@€Âÿgc…Ð:´]0Òé§í7 Fà 0q@ý,òþÝÝ¿qQ4Œ>iÈ?¨@†püÙYtÝ¿at5W(Üù ø°§‡zØIMó¯õ¯›ñ€ñçg`éçíçµ tÞüŸÞâ,¯tÆ&Yôň¢8fŸú;iãæ¨Mº+ÚÚWSgêÏ•GMÌÐñìú´`f[A Y< I ó°Ìò4Ö0»Â|M8Sl§ùæ»f™=ôáÛÜþ¾L‘ÃB­ž±ê(r ?Mæ¼á”Êä\Í—H EÑS¥òô$šb×(Ž%,[ñ¥ïS…ýmÉ"žŽºJ¡·õ‚Fš<ë¢32yÝK‡ô6ë¿ R7§Ä:ñÕf²)²×Øcœeí׈Àò{°©8õï¯u¦¥egÚ˜ Mñ¸Aá"ÖãÔç¶r’…„­Òe`Ÿ)ŠéÃ݆Ì.ï¥ÈiÙÉõ5°t™W<‡YùNžõA+Í™RáÇŸ›ß´uG Øö('C“Ì;r¶{·’_jˆž¾#5åQCÕiÍ"ºl¸ó§‚ óó°õÛ:Õ¤ïËKγ$šŠ“ÎkobdéÞ²ŠT™"!"wÙ+gx¾P%6òT&¬Y&]"ô~üà†²ÁN"IÝÚmƒîo7­“ì<…ñ¬µò;ï;Ù–êrj¢î&A›¶“.?+ZL†ˆÏ,å+þ0 ¶!/ zïðè{ÂÉæqã~¾¹B}Ì}XlZ×dðšÍ³†Žûý ôY¯Æwz:8Ó7}Ô/N±Èí†Ô¤X"Ša•M‰¢­Jú)cM,ûAÚíÇÍxÝ#MÇß„,ϤﮇºYnÔÝwû^ᮨ #朋”Y«Ö áôÇ&ˆ1 V&<‘ #7† ÏÀ0œšð×arZ T…Û–l›ãHë/ÔÜN{è?·ÖÑ jøÏ°ŠÃT%¾{é‹,~4×{ã«€èªÛ ÚÞ´2s§SÓiÁ‰±¸»\zÕLZh‘8=Rh®¯r }%¼f#Ür»º¥¡žî/tÇg’†bp‹³«Ý“&  Ax~za¦ËÄ`˜£’vX²›Ý’8…yü#m- ƤzMõ X›ÓÐIè9£jžÝ×’ê±@Þh¡Ò¹ ¤ø}7^½—Ã4"œ×aŒ:Wg{—©>ð &GæYŠaÙ«¶6¬Ú°ªpZ'y-ЪМL`Ö¸ÿbêÅ÷MêCÒ6¨)3é쮑¯Yžò\¬(ž‘Rò©Û&OsýÖNN A"ŸTË`XéG>ØUt¸E“¨u3…²îêUézæÓ¥ñ„J­CᦈúÌ÷^­y;)rÝõiLÒi¹u2˲|ýk¥•X*¿5¥çÜ™0ܽée¨”>WŒk ì„OC4%o×7@5œKÜáBFõÈζ© q®oYQŸí=ŸC^b¯1¨îçÂ> ÷ùžÄ7ºc4d¢¯Qïà¯5ZþII°÷Ö¢¦½`WpÇëuâ˜eAË÷9Æfmv½ùílõð¨T²{Kúœ šÏ†ß{%Oóõ³5°8™”êÈ×oË­¤ºñÌDˆ{ÍÞ‘y¯/„ \S#N•/ÒMµ>I5þT/áРõ5(‰oÑãŽh ¬ŠOmL²EÞŠP#(ºH1ô{F~täðÅ?zÜšTÙiñøÞz¡vöªbúðÖLàÅ«þù<¤°§Ò‘ïÒj­ÍÀó©çòV`F‰Ô:8­¸;g8ÍÆ ­˜Ë».z2wvTÊϘRÊa|ô*—îl¸`¾Ëæ!ÇsDz\`ÆÙ9»x9:÷yê¢-Ìz–í%Gü§£C+R XzâúKd GDÄkÁ¤ÕUkîfãO½! Â#Ì•ª½ùñúªÐ#–éÄÏé—žÖF±ø ¢‡ÙbŠ2¤ï ½àï³×ðª³ëÆÚY"'ÎÊÉ­qÞòúÇLôÊçOeð>¤Ïݸ*ƒFÝàƒïצ>NûWê–‡aì;±#¿U|9È`´Ì®èQ…­ª2]Xï•çX–›æ#ÀÜ=´CÀR³œHÄ’¯õb¼ºmJq ¿ŒÔK&¬±ôw™ ÜÎn½N¢;kñ;k-]\?ãsWÿº×£¤ /iU£zН›4­òàì Õ™fW(w\êÀ0ªêÃc;?5%9ö°È‡boæÏuoÅ êfÛxÙ"uÝñŽ{°#A…uîs±=gUϧ.:þ–&‚7‚t}ö¯ Ñó]tñs‹w\‡#kî_ÒöjIŒ½:ž>jCPÌcVÅ=ÖV/~תµx|Ô”Çu‚ëžÇ;ûlá›Þ÷¶­8ÿñ |r‘ø+[~ùz\ ™)ÎçqîXÝøi9ʨl°ˆØ^îC9%69W¸Exd®Y£Å«]Õ¹{Ÿë3¾¸Ô¿ïÁ]6ÜåZ˜³žc'b*\^í¸F´aĸ†#¦Þì”ߥ1øÔJYQàû„< 5qÁ‹‰lŸ7(©(Ÿ±7M¥œðîÅçþºA_h\°‹Qrj/¤•_èRY9çü!¸²ô»S¡öœa…É=öeâ6¨ÜÌà »_ĵ拖9ó"ÚÂn”†/°·ó™öklŸe*kC¸2«Ê»°7·UžÊ²“º¥,¯}½)ûQ¸8üvé$–ƒd8O ÝŽ¹ÔIç¦ì@{ŽÏÛ%mÌ·({$S8@)ÎÒ¼ þ”mÀè[Üã )Ž=µZ<[¡æZݘïM›8ýæï(0,pß‚ïŽÝ®Ÿ6nO#íÉNz¯=ºL¤¹½øÅZZveYr …¼÷d±Y¸›ù+Å’IôEÀcŒ Vœo¢¦Û&q[îú¡g—“2×ñ9èß3Ý¢zú=8Ÿ‘9’_K×?¥òà3ð:º)„ðÕò½– p®%U |2M²ŸQÓ˜9GO,ðEPµŒŽn°eX˜2Ì]iÃ`¡÷†8ÑÒté‡<…¬Ï¸ºîiF#äkëV3B¯m¶&ƒEJW (¿t)2š±Hª¶R%9Ëà?´’T~~âï}³Æ†XTä¬XuÏqª«þîÅìÏëwÊ*ÉV:Ü_Œ 3-¿Zš¸ÛˆÇ]”4¾'ô\ßúÝ­ª#¾£¾|±ãµö›ôO=ŠJ-“?¼ö²ýHÐÞX÷ýÑã®`Ÿ35¶ë|ºˆâ­1À‚–ÑÛ#i/Ëî,Žuª¾ÍN×Öå$‡ÉPW¿v`¾sƒ‰g¸£BGpÓ§7ÖP¢•K~~þaŽ!¸¹C¾G—CÃ53ïÃ3Ysæ7®é¯Cò£0*"³k–IS%õÏçO$޹ܙN¾yÏ÷ûÊÉCuånb|~ˆºG7ïr(ˆ'W.ìJÃ7<æpÜR³›¾²ZxîËʵVc˾à_AGRÊÜnÀ˜+ä@.û´;l*VòVM{<ÜWYÞ·DM,8êiÑ?2xÆvÁöB†‚öcØl®Ü–½¼˜›ÄJ¤‚6ƒhkš~ƒG'P¾‡6ûKqž´’ý³•¬›ƒn4µBÎWùª¤´$]1àH$*}WólUéAF«7v5÷)Î\¦ÒBîºi®–‹ÇÅ·utZñë#Ýû½8šj†Î”–?‰èb4ißGÖ{ë{ú'S!Û[ÇjÏõóòò?©./0y˜Y§zbr®ÏÒ¦5Å[Ù>iÑ '—Vr¥ŸâAíéèr´¤]p%_“qYhð'ÅÃlŸŸìIÛNSnn(Œã©K5<ºb¡Î`'”Ú¸Ú¦>àßì>`hm1‘CCćÿ”Mµì]†ºòü¡)­œÛ!®ïDf …þ»ªL[X÷@¿b4ÎéüišÖœ¾S}nƒe3¶ÆÍVLˆÞ1•ßҤíÍ[÷¦2Òâ/CoØÞE+«Íhr¶_­|`³ 4¼…C†»ü ,„tUWØÅ¡t„ðÔyL²Õpèµ²Õu¬õÞÌ?WgjIDn6ñQ¹Î¡Õ7)3i R/¦¤H’¬›æ«Ò7#™RnÅ–š“' è¯hHËå3Ån¿DRí`šX­?O‘™0ûqû*E´áU\ˆGˆJÖW¦&Šg-ÔN «3Èš°ÕG§àQWãl:ï+I¿<ºñàøl.^;s;¼@?•C…"Æ<õTó“Ör¼»p4ñˆQú¥'7Ë—zé‹E£ôŒn¯ŽáƒË¥ z¹.Íð6ÁU1Z ì&æ1ÁT†ehªrBf;ô+òËÒn¸BÓje¶‚yÄûRLïƒ@àüÔöp°(éA¡éêpy=oiñ˪êW— âqõ&Ȱœç3åý¾›Ÿ†zSqóã•D/Õ<WMpzIDGØ£3ã9j»âð‚Ò?¨U¬ÏIäenÔàèx}Zær¾ƒ* _[~+„Ý}l5jÕg€9bF©7—LãJ$ÿ,¡úà–¡eçñ,„î=gÖ ¼œa›½íBÕäsD¯¤™ˆ"è?ÏpIÍAëäN#£ÖÈïgßeÔŠqŒÆ.ïû\Ô]¾IL.Ÿk\•˜æ²°÷kØh~,|³vè©•›KxÄrÕ+!)p5SÚ¶iGë¥æ Véâ•#-‰á€I›·èËŽÃoi¨nJÉu£Òæ’pŒLôFÁýDÎÂJ®–Å+õÄeC]Ì.\Š Ë;0‡Ík8Ã_ì1”PåSvß3)}1«û^Baïü9K$B-KÍJGû5cÅižnO)É«"^w »Žƒ—m/áßguïÚòD0šævÿW’¼¶ÙÊb{Z/š‡˜Q8§?˜¼näÇ_²™³UzõŸˆ«Ê!¨Çzõ‰%W«Y¿vLUŒ{?Ú7¯¿—p,­hm¨Wž*ò­‹Gñ¶H}¸'¼ ‹íå²F?ÑÖu¾ä‡í;|¦,¶W˶aü’”‚½ÅŸtê!!yrs¯·&ko0’ì8­cYÅ•ØtݺúÒ¶Ui³%|oâümF?/6Éо»¢Ör,·.†F³àG¹ž|»N²FØÒBTIãœNxÔôÚA»µ‰žCSUÊ! èõ(¯‹Æ'a3IY‡ŒÀò—’ñóÞ¦cTtSåuksETIOKiÃÆñ^Ò ~þÀ %å”vœS>f‚e=÷BTÜ…rùG*+{0®zÊ ¾*ŸÑ€cd Òg·X=éÚzôÜÍ·»N¥·[î‹SGb¸ò-M8ñ›Ý'K$ÂÕ¤æ|Y{6$S¶¾è­ûéI=V!Ëv®'º¬¿áÝc3«ŒcÖZ‰f±þ4ñÍÑý‡$°ÎA¥ŸKÚEæÇxVోrwZix;t-î–Þ·¯Û¶dCx¥n)YþÍËebw”2´®”°½kŸ¸RàV.½c{nðqBIû¸ð(¹™Ê'ËûÖÇΆHÕæ¥Ç79ƒX"ÖUReßF4IÛb÷x}g?gZ¨1¶ÜÏàXí‡A0ãXÔ2Mh-WâDøÆ ¼|¦¥<í ’©<ïûÚP¼0¶gI­xfÇ»ùZ/ÊjVãó‘SÌûpMÌü…I—²£.Íëh‡Ç\ŽòÞV%¦ÙÃÝ–[ÁAÛñBªß¾0Z+ˆxUÝXÕ{ì5YCß$|Ëßïâ“qóÒÅ0pþ…ëéã–ý|ÇR¦Ù°!BUï]¦E3í vl¹ú"åý“dnúšÉvĬË^$×lúÊoô]·˜¬\àýúK¡G]zìŸqpÏWÑN¼ÆÌá“ØzpS¾<úÝ~:¿}öûÏõ­Ýo17X²‚Ï8=÷†(™eß‹|6<ºÓÓþÌzúy5÷t6¿ÿK½Ä–Z½™ aòîbŸ7Ÿ·ñ˜b úð޽Ç“WÛ¯Œ®Úò³ÌVÀÛœúI·ÔeiøN?P™½ùzx+ÖÍ£ÌE‡à¥<Ž|7„ãÕS‘ô,XÏP,Êèl£—!t—û³ ›áÀùÒü« jáFOü,HŠè9oï|-²ôþ!'-E]KéIÖÇM»ƒ´ÇµF0²|ç¾S@ ­® ÉRÈj»¹–ß]Š‚¸ˆÍ˵3*•-\¾5ˆ©ð$©ŒÎ97¨&xÀôc‘Ô$­É¼ðD³ì PÌì›Tâ.³Ö§ázW'Éwyôü¾· €›œÁi•—Ë‹sscä‰;[2ÛâdiÔÙ §ço¶øDáC˪q邹r—#qÙ+„V ¤@ƒ®à5inz€ø\2L÷¢Êq;`MU_‚1c^-Ȉ×~‹ãÆCg»·Af‚åíÜ ×¯µSØßv6ÊI»Sy  MÒL¬ùWœ~³ÁT*ûcþìwcqoÛ¨]ŽæÊ"7žÜ×ö’í.ðà§žzõb1ó„K‹èH`ޤ[†#nÁ—, K°C´†d >í²ê=àÞöFˆ›V²7lU076mÝ,@ù4–#ÈñªxzEûÁCÒØû¯6so3Ókìl&6¼x®ªµsõàƒPØËIG¨Ì,¿Ÿw°Èø£¬:°kˆ+áß ¿º½¾‰?…å sˆæ~ÐW*eúbÄÀ¼ð5"-ç,pwšéÅ> stream xÚöPœëÒ ãî'0îîî.!8 0؃{p ‚»Kp‚{pw—  îzÙûìsNÎ÷ÿU÷ÖTͼ«{u¯§Ÿî~k¨)T5˜ÄÌÁ¦@i°½33+?@BISŽÀÊÊÁÌÊÊŽBM­ r¶þÛŽB­ t‚€Àöü0$œ€&ί6IçW¢Ø ïb `ã°qó³ñð³²ØYYùþM;ñ$M\Aæ%f€<ØA¡–;x8,­œ_uþý 3£°ñññ0þ³:ÌLìJ&ÎV@»WE3[€Ø töøŸt‚VÎÎü,,nnnÌ&vf°“¥0=#À älPB€N®@sÀ_%”Mì€ÿ”ÆŒB дAþåÐ[8»™8¯[Ðòâbot¼ª4ä*@û‘ÿE`üs96f¶ÿ¤û'ú¯D û¿ƒMÌÌÀv&ö {K€ÈP‘Vdvvwf˜Ø›ÿE4±…€_ãM\M@¶&¦¯„¿nS˜¼VøO}3'ƒ3„²ý«F–¿Ò¼^³”½¹ØÎhï Aùë|’ ' Ùë½{°üÓ\{°›½×¿‘ÈÞÜâ¯2Ì]X´ìAŽ.@9É8¯&”ÿÚ,Î.VVV^v^Ðt7³bùK@ÓÃø·“í/ók >^`€Åk@ðõÅ bâ 8;¹}¼þtü/Bac˜ƒÌœ¦@K=ʳ¿šÿ¯ýw¹>²¾Ž€õ¯Ïž ^'Ìloëñ_úß-f‘“UR“fø§äÿ8ÅÅÁî/&.;+€Àóúàó¿yþsÿ®þo«ª èŸÓý‘QÎÞ àûW¯·÷ïB\ÿ™ ºÖ†ð¿ Êà×yèþ;þú¬\¬f¯_lÿŸ—àïÿ³ÿW–ÿ×ñÿ¿'’v±µýÛO÷/ÂÿßÄdëñãuž]œ_wC üº!öÿ—ªü×B+ÍA.vÿ×+çlòº#bö–¯sÎÄÆÉÌÊù/;" rš«‚œÍ¬þ5KÿnÆ«†-Ȩ †€þzï¼F±²þßëê™Ù¼¾[ ¯-ûÛ|ݬÿÕ•²7›ÿµ‚ì\Ü''”× xE\/¶×]5ºÿ=âf{°ókàµF€Ø 坯róX¤þ2ýxX_gì¿èÕ§òÄÇ`1ù/â°˜þ½2ÍþƒþªƒÅüÈ`þÙ,ÿ…¯N‹?à_NÐd‹åÀbõä°üI~=”íðõTv½žÊþø* þ¾ ;ü_…œþ€¯B? 7€Åùøz5.ÀW]·?*zÕuÿ¾êzü ÿ§‘f.NN¯ïº¿7îµËÿÆ¿X@w ÊâØL Ⱥ&¨õ¶ZŒÄéç;'zÿyÄ%’ÎŽ‘ëžZ€„àÝG^ä‚XçÌnäÝÎ{ñK£.>«áºwj$?þb]+¼…é6£÷ŽÐýDÑ…}W¡£CϘÃÞÜÿ¤Iý‡Û¥è4=£ãÙÏÙôAš¦Ê› «DÚ†_Ìh0cQ‘ˆ„¼¼h0Ambém•âßĦgÚå¨ ç¢c¿ãßJÜ¥ö ³smõ/•2Bƒ6SIÑ.Ó…Sö»¹‰Œ'ÓÔ;ÝR„_è.[ƒ>Ñ™BF4D(2äv&ÎeÏw¢äÔ-›d6`û’a%6¾¼;Ý@Ër7¥Ý2xß!Yü=b1Q³À“ƒ‹¶´»¬LWHµwYzòWP s¶‚·â·ÁÉ8CMHbÆtjãpnƒªÆ…×<Û¤òpd S4ˆQ¼íí¶Ÿz Tf¢õ¼­ÑjÀ¸ó7­üÖLYTj ̈³²#Êí|Roîñã>P^òÈK+-Kóœ8–#¨oÇD=æ÷Ùà9¶‹þ^áH#™°Þ9”îðÌo&h®ŠïL`5î“˜Š‹s-÷B}œÏ[6E½ˆhK&>L_e‡Ë¡z/çB°'AÓOÍۤá/Aµ‚0a„žß~^Ò’l3Nu’_‚ßj}±ˆ@-óÏqŒ24c -Oô «æîö=Y‰×è¨)í­u½¢J¨½Õ¾£:«QùŒ’û•å̼S Ï#ÁYºÏ*M˜È¥ê+×¾oß?UË^þýö@¯ '1Ö"V=:ª±ƒY]Wâ÷:Ÿ!QGªpÏ‹ö•ÈVºÏZÄÌ}&ä<ºS\0^¾ˆJ©ž{²ý¨š›˜‰WÖ…‰"þ½o7¿"¢Ì*ZUT>È9ãû`½N"n¿¡ißcSm¸ÅT¿:ý¥ì!ðçSÚÜžØÛ—°ÀŠPsg&ø9~iÝWÙª"‚šõÄx)T²âçCh´ãñÙ’=š@,ËÙµn}vqºÔw(47=ójÇI·vdêî;*:®Y oæõ›ãhã™exîã ¤J|š†BGj¤÷Bó —ÔñOY˜HžqyòÒbÔG¢_l²£§È7.¦ÌYüaÊ 2-/z?°_ ê?„S¯ÇÛëоár‘Ã_§™Z3Ï“CηЩnu.3H¾3€o?ä`'¿‚1=?(Ʉׯ€©ŸÔðõW¹*©¬Gß§I0~:°ÚЅȈ½«Ý¦ÞÃŒíM~7iuÜ"øR­ÍÃSiE™HI’Ѝ9>‘㌧(Mf™1ó]]ý‰#wÙGÉÌǸû<)6ÿéSªLI•>‹Õ¤½“Áœ{-%tˆÀÚ'v¢ê.k§} ãÚ¼dB{Ó ²¡$ïC±ðéû¢Ïqœci0ÃVÆü–$j–_ó÷L•7’~ð½k¦,€–|F5PÚšŽ³åᡟ(æSÊ—Âòòx÷%CMÌí’ål²”MÌ8—üýkT77´ˆ†?–%{ãt02v4Ã…Áïö¾!WüòšÏëG ½]ÏÚBhZ¼¾#c;›QoËç·œ¡ïàý¹öÞ^àw4 X/lË>3Ü.4ƒ3("÷J‹è=☠q‘&³^f -•mµùσdQWîéòßÜ“OZS1¹‹Å•X&žº5wÚÁîüãÙÍ‘0*IløŽœîaµÔ#®D¤´jùR‘ "t®ä$ã"ET ½½yt4+È-Ò.Hi<òYèU]–£}V—²Zù>û@FÚâmOápœçjéølë(K›'Ã~…‘ÔT³$¦2nÄ^¨Ê§Œ¿G@ûØB2üÒIÎEC†úþ é|þp`²£ðÖÌ/™ßg«EiÑÀâw ‚ÅyÓ½ô˜÷ çUŒíœ0D­?³š…ý™ wðõgKÉ!‹aò´¹¶8ÒG“Aì|yÏÚ&ÙÖ—Œ<ÿoËàÏ,Œi-é¹Þ •ðëWXaf}Hp’i£Hlöð}›u걂6ËØ¯3©·ô,­\§P‰†ð¢æ–g©ï5H]ª^ÖéXÕõB-Œ¶yHcáÉ-ätx.×îï*Žy¿“ªvóßMïk+8¨d­ä q¸p æTãwPHqk»d°—˜ñuí¸žsCeÖð'›Ô2¢{ë#} ä=nS@~ÖtŽWáû˜wâ¶Æv; Ù‰ HÒ¡Åî wúLÚ…ûC/zæ(s=†!ãç£aL]0S±³¼û …ùæÂ¯(‹;‡œš^zÈt•wѺ$;1ŽÜî/­ÞiXJI‚}’¤Ð8ó"ù©â¼^¿<@Hƒ`ïÞ\˜âÃòXô_‹R˜Z ÂX"‹ö¬yíf%4Òj7óøÞ ^ÕM ÜÙIÀ ©Ü•C†ØU®Õûæ@£|uÏ>6Éùµf~5¶–M±ûœ ÍÏ(g›B+mAª#MË"Å£…—'¸K3O>‘úiÇotå9ìßV~^Ž0-ÜúòTô`]}=tª(¢QKœgýØž6)¦di1«»+Òàn@ j¾MÝ=µ×¾?ù”»B=ÔôD7Ù©)K˜:ÊüÝ„MŽ¹Î­ad¼&[Qö|pC)Ådl)DL€Xi±J¢_#%â÷JL“s‘z‘&±DæJí‚ð•ÆÆÔaPø_NÈ|pê  šËÙœd](îtöݾiã·â?²~ BN—üiFÊkCÊ|{lû»ªÓÛÖ;­¤–¥ìvå«m*»ª ò¦ýÍô·™[[ý^‰Çÿ þ¼ÄÍÏé´ÞLÓ`¼9.hàö°dQ9ûA*||´†c)£ 0—4ñ¶—5—+Ñžü{‰¿k…pÌ{ s:ô£C;̈ïÆÉx‡Ó ½€¼@ó×ɰ yöúÃ)f¶;¤m(Z`îgZÖ½=(ýâC¥6g~Ò˜O–ëýýûÒ v‡N sø5%ïß8;ë*¸¨Ã*’gÃÏa ô‰ÏpBÿ„‹QµÃÊèµ ÚîTmIª4ÈM††}È´€HRP7ójBvì¦Öuf:àUX7™3䜤o†fó³k»Çù cNs÷ Í†^œ)ûßi³¬u8›ãSK™‘*;¾÷ª ﯂Aß°á›ÕÍkNð]¡ÑÑ^(Ö/Ò-!¡ŸØxQÈ©®i3±¡QXeƒëË£¼·Z\›¦1Q»&é.BvC›÷Cü/OMl3QX¦0è’…|O>ÃÒÆ¡°s¬7f{;ö¹ê+r×au ‘O.G`|­†¹ãù ðñ‚®–!¯Í½™Ÿ* v~ä¢a?;;ÊÅomÔÓ§zîPRãWoéÔ?îs+¼na¹pµn{Ù0RL )¸)uëQžjBò‘^1×@jŽŠ8v‹Ø=£þn¢TlÖ“íwÄ’{LzfjJæ§À€—–Æî¸Vfa­„Õe ÷Å~ÿ"æ<˜%"„Ö-j©Üð½nn‚IMqAzX8¸áì!bó-Fb¦í1;} ÷/a¡.ø¦Ýò9/- õé%谢ƲÝŸÖ¢Gı֭%Üy>]!WoöÁùæŠ~&›Qê‰~ëC £gÊ]ŸgäDG<âna5Eb>Ô@vCumˆÎ;—ñG@‚Ý¥‰º[ñú¶Óãc5®¨D(ªaF#ºsÃæ‹º!kaH䨂3Ή×l¦¥xíûÙõ¢›I‹[Šu!ïȸµyØmþdzxºä=¾Å2ØQW2k¿Ô¡x›2UTKÒm–SÂZ!„9Ì‹ë/FZPdÙ ]‰ž‡äñhš'ŒTÚ%“|ØÖ£ÇËzëõ—1/Xo_üp¹¶Ú׋œƒf‡ÕG*/¦NÉØ˜]o{åg {©¿ƒyR„ÆBÝ'è7xã*!fµOÕIÇ•ãÁ'Ì{$ÆË >Äd¾ÍǨ.ýž£1¤òMÌUD$29!{½ef»9VÛ"dëjº6ž Ã;è]ó£G¿ÍˆÚëtª¸V˜†Æ“T/ýªCç^»Mã¤%u¢Ç° 5GõCÒ'­õJæêq?(d¡èÙ`øM¿ÆŠÌ0”2¾n,mß”ÌeW—zþh.ÚÀ?ôÎÞ Á®ÚÄ™ yœ^™ìÜÉÏS$]söbßc¢ŒGnàÈ.ʦ³Ð:#é¼èºðúÛ(ªä™ÅÁˆù…ßß5”ѹº¥…ÖèÃû)(çy¡Þƒ £ñ3–ߌ”+çÔ`šp¯Vø‰sŠ3ŠàÓã—õµÄ;¢áÍE†êV+OQ6µƒ”²×Ƹ<Ÿa”ìN’êAýÏmòaó ß ŽÜ@v]û=ŠÃE9×›f…J“Ù6Âq²rØ"©QÕ7£‡Ã§P,ùoVdO©eÇ‘,Âã üŒpÐÞoBÏq}aà“š˜Âa×ìEiú±+Qw­Òü1¤v·rúý| ÊòN(TðTt£–]‘³ß°¶}¶ÈÆ0Šwã GpçiYÊÖu¡ G£û‘ùÏGÍÆÐ:÷'ˆ@ÙºéÀ|3ú–vÕ aÞ;tS7s¾5uô¨Ò5Ô.$"½½A‘¶Àƒ¢þ\eè»qÿŒxÜúTSSä9:Á]l98,CåL`ÆÆÍµßÊöüzð£>øÕÄéü™Ó »KDW*`³Úü+µÇöýtöXÔ‘:x?œŒ÷.ªêQ tÇ^×½t¶^<Îí+m§¹G‘‹ Öå(?Wµ´ÃhCÝš—ó›¨Ô{o§ ïJ§G_kšg\Ë·­âʨ ^ý„ö‡ñ9ÌŠT¹‹l®ì‰bÿ¸ù ü@³- ì?>wºðûnÌpÖw"@ÉøpxRõŒ/ÅF~û*¥y¿¦ìùB˜j8úCþC˜%•ðŠ"V€ØÑ­x°§F#4èp%9Øã*^KY¯7[F$Ãv²\qàøeoþ ÁVÕ®êvÂÉr©ƒ¨=£‡_7)çþ(¤zˆ¿Ï]9­>ƒà PAׄò)ÖÅÙ®áqÊÚ‡[S·=R°?¿…!ÜÒ¿‚{Ë(½fáa`!kôd( Œá!«ϲJùŽžlöŹ ᄹdØ¿¤“b< äîž™@ôÖËïÍCârMÂÔ;r¢°«±%UàÛv"®MW|BþNƋ‘Ûy[ÊŽ¼ _½çªN'$o8aÜ\²IWö']ß|9#κݞÃ}…Úþ‰ÖÕŽç§ Rt_e§þFUŽË„§âË%Ⱥâ®Ô´ h½H!¿ Üø¬LDصÓ|Êyž2¾ú~¡÷JÈK/ã—w ˜A‘ztUè©úIÓRë:ˆíâSæ©è,fTÛª–®Qo—±3I‡ï¸]mÿÌñ@óx<2 tŸ°Ð"Pü£ƒ­v“/Š¢µÂÄ] \>XlþÎ ‡_ÛŸè|J¡cAäÖÜh'¦<Ò꯸y¹Ý û¤7ûaRqYå¬0ÿÆC¾\-žž´S–ŠJPw ÕŠVY…¬n‚#ý˜ü¥_Ìþ#Âdgl à$ß&Ñ×~×Þ‘ì(>¬ ÅJœû¤Öкù`‰Ôýó´°©H@\yœJ0'mírKE¾øô&ÿºöy/Ê)yfZ6œ?d€—̦ù™]$”P3î˜ñ^­ðò\óÃAP­¸Ô¼|;S«ž)2þPµŠF¿ÀŒ¯£4ŸÙÊÛÂ23³d¢ïs©íæŠù¦¬ýIÃØ‘_׬úôæOkÔßÈ „ßÏÈ”¢JÇ,m{ HÐmåÌtíØŒé ¡Ü¹s©4[¹RvÉöDpQÌê2Û/£b!ÀšÒ ¨¾üöT·Þ( ÉßK&•è°î_ê(îðr£ÐÐQ•FÅ®Rgªn"|0L_¶9ÐÛsYØ—c ôô†¸Å+p× ë’¤µÈt6<õ¾…+Ý8a_RåiLlÓ>¹%'ùd §-KWŸ®:,Ä·œ…iWŽÕšTNX€WàG{Æ4o^²*‰lF‚VñYhíÁwï• ôf“-ªÖÞ׉<.AÌ(P ‰@•jÉ<¿ Wœ•’\Âê^™:!ÖЃ;«˜†#—êEÔbY|¸|ð‡ü˜ì»êë$¼np.‘(¬Uµ&(ºþ:Œ¼Âlò;‰N‚baZÒ´ë ‰t$ AªÄxÑšúwÓä‡ß}Ý' :OqlçiQkI{9,lœEÛ³™ùü¸ÎïÜ{ÔP¨Lf°~ÞÏ©>ìhPU.ZF1#fŽ=D”Ôôˆ²]åü‡'v²©D õ’-å¹$Ývñ/ãtð3’^½K߈>NBídúa cÈNJ·›õ.)KâIœ®ÍÁ€È ñ“§ÛA ‰„5a0ãUz8¼bÜ zJV$IvR72º™3Ù×ÛÙF-Ä;.o Xœh:P[k*õ·’³3„Ó_Ú ÓâŸ-JÝHL „½_I¿Ž JCödn$—f2¼v<'²ñ[bfIøOý¸&Áf†ÄŸÛÈW"¯·êñ¬•Ñ®ŒM·Þ¾Àõ[GžqèìvªÀ=_>Kec˜[` êÓ{·ÔkZàA÷­ú]¢³…ËD ÎwÇ;©=ï˜Ì)@|´i¹¿ÿBùF)µˆlY¤Žm9³0hJ”c3˜4úỉ™GÄSW9*¢?ö…êggÔ@S¹ëà¶6Éúm,7ïB`êPŽÉEZTѤª5u,E8ßt¹;㡃79$û¤þ@îÓQœÁ3[6l öa§ƒN3vÔ£ÐØIK9Ø3ÂnZkŒòÆë}!Äé7ñv·i.‚Ò®ÿ”@*“Ÿ‹¤Ç(w2½+¾Ø×ßÙRô¤°u_Õ¡7°g%¯û`(²@^˜çù«_çE÷Ç4øáé:qó:"Ô4|Q/œeô½êQ¡¡Ì^Š‚ä ÆËf.%L·P“§Ðm2¾Ó~{,˜ÓAªYÃÛU­7.$È “ÏGm–v¢G<ïDƒ_ç6ò¬"ß?t‚=õ„OI‘fc\ê²Í¶(“Ë[[3†ÏRŽÍðgê¿dTìÑØòÝâ"Ë®Ä}âÕÍ7ÇÅ( ÁûÖH,nñ¨GuWY¤Ì<¢å  —°^G³  S²v!ŠâA^k ¹ÛÄ´·t@è$Ïʽ³Ö–alÛ@D\Æ34Çž?ˆh'Ÿ€a2Q çq5.Þ¬Áêõï{9lýIS…á§ÉµšP+‚JT 9©˜A‘•Û}CÝ«µ´˜_⊿˜ÙØ'?Æfç $~”­X÷^Çjèüý­À¹Åƒz¾D ZKG}|M Zaø»åa^ŸbG5o°ÁXí³Š‰d¼ê¨Ý.¼€§¥&ü%ŸgÙ;=s Ù¨_Pƒc78ì[/ŸcGÄÄÙLÆQ&¼²”¡´/@Ú—Ùi,ýÔ:€–l”2aÃZ‰ 3GN¬Â!à’婯H ¤ ^E 5Ô\~‹ÇR*ô&wاƳ›Rª^”Ýâæ³éT6IBºc*»ä¢~Ô\çûK¨Å ÿ™aŸ¯Õ²çÊ~»j$S`í éÂ)ÑíÉ=W“¸dö.4šÖHôKo©h/M7Z™Ç ¸•]ó=q¹†‚$š’Êù?[ÒËè9Ad¸hÎa}-–Ùb;æ%Ž{õš]rȯ¡59vO-rjÙ@§ AYõ+éö‚ɘµŽo] a©gØñS}|'ºÓl[Œù×ÜKËÐÐVD¯“Eq%#ÖaÉÒ\ÓÝXêÂÐXÞš Ê™“`sGFˆÛ$ ©læÆ÷7fÜŽ=uÞ¿7ݽíÉÈŸg—v4\5K¤Ló&íS/üõ>Âußs{ë±´àÍ·ü¤*ÐFw å‹Ðw*² )29£ND“ÂçžÀØ7o:MÈû.,ÊÄ9‰¯ìÝç¿ßúoÕ¢ìøßtÍ_¸70Õ æ·ò‡€®¨æ‰®?G‰Ç𹄠“àj¸ÛQËÁ0ÈšVº‘ùL*C3=Mòb…³{Ê<ì[š4/€WÑy´áå“R¤èI–” ç¿”nÚá«“bìs1 <>w*øŽ Ÿ÷_£Ê¡Ns¢9³ð@c‰W" Þê÷×2û§!.5³9Ÿ¥Ižåä³ÁǾ×z{à ÎõI{wÝ•hd‡Ñ aýÚn›¯jyÒPÁNªj±×4hþÌ©v-÷&àñ»QíÇ}¡ª ÄÏ>˜˜PÕ'RýtÎ|t•¢/ê3ß9|Iž6‰bŠçüÉ­îç2Ç9dònã”eCi3|úþ·sÞê½ËÂ$LpYËÂ$;ZcqÒ£ù]MŸ™Ï~ŠÎÞÔî5oPŒº[ ý‚´@Y:òùX}e•3BgÌ9Ñ7]wÈEá)ünRþCööX—ËDê¶À î]•©ë,Ì_Éã tðùùKJcHÒñ·‡3qŠEŽIîš2Øšo”£ì=aGZXïáõÓ˜²áñ–³Î¼|‘]ÉÀLnˆoßõ;¿KÍx:ÃàÀ6D Åå9ÚNR—›öòA¼éjœ½Ÿ¨{•s0l³DŒ¦eמۙ”C¤™ÆÓtPIb½!·»)zí j#<§¹§"òˆnBÚ(æpæ5NñX3žÅ€Ä—©ë/d¹a¬CFÄ1DëÒä.ð´ã‡ZpÌ̵mÑ™nY±¿ØÖ/VOîFo–óÉàÚðb·lNb[¸“ŽÔ¤S)›ðàÜð”9S„éâŒ]¢”ÑfJOLâ;Ð:úùD›Ðv.*ÂOÆs½u ø†rÿ2@,(Ko‘FvÈ @¡goæ^k:1El{æˆh‘øAªz±ÂÔÙ““ÅÍàè{4Úº|óaÏp„½ÀÅ<+ÔðgÞ¨Ò§Oé²Os‘ðRL«èwõ*˽ 'Bƒätµm[¼*ਿÛyr,È7›D(yÁë–ÏÞÎg“ä°˜ñ;^çaðAÕÓ]h|¿™zÆH#ŠÁÊ î/´=S Pú”d¥óÅ´JNcíÈ50^t]#íh—F|0ñ¤½eÊ´7y'uK¦Ú†ðY¤§o)ù°ÎòÆâFjÊêè÷O¡Ã¥ŽŒúã…+¾*Ò&D§¬ ™:šTBÙ}îàEæÖ”áZ€sk®ðAI°Ÿ÷7P‹_ HÛUDŒ³7 üŒm¾­mÎóÆª¥!su¼¤ZLEÓMS]:—gg¾R¤_¹4Ë­^ìDW‘ ¶[³­¾˜&^’K-µ74~—Æ^B›ŽfpEŒt›6â] GÓ1˜i;:C)<0ë9<‘ßjA œ¾IŠXåbt+ã[ª.eô`§¿WuÒ{²Ëûª™Du?U×WS¤ÒVt)ºq±|OÝ× æ“£¥2âJIÇf±^¼/IèÚo“aø Õ¬„ÑM0~*3\;¡ #–ÜOûYªùØ J—â„$«øÎÀßè‚ ÿms/ÍòâùDz›ÔÚ¸ŽÞ뾯 1¶ûJñ¢Á»«$¹J1ýé™ÜF] òÊ A¦þ[kÚè/«ºÙãØ"…ŸžN ®tR»Ž©'¿O,s„ÍþHé©õF“¼€Oò¦žÚÕ²Ÿâm|rØJ7s+-M®ø±[Ïk@]%€æ<Ùjá]ÁÂNª¼ßæÃ©Ñi-õpËЙoÔG™\£¨|±°ªýåúò©Åf`¦)ûçÃo˜)ù;¯qìô/„ÇÊpÒÆ ¨|¦òÂôqî’žOàOé2»}–4Q¸Clîóåªìà_ï(æsÔ¥-A×éµdü²ºÛ¥e«Ëž­?æê\§,Ç<¸§£’ÞI½¨%“‘-¯ÃEk# ’‰QöÿÎû”WT.S|‘"³(Æ·®¼R1†)TÔT^ 0\—{iÁqn^™%°­ôA« Þñ²&ËQ2sj¾"ýr\2EÂ#¶L¢ú~ËåF¢6*ïB*tù,j0³´Wnqÿ¹pޱ/p‰Ù… %Z׊bŠ–µW!;ЋMÉ,–‘&ó5CÊðsá“:Ûâƒå@ÁüÏ/ ½©²-SßgÆ4$Q ¬iõíõe‡ÓÎòîäÐê‡#Þ6Vfõ9Gxê}w© š¤óA½ÊÅjÒ¼Û°¹¾µ0Ý9„‚TŒÝÙCz’VÄcñ9AãŠÅíO6 ¤Ëƾ9ôXãæ…Mˆ®òiÜøè*m½Ì7̇òn‘½Ã—.!Âyl\` )$ Y£ÊÚ|ÒÃѳh}ñpO|ïúríÕïÙ|¼_éüaRF²4w*aõºØé¬éL 䌠 ê.¦°Gšý'ÄAþÓ|k—½©¼®Œï©DRK_Ú[ôç˜ÖüHü°>U‘±Š!ý5ƒD. \ŠÌ‰ÎEø¸éÞŸó]¾@4…ø7E.……tÖs¶êx›5«À|&mű<òv:Ò‘Ÿ¿¥ƒs~zÂç–ç<8òžÊvÄ×Ê6¡ŽÅ(×sáJÁ™à=¦^[Ö@ãî…I¬V"e==Š»Dk$H€ˆ×~2‡‚ñŠÌ¾mûbõpúE£K3ûձ‚Ø*úõú­÷éc> dÜ‘.Ó]רŽá&Ç-;b•”vÊÂà´Eã”(z~GbyaueI•Þ+#ÿÆë~Û§ñPm+$r84@MªË‹®é)àƒü]}œÉ“ØS­ë%pˆ'‘pÒRÇ]ñ™dDÿFïCšÌq©ÒC™Ô® XP’-ÊT@LYªï<™˜’²é˜¥®”‘œ‰ç¦E ûãP²UÞY2ð$ô½ìQ°œ¹Y®[óûï°§é¥Ô‡Úf)&÷à[¥­ –8 >Æh Ö°¿Ce5X%“ õÛYÞØd Í‚ ÚéNa†fÒwÉòE“ën| ·„Ì cbOwûåE¸/â‹¶–1à ¿78.‡.3^øjl×el9‹w¶¹>±,X£5¿ô·Ï¥:Z2Ž`rm®~…øÕ4ÙE‹‰ãiäãbÇÌÅ6’ã§s6üÝ„uíVUŠmè_41ôÓåkÌ?°ŸdNT¡†CÑ=iŒÊæèÔ+¸ ZGL–]ù23A†L,X9}¶³íN¶¨Ì˜"vÀsƒ%÷"Q»y~;¡*%n! ¿¨,NƸîËé~ê…!›—;í¼ƒýý=LEÑNh“"Ûß³”ðã »µ \¸•Ç}CËc2¸£ã¸ óš™o:0â„òqQ“&æµ%t³ƒ “§Û6EõÃÐ1‹dcåÓgÓ+ÈUÓ¹~ûA|Bõ\¦0.Õ^Òï9œé—^rr€Ž®°ó–È:-Ê46ŠMŸ#¬^ÃhÊî×)º <´º0Äwª l’¿õïY'±Nµ:«–´¬Ü}(´Rî'eï—…£äúyéa8*‡Ùd•ú†M=sî]ë¹àĦ0æ<üÌ»¨™x;æEaÊG€§x:¾ËÔyjO9é;Ù^F¸øѫҶ%¡^l,Œ‡Ñúå Þ‰ÂoÕ· ê`âÒ6²‚ŽSY‘•ÑCPDSßh=w#eî*Ê‘èh|.JT2âÑÆ±!BîC¿¦B<Þ)áÅÑþYõ±$‚–™84Š›yoO8ø)A.¤ŒL›Ý·V¶¯On‘á·Cö.?\7‡™4VÈ—,) u¾¹wEü½þ¾w\•®¹—¦ºÚ,•ŠW­ÚH àœ´Qš]½îˆ’íÁŒÄ‘Ò£Nà-iÏ=ã¹Õ¦o]IÝû7B;¸è p*]H®2¶9z3ŸWÎ~·-8xß³KÏM´£qb'eÑŒ˜[5E iý°j·>÷5Wú"QB¼Í|ÈÁ~0‚ß¾=³[ì‹‘Œ¦O¹ýøt(&ÐG-W÷K›·~z€8´W{?º~ϼãøcMNîigž%~;LŒe 0G÷ÅøNfDàÛ߇ƒú|û+ÇEëMý=JpIÞ¹æOñF8> v•·L\#¢,›DýÆûæ6<ò—ç)*ˆ÷;†7$#{Å݉Çzfx­ºçÃb³K&B÷ñ)„Ô/«AÞðÏ{©˜oo°“)Öù³¥¿Î¬fUfÿ„;)ÎÏêï;/dâßMÃ/Ìå¯òƾjŠi¿KÇÍñ—®Šüöõ)ãÑ=>aþ§pÿ¼#O›Ü‡÷>‘¦ñtïÚýOîO›ê»÷=äÕöæÊ£½®]šòp²êCh‡hšÞà84WµÉ|ñ\z DR%Í”`©Â¨‹Ñ£–Ÿ ˜ïüµ'ùy¤˜èFPV% ¤ä°cŠîÈã1ì†ýH¼"+'ŬE¯0¥»ö,‘s»M’’7²ÖõRÜØâ¦ÿ~¹3%>LæÑ°#*°@R³ÃuqìWo‚ 3‰”0̬Î5²ZtŽÝƒ¢Ô¹°ú•Sþ—W(0E‚éøÁAˆüa]¡Ì©¼ÈB†#ǃ?‰ ¼äÓTA/`D%® Ž&D:—c¡™F€šîE¹žUÉáàïsOÔ1@–D4o‡K(ãÃüD¦qd*R&•Ê“jÕ[Væ{…Wu×y]}=·êf)_çQ[, qb/çtËoö›µRÝÍŽžÇ¤ÚI1±GQ?ÖYĹ®Äýd#ùâ¨ë¬÷ÞU4™(ç%!Oà'Ò½íf?m¸6F'VäK³—ƒ¼o)4ä¦Ú-';@è!éî%–îÔ [€ç¹ÿ Œ!cèÉ^•åJ\ªÓƤdRû¯ÇÐ?VÓÛ>ì_¿¨›kãYs{Ä>®(~l swîŸ!y”<É MþuÈ&~„ç^Ž;¯w<§(çXš;~—†J¿s0Ø ƒ(>Êx§(i’x´ôær%r2éë]Ë$7³úîÚD:«v K"‡¨&‰×2$DîÍü©¶NÖ¬*†ŠÅGA!;6VÍe¥„_ª[°ûø˜Ÿ5ÚÇÖ…};µ®5¥tG¾_‚•5aå¤0QHóò¬2h—' ISìê\ºÛY õ§¦Î?‘ÌâÍ—ÌÜ( ž=@+ Ÿªºcl»Í²‰A³F¯ÈŸOw¶€2RèÄSâeéï ÎPŒ€ÎžVOÒ"õXÒïs£–Ÿ«¿PTöOamn µÕdMÒ”†ÚÞý̓š`<‹­³D”1nC5 .I„Ú—FšÙJï›Öä:ܨ ÷(‡JóÍÏ™f-ýN8u9^~;Õÿ‘ÁçéÕAea´è86Õ ÌúGsœ¦° þOñ`ÿ­Öòz'6’éniïâPÓ”i^ÿ]a¡øp|ZÎ"µS\«®•žDï·¢orm·§ëief¬$Ú»Ö-M+ÇÛïø؇ŒM5¦úØ÷õ3d­KB>ÒÜçvªˆ›¶¬DXA^#±á®Ä6ÐÄgÉ|UøkØË5O[“²¶'æJ® %?e{Rø^½±?_ƒÚwr¸´½úVd¹öC|¶Ç!UšMx[e¾n¦Ç³àû ãÆÀOGaÓ¥ÈB’ò·-x¾8Àcf«—«?ø£Ä¦¨æçá´Ö BGÈg’ò÷·ûÚá>—{™"ß…=5ây´©X^ÐÙjÀÖ/Ûv˜¼Uì±÷ISÇÚ[…v´ƒ®„&PÍr?_ÛþÆÚì÷"Rž.ÂÔ¨oѨw¹C.ðT[&á¶“¶ íþ‚–þe¸eø8£ëIYžü ¸»ÃÕùýæÇmÀ˜Õ6õ}ó'Çæ÷¡)§$êÙox)¤ëñ*^Tg•|¨ñAèg‘ŠP\Àû*Nš¡kåçÓ ÎR¦ªù´oÕÇŸÏMò†-+ ïºE<˜kÓã2ŸúçÑ H*YÅÄÚ\Ƕ³=d˜yŒÉm©#Á€ H`$§ÀÊUG“ ¿»ß“_æˆIqÃa¦×(Š 89­*E¬Gôϸáò f)†%Ÿ"…°ŸZS6÷?~øTÊeoÞ­º´¶jû1ôÿÐ*C7 endstream endobj 1546 0 obj << /Length1 2839 /Length2 19711 /Length3 0 /Length 21319 /Filter /FlateDecode >> stream xÚŒ÷PXÓ càÁ‚»{‚»»;ÜÝ]ƒ[pwwÜ šàîA‚ëìî»É~ÿ_uoQót÷éî§åœ‚TQ…AÈÔÖ(nkãÄÀÂÈÌ ‘SUea03³123³"PP¨‚œ¬€ÿ“#P¨A¶6¼Xˆ8œÀ2Q#'°¡œ­ @ÚÙ ÀÂ`áäeáâef°23óüÏÐÖ jä2È1¤mm€Ž"¶vî óNà8ÿû 6¡°ððpÑÿu d t™Ù䌜>­ÁMŒ¬*¶&  “û\Pópr²ãebruue4²vd´u0OCp9}(.@SÀ/Êy#kà?Ô(ª@Ž+TlÍœ\€°À d´qq¶1:ÀÑ*R²; Í߯²Ðþ)€…‘å_wÿœþådó×a#[k;#w9À d(ˆË2:¹9ÑŒlLY9ڂϹ¬ŒŒÁ¥nRþÃÏÑÄdçäÈè²úÅ‘é—p™ÅlLEl­­6NŽ¿ò9MÀuwgú§¹–6¶®6žÿCf S³_4Lí˜Ôl@öÎ@)ÑlÀ"„ß2s €ƒ™™™›´ÝL>0ý  ênüKÉòK æàíigk0ÓzƒÌ€à?žŽF.@€“ƒ3ÐÛóOÅ Àdâ0šƒl~{‹fcpÿ@nfðø±˜ýüûI³ó˜Ù: üj)'€Iè—èoÄ `þ¸L"¿7€Iô7â0‰ý‹¸˜Lâ¿ €Iâ7b0IþFl&©ß]æ7G—ýÀÑå~#ptùß]á_Ä Ž®øã)ÿFàx*¿;€Iõ7GWûÀÑÕ#ptßOë_ÄÖýF`ŸFŽ& ÈÁÄÙú_9 +ç? '•)ð_9;ë/1xÀ@Ž–¿€éÿF`c#KG+#Ç8dÿ%vøC&gì`d´š9ý!æøGü÷òýë•åo±%Ðé?ö8ÿFp \mÿ8öáü›8Æ_ß™Mlþlx\~“·ÅäÏq´ïàvàÌ]ÿØRp4·? 8÷? ØÇo2à@‡¿SûÏýoâìn¯Ó_O4øqøþë{è4Aø6okÂdQôù¶FˆÀ•agâÝ ÅŽF* ƒç7‡ç{¸$šêŒ€5‡k¡¤á^´å-1ê+Áï$OžG­ p¡m Jí^qÊÓ;í‹S8“GBõýDð„ ª‚»^Oö^êþ–Э]Ò9öÎÜ(Šy˜·®}nõýeKc!ó;J»Õœ2ˆe_¢Õ¢tý‹g)r3çÞÁ:1½¦Å8sC½ºžÁÈž|!‘Ž£Cð>Žf+ôÔ^gýx7ç±R¡ÊêØGާý†ú clšÒSx?YwÁ³¤(jíÝB3 #ÄÚX?‰×hé ¯Ž¬.³•Ã7‰1rïöz&Í/Hð;*†v´uvG( oèÌ\]Å%òZŠúôw‡BáoÂèLå\bàäÝ=w7¾Á\i]ÉÔMêxŒè˜mÂ#lb/7 ¯ˆ>…Ú{cÉÁ^îrw{X¤—’×z~±˜<¶›qÛuSé(ûጂW”†Š†Y𩉋b8]¾š§Ý^·‡FgﻸÚ£ýùî3ûIî˜v*·Ìƒ‡«+a£»Uâd‘‡1F3‰…I܉6vJZ}WvÁüp™L-ïí[Aé%Ó*ÁévñcÈq¹Ð½›ƒ²ÜSô·ætu÷Gïš÷ßEÀÌjg†ù¶4§hFÈ_aEHrh1}ßâ×®(Ø/È íO€Ä!/Ԡ̉–Bìeߟ»¹FŽÚ†MF}óÁÎÒ¬9L^§2ƒGøqxä¢Z¼WÀ\9M¦´^/|<–_eF1 ¥û5¦¢ðÝUÙ¦Îá—„oz5o}RÇ´gz´ÝG܆ª£êBký5(Euâf Ò)1wÚæøÈxÛƒ½ï¼;ãÖº¾L]Í[0:M°YÏ:9ѽëM|#^é'Ž•ŒEk¡Ù!ºõ…•‘üøt’Ð7†lúª(ÙŠ.”ø55[=‡qz7ÊÊée"…"0úÕO;öù¨~M„¨í†6æÕ°'Õó 亶'¾ì”ÂÀEQíô®ЇíERU‘*·‘ Zm7å¡wŒÇ_|¼ù’cˆ±r=¿åŸêw½° *!¶ [C¡~S®ÅÒº{]ž£Ö5]QÂt¬pÿJÂò˜Ð3ˆúL?/£È1h\†'¯ËM²ŸÓeSìq–ÝÍ€ƒ8l'&ç[Ûcħ>úˬbÓM+*ãQã{údÉkF€BBÍ>wÿ¼¼¼ÍvŸÄÅ+8{çqâæÁÚy¹D¨ÎËó‡ƒYS&h(F8ÛÚ¾ŽdŒ¶NÔ‘. –Ýíh#Ë:o[T?ˆcêäSO!ü)G¹-"C:RVã¨7šÆEF‰þ×ïÅÔßYº\²å 5…,bIÕa~Zr¼; L×Ê=sý2ê…[ò&3ÄE¤:Rè±%S»}`(¸) §@Ú]þì1;ø%_Œ‘†;_(ÃÉ.eÂöðÛaâëªJd¿@,Ã]R¯gßJ#ÚªqÓ=5ÄX8F')Âöq ÏaÄ´ÌÐ9ö?üÚjýªßïÉcªÁ÷³ Ŭs¡öùñ(85é°yEŸq80CÿlµX|~Xä†[KÒH%´•íïøüÑsùİ+aÈÔ5‘Àk!YBúŃ©9c_’ªS›<(üDÚ¢ÜDwtÁúǘm´±°¯ù¹Ö<«eíVѿܨÓP`blì[Ž,³§©Ú<ŠØx³¿oÖ~qªñlwÀíQÓ~á+ËžŸ•¨ÚpŽcçùþ~ÝÐϾsœ) `ÃçÅm39F%Ø+ÃŒ‡d¡B [óœÑyHØÍLÂXì‘ÑUƒï!8Í–¾·PXÖÒR¦ÑŽÇìµ¹ù#=¤ÕW9åg¿¯oP䆌hógɯɺ\Þ.7½?æR;69ðÿÒWAÕ …jÓ":•6Áõi¯Hcu ü®äÛ‹ú2Riª¼3,ÏËwTv.ôHw TêéÞçµ5ääÞî²J‘M[…^ƒD¯ ígBá‚Ý@Áfm?!’•λuA£| [ò±@ìÞ{² ì$È—a8­îàÓó¸áªEæÂ V0˜ß¢yԞĥ«È0tìôz¿¿±›øµØ“3Ñ_r¢Åí79—à‘»·Ú)"Pïw•€ûÈK,ÉWuuŸ«ñB1¦™tC¾Ê‹k<Ìc·Õ ®oúIx‚x^Z™’S‡A•ö„4#zÚ] ÂJ®ñ‰±òuõɈf¹¢ÐÛ³8?U23Â(2àMʳf1e¼rgŸØà¢^ «¬ßaH' Ò¹ÀnÊ1l&a#4³ÀëäRÜ6æ°Æ3'´f9þþHhMfH–5rNrìfï þ5‹Ð&çÞy'{-²¦¡°W*»Cï2‘«ê¬´oÛÊ»rñîÜ›¨Q¬a´„zKedtœ™1¡«ú#Ë* ¢éNdàÛŸ#R°ã|í~Šz¸Ãç­8Ê]ÏIûImRÐô‰dCé™ þ×[ÆÂÙ“C Ü^ËK“Y«+§º|m’H6°&¬akÕn$š°X9,¤¼ëØè­É;h8No‰¿¬ãP´Nß$™E¨tu<>eÇ!‚½#khRîE‘ -îƒ_õ+qº\y8Ä ÂGöÃÜLV –0(º÷‘Q¤Ža1bðb¾·¼GíÌ¢r¨búêâÍŽù`Cš‚ÞÊ«é¾7 ¼¯"p~t%¡ë!Þ*cèM‰KU©Zº¥[˜gI&˜¾f¤æEèÙ<-Ï dŒš‚J¡(°³ï*»|ã[ñ^ÿÀÃþ»§~ü¸‰žCú&§mUsHcëUîÐj…“d‚ê²ÈÓä IoÊ~Ó_ûŽ˽¥Y8ÌVÖÙÏóYwS9@ü¶ß›ö*âŽ6ã¾ìÞÉÔ1']RÞä©ýSñC‡„ϸïboKpÈI4kZNˆ±¥gìèAÕ5Ä»3i¤!ÔP–öžý ÂT‡óÚ;ibÕ‹ûÊÉ0lHî­f›Ì©IÏÇZÔÏ·÷¯³œ^¾z+>':õaçK»Õ'q/ Í(.m0âêzŽ:¯¼è`‡4_R´²õ„Å<sõêÎ9Ø„ÈK“4>GN^¤=åˆWZVÔ!Ö¿ºèT°èJéÀ¯Zû¾K×§ÄŸ›r,•›SO‰¤)»MMþÙ³W&}®¥~>šÎÝpÜÍÒókúVZ–GS®aH“¬?ݸxL©…ð´³Õ̤yj&Û¡¡£‘ßÜÊ'ú4%ŽC«Zfò¿Êιޑ)‚`XŸÇœ¤ãzÀÃÕ2öÀ!ÓÅf[䘥:áŠó®ù¶oâSnäü4o*v«¤©ÄÁ»ê.©µV.¼ôI&ÏX}wS‡ñ7nÈf¹c$÷\ÒkÓóç·A 3Ý7 *™¥[TÙøžx­GÆ…3&–ŠÆ¯}ü!˜sk{¿Ü{lIÍT’!«¶D*—‘ÏД3åì CoúSŠ¥Âr÷Ô§ >ÊÁ»ªÝd³§,~ï£ÏX…j®µÒõ´è¢í/åâ h›3}Úº=–ÛøWÝýÕvE¦CR´òC²EõÚF׬3C&8¼ðÄUµÑçZ}>¡°E¨ç5üÀËm `W> ‹!Â)¨›j<<Û@AA.µ ‘Nù¨ÑCÙ…ÝꥡM}t8%) ?,¸:Áeªð%w$È^#2Ä(î¯z¹7‹×—Î…˜–LŽ –8Õý`aÓ‚G^J÷UêGô‡¹TÝ„ˆ»mYbãYìújs*nìáÇ¡²Ê*R\|±Ù*>¢ç`@èVHƈ/ÞO#í€ÕaÉLÅ5×YŸêWÁd2%‚DW» ‹t섹š,7õ|TW%S¯ô‹ìQù'©OúÞ÷pHt#¹Ñ b&ô H àMSè¬ÊUÉ<‰Aœ5.ê K8y!¯!cÍ`“÷²Ê>®™3¥¹µçÖÈOŸ ̨± \˜‹üÀRò/Çzk‡à­%'£p×Fß9s#”zª¬8F§h â¿ÑP…ÜI(ˆE^?2 sšP‹Wød5s­êI«Ë¬Š8NL1$ü}'G>©NàsbŠÞËɳìYC¢fC-ò÷Õ;ÑD#yØ÷FþŸÙæñîL;ËRz÷\ª9°ê¡Û28€Ø}¢fÊtÍ&ïAP/ŽåKšqÜDÏŸf§¬­#ý>-ñ‰UVËöéeHzUª$ôÖÔ†6Þ^ uÁuŸÝŽ5ïìÕt· –Yí¡\ÕÜ¥m†nJ.‚T8Qpî"vÏ€?zºHÙ ý^ k©š£¥ÂÀ~‡­õÆQÍU5J Ë.Ÿ„2ÃNŸ5Ï.jŸ}>Ó,2 sJdÉe¥šGñ\+¹=Ý2 \¨É‰+/ñP¤U-MQYPÞ1¥%Ù³’?Á]Œ1Æ´-Å‚ríp6è/†~Cȼö†­Ð ËúË;h¦ý5_¥óËzg³ËÍ<„s*)xy˜¼öMaL{‹²ó4„‘GŸfê貚r…0§‰¦ò6ÛK9)%§Exæ­^y}ár¨Ãø)qGÁz!íðÜÄ¿J]n.¡ßÛ×PïSí¦x«ï'³¹:‹‡Û³®u¯,:š@vÛ’HŽ%`ÖÌEœòýÓš7zk¼æè¶í~ØÛcÕ&j0ЋëÊ'Ûí–G¼3~v$„åŒ0vššÚzýGÞ (>˜' Ýd²ë8¾-íçŠ`¢íÕÔƒõŠ\dHgš±< DüJvñP·Þ9À‡Ïæ%ç!¾¡Å~­·¨B(9’*½Qœu@ãó‚E‘Ql¬Ó…® ð弟;[Ù5t­uP¬’#_€,KcÇËo®JŠóG4T¢&•®iľÌÝ¥£7kž§’+¾ô¡œæúGl›s”2”jº=Gœ±±ÏÑ}<ÿëC7ž±w°v~¡¬æ¬çÞ”!/ÉäìyTCWC\ q$ö9·y”$ù•}Ò˘œ¡»·ÄaQîˆIüò«ÕŸÉŸ^¸6dI¹…G:>ZA3.#AÙ^Oâ4Î9‡¹ È4©Œ3kó—}¾ÓϲƒÚ˜?MDæ× 35Ê ëû ®7ûÑÁ“¢t®ó[K´ºD»›3ó¼/n #f9(:Ñ­NTH5!©8¢ K©6GÍͺ\`|î7æ’À+·[ºX‘{U‚nŽ™ííÎ:”Ü ¦l·$ï•ÂêÓ\<Î÷ÒûÏ€aßÎÙc KÇ€¥Æ7©måêTÕFngØÄH½´ã8ðƒ¹í>‘r;""—yáU‰WÌ_É>8¦O¨ˆTíB½ëýê>‚ô 3û0«ÏOÊPÄB^Ã!›¸*‡Ž.Åðþg.QêK¨éÛ iž”iR1ÐÁok“ýæךLhÛ…Ñ!éJ*±—Å5lLCä¼'•µœÓŒ¡›*]>환ËYrhÛöÙ®`m–d×~5Ë öÁ];‘è}?û¸Øx4FŸ–S‘ºen9¬%ÆÙ,š½hh&Ïdh[Œº7±yÀÊ ‚.¹w…§fÒéÜòâ)ZƒÐ3ràÜšÓ¼BÒh¡‡ú,¡€s®t®eX²g(m \½Nü¡xÖþü“‰”o˜ñbr ‹¶¯KVî¦bðt/ݸ+±HN/ÅÈÓØV&]j×fZ-ȶ…ÀuΜè‚-¾g.Ú+N¹ÓžHý¬œê¾uRå~ÞVj^]·nI4¯j>­c°¦ÝhplBX•ÿœÈQP]B%ÏíŠ*FG'Æë…1BÝ2SvZ껑`Tä†Ã7©…ôѰÞé¦vb}qzZJf츲O߇¯m3Bô¥$m·7 85F]Xk>°hûP„ýðŽüXù®ù%»ñy¹âþUu6ô¦4¯‰E[Q€+‚||#/›ˆH,ŸÚˆ ¿/ÎP½Ï»®ÙP£ø™7CžX÷÷r£íM×”Þ²»6ΙE¾o Ù—²ÕuÆÖéó ‚KÈ®ËcY›¼‡çgzL¦«=·ËùùÓ]Žo–[ÖpÆ<æ/CùïÇ!Γ‡¦òW xÆ`Wß4ú“e85¥¤’»L•Íçªô4’ßôÎ}Ÿ¿²Å …~åK¢X ߬AK¤-2<¡u¨âïÄ\à7gƒÛ01ßÝ»£TË1‘tÃàÍðzvçCO:-n‹”HUŒ'ËPkˆPDêšvý6\žƒBµÄkD|›T Î-¬Ï1y< -Ž5ctÑ'ù›:b*WÙ^ddÈzŒ: ²¤ —zw¹ c3—ôÖ‚;BJú¹I¸ª@´é¦Ï:fÁ'}Ø” ¥ŠÝn- ”Ro>¥½Ä€ÞQ£Ž¦äì{ðèx>‘Þ¿Ÿ‡™ÖkÈÝ“²r©rW¼üöh5£C à„Þæ1íºÍ7ý¤'¹ú@I…ûÃ33e.S«BšÝÛ±õŒŠÜ.ÝéÓSè.MÍš5e¤¬Æ˜Ò,ò–,WMe½ùµ_³ÈÂϸñ¸&2ưîÏ/f€04±ô'ÚMÔ\¹‘ë^.Âê+ |ëôTh¤2ÒFª"˜.,¶V7¬>~Àír˜á9xG6±+úiŽmüÙú)rž_xÄú›±¦ ¡Æ¸§W„·œø¿k¼=ª×p\‚g‹÷îEóßõÑ›”zšö9ý8ô ÞÞmù2==ÆfßF1Ãì \u¸¿@oÜñUÇkÎiÍ2MPÿ\£@ þGIo|Äõ6-‰¶„!Ñf¢û)†^ZÓ¤´ãQ Zâ^^VžVÇ?Ú é'…Á¾4B!âTÚ1ië7*±¾\Q á¢î0Täñ›4æùôNCÈ›*•£f(‡^nó#œi¾{Õé³êtù™Cu)i\•c¿òCÉÓç2“V¬H³Ë‹ÃŠJ™¯•j¬µÌ\CD B|N½Öå„ðX ó™Æ e-* )`„ Ÿ*•×]SWÇ™ ?Gˆ(š"w^ØÁS5eÊw_¸ÄÖê„òÜ÷¬ ?4jø!ùÞâC|Ê Éï[–óšk»ÿ¦°R9¤¶ü*³Óß7ö S¢–qyÙë㛽޷ýªû­P5ŒBèZ Øûý«å|+ÈoºõKf58ohÏïvåÖ÷izæGôO ° åÚ›Ê'ÑKK¡ ™]ïϧ•(£ ¡0‡Þ 2¨©ëQDØF XJÌùfóZgœjgv.ÊÇ8ì¦ „oçz.£¶Z#ópËÁûGQ|ÉÂZ›$b…Õ»o¼-Lú´;ºž Ÿo˜Û>Ðl-#‡.¹uxð*ÅDù¯/ĞϜRÝr&³õz¼µc¤ê2ÂΆy %¯{×húó¿šÞ!êG$2víBõêja#9aOQêK5·áD„–Ð=êU5–æp¸8¶>Uà‡bÄíbJ üœ|ÓÎúøW£ˆ»îXÉ}ÃÕf¥kç%òªP!–³xV]F†Å5Kú †©”Œº™mÛç ËÅ‚eÝt路Âc‰®¨Ù@!§HËüÍÕRÛ{.åè$i³壬‹×µ»K?XBÊÈÙš"8bÏGZ&”îI§1­Û}Ÿ D`y¦zf¥FÅȇ]<(6©)6 ÐÅÝÚAÞ)ÛÔ©BÕÎè+”…ÕòDéÁ·,Äݨï/r°°Pë×ë8Þø|òµá±Ú/ èîIéÚEXo›Z–< !6Èlu0‰¼2“zÎÇ5ò:fòÞÙ’ùû”VŸ\"×­ñÅi¢:…[}!Šwö&®*Sk$ÊlnÍ„½àp*¦Á‹b…}ªšsÀ!b·Éî·Ùt„ /2Èl¼JŸûJÞä¬lù°HËœ z±|Ö¸.ü5 J9¿³Täq”g0/á4[T½³Dä;d!:ž°ò=††«¼!í}/·1ý@~‰Žsï&´~|>Md—žˆ¶x1ÿJ›váûÒWí~ü4n@öî„O?É—®¬úÝ2a]I¿ c¾+ÓÛ‘£IÁü–œ¸®+%öy8hØlﮌ¸mW'+H¹WUɤl=ŸåÓzQqßÙkF áY bjÌvQýÁ_íî“oÑ"§]}Ê¡ÙëÙõJ8?>Ú.âg` Õ’ÂR*¤eµzN‹·o¿~ì±ävE§*ìn(OJ¾| 5ýùp¥7ZJn­Ð|ŠðÞ¤ ö#:Ö@­wÈíõIÙ˜Dû>U2wΌЌRÙPÆëúÅNÝwf!ÑÔ¢AÛ£Ó¥•RüfotsÑ`›Ÿ€\ú¾3ò¦†¦zø‡ž;Y´xU+îl4 Lâk–«dy]ÜбIÚâýîNh¡W¯­/ˆHÇ%é¸È^²œ¡O=¸Õ¦~hž†I ãþGh—”8}¿Œ•;u¡‡-·çÖÃh•ˆïw=€ûYy&6Jœßæ¦ûëšB)¦ìÅû¸<ºq{Ä‹;³&˜hKbñ•k Ká9;U’<ƒNîj Ó_DI1*{/–Ø)c]CzÃð»oZ¿*rDJ³I¤LßY´eÛ:r<=ªü“ãk  ®¹ç{šD3Ó’Òš3EL7,Å;κª4½\¼ƒ*|6I¾þ.µ¹5V°ÞcÒËM0)µ‰"«ðÃãý¸ä¡ ùQ â¸R¹Ð–àIìÍõ<ÃÖØÈ±ñçå×wßs)ÜFÅïaBÁâß@•5=<_sH(5}9; _qÍOqé|Å7È)¥+\U*í»@½¯DÉž¹nƒ–êSê0íR_A¬:Þ¾µØÈËeõÙ‰„äQéUæò9ý;äecÇŠXáeçü$<÷è¤n³òüvnÃŽúÂÙ%nF@d[Ñ}ÓK!‡BD¢ì™]ß®`i¾ÿ³µD"-U¤:†,’ðØ“ÓsÆó ÊüYFýV„@nß”Î1I3Û¯©œ´V»<1ÓyßXã¾làY®W/Ä5t&Œ@ã7Ȧ"„Œ¤§óÛ…°­$rðÍÉT¬lÛ"N4ɰ èÈíJš¸+\…B Óƒd…{,ùlj-LŒ1/çJÔVBîð?$¨Š¤pQ÷îM(ìÀ°„wF}¥¾å8ªÚZHI¸Òçöë™ÍQ’ìʉhKä@22SÔâa9w5amK«ÌB¹†"ŒDEg!•Ìã/#Û¦Îm¸ÒøùÒæS%Þ8«GÐ(åTËõañºxPUÍù³Š|׆¾Ý¸jxÑŒéxõË·š½£“O ûæE"ÙU…’Se:ÆTñ¥\¢w5›À™”÷Ó¥0fyë‹èœëá˜`NT}rˆWÜN½ÃÎ^òmÙ^œÉ’¥èв¿ž¬ãŒ`¦Œå׋‘ æžÜ©‹EÖèÄ –;"ˆ·ìÉeóŸ^å³S§‡E¤Çòìˆ>ª¯ï¬ŸŒ*¦BÕŠcÆÞQ²+iÆïð`ô®?„ ¿kRÞZ=/Dñùùí=‚{ºƒvbÅeÓ‡!i>¸rg.ä£V¶¥ÝË’ƒŽЭSÉ­ªU%@^6Ž£ykA¾í¶Æ}PK‘_$ZiŸf‹,¨u¡^û¤§Ö¢ÅlÔ¢‰!+±ñðîœnMÕÝxX¢íYÙµþöÕäk§½ 9Q,ôã/¿N(ê*ÎÃ1\]耆´Î˜.˜P9I ß ’‘}'Ѹ1EQdbšZá±È×ÕøÇ¸¯LÞ¤— ¿„|"ÌÒ»çÂÖÏhìî+“-dálJ£ä@›Ž¹s‘tŒ¿¬C±‘6u|L†qáxÖwø ;®*~ù˜´øó1í:—æJ9›:¢‚þqÅͶ=…õklñ·»Î.¸Xú!£“4!4."«y™[Ü·“ê=hQ—¶«ìŸ+»$äð3¹g0›RüQO_òëBËŒq‡G+ñ™ý£ýôY‰»7&àÙFÞ–ùÕt›Ú´Bx–ÙAêäô yQÇF°†-É[—©dÇò†ÐÃ×¼‰ÖÐM`„ýþž*Nù…P¯(?áû½õ,Ù›7Ê8ì§™mÝ!X)”lgßhø™Êο^'“ ¬!;Ì;Ó$²¥„êwVQ«x¶ÜSw©Ly.²­¸O-ùî OT¶¼ïñ]¤y§% N}U¼-jîd—5tÇèXˆ­>Ô×hG¶ÿŒ3}Í…¾¾ZÊksÝ]ÓÇÞ¹`¼÷œ» ý;3ÿÞ%fLV t´®ÃûÑÇKöÔ6ɶÛv¹$zµÙ‚½xƒ}ˆp§3ý€0•ÒcOP µß®°ÈË@‘¶ê ‡¾Æ`¤m¡°—M¼;}Žw.ÝÞÜN†JÞNo¸`„güaô£õée ½‚Sš“NãÇ¥Š)™åø¬P?Øm_EGÔPq"ÿ/ƒX QŸzp7zBûaN*;õp®èÃÈ{#¥EÚöaÜ’f+‡[ªP.¯%X«”e<;ë‘Ö¹63’KAÄA#¨ i$Hó Uù:ÐÙA‘D¬áΙV¼yØÜy••.ŒRêf÷¦/˜ÍU­§š¦FÓ¼t)u¼Ã¥ôõ<–#wþ=ÊÒPІ¯ón/”CÆ ù"åì.ãÎÏ#§aTþ‘.Lš\H·á¶§å¸û#†@˜W[l †›j²i)£TÖQ\VöðÁµ£ælˤ³ø œ\g»+«‰f5‹CÃÞìý†;Q&”ú;ZìÕ.SÖÛì ñP–ÉâŠFÄþ0ÅùÓ,Gx¸˜.îü(Âæ Ï>-"h±UHWÚ×ihû]Hïãe»L¢Û&#áqн àPG’·åk'e+ÓàGUm"Þömkz»¥K''ÉÂÜ…C§kÓÚÅæˆÈ"öʰkÄ:bÜp}l7ù áç‡)-HæÔ‡8ŸµE|= ?Àø-ÁåÒÆÑäÁ‹4›_ÇS~IUA#H4"Ä;×j‡_vðaFÄóŽÔoHÁT—Í3üIóƤ#èÅd~>¯ ž×à/;°¡fª•3ÉêrÖÜ=Ÿ27T+L6z8ã~U¸óÞWÖØSñqÓëÇš¼}¿ÉxHǸ,rïíF:£2Þ§/¡q#ñðïÊ.VZ«1Æù\Ýaزµ¹J†¯yiQW´´Š<—b ùÙE*S—DoIOêÒ q|õÕ¹­z7 Ïtb«µÀ–äæïÙzçÔšÎÌM “á0îXʃöCeÓH¤ÖÄÞ1-Øm²ís=ñõGRMfË…•º9[ŠêPUÃ!|ÒQ>R‰¢Ä ”¹ÖüU0·ÉP…ÌÑBñ.ZÞfÓ’çÏ™úCÞæaƈ_·;§éù ~ c2K¼-Ôèë>ß‘!F3tXME¿×µ×q)]'¢g¼]QAçg;#T\.ä·,ª‡œ¹\W»yìÔü‡©yåLø‰ÓÖ Õ…ë*2X÷ù÷ÒüDçC¦Ó_=gPÈz8‹ÆfrÓ¾+¢hQ–¹É«™Wim·t©ëUü¶‘Ó÷¤gyQ)šxó$‡í,b.a:´q(#k؉tp”äÑòð<ïSuÿ£Æ¸í¢òž§À§6Ò‰jÈïœ%y2Ôe˜rlŒbøª<…RG‰Å(n2íæ¢èzÕ]Éec£²Ä.ÉÓ@t^oƒ4?5#:òæSZ¨mÐñEP7E’¬Q¥Ú²<î3ª†ho¢Ÿò»@|"ôæ,^k£ŠZ8L=Öù‰åù>_ &9™‘ÖÃt‚O”™ºÔæk«²‡º'ͶcbÌ¥JÊfiö)[jD)ÂŒEVK ž_åó ®¼O9Ônè³Y¶0ö,ïsôdb ާ’ÿYÒç×â—E%”}¤Þ“ÒÑY>ä˜ml÷Æ'4y^‹å_O˜>R{ç(@ å(ÄÌQÉå;GõµîVníKCv0TÅL©f¼<”¢¡© ZfÃØ,Á…‹Vzv‚B•‘we·'TïæÈ»íYëÓê&‚½°">…¨S¯K¸q8Z•*z{«°‘z!,ÏÕ€<ÂKs§\ †î N~œ@lvðe©Ñ‰½ÈѲf:|`_Ö "\eг;\/rfð+âîûêóù›¢|»Èx;â÷½©}bªÀBLÊç¢ð# KËŸl‘øM·g®UäÝÔ &#þ×+}¿¦Êb´,0cÐë}O¶¸ª¦#õ ZV.åÜmËäŸ-yeW>')±³mÁ×EIö]Úso¨Ìù@ØÅ¥+ O‚‚hlÎ<.o)?|™8¥è±5ÅFp?¸Ý±beb]¼0In4Ä•5ë@yúF5¤QÃÍþþœohðHáeÒWW¡´öD¶!]/ÏL`L¤æô6{;æ,é™Tʯ[·hœÂy ši¬d^Ò+QÄLöu–i›Þ[XÈià‡Gsb²ÐÈô7­XTw(™)a‚i7•¶løðÌÒ4ôî¶Ô§²ˆªù1ŸNi:iÕ¶·Çç6ïj© Ea„*6Ö%Jt¶7°¿”Õ]>¹Åš³ßRªá>êz5{žíXHxè´ÏEéa#ÕkÊÖ­ ôX˃8îì“uW?$ö$_2¼Z*€Y/HUn}m•í’7qó '³X©¯ØCÕÐçê¾¥×?#ab·;¥ùØgÐg§»üuæÓ¬H£¾ tÍ»•hO[z[ΛôÑ—›k`[gÌá"­µ{aû—η1³{»ÑóMð_,$}’¬¼üC€RÕjÉyª»è#ׄ°¥ swƒX&¥û2¦#‰ã:yNO)ÙIÃÑtQ륫tG<( ÊbëêÅwëô³<‘%í½Œ~À¾ðñø€Ø¡tZœÙ;S=ºóž²¶Í;(?É¥«Q/ö7üáA©TÉçÇg"Çô5 ~)6>jòs­îPcˆ[UñÞ”ùA†Ð7·ó¶§-AlH{múlÌMÒC:Ó¨ºKE-§¬‹:YQÜ uýN’J5¶8GùÌ#s´Ø¬mâQžÔã^…ÆÁÔ®Šús–DñH˺B\<ž>º*¡½ûñÑ9EÈéǶŒR–NØzržF»ÖØh Ë®WÇ3ïšÏóCÅ@O,N‰Œ6ŠQCé\ìÖ±1:ÏÞ¸êNYÙ ý0ŠÀ›ñô6õ×’$Éжcä 7õõ‘V®Õƒù“×jáÎV^þwÈŸç7„Q}‹…—lÔótñµù#ñkŽö¤ŠÜö–“–æv _ðˆ"ä¿ÖØØûéPxñ ·7ì1,J0l4w*q½<-³Es½X`Lº·ì_]Û¦`FåÇôsJÆJ-Æ-©ƒ<ð»½ç‰ð¥\sN´9ˆ_¤²”í—/ØûŠøÝúd[CÅ‚Rnú¹cmH«e¸w4oI¢ÈDäÇ®ÍØWÕ¥ÎP‰UUàsòA7ú <{$ÁJÕ˜3­Â±òs>†Å‹×cß³µ(1?2‚ü6)˜Nh*ö¤Íéúúgà²BäJJ•/n1wgv8&½[Ã-æÚ6$ý!«J¢6œöã Á]y²7—‘– Ä Ÿ6é‰u»óÏÚ«(½,ôvwøÐ€ V¯úÈå‘ó:ð“ìaIÞq»¾}T`˜ï"r©£oë²äÿÔnÀf*{õX%<’šåS‡„à`\„Å2,K4ùàiD|úU’Ù¾3ûFêý;9¦õfùhva2½ªy(Ç%ßÎ. ÑY”Í.äjáÛØÊ¹²+q äQZí²Ú¾44tꂘuÑwX-‹˜4´tòÑÛCoMÊWG ö½y_˜d½°{ñ M"¤…-F´!XÞSxÛS¹b“%J%@*¼®ûv]E<ÐvÒB»V!¹¬¿’Ád”ûÕ¥`{Ãש愻J‘ƒmö×-FÚSÐ¥¯ÂL_mú2dù'‡^ítCM¤Á7°j¶&ù§g–í]õÛÏáþ$L(Ýy9¼ÃB ‰³ç˜aˆö9Ÿ °à¦êË!~·@øZlûr²}=3!½:™5I§À ²ªUK'¹t[,ÔC:xç²ù„«.‹sS~W¤ûñãp²ÑsÊ׈°O‡CüRÊA>0NRßÙ–s–ŠVH×䣖Ò~~æ(=¥\(Gí~„mâáÎAaS²y_Ö¿>¤$‡r…ŒëŸÿZ¸5™£P~Þ—¿æùQc•(¾þççbγ6|Õs:Ÿ‡ñ½@…´8§9Ï£T¦@Z#V’cæ½Ë=UmËóO×:Qgø‘úfËÁÝ-bDœM±O/¯TÌfßXm/é¶Ã,x÷%éS/Ø=²åu“?6ÂGléí¥eO®9 ðM@ŒúXûe¶-µŽ8Ù§óhs$æX4ß<$L¥Ò#wɰqÏÕ8T…{5ÒÉGæf÷ªÇ]y±¹7™2Ù´$pV˜È\šuK˜ñ’°£núF=ž¼{øø­'DÀw$™¶ø[¶#®ß‰QïÉÐ'ä«­5 15]¸PÚ&†<Ÿà`ífÈý} Ô{9rXNÖ,hG¾å¼fÏ‚;d|šqFJ¨Ã…ÎËKÿÕc¾†¤Üú©'•q#ÝZnrD9,#ëÅt‰ Ôt:¹Ñ—z$ÒÀØ6¤<ãïÜÚ_¤»ÎNßü¬ïê§~¯ÐéK+"&y*œgôñèµQ?èŠR«u•)zæoxb ¶ ñµì^k 8‡2útïÁ§ˆ"[Õ!J3æb„”þxÖeÇ£cˆ hc„ß82íÙVy,ư)ì’–hÝj¥‘Vn«‚Ž ÂÚuî`­È¼ tÊøªK_˜rBçš_¸×)§c¯†ÞœÇmxº´ÜýV!™°„•ôƒôÆtuP›¥¯k?œÝå\îbùb,jÇÉ·Æ›×;¥m¹*ôHü¨ÕndKaèÙkK2Ec™ë!(­Hc}Îü ¼²'ó‘WŸHÖ˜U¦dVÌaó Ò\q \+ö jsí”Mnê 4ˆï-Iΰ"6(óìö˜—:>´³<˜yÝ(,éÁe(®2|>­-Zß®>T±Ú¸ñ–CîÜß †-VH¼}Š/óibLDŽ'=LL¥Âyl€áuÆ„CûJÕdé¤Ù4³æ J4¬?Ò !`‘/!±7'o˜RzÌVæÄK1"æ#·gÂp @¨~QU¢®¦Ø*»Œí'y‹ZVf©g­ýD¹¯£LäOï]ÈkÎÈ;; ¾™˜ÅVfÆu˜+È>v±Z,øî­Ð²}st{$¿”“>·ésJ‚2&Û#jÁK™Nö¬Ý]3#à5[—ïŽÒ8J|w©ì‚#&d·î«“Hõ)Q =9O…ñ1v|ü–Êš5mTgˆà,GÜ—â´è:^½;ý'Ë—$šç ’ª'W…_¯Fš ÙÔÞr¬°o•ÒÂpTÌ­¼ã,Ý݃žÔÝiïW·µ¶=Þ7fΫÞD‹œ¤”(ÃcR9ÁÀ¶eÊ~ÒÀ£™$ó­Ú@\7öò•cGÕ;Ç]„›ýäÚeŒg ïäDX¨ŸlÂm×N_ôá»Bù¢X‡†v™ø‚¬ZÐwryƒjUØÑVÊZQMjî²c. s*½µÒüᩌªò'K‚B–žb‡³gJëŽÝ‹ÐÕZßã4\,«^÷3‹z˜×?žÉKaúø„xv/™z¤s†'ªPkÅ$ˆcýG5šâ¨<ƒAà‚ð9‰@Nt¥Û´¯ËÞÊäIÑÎ æTjÍUp¹ùpÆ<‰¥»•='Çèƒi@òŸt'_w~óõæú™Ð+£… ¾™ºkl©Ãó5*•zû-•ö lëEÄH¶_ì£ *‹ÚkÊšœ÷Ý’ÅiæKÉØ¦°…îFoZL KäéèýÌ·l.ö¯$^ªè[òF&¾xCn2™¸°9ÍË|D¯ 쎡\¼Zï¨y'7á Ybep ‰n®Ò»çÝøõÞºÉÓz"Ki?t!y\ÂéºÊ9ð&›“»ÀnÛËè'ªLHw6þjAÉ‚†é'¯øã€lŠª&Öóû¯d–k’?¤Ñ~jJ4Rü8Êó³4¨†ïñ(ÐßÅËT33(!%œ&‡÷¹í„Â7×ÌòDF›ŽI™;Þ¹fy£q à¾SI9 ÒªhU-¶ù ¬ïØhßn%*ï¯n¥ÖñP”~Æ#S»)\U’}òž»Ų-ÇϰxÚ¾ˆäÁ¹ß/Ü.œë:™Îîé¸A"8]—..Ôõ-ÜX»Ço\G”ÅS<Á¤&ã"œþÖxîÉŒ(Šè¸­½S´‘ùzÎ8#PA`5åôÆÛ¬ïùƒ¬­RÐÖ¶É¢Ö"›co² áÃÁÉöÃV64Ó»ÍÔ&ûƒ:è7ñèÄ£×E ™ç ûH÷½]-öAF¨ÇW-…Æ€ œ}ìQ÷j²«’ô饟™›Ujð"Þ·;_)Kßêº<2£½ó õp_/,UM©]IU5òš´‹åÏyV‹iâôÛR¼TÂ<u+mSÆÅz©0µ’5ö |ë»ÔS½˜yÕ‚nÉZŠØ5L:¯ôYGÅÿ!³eÜ~‘„£cÕªŽèÒ¼bá;Yæ>«û=ks¥Ë¼$»˜²,Ðü§\POã–á(¬ÞX–ì^¶žÅ3O2Oi-ÌÜãašŠcYcu¡šÊhƳc1¹¼›¤ ÷k­ëâ¨KçøTä”áG³0ߟý&onWî›D¼˜A—c˨\.«ou¡°ÄŠ7ƒjµfbËO¡÷X xjM¯ZÃÑ é7ÃîßIG?£x©{@Øî³±W‰Ý¹‚F²Vù„C?iŽ)T•»½: ¾ —ÔVÖå7vdNŒ´Û7[¾K*fë[;déïU}OèM"í¶õú»ÊTp`ë(\èëÏ©¢¡àøÔ#^ȧ§—?]"Emw„Lss-}Ù‡—P.¨-=Ó'?~7Ó•â- c¼òo5¼CJõ°˜9kÑŒ'ÙÈ(b”_Çì™SoÜX,³éx›: ôjmNë1 6ÑBßøˆQkFù¯-&‡À5T#C' q¬,²h¼޳íºPLµÃE¤8½œ>´$}pk=™˜µÊ$£™Û#aÎKÂ7 ëÏ2<}=?N|ê¡ b~¯ë´›hÜþl }z‚†Ž{âVéeŽ­:;‘·9¾r(DÜr>YR.znÇs‚ârÀ­ÜþXر9!¹ éÑf¤k¹þGM©›'½£0"w]€ùbO-èî‚VÈ?lV;Äêчr·W©Â‹ÉX ɼIé3¸DÚ± ØV Eö<0C 05fã.lX¯9ùS¬îØþ¨y|gT[£­ªØ×RÁ°økÞ`×$[iÁ#fïCšwž]héwd¯ßþ†j‡Œ¸aÜ–dÏ`Wªe{ò‡Ã^&3©¼õ£5ïž o¾Ü š`>v`޾dæÈM}>e»‘5§r´—µ—öŽo‹i³Ã^ÛJ L‹9¦Ú8ü0 ©liÒ3oy€ÌµfuW»˜)Ž ŽŒ Óä'.„Õ“†Ç–›æ®±SËt9\Щr7Ð-E¸æ¶G0ôƒE_æÁ(’1dÃ1ê ɤùH6âïRµLß´s3Öù<„Ù¿D í@À5e¥Q•)uð!\9eû¹»NA#ó0è~áºkkèÕ÷ÑŸÂ\fb ô$ñu 36@!Z>”©=q!Yú8)=˜Èp¿#“- ׊šÿA»\Rá QqS¢Ò™pÌÁ\ps„'Ç$¾€^œÕ³ã;°Ö/_Èñ™£¬}>šù?oÆY!q§í™)1?³Õ©ÙÍ7N3„(E‘º¢”l¤Øå5sà- Zs6¿wa’‰]¨÷陂O‰kËi/Èùüð¿¶Œ'¡7¹DJ Õím‘ÓD ÑŒLõ 霜p€IÑŽË[ïnÿÛ³C@Õˆ#¶wW˜Hbϸ·ÏKÅiä¨zub6¶\’àüiü±Éó •n‡g/Â. ê*}¡Ø‡kŸÞSxgŸ ƒ?s†s¹V€‚"LéÁ%`7÷K?Ïâ|EGöòQ²Æ¿—¬ûC/»Ûå—-dbꙆäkË™°t[8ØeÖ·„8Îf–ãù€G¼ˆÜ> –¶Ó§ŸqGß‚¨ã ¡¡µ†ad9V$í ¿ëTSëª÷f5AñU>šVp’ºl@X/$‚6½4l0¿8#ð73Ïyu„Ž“zôYOÞ¾*Ô¡Ù`ué‚•IUpkË '«ÙkþÔÕ/²–ͬðø¡'wTU³ÕFpkêÚcë:O>/U¤ãÇxÎmYÜéœnOðà8sèw­.*æ ±ëëz½˜”q~Âä‚`…þ=vaßå¨a^†ùo^îæ#Á | b®xà‡¹E%I>”3Ãøé'=þÛ…3”8Eƒ¯à0—Ítñ*˜ô,…Á`|¸¢¯ˆ—ÈTE3¢Bc†·¹EÙÿ(ÅÁà4K 8ü¢å ù$Ãz²JˆwÙ©üÀ^‹&=üË—+P…÷cÝ+ksÎÞè‹ñ„ŒvÏZ ®›±cºR“yåò³áHh„ÏkPeR)Ã÷Ò(!N\ÊÖé#V“Lò}°T*lç r€C^ÿ’dçB6€¢¡9HгÙß² A6óÙ¾û]l+¹ÝܹˆP¡ùãëð·+ü± Ï”ºÉE/EZÎÐ=ó+ LÕ¢+‡âîf÷H]'„!pxiÅý@Ù@$;ÕªîDµDUð ÓÐÜ_°Þ¹;ÛÅԬͶ®ܬ„ž?0¿4ÓM°NlØ:” Q|¨Wç²þa£öê¨Ýˆželzº½Ó6™Ç4/¯´èLJ£’Ó…Ó]oáð¢†{·Â.Lúé–Pöj:ùúgçïå~Ü—NcÍ]ë׎·€A–¿‘Í[šƒR¯›Í]‡ök5 ·V†k>Iâ–½”;½-:Ž ôAòDŒQx5”X{TÈ4à\»„ÚGºž•+J%s¡æ¶ ¬CÒ’Fqd¼®Xåiwÿ/»ŽnW®ÙÎi¹; Ttú¨)ÕPÃÎf,ZO¯i‡@:Ûûæ‚3Ù\êwÁRFZ†I¸íF,‹í©ùÀ‰t0 \À…â¡©@ê€Î[½æXe1z'*äR zãƒ$pztŒÉw…ƒynüvº9l¶¾¬/¨Rµ÷ßÉb±–QÖx³—c=.ðêŒÀÚQZíɱ` °vñs˼"Nzs¥„ø‹ŽÊjޝiJÊÓ@ÊX‚½&²È©:" ¦Þh­RÑc›˜ ÈÀE=wiYâ˜ßêTävâõÆùžZÏh³ßÅ‚8K† éBÿ˜[¿À&ïgÚBc× ãÇžÛXŽ»ÑU „;ø«ošþœÙ\côùâž¼–a“w­kœ¨ž8Oêý>¢Õ› å]_±M™¼$1“í{¡LœU]õµ(Ö"ÐÄáŽ3 vî¾&> 4ÿâåµ9«×94HêÕUsg˜°âÞ¾·ç1¬NÀ‚ŒÒ_Õ³|(î­K–{ö~[ ‡üxa3àª,|#iien ¾ ‰¦*UÔѪ©‡1S€!µãDßÞ~»[ 5å\8¤MÔØ8^• »°SŽØ%<λ?\· ‹mø°Ä"­E´8]Źk)BDâï’£“2ž¹ %³ê@ªs¢•ë5’çë]‹spÒ'e ‘«oœñK­AHGžðÖ:̸€zçÔ?§dv ðé§ÆJ”Xq ‘fÜ*â‰ù•:ïЖˆo{} ”Ø%<‘··:9~z}t©Ïìþ¤Üû4ÐCgP“éäɶÆ‹…ÏãÑ)­0R ‡î„¯±“1QÇ5µ-ÊKÔòµÐï_uÿäF6ã¼¼œ)™¦&¼h¶J5_Èä:Ãþƒ¬Ë¯û¼äEgE’­>¿_9¤. „«}[ì®¶™R_¿5U¸€Ê–Ùp(ª[vÿ¤“ÇãÞ<Ø+t¸šŒ&Ù¡B¢_ØÃ+ŠB®gh‘4£.tfxú˜¯}wÒ ´§í¬H Ìÿ¡Ý1ÿ¶Î²ì>Eó‡e¹-¾€î‰Ú×K6®qX÷&‹Ëu"D•·ýqæÙÐÁ('¶òUÙòœ¼ÖY|Àä„sÂÑ47í-ª*þm¡ùÛÄaŠLεS“0«EYØ—ð¯™ß-öù2ŠI•®ÿÒ×ï›w}ƒú²M+ŠMV ~&0 ¥Æd5$Ù,˜ÂsrÌl2°§•±çͽxŽGâ1Ñ·|G¸IÀ§{í ¸…%I#“QokœIó˜Ú—êºÆ©Êõä#_ÃŒŒ¢7sa=Ï+à­p$w( ë\®Å/Ýð±zÜóä0é™°Í wË@^fµs4+t_‰"jAå‰Ün4ç {{?­.ãídéÓàç´õU½¿Hnq‡™îÓ×LLõ齨¹8¦c“q}ûX”ƒç–ÌÁ(Ž æfúVðvwZŠA7òmÌ’Ås)§Q…uøŒ\& ¡à‚4Þ¶ÓL ŒÙKLs- - G¥˜êôÒ´,˜‡œ©ÉÏœtU],Ç&kÁÏž3©zÁ%ÙZÞ&•³³xä™±­\v]© ÑÑ`³?T§Ç´£pŽ<”cŠS£Ò º }Ø’°ðÞ„ú­Wym¹ Ï9ÆÃà„e†Óߘ*!ÙZåW/!žƒç/ÿŽö&Ó/` ïLÅ¡~‚›@§,䆡ºÚZ÷ À7¿êÔ¥,!­ÿ~ßs #±l½›Ë¹ªíÒ³G÷÷mÛ.B‹3•y6( 7ÒmS3`ÐR´3Öbt[cø~yüéö`z%þõ§¶!\$“´!]ô®7¶‘ËCÙx/¼Pk‹¸¦ÕÚPiò\1êçþï®L&¿u­ê‡ÚÈ®ÈÓGdHþFØí/ʸFæ½ÚCú?Eš.í̈šÀv‘ ®+äZ×@B¾7Ÿw<%LþhlãI8b"âß<ÓdFÿo€![—‚K1T(;Ðy*®v|RIò÷ZT)ÂÚÐC"7}mR÷.ËœV6 >ݱWû# GÒ$êÜQëBk[Ì2#'×~iŒ?w:؉öäýڌο‰C–z”K'¢#m×­~’ËÊ®_8¿dÑü,J“¬[m¶îžPÚ}”ú}^ÆêVSÜ «f£$Íý$WœüŸFU©ÇQTð3£¨Á›6‘’³ÓM¾Tï¨?ÔjŠ%Š›„ÿÃúAÖãDhA϶VµE‚·Ša|6þúÎÕ¯µ„DȱL2'&Ëœú­nñá þo¥3VÀú 1¿Q^¤¦Êl럯/@;¤"éF“ØÆ#AsÕ_ÃÈJS3€v•ó_3dP|‰PLœÁ¤Ý!Uè¯ù8¶À:¿>/»ÊÙA£ŽÉ²E‚Ô"¯š®-æâ­}ªZÊø÷¤£vÕà»掿Á%U•«Ã!CB †‚¦Ú?©i>•Bn—Œ¸èŽßNšŸFVäßq‰8’2ñ”Uæÿ´jøPGµ×°èž&÷ê0&©LfÆÔl"ß6,ÈD¾À ßî ÒÝsòÀæªDZm͆J©\­¾„ãÆ»ï77î_0̲$Ö‚„á^îÈY˜¶uÜw¦H£ÔÞ4ü·š\Jï’å>ô”¸×r©å‚;Û™FO¿Ák «Ü©„¼mûPÛ¼…I8aP£û œmx¹º}kó´wÐÃHOŠÓë°îò㟕ìP…÷ M\|«×`?š$óää{êdç‡ 0y'û#7ƒ2{°ÊjZŒiqiòõãÛ=g¢8­uY“}z®g÷Êòm “+]"Rªf(ðÞ¦`Oßbþ[$îÜ® ²pˆµ3*öœÐ†>‘#AO+Ý"s®@3þåC‘G´l¢£J)qƒ‰_¶?ϘSÁ‚ûéÆEÀ$i߉,§0è<¤Ú®øËßñ. ©RçÍ è«Œa<„Ø÷פìËœ´¦Ã÷³Ëê‹ì OçXôÍÛÌ3Þ¯HGéÕÈ´QØízÁ,[YrÚÎn+× ¿Qëý`©#d¿Û¶© ž endstream endobj 1548 0 obj << /Length1 1737 /Length2 4645 /Length3 0 /Length 5702 /Filter /FlateDecode >> stream xÚt 4Ômؾ%¥‰JJ¶ò³“˜Å¾fƒlQ–4ÆŒÆ ³0Ö%¡ì¥E²ËRÖŠ²K–ìDö,!»o´¼yßÿÿœï;sÎÌ\÷ö\×sß÷#Ìob.©áHp@éðdI¨D Ð2²°€ÂDZ „…-°dê$l‰"’°¼Ò®-" A¦Ù´dZ PpT€Ê)Aå• (þ $•m„Ö0’ x $¬Epó"b0dÚ9þbHqª¨(úg: áŠ"b‘<`„ cP®´‘`N@bQd¯•SÁÉnJ`°§§§•$E :©‰Ÿ<±d `†"¡ˆ(G`G2`ŒpEý–&,0XÒ/‡9MöDQÍ€Ã"Qx-…‚wDÚ逹¾!pÎ …ÿlø+à4ðûr¨ôŸr¿³w añ?“H$ÁÕ ÷Ââ4‡ÎéJ‘©äÓ︈À‘´|„‹C8Ð~RGº¦‚¦ð·>’ˆu#“¤HXÜŽFðNÚ5ë൮®(<™Úá§%¢´{÷ÿn® žà‰÷ùƒÐX¼#zG†#Å |u§ ôµÇÐL ¿6'…@ ÒÒÊ@Q‘ðÎ^n¨ŸN莙¦ÁÏÇà i2P~X4Šöò!!MI ÝÝ¿çBì÷Òˆÿ>Á˜@›f öwøm!²$í ú^Ÿ)ÿ¿Éß©ò¿ÿéRp¸Ÿ~±_ÿáŠÅyýŽ M3…LÛ #m?ðÿ µBýZg#”#–âú_¯>AÛ ¼Ēt±T”£ –ŒÄüœ˜?m UÇañ( »óÞ’Pä?>ÚÊ!]ho ‰Ö¬Ÿ.m£þ}¢IpÜY=˜¬€ ^ m¾`²²€”¶£Ž(êÏÑÀRx™–ÐÔùh´ÓREyŒØ1ýB Øá/RÀÈÐ;°ã.À¨]€Ñ» 4vÚe0f”ÀØ]FÂe¤±Àí‚4®!”F¿ ÒhvA · Œ ѦŒ°‹7í%»ï‚4fÄ]ÆŒ´ Ò wAQÊ.H#êñÂh̨» ™×Oø¯Ž!)D"í1û¹T´vþÁ?_NŠŠB‚>wÊÁÎyÁ¯Wr4x<%G›`2¬5 á‹ÌV#:\c¦WµTV¿K?ŒèÒ¨üô5buðÈìév‘ËˆË¥ßø%ŽÏ¯™Ð_Nû¶Q]›"R`_áÖ—¥ñŽN³Åfï=ÕÛÄŸvŸmO˜ÐS)Îúñ3»Oôå´ CÓ͈}œ Ò, Áe eYš¹mŸÊõ…H êM3wOèN¬Æ¿«‡É×tgœ¦ÇÅóLN²ºÁ&ÕâÆ«å¸á—[€6á‘jÎõ(±«‹¯ƒÅH &Qé2Üh‹…¢©tÊ9à@bñLÞÈÙæç™°/Ï,Z˜MËŸr²¹=*u¨ÛP“9«t˜{Ée"öZ墤NX‹¿¦SVãrúÁ‹$õ}Ÿ ó¢'›::Ï·ˆ‰KûAÙe]fÌÚ‘|a؇wæ¿¥å0…ÝZXëò]KuSO4ÐËù¥îÐwÚ³±°´ XY7”²Þ/ö‘:Ìäp1‚žå»uáGZZo*ØúTÎôã ¶m«!*láTNÝ&ÞiǘĎ«îÚDSqçÊ}÷u¦L3N·HëÍduªp¬éèæÕÊRÕS+Ž £à>pß¡«*ü¢‰5³]_gTru곺vš%Gë—{\½5^¦ñäEPžhã’WU´@©?»ñ’+Y]®ºO¤=sèâŸî{ uo_¼w¦*‚^[õ¬uIH Ù—µ#|úöM†Óå¥We1²)ƒ»¦ØÍ–ó*FÀ ˜üÐ9|$}?¦¾JhOR*cã­ÏE#n½çõ¾;üæ•M·×Ä+†kÙêÑö]Îu¾q“#£Æï"‹ŠÉ¢¡Ü'ŽÏ{΀;/§è»NoÖÖY²òºpY)|!9Æçšä¸AQKA$ûþùDPÎeg].áÄ×3ñëàÚåmïQ"·û³cëh¥=t'´Ò÷<¯õÐqÁöÕ)IÞ˜ä5X¶Ñt…—ºc«:ªæ¿8ºqùŽùWÄx(BÆú˜>}–KCíµé­-šÊ¶+QȪc›ò˜Ï[ý:yGýÄV»\v1Ç×)üйxùÇ‘gY yÚ9öo£§bFLôñŸƒ4]9ÎløH´r+Ü—ñˆkÒ¼æ:r¦@Ñx}Z‹”MøøFõ—l¼Ãÿlz›Ò7[dé‘8€ü±Ðµªê‡ÙÑãø2‡bvËWieשó·»!`߉N©éžbÅ,†{7Ò·úž]ù@Y  <Š„¨R™òíû³ïÇ«G‰¸D@ó µ,Þ3äÞ|…NÇÏùvæ%ÙôàéMž I„OhŽW+úña·¿x#1÷ÒxÙç¢ê=Ÿ­£ î5^c/2ðJIzõã@ž/÷3æ×’k‹Ozóº¿d%ŽÎ^á¿XËdåˆWtŠfÂAû~¸-P"‹ ËØÐ+[›Zˆ¡Èœ¯6¨Õê:›¯mv³‘œIhk`NoUIÑ߇¸‹†´ž9ääÇÆ#æÞtÝÄðë¶UÓ½yíúƒr++*¡¸køFa—v¤žQ¥W9´ï«LZ9U¯ˆî…AºÒíN¹p„è1Û—üEˆ°¢àÁ·ßÕtœ×5\},e$.u~à~†£¿›Óïç\¾7+¥sÀBèëtemÃçRˆ¦Â£§5tVì„ÄœUA3sÿ Uæm‰·âX# Bƒ!QUùâàòsM‘póÜàµÝG޼h…éÖo¸3Ð’¨f=r¾Âzáí›íCÓHÉåâàA‡6j(ܽÅ÷@¢8Õ”Õ¦x ¿W#ãÚý“‚îJµ­ŠcŠ<]z¶ôéù÷±Ý׺ý –rÑcÝ9*Œƒ|¾ë‰·°=RXÇ5·ÎÙ!jdõK¨¡oebaßÿ¸£Æ_zn·]’nfþüzã<~VÎU?sä\uµÉñC\,ñÒ·Ÿ{§+Í‹ÍïêµÞ |=%9Û@qÀ Þ;[¼c‚òt dLøÍt ×[û¹?l Ô= 3-ëâiN—-V„ß7zŠ“¨|sê´ÃÓ…@Í4¶ÎA ¾°MÏÞüûvï)yїߣË’¥! … ¾˜Ê3×ϯ2ª>#ô1ê9éþ—D·—\»}˜ß°ƒ^í+pâ›{°Ö6ˆæ7bTذäuœéÅÅ&öÏ_á)sÝ<ò´VË(§­èñXÇç1ÏOÝ"ÙVùÂ1E—•1亻-O³°ÅÚuÊÒ|äám‡5†gèÖMd ¿‡ë¡ôtªúcœ±jfP ÝÃ¸æ„Ø$zãÍ7ã"Å™Ïðí9NÎß#Y‡l Zš ’=%—r/T8}_ð\ž¾€˜¢^R¯“5ç ’/Ïë×ì»G”í6M²æ”ß^¶ˆÅ2}#+ãû“¥½šâ¨M$r³3!Mgÿj×øÑÈ{6ÔêÀ§Ê°š9}6Á€†ž˜Â½cs Û¢~²ØùH\ËÃR%ì˜~£>ý7_æ72ã K˵«/fµOªrmÞ qß½òÁ}1rú…9òùãýÀ^eú²”»“þϸ j™Ëoôj¿×…:‘Ÿªt¥O%ÉkÞÛòù…rLt“Æ)ð>μF.ß·¯¹¤Ê»;²I¼¯¨ªONÞžáä&i·ò©u35éw¾ëý`Ô„³ÆGš±OŽ«'!X,(Uß(5ÿ]¦˜|!bj•N©Zì˜6 ×ûº5ˆŠ¢ÞÙ'²ÍXR(:CŬöøv"¼Í¯«‚á‰#‡oÜÚ>@,”ç%Cˆ½rT$© H P¯bÞY.–‡½Õ+¹=0ݼú5¹Åö®y·çáqÆév ¥´àÜëÆœX^{€eŽémŸÍß8i¯—$º²V0Ê{.K¾ÛyMÏþ„˜Uc¸ìÉ «ÈïhÎÐÞ­²w‚8nØ›¯Q˜Ï;D·'ˆalÎUÿôþ¡Ø ´B}%ÛéVSw\¹ÒÏÆ ›Ôï¾UÀ†ŸçÊO.ö§ÃŒ1^Û" ™¿V/æò¶ÖnŸB &bZ=¼\ߘ;,y¬ú;,¥ övFà»XQ†Æ¹œö¶YŒõ°<>,fnñÆ+n„î}Jø–Ö‘õ*ª•§÷YzVÏ;!Íãî•§€çã¹ò>N§z³®ï ´¦eU($:$u_ßš\‚ÃãG¥¹*$‡|-3à_ÔL}¼úÙoJªå<Þ„‚¼8eý&8OžXϑԷY7.›¯SŒåõqÁvñN–ë$]xnìýñ8žÀQÒƒŒc{¢8ÛŽŠºw #œRos± jÐî=^œF®ž:¤gdñÚº º4øGtËmñaîaiîˇg¶?1Vd²1'™Š˜'©QÆÞ˜U×Å+§Ÿ$ÉsiLW}—ZâìT6Z}h|Qu^òvÊð·;‹â:…¬­ï%^-L±Ç䊎åðŸ Lª×á˜|ÒµRÙì·=9ƒ]Í|34/³ÿlj£Y’»ù§=0ìÉž„SÄ¥¨ÏìÂ/nš^®XŽƒ6ºÔ-X'¡÷ÚsÁníô52K­ñ]’„N{W*¾ŽÀð_„ßv·¬®¾œ„ÊqÅNVG}p&+¨`Öbzm8l>Ü6;þ#:Òû˜HÃËr¢Ceª˜[Å}¼V-ó¬ý-|ÍYÎ3`¼³›žóùÛ¦¨…yŸQŽËä9у ÏKŒÆÙÓÌñlãØ€­"fl )š—ϺþQ™n.‡Kê·©œõè‹­¹Ã |¦#F§}9Ë3™²Ÿô0Ržl™½×¼E…8¿äï¼»¦’¢ÔpóÇ&û­§¬³ÂN}œ*ÔAÅÙ]Scêrñ·~$S÷™Ré cŠ,L}Õ–=n“<´õfÙê?¡5ÌvÈ#F4ޝðºòQI]£½ž¢F²Ú7r¶¦ŠO6Ý[<c¯œ¿|0¼“r–eœ¡íNàðÝrChJWhÆø³~Ûq \ º}ú±N¥st¥áöù.x`¢éȋʦ™Æš“?d„eÜZY»²™¼HUÓ/Ñ}ðg|Ð-ðëømt&YðÆgN‘‰'+¢±ßK6‚"Ï UK¦èy–nèªÚ±ÀµŠYN;À]æ9mÀd÷T‰;5‹ {ó8ú½ÐÇM娔C%Ò}ßèáfåêïg\(zóá;Uf}ƒ‰t5ä}ö…#oCµffÈý·ší’ÎNW+øÍEÊØ—l·çëG{ly…Ýâ긖ü\U?* I§^ÿ†(%{»Ó'4#Í–Ø|dgžXßôÐvyšðl®)¨ï8Ÿ¦kKÄTÊ ¯WeXüÍ1lŽÝ¯pV'ÈjR?_Ó¢Pud/®>Ÿâýì&sÞ“ÂÎïòÐK7}ËfšñLASÆo­¶ÂÉ™gîo?8TàIì×\BÆsk^äÒ4©&S^Ó,G¬–œêŸoåTM?ý’ø*‹5Ì1¥ëŠcŠJÃnO{£`%u³ŸÃX^M0±IlêÒš:Š÷+zŸ¬•HP¬~\* ¬¸éÜ¡z\}¯Ð9ƒû µLwáÐ(¨%jÜù9áØ™Œ5çwÁ‰µ³)n…ÂY 6S¬ü":ƒ¾WÁ¸³ Á¢~“ëx5Ž@“ýbÛ ~]-‘2ä{t{×ï½å‘Žl<'º{º‹Sð-ª×N»°ÊâC»¹ðz¼³‹ÜÍê¡—2Û©:öÉX¥Ü+Î+›ʼ~äýèƒm vÓNr6g{ŸÜ9¬Ã`ÒëTÍžA#ûP³Y“²9n)ƒ”aÊy\üo—#ãÃúÁ%ÉVÚÄ ëLmèñ_Ö¼àëáAz½òô]•~1zø|ÑЧڞW–÷ƒð/Ðʧc >Š.d‡‡úy³ŸWIoR~®|Î@ÒZÈ1¯wál¾Æ§®½®ûÛ›ð";ä{$x:Â{AÔgMqãeàãwZæw{c8ž¤ÑY©$À^£CÈÔ+ÓsÉ}ôÑ'ûçã÷ÿTöþꣃ_r7Þ:`Æ6éιõ{¶°3°¨=Ò¼k‹bóÏ•öñb{²|*—eà„§ÀSáLê©mÑ;CµÙ‡®E\²¶Hö­‰W™ 7;³4ö™G; q¡·T«¾U½Ê g¿‹)g[X1‹˜¯÷¼°÷pÔ½Çúpæ©A˰4Ž£¢<Æœ·gRgãTì©Í\R]v¢Û=;ÎèjŽ„•fŸ5o£ŸË¸{}ýþ9t1¤SJ§µÉs4gŸY÷„á"4Â>ì}ÆÓ±ÔdâX½„Û׋v¥”BWŸgÜ6ßø(BöÖÐ[ Þ¼¥®aOAÃÖWÔ^Ž·¹xæ£^Ú8>X»û´t­ÔeŸ$ûVã¼Ç†køK-Åð,ÝØ§'éÄ-¡iý†IñãkW£®iò¹ÞL;Úd6÷"Í,qtUûÕ/õ¸¨¼žK~aÇî¨}û‘j·Å‡·²*fcðôƒG²2k¤áÿzAh endstream endobj 1550 0 obj << /Length1 1884 /Length2 10677 /Length3 0 /Length 11838 /Filter /FlateDecode >> stream xÚµPÊïw,Hpw‚»ÜX`‘]dq®!ÜÝÝ!Xp‡àîÁ‚îy¹'çÞï«z¯¶ŠÝ_OÏ¿»gº:*U f1s¨)H 1³³° $”45ùllœ,llèttš`˜-èo3:ÈÑ …üËA„=Ù$°'?%( ïl `ç°ó°ó °±8ØØøÿãuH]Àæ%€<rB§“€Ú»;‚-­`OaþóÀ`Æ`çççeús;@Ìä6BJ@˜Èî)¢Ð 5ƒ`îÿ%Á dƒÙ °²ººº²íœX Ž–"ŒLW0Ì  r9º€Ì PÚþªŒ ivúË®µ€¹A€'ƒ-Ø qzÚá 19ž‚4ä*ö È_Ί90þ>; û?rïþC ùs3ÐÌ jg„¸ƒ!– °- "­Èsƒ1€ó?¶NЧý@ Øhúäðgæ@€´˜øTàßå9™9‚íaN,N`Û?JdýCæé”¥ æP;;æ„þG~’`GÙÓ±»³þu³6¨+Äóo°CÌ-þ(ÂÜÙžõìà ’“üÛåÉ„þÛf ‚¸ÙØØø8Ù ÈÍÌŠõyMw{П‹šŸ*ðö´‡Ú,žŠyƒ-@O_èžN@æè òöü÷Â:;;Àl˜‚,ÁôßêOfÅ_ütùŽ`7€>ÛSï±ØþøüóËð©½Ì¡[÷ßîÞ/«ŒžŒ†˜æ›¿*þgM\êðdæ0spr¸¹8<ü|ïÿù§üÿ”þ§Uþ;5¶ßzr (€ÿ¯ žŽî?U¸üÝ  #à¿#(CŸ:`øÝølÜlfOØÿŸÛÿÏ-ÿ]ÿ‡Êÿ­ñÿ7!ig[Û?—þ\ÿÿ,íÀ¶î;<5²3ìi(” O£ù_WmÐ_ƒ¬2;Ûýïª ø4bKÛŽì$ v™«‚afV¶Ë.áIÝ ©BÀ¼4fv6¶ÿY{š63›§×Äééªþ\= ÓG”‚˜AÍÿ˜:nÐÑèŽÎöÔ\ÜÜOö§ñ4¹ýÙ×Vö´ðT7ÀêˆþÇ…òpXÅþ0ýE<VñßÄ `•øM|VÉßÄ`•ú‡xÙ¬Ò¿‰À*÷›ž4Ó“ŠòozRQù‡øžTT€Uó7=©hý¦'ˆÿ)OàozÒ4û‡¸Ÿ<Í ¶OÇû˧Ìjþ/d°‚þ…V‹áSVÿ§ãÿ Ÿ"Ûü Ÿ‚Ùþ Ÿ2±ûìOqÿ•ÆÓÃÀ ý>ŵÿ¹žÊµè¿}úçÃêð/|ÊÌñ_ø”™Ó¿ðIö/|JÔù_ø”¨ËŸø_ÍdæìèøôÄþ9íOöþó=Ü@fè ³P3Á@ëšÀÖë*12Wæí¯ÂStÛÚIŒÌž ŽmηØ(ñŒ•iþkŽ—bñƒ]¸Ë›R o)<šêPBšcÕZî¼î£Õ'¶[ÐçljúÆòÄj{ÉÑ^2k¾ÝñzpðÒò³Al‚o—§ËrpæÃVÍ!¸ví‘q«í-Y žÝVÛ©äQÀ¸/™dþø.ÂÀ¯pš.Û4}æõ339êkü7œé‹Ë)ü̱_”òÑoн?ræ{ê­sDÞÌx¬”ir8uÐ’è½ G¼À™ ÷ÿž O<çYT±&<—ÁÆÈ7éÌ977ºdͧâ`–™{óþ´ÀtŽ· |Moðhç„p‘´Aån<áTþ|7:ޤZmÆôŽ)N÷ ÚŸí9²bMûånõбNs¡&ÕtK‡ûþT`ƒ «5·Ðšd|ô»ò/@¹Ÿ&ðùbÇ,ÃAPã^À¤©yz²áÂañ[§DD{ªî¥¹DÁö›ö7÷ëG³"¥TÆw€×vù“Ñ9\oH ´9”ïê¤Ìâ¤^—ß#Ê G_<B1g)ky—;Å?v}¹˜O“d¸ÃG…•¨áÍ L,lÉ#*Ý£¸¤œäâ“ÒÂIVL#m˜BÉXÞ¾lÓç&‹¥Ê§Î³1þ0L §>1eJ<“4aÙ{†Š9èéV4¥¥du8­HË©Š´ôƒprÁ½›;­Œ©J=Îû*t·ùõ³RyBHÕzð¿¦ö ÍZ~–Œšò}t¤5‘îØB]-d­N¿u¼<ÿÑ´rÝeè+ ~q†¸-g@û>Qx::§nßw,4 1'2‰ha˜ðz m jk?¡¦³Z­£Nã5’±âW÷-!Ë×mT4â‚Òæ„î·üX.Ò,Ín]ôÚ˾Äc»“fa ú“{_Êo­ÏËÑ–bÕ¬i¦1[YNšFêˆA˜­Ù15ëÊq#»y?nIaó‰Í'Å(W/ÁðkQ}'§eÄOÎ}W;Ž+¤ŽH{ñˆ4¹_dì0÷QeÒØë{þaÎB+F/àN䤄Ç^Oȳ”ë¹Oš!qúáÍò‘êVϲ°ãZ+ˆ9}lÊ?éi‰¼2ùªn4íN)¡Æ…êÞ¹:€½ÙÐOïIâu¢f)>N’ïb[£š}ü¹¸*g¤³}´Û#,ãnëŠîfè’׌þ,—þ¶ÊoѸVkµ¯ëê]pG±S›v£Y–F\2íx‡‚/ç\‚&à´·KRžØïÎ{°•ƒÒ•®þj[³ %¬%haÜÓÌÏ1õÄô0ÜÃÂyÄýâójÊ fc…«Å÷'¢ËèܲÏSå"@†Dø„T4A.+zέkpz;ÔZÝï¤3o]˯˜Ïá_½mñy¶ ØC jX¶î€ro’0Ô÷qGÚ«U~|Ôi•ð€ÚÈS;ÞY‡æ†ôë;x_2YÇÀç|Ó?&\$A'µ ‹¬âÿ€ÆYŽ9ßÔG,YÜIs†fF‡Yýy>yɽR^B¸Øy?î1Þ¤> à’œ7T,⯵ÓËYd—ü«‚Ÿ¸Óÿ$]XÛ¾ ôͦílT'd.„’Ó2«ˆ†·O`HÍïEçÛzÕ÷“̇Îo§ª%µ Èœ4ŠcÝÑä+ÊÆ°*^ÙG%Ÿ¢7»‹Ž³ªç„íÇù™Î§‚“ArûËL$ý¯°³•u äAçï”Ftó§&ä¯.ÐÙ4}²1‘ä<Ñ(ƒx¬…³InF\ÃçÑÛòÛÃP[†{yƒI®’¾<[:$è½Yð¼- ,ƹÈlµ•­kMfû}Å+„¿(Þu7åñÏëúÔ¿9žï†l5“„1°æoð°`Iè:emþÆ%ML¹•’ž“—¿´†'Æ{÷IÃû˹]¯®Ï®mxônïÈok]ìêëàýLQšb“à•xQ%ÐA•›½U¡ìäSbcµS^qŸ§É‘Ò^<pSþìm“†¯w:ýxM `tòÝgà|5y„L&ˆjX<9ÛÞ(–ž@è>©O]2Ë+ç%½}ÍÂJà`¢°ý‹sœïmÂ+áHœýÖ’Û¿\öÊÏ–ÙÑû€×ÏÔE!’…§Rd¾|¤hþìc¿:f9ÐKÃåwÅ)a†ï›l†¥—kÛà{²ƒ ¬äeáßçô 5ÿˆ-ˆû¸5™£–•ñc¥êÚ˜ÏbÚ¸L;r)= Uó ÈéÐS@–YÛÏw—½ú¢ùž´*ÓØ¼ñq¹DoÁ«+„QŠŸŸ–§èG~á­²( P¯CAo?7Ïå4½¼t6, øî¸Ôî“Åá{©ØH¸.9œbâßèZ¢ÝK5¨‘aUþ,ó3@ëÜwmŸet¸34««öš}f³¤¼Ó#ö¾¦œ¼ñP€nñm#öÙ ¡ýP¯†Áëõ鎟N -­”|í ™ßXµU mãmƒÕªÒ‡e§É²¬)“ɱxX7få,ÜîË-Aí2Àa¬©Ç†I­&Úª‹ŒâÙw`ta:•¸¡Qulz¿•ðåþ5‰ì³nia¬Á޶Ï@…‡¥(Ó!Ú `fÃÚ| ÿT}/!?Bƒæ÷Pmí¤á,oa{AUY^t <­zÿ¸6Ö]YZ|Qš]ðÊƹUe_3F™#Â|z2þ7m‰WGi–Øâª­UàP0æsýgRÃá»ãÎ`w—¶·nön‰_ÃB§ŒòŸ…à0 ±ýZ½Ù¢;Q¶ý¹6'†¥±÷ÖÇ«H¡Cõ—KŒ5÷Ô·šY{ãkz|ú”É#|£ÄB½-9fþÜÙÞYª•ï HâÀ±1r'º›ß)K±U”ª¹Gq•R…˜#fZŽnŒûº1y2]-ýÈI‘–…HµQ4¦èPHRßLafÁ?ØÒÒcá¯Ï¹´±èŠxûágU›¾»“{¡á™<ÈÁ»(ü±xòSZ<Õ&põzÁ®/ ‘å=uÏ€Ïù,ó‡Ê fNõ;œð…F½QýÄÄèXúÉÍA+ÉÇ+¯*1µ5 Y€€Á«ðÏ&À›M—žÂÐ,dg¤qúH¶‡¦µ–8APp»¡ÖWÔ^³¢ë¼´ÈZ“”¬àX‰yv?|ÖÌ ÅJíG¬$]3›Áš¼¼°qTdr´‹no³B¾ExE ¯¢ê^˺VÝRJ~–ˆèÓzÑÔ³)ð- °šñ7¾þ3ØÝª“~—Çr\LÞ4YîpRó!É—†tš#"#'‹øÞLhyT>¯}:YÎò0Ñb“\’`Ûã¼ÁWgäÄú$*¹]”ÕêH×¥º !ÁƒÏ/4Ò4¦K¸g2l߈`F¶ÂU¶Åá©Ü~W¡Û|\üª¼èÆ8Çž åX¸êÞÐHÀ†Ðû´™s²ÆÅÁà1 **ц*%¸b’~{Á1ò!à­š_ôk8b— ïinN»õù”SÖ¾‹¢ÊÚ²L’:cœ°³÷í{ã2'pÄ2‡%ѱ\qþLùåö… ¸ï®é²› ·¤ÒeÃ- ƒK¾núÔ_ÖÞf*²R¾j𢲤5nþ!_µ~ª !NÊxo½øÞ…ãX“þþгÔPˆ6ù¤’ù>LèÁEètNY•À+Glà^†Ô&oƒEU·¼obŠBE°¨¥M\=í%cy1´lOk¿ªûDãqG¨Ç?IØ«FÁáìÁªÐCG®>豌ö¬p¹°)í%;ßz?6 B ëà{»ÚR¢{Ù8ó™až´Âíd!žÖOI¯£{ÊÜRÎûº”ì”íØ-–qDê&Ñî*h ¤fçî1Ô·¨µeA[ÿ®÷q'oDÎ4Ò’èÓŒ“ëºæGúÒ⽑ό’A²å$Á°fÃOÎg¬÷]ÜWs ¢i¤óYucýÈw](k6¡[äA/Yâmë.³ãÜŒkˆ)DCÕʵ'89‚PRl‹6°ÈÃw&¼üTæL™¥v' Y^V¨§«J>š®Þ”¼SY—Dþœj(ÙUÑ¥ˆL)ŒÄº<Ó}Ñ©p„ÛQ^•g2»>(`©f<$‡þí92äÎAµ²¥¹±Ô˜!Ér^†LÞ®§öÙ£kï¾Ü¾Š1gÂUèÁ•Çü‘•ÝæØÇ)þ“k‘–^f bÖž»ArŠzYÎÚäáɘ-ÜÈÛi ߥԠ³å®ú:Z¤¢Ê“Qšƒd]ÀWžÓþŸÀµïl­'¶µÄ~Åz¼«óaÕaÇÔÈÞrþühEÏë|VŠ$s®UôUÌxº“³GéþæÛÆû.£ TØ[H ‚ ½PÿÀÝô\¼÷ä‹“k«Õúî[·lƒyf “UQñ"8p çÑPžÐ6Æ5O5ñ¸2}šU‰9Në€GÐYL(«²/bfó;¼7y$p³&!ߣ©Ã‘qUõ îÖáš·Ätš¤^à<¤„8LYH9¥Mi#Æ”(F´üGcwN#¼.d³Ñko/" Œ» íþ#‘â7Av—˜·Ù«žxR“e¥Ã8X˜)„9¬)R‡¼oD±ÉLê§ßðvi’O#' †²æd¾ÏåˆÉ‘mG„7Ê¥žq3jTàé,Pº(‡×ÒbƒÐÄ'+ÚíÜ»ÂzŒÙg¹`÷½?d]MÛ&²¹&üBçTp@×üv³<ö¿|¤Û  ~ªÕã82ÆöeIˆ!i>’ýƒeú2%¯ *cì[•þºVØ„rö—ùÇ÷ý*F!I»ÓXä.&á¥÷.&!v8轡:YVŸ‹E‰ªÛøAdìçʇë´f;ó×t˜a8#„´”]LšÙJÏ8ÂÇ$TVîµ²A™Âñ×ý¼ι~ñÆQØžniXñ‹´‡4z§à/ìC!%¶iîXÝ F^jwÙUŠc¡fíô$ã²ÆeT´±¢¡›/+Ћî+XÞ?±Så®›•ÆÎQsúãµgN^ã¼aÖ+;Xc2_-q·È.²s™N5„¡= "‘“™,âùAëæI ãµeËð«~¬ë"®x¿RU6iÖp· ¢eJ?ž‰ŸR¼œÞ:k¾uű«e³#ÓBÅÞFK°õKÆÕTÀHry攨u†6_^ú9´nKFZ° Ù9ñ‹’÷d¤§DíØAdM6łĿðì&SšIÐg—Ëc1Ç9 pß#ެ- #ïæ—’#º%2R›€¹°FÖˆåÝž6¸S—¥å¹µEU‡—R´ÊªFÆ Aþæ7ß]ª?_RãhfÂIèÛ½ŠÚ° \'ýؽxñÏÅé¦~5|·ð§Ì%Ö.ÿHmÞ±Áƒª†"l»šLÆî•bÙú¢‘N»éL$˜If$˜ySŸ1B7Þ‹¤k"ˆ•^ÁM¢UÔhITž¡ˆ¹´RµÚ’#.kÍÑoüP>|U~­K;üÆlxzÄj¤È¸I^NCfç¾ÆN*|¢°_O‰•\ùÞ\óœ„,о á¨1'eªßjKýŠ7îz‹^«¼Š»dn•^*/ÀÏ{*mK‚¡ˆæ†hÑ·#-ªôÝ×8Çå5°/!‚‚¬i¡§“”{¦ykª-¾+‚ª¥,,×¼‹ûÃæ×Tlðm‘++õ˜ ƒ…ö)ud¢jÅ&-ž%4ð¸¨}–í²oÇr?š#¾ÔÁjö6ÃS ÝœÚWì¶ñ…ËØµ¡ª·ÊÉkz·!Áól_žåàIl·Áºæ,#Ž ÅKÃì˜k²³ -»ÊÆrø%+·«­LUúc†qÏL²SË»œq£Ù\â´ž¼é˜a¯ÕÍ ‡æ6ۤ퇬Ù_—›?ÊúNÇ!:ŒÉ¿r-?Ê…U´‘ú&úùdXª¬˜¨Tg寙MŸ7ÅbV¤)¼>cMÅ»«Ã­øŒšÜjù©†_ïRˆÍeK<ºñœB[A8ÑgK-ÚÈ+4Ö¡%Ä\®25òZõÍu Ëí î1™£·%ÈòÚ‹rÚ·ÿšPn ìÀÇÛ»pœBÚØéÑ“L À;*2L}> êt¥¥tËÙÖ—‚¯§|]ûÚˆ&Eõ*”íü%Þù×ÝÎcÝGÏFÅ Úúv˜!  –\µ²Â IùѬ9ÐjØÏ¡‘iv œ0YYĤF1(vx¢p~– ïw›ŒªI‹•Œ“¿š…𠡨=ÈLnÒn JŽÆœSꫵìò9 )Kò$Y T*ÇyGñE¶9ÇzçQQ·t?ë˜=Re'`RžÂøoÚñÛè¨U»›ÑCí‘ùÄ·^ÈWD¼»ˆÜ.µKÉãL;…ˆ%ÏÐï1Auæ¹çUoæÄrý—F÷Æ&[¹ÖÖ»c!©õ¥†ßº·ÔÛ²—Ûžé>z´t¹ÉVókyŽ«¿ä¹"%æ¬ðB©gàÑÛ¦øZ›ÑÇ$h žËîÖB¤â¡&í–;¿Jž ûJïoBëwu肃<¬LƒÂšøÃ½Pj2ו*â6bkâWn“ø*¾é‰2´}¸ttÆ]µÆSä¯ï Õ‚7ŒÈèêšHh”¬š‰f«¥é¶“pÆ6kbζ  E´›Ô·œ´oØz‰lm"½tŒlß‘„òôìúÆ7ÉF~‹œ;0Ö+j˜q±õ?ŸÏ€Úª@´~,¿½ärñÌ3hV íªj´oàKr’mD›sSQ5S$…YÊXSãn©CÌ?Õ;n„/ðûOëÇ1~Ÿ:ÕTîV KÇûârølÕÙ Ô.ר5Íšõì7¤ðQoOöŒAR3TsñUÓäb@ЭžÆ¹WŪÅûgè­…·‹†Ïv› r.ž×“¥ ,b6¨‚4\gxT 3lDdÊÓ{rIZz×—gg'{6UúFˆÀm[Sïè×€½³§ãÑC¼Ô“›ñ(ïí’~åÀ[b ¾\uÈ9éKlà¯ð•oƦúšìO\ óëC™¦^q¼šÍËœ!ù…h¶.¡×Lú Jù¯ðј®oÉæ’™9«dÎXU~Ö$œÏ§Òâ:ØhõaÙÄ„,B Ö ÀEˆJÞÐû ;š…P3£ûغŸé{_…š1,·û+Ú`ƒEQ# gk3;áž=ޤ^wÿìž®\áò$OU(¢Ö2”I²±ÀáÅ™uí±jª‘˜0×eÝöˆø|çß“ËjÊž›Zߎwª‹R†>­©ÖHc&CD™íÌ»ëÀÍ]@°|O- SÍ ëÃ; vº·»DâQç ¹‚8ª0 ÃIiž a¯=7»¾¶/Y ¤iè,þ<Œ:ØÎ´ñL›äSVx©Ë§zÔŽí]0>0MMИºá´ÄÅ6 ÝKD~[‹?ä‹|¹ŒÜE|%óÜþ'íI¥rÂs!‰ˆlÑQ›gÁÕ˜9M$Ä(˜f%§fÄrr ´r˜tD†ýË{Tr*“¼ŒF²{Þöˆ7y#ɆµÔK‘§Š" h®¨¼{£AÞÏÊÅðøì÷(;§æ¿a›N §ÂÎ䵌ø¸£[C/ZQÙ±7RRƒ¶ðG ú¢0ê8s¡…áY§Ôv«mÕcì"¥¹d;¢—å?ì$93ûâbt»J‘Y¹s_ÅÜèç/qøÎµô3WŸè…¤¸•F\爰ÜÛËÄ(MÉ4tbÞZùf² õ,VÓý:+;Õxä)r5\BÅ,‹T]ŸÂ4#Z«{<åÃuæ ÉËöÀ.¤lN‚Í%:õÅ|È;ñM¸r»íìNòùQLEŽ; âÑØÝ§ÏM­ô|Uƒ­iZ*ª‘é&Û¹b'Èš…”Q¸Uvj·Xðþku9äéý1‹m1a/„q†F¢K‰Âky0ÉøVú"ÔÞ~FÏ'k3?Õ¼§Ééܧýæô 9½(h‡¿Jv½ÅãŠZ{~ÔüQ)¾?¿†kýóa‰/÷üâ§þ­)r÷ŠáÉ(øŒ7jõ÷ÝGÉöUµ31™ ÂPS|µ’7p‘”Lh ElzF‘†±‰¾ËWÞ*Šy‰1ÂûøkÆýB³Vù1¬2b„GõÝŽÂÙ‹;8ÂfYB±–IØ×‡u·q¸™‡Ô“:ÅWc_£œO•/¸e]êqÈðè¥á¨—"¶Hò¨ZXP7.Œç‚Ö®+ª}‰ç,´ ý?ÉSöo|™4µô2jý,³%^)ÚUˆêü2JBIBžn0jÊk@Ø2š<ß{y@¿TcÓ¶d·?à,ÿ- _ñµàÏÞ2~Ófz[Ê- ­‘îÕÌÔ‘±Læ·$ù>ù­#5NÕ‹¶3ÒXÁ@Ñc¥Qò¯’ìÏ$:[–õth—å‡ã›È#VÅËM®²ís2¿—ä´EÌ@ ‹D>lsØõzM"½î °~©Hbã*aSyÄnº¼Å„¸)lÄ®ÑQ¹tºôò*P¿¢t§> ¤R½ž#UÐ5›Ÿ {Îâ­Ån¶Ä}þb†.ÚAQî¾Ç(ޞ䬸’Û÷R–o[3°™EQ‚ùu]f6$r1FNO’^î­BŽÁ]c¿0ȾE/†n)æûCÞÊUmO×k²ò¤¡•+ÿ1¿Ø¢²5úe®¤$Ù©ö•ŸnÚZÀ§îw4‰ÅD«ogœ5Bq»…¥¨0Çñ^Ä÷76>œ†b…tŸ„GjŒv<¬tv ÊjÐ@¯÷µµuØHGU`m]guÙ“Ꞥžä¯#¶Åuu*|îøZò6Þù³•ÏŽêi´Ï…Z¯ 2ÊÕCÑÆ.wÖ£ÓóNÌûŠÙû{­æ*xø±PîÉ“kRˆ!÷LCÌëùy¤DªóKLÔÈ~ÚØ½Ì1ÃðTw/û„îOîO’øÉ*hÕ/EÈÕ[@ý*e}z]ª[ò Í#táÆ1 cI|‚¼³ç÷T[Ôº™½Îœ]îüp¨ T‡Œ*˜Ì ›š¹Þ]±ÃÀ[]†Öd>åôÐtbÛÊÒÇ~ºæÉ¨Î1êkHœ=UýYuÛ‡©« ‚(²ÚçÆ”ËbRë(-­e.Á¤„góYÃwõ­¦7;ýò›¤µ‚ò‘Ö!+j}±"›"_‚?¾T“í« ¤½“~Û3ÿc½i@4Í%ÅáwÈ„ŠÜöèj¡QéÞê4v𑞟gÊ ùÂÈÖ½9¬É7™K]Dr.~sý윬–Ž6f@‘FÍt®iÝE«$J~Õ*¹óÚA¹Í?I„Ã{KáV%Ðó5$Ó½n°$Ñï øcÛæ2bkÇ”ß+ ÞϪ¤±ìôpšŒÈÅøÎש/oˆ(ÎØ™Åû݆Ȏ¶ôeÄù±éH\ÆYxÀØ 4f#]®/*6LT‘L¸Í÷ÍMʱVO®ÊS27Ôn‚­Ž¯}©âtZº6¨Jpb“„ ¯(„1uª1}³ïÕ¯o¾ÞY,×§SÕÈ&žŸLC}9Ó9¢zúÂ(uäXoÜ’œ‹ …Ósõ*ø =ñgq¬à´ÈÁèé3ðÜj¸­wj¶s~”aŒ¹¿ÆxÅ9üÆ©ÄÎѳiDàªÊ[Έ£hðâ!7Åãcki°ˆtGLÈÿÐQ¯¡²U@%ôu Tü¹usÙX…I¤QTBÂB¤]b†éRÉ‘:ÃÆ){É´Äg!ÔwM½RöÙ>92ê3"n‡>¡dE¯çQOâ9â‰9pd8¾µûlÛ^ª{èÉiFtu‹¤ÏÈ2ü(# ‹]*.¡£Ÿuªr{Ñúöa²"{ ã-üœHÛ$‰):ËÀ¯ð)Ããµ¾[¾È)غ[÷¢*JÅ ¬èº„ßÏxbTv„ lQñøL5xú2Ù‡»Ì—(G/ÂU•ésÀ=lìCmxÄÜ9“\ö©Í’ÂsRÁ÷ÐëÈUë¸ásÜåñ…µ¯ÏüH@‰¢WZ‰5ÔO²'JùHá ©Ì×BöÀbª·-ò  ùaÈnj%¸&÷ÍgjóÕûJ•4ºo¹%zXb¼ZöHá–nq …_ÇLM0Œí¢ç••+u…?,ÝzT¨j’öó˜&‘À]—©e-yÌB9ž0Žî0Ô.“PnçmÏ«~ê[*’ 9L¬Qç²,¼Ó¤ IÏV™;p®<Ý×PÖkÚƒ–ðûƒJ÷º3|Š©”°&,Xªh`\bÃ_–ÕHt~ísI¤rÊ‹¨™[a UÌj²Ý¢Ð¶‹ûsmˆ%VGèÿœ‰0>mi4cË_ì%Óä£/WqˆÛ@x8qÅ7^E[5Á<üN'_­ˆCŸC&_ôËJÓÍÌУÍãæÀ5µ„î]=TÖŠ0îPxëãŸýßã_;Þ½îÃÔŸ;Ë6×',Ÿ§nè/lüŠ-梕CM›Ä¿ݥa¤"aúþÞ›q¨‰ÏsW ÏÈäM—³&”†¾1ûæh;×ãjñøT÷ÕÜR‘HK~ýÁë®õÆQ<ÈW¿%R½*ƒcne«çråoÅ¡œÊȦq6Æ/Ëí Q‘Ôã §kºv ó.°üÄà5åVÅæ’Z?|óýz~âz@êÇ ò“Ê·)Êül³GyåýeøEàAw>• îh†´$÷ÇqÉï2%vÊDP—-i¾´dÖ9ò|û£cYÖRCÍ·¶ñÉ[OC!„&6Öz,÷Û ­íP\&-%áõAF·¶ãs÷¬â©?ùÅÒ3{û…£É)5'ÉÝk–VÆ<‘]"Ónó꺴ÜÀO{—w‹AGX_cÌÊ Ý+úÚÆ12;%” [›$ö˜¸$ùFšíù´”NÚôÌ”c¤>ÄloQ>Í!¥Ò³ÌÈ»]ªiÃÈZ­ª¦;øeçI‰NÚZ/«êÊ>³. ü $¶G%ñ/y9“#ƒQL«×¤:T‡êµ Uª$dT0pÁIûUºB‡œ‡!aÈ4|Â!xa¬|[D²‰w{o“6€ˆ~޵Á±$VT"7‚/ßZEÒÞì[× GŽ ³ôüh‘B¨&<_qå¬é*$üº]“o½üâ“`úþra:ᡚMåz!2ˆ±‘/âEªpJ]h^—UóÊþªºŠuÚñ´n3'7÷†WHE¥þ€‡C+Fùw è0v”TU&Ìö5òÌïYn—ü.ñ8È endstream endobj 1511 0 obj << /Type /ObjStm /N 100 /First 1004 /Length 3921 /Filter /FlateDecode >> stream xÚí[YOI~çWÔã®V¦ò>¤ÑH`Ÿ`<àìÑ<4P†Þiº™îÆcï¯ßˆÈ̺«L7ŒV+d’¬¬Œë‹##Û4לe,ãš‹Ìü-3¥N``FâLgÜX‡3“ æh»Í„Dà2á9ǙϤ4f‚gÒ½3`ǵÆ5)ÍŸ°™²ù Ÿ)ï Ì$Ï´–¸OÊL[‡œáÑ0I3 Ê…5ŸMkŠgÆs2”̬`¨Ò™UõS6³–£ å3'ISÍ3§­Îq!YF4À 6Hãv<èæ•-¹b#î·™·šáò±ôƘzq½C+Ðv°À"ØÉVbd¥v¸àøÚZ 1 äÀÚ0å¸KX`Ï…Z)P°D¹ó8W($:‹âš!™Qˆq°œd`<çœSámÐhÀé‘·` ‚ç(ãÀsĉƒåÁ™VcV1@ÍR.0 9ûúQ‚nVIÊx+‘T€™&ß‹_!Ëv!»¶jÈà¿þ'?€¾µv*-a®!åÚ³ûŒX£ˆd3²{0¥¡5É«Vj›«gÃù.cïVC;Ùµq„ò²ë㈧²]ǰƒLbW¤±¦df½Û…óFÄS˜CJî*˜ÓNî‹W(G)x«i4œœÆyØE·† ¼Ac<ªÕU‰© @=¡I##$JBÒãvñXS¥‘ƒc 28¹Aåî˜Ð—QE:ºf˜è5¨ƒè$®„1ÐÉ`CͳeDÁ©XI­ÃÏP×Èá`¤Ïê1]©ÄT³ú®ººô®¦^ŸbC?¢£Eàª,ªÆ/`šeÁ1´jÐ ã:R£aÖ¡ÃpŸ4hÍNp5§ñ}\#ÚÈ]b¸À#à„¢¯“¯*CÛ³Ä,z£6¯yV>è ÝÜvHÏÁé‰_pHím0 ÌÃXÅæ/†¤ò÷~àŒGöaÓ‰ƒŽ‘¢1¬8ƒ;d)¬HÄÔ9ŽÈƒJˆB 4+»¡QQi¢„> FCÆ@×…¿p‹#×Z‡Ãܹ+©ÔàruŒ mñ¥.–=hnjó¶UŽöÀT‡Ds€ zhÍ#EXM8ÆÌ”+^cLFÊrwØ¡,Ú®4P~DãmÀŠæàè­ÀDIâL›‡Ž‘c•@¿ g¡£qÄg§<•K!G{lÈ2БD­n(AÖ£pÓÐØ»Cö@Y½‹ý¶AÈÂ,Ñà=ƒ%=Ìô.¤ Ü-ÈIŠö¡`èEœ†Ñ8[jrêhÔ-Ò 1D¦TBñ7oè=ñ´#*ûiÕãV¼´qGµ7¼‡>žP@âT»è]@(ÌiÏ hœá.†áL9Bé#rI?*¬„-ŒaÅJÌYzÚPôJc~I»$DøZf¤TÀÀÓž¨dâ(#oÁ™xã.¼Úà](=@/Ý0¸9«ž’U(g‚­þž(h î+Tÿ݈¢ÜÞI ;¸ÉÁî0‡wXÐ8ÆÔßãÿùˆÍ6¤œ°¬ÒhLX•eŒX€ã[HxçáíCÆÀ'ŽTƒ,<;yEG…¢€ eâÐÀ±&½§]HÇž¿W4%sTj=%¬¤.(í1ÒÃl1á¢c‚ÊQìIŽZPClñ<„¼¡C‡¬©Ïk–)=²rxª*n[sÅW&Th]<8„"+´E)´b•FTmm%”(Å;q)Ci£·’Š YDóßv~øa'?ýv[dùÁb¾~V¬.–ÓÛõb¹CÏG“x³ÿêãÑÇWÿzz¸Æ¼˜M®V™ ;ö÷_³_Ÿh“=ô™Á‹:ô8ùÞꢘ¯QØùtrû²˜^]ã3;9ŠÁwO8¾|µžÌ¦{ó«Y‘û“uqó3~œ³“ŸE"ð>ð¸ž,OŠuö|?š?ËŸçù«ümþ.?ÉOóù$¿È/ó"ÿœ^Ü-ó«ü:Ÿæ¿ç³ü&Ÿç‹|1/òÛü|™¯òÕôk¾Î××Ë¢È×.ò»üÏüë?ƒASPƒk—©Ÿv~üñ^½~úôÍËý€@H"Bø9žóƒ|^ä/¡×€Ña~8ç?E¬~´Î¯‹ÅÍÍ„P#6ˆÝôKÑà| ø%o‹åtq™À,¾óH¿ ¨ù·ü?ÅrÑÂVl‚íÑÙñÛgOÛÃWCÑ'EŒ>¦Üw°•cØ>Á  èZQWò:¸ºjÑ¿kQVbƒÈ¬# ßZæ«MÌt|r|Dæû~ë…OÖC?ùHÖ«ë °ù¦n+YÚ²ÑlbãþËŸ÷ž¿ò°bÉFæbc™=ÆZ¸W+0)…ÞÔ’è}™F?C"åóO€Êdµ4V¿ç“5ÄÇùrrñ{±žŸ×i¾¤ Ü›-æÌ+n.'«ë¼˜‡_ÜMf˜øošÓ?ÌËY•š×ßn¯!ïêá×ÌÑɲ˜“|šéà0&9îvv·‚,þãn±..Ïg´;=zªÖÃbÈú›i°¤ÊÿÕ u¨}uÀm$'¯N^¼¦ (±ÐS‰õÎ=NŒè‘yME4¸± :k ¿ åèš-7:ZÞ¼øprŒf›ôO©ÿófã¶³ã)Qž!âà§v”Îï±|£Âÿìð—gGÐr;`¹M– ñ(–[ÿ—Y¾QÍ?:8:|uŠ–»H7eÉgc¹ùË,ßè$8<;Üÿô -8ìdyØãG±|ø¨Û§C ô˜ÇeçtÖíBíåzÚ_¥ÛhÙB}¯øö–ܱBÛõÀfeöÃó{XoNNø@Þi|à©“uÃ.ÀÇš ô½‚u[}<‡ÓùÜ€N˜Üˆ«Éü²áŒÏÎÿ¶lÆ:]¾Ú¨¿»wôïA'‡úìÅ<þ_-\„ð¿8+lð:·êÅ Ðsh,B,`Q”ÝF šó;ˆªu>[\M/&³ùbßLçw«Õ䃽—‡ï‚Óáf\—UY=R;jÜ ؤ½k´ä! ¯êGr3Úý¸R›µ!gï¨ 9=lVüSüÓ ojösÞ°Kûá^ÿ­ê›žÛ¶©©E½˜N/¦Ë‹»›0_Og—EÕ¹ž×j²ªûv¶—‹Ù ˜” .µ´Å׋Ùä¦~Goõ±³bµªšÙùÝÍ9äøôj¾]_[¬ÖSP)•Õ ÚÙž {7¿].ËŠí—éjzÊÞ0#-®Úìôüý›O§!¶z\ޱ…ŸMáÐúá±¥ïuÍí–ÔÆGá¢ÛM¬žŸ^œìãÎ~ó J–ñîÁ¶œü{­Ï˜B>B&ÑçL!TªÆ‘i¢¢ÎPëî|M¸úìOV¾é~×1üÑò̦ËÕÍÈ`øv¸¢_¦—ëëUø›¶hÀÕQ@vP x]³¹íO: è¶Ú *À¶@ õ9LG¾—½T%_nÍÏH:ò}[>fAÍ~Uw€Ú\~ëúÝ–/ùx4ìçvsùÍ{pG|7þêðëú~séÍ»hGºÞ@úØ7ïƒéöþÒùŧy'ëHß$ð˜Û"ðZ’¶ªyÒ ×¾-|ßnû; tb5"¿!‹Ìo÷Üùú;h”>»Mê7[ÞŽ”¢¡€©# ¶¨}­¾¨£@'•>}¶ˆÁVoÒ–¯ù¸|îêè9ýŽ'WŠŶ3;ð¼Äæ…þn™8¿™^®²_}8f3ü»XüÍ™1aÂÃÿ© ('ž”úm+A@%%ˆP:p%ˆ¨ LìD UZ¥‰“$'ö!Vá_P6ާI\‘,Jœ§‰y€()"\RD$e²SÊ©TiO¸‚n+J% Z¦IZ1If ¹!€²)*(]RÝE§IŸDù(J…CyL¥BŒQˆ¦ª‘)eP±2¨ÒŠõÉ3 ý]Lì $úƱÁï|‡’%™NúDÃ[dïîÖ3¸¯bQÉ¢L¬)ô%z LuI9…kyæÓe`/ó±Ö%ÝKÂãeñ…¾ßP¯D:öfD­F©c_×ô­‰^²âÅúyê^MbŸ†Ô±fÖ| kªÄ{fDªT,`‹X=‘m!º"BœSusYŸ¬„Úy7Bí¼©›Ûꉗó¶â¥Fy5€ïÓ ¿ERòbc¼\Ý IË&/WºÁ9ÓÏ+R‹>j]Q7ñåŒÕ5IE-8±T,¼j8Q·…Tn°-7$¶Aˆí3ÐVÀ[5J]Þ¹>àm¼ec¼Lø^½L¼1£¼n°}n0•ŒåÅ꼌ëãU¡­GÑÖ¦Á«/èu…½À>R÷¡­+´5 .Ñ@;…m µ¤&…Zrk 5Û©*§¨–SD½<:Õg®ªÜ Ä(uà ªÏ ªrƒtc¼dà ½zÉÊ Ròj8Eõ9EVN‘¬ŸW –}A/*|…q)~á¬Æ«U=dýHð UQ9E4R ‰QÔgnuÚºÖiÛ¢æ 7ˆ>7Tg¯k½m^ 7ôêU½®uö¶x5Nb×w»ê$vé¼à%¼ú*Q*åÈ‹ðÂïÖx±¾H±»m?¯H-»Ô6ÉH-ÇB-]ï"Ún£T3„šPcÕæ£D¦c5)œ„‘Îö(œÎD¤Ö£Ôu7ØplóR/>ÆËÖÝ0 WåkGyé¯>§ØÊ)VŽòª;%iÙâU¡mFÑ6¶ÁK÷ð2öf{ÓÀÞöao*ìÍ(öº½éÃÞTØëQìu{Ó‡½®°×Øj݇¶®ÐV~,D3:u#’š”N)tS:qÖ’©*¯¨–W’” SõÙ«*?(>F-~P}~P•¤åÕðC¯^²òƒ”£¼^‘¥Wþ À6( endstream endobj 1556 0 obj << /Type /ObjStm /N 100 /First 884 /Length 1754 /Filter /FlateDecode >> stream xÚ}˜ÝŠ7…ïç)ú‚%ÕÔ` &«BÈ øÂ†€ã„<~ަ§»ºäÚºØÝîéTé;RŸ™éT·²õÖ·®øC[¥±õºoMe#üôZ7ÚëÖKßX1´Ð&L›îû¦«»ÌŸ‡Î1½n:ú6¯Úv›ö}«¥\.†â¢nµjÛrµ¡¦ê¬]ySÁ`Ú1X0˜U*,ŒÁŒÁZ1Å«Œ! î; ƒ‡à%,¨î¬©=‹bUíYt.ëYkj(úP,¬=‹beíY´´­¡è&ûƒ6pÁ[Sf\”­õ‚— Õúh¸ÀàYT:Ï¢Òy#}H/¸À@Í¢‚š+L *ÌèEƒ¹w@Å`‘Iƒµí›`M®uEB<: ƒ÷ÙXk—A á‚%qí(Šõsƒ‚¾™p'X s“±@f8ÅX ‹Òƒ±@žD}s/<ê¼Ãà1a¼£q¼-š3f½)?YçŒI¤ácœñƒ±@áÙ (Oy,P&žD¿ø‰o6QéØŽÜ&ëc°@yÞ—ÙÏô Ûå1Eá+aÝ0¸)šGßJŒ»wl”6·.¶"ãŽöªtÂ6,þ… î A  …Á}îÛÞ >75+N@¤ØÞµ`°à TL¥yHîZ°ågᦱ¸@" L/w¥þxÿþñî÷/ßþøŒ) §ï·Ç»ÙË_?}ýüç·çY<î¿~þ÷y@Ÿw¿|þ¯a³Ï»îZØ«—–ÄZ¯Ù5šÍ6»ºÙ[Õ:™gû¦%¯±?}ùú÷7kóçOóŽ_w?þõt~еd¹JÖîJ^EŽÅ×h¹Ø×lJg×{Ãu´Ì†²gZÅ->쫘 ER-gJL)fJ©‰Ö|ß´^].ZmÝ{ª%N‹¾×Ò]M‹R­;û³ËEëb¯ã ö¯ÙmÃfK¶mÙÑ>ım¯6mûÚ"ç¶kI¶’‹)|?³3õ‚†Í†ÞÓÙwf’~¯Õ͆N©–³!ì«› ºgZêLé‘)j¦¨¤Zä´j¤e´5¥-÷#pv¹h{IÙ‹c¯{1ö’²Ç^"öbì9eÏŽ½DìÙØóì_³#Úl´9Kùîè¾;»;N|OsëžÇ oŽ–šæ yW®*Çê£ÔS2ˆÒÙΊ|°4Ö%-—Æq_–ƺ¤ñªå\‰²Y-›uÉæE«º3Т3`¡«5¥]ÝhѰ֚²w¬Q«E°–”½‹`"X-‚õLÐ7´šÓŠØŸ)=µJ¬uÌŽBWÎ\þˆKÍΓ8-öñt¶yœ'ÚÓó$g~ÏšÍw,Ãáu\¯Ùg¶Æ³‡Þ:ž’¾×: S‹S­æ´¢¾™VÉ´úäÙ¥×êæJ×T‹V‹´ŒvOi÷r×zu¹h{MÙ«cß#öjì5e¯Ž}Ø«±×7س5¢-F[Ò3@Në<]ǸÚ|žs»g`_Kš)²˜Bnñ-WÌÙlv6Hd› Ì©–³!ì‹Í.±Ö1›#Èl Ô†âWåm`gƒxx-i6ÐbÃYäå|´\2ÚÈf7×0E64³¡qªålûjfC+™Vu¦´È”j¦TMµÜ“¨EO¢j´kJ»º'QžDÕØ—7Ø¿fG´‹Ñ^ò×o5öB~«m[üVëkI3e‰é«ÈQr–ËÓ¼Äô:ÛÙPZ¤%¦ÕR­» oôuÙÀK{tÅ/[üòHm ·!Nƒ®6η"ñ‰gKi^Rú*r”Œò-—yÉåuöÝŽr™-—yÉåUËÙ÷e6,¹¼h¹\æ(—Ùr™—\^µšÓ*‘–ÑÖ”¶KiŽRš-¥YRöâØkÄÞâ—%e/޽Dì-Œ™Sö.Œ9 c¶0fNÙ»0æ(ŒÙ˜9e£hf‹f¦”=9ö±·Ìezƒý1›"Ú–¹¼dîòÈpÙv=Œ^Œ³Íç#ã<ž×çZjZ6ó’ÍW•£f”zliÌK/=FùË–¿\³7Euw»ë$ùZo»¿): ëmkI3h‰é«È q´í,˜y æe¶‹iŽbš-¦¹PªUVÔW¹>¾Ó¾'Z´ß?¾Ÿ]:-Ú‡iIªEN«FZlZ5Ó÷¯NÎ.­ë«=Õ§|uBCM‹R­;û³ËEËØ÷”}ïN+øê„º±ï)ûNN+bß}OÙ«cß#öÝØkÊ^û±Wc¯){uì5b¯Æ^RöâØkÄ^Œ½¤ìű׈½{IÙ³c/{1öœ²gÇ^"ölì9eÏŽ=GìÙØSÊž{ŽØ“±§”=9ö±'cO)ûæØÓÅþ1ŸÍ endstream endobj 1557 0 obj << /Type /ObjStm /N 100 /First 876 /Length 2269 /Filter /FlateDecode >> stream xÚ…šÁŽ$· †ïó:º»–HŠ’Ã€‘ §À’Ü&Æ `wÌŒƒ<~(VW³¤b«]LOSúI~TÕ_Ý»-Ä€Caù‘BÊ0•På'Œ$[À*Ëb”e]L!#h%p¬òWz‚ÚD&¨9Tdù™BK²®”Ъ¬+Rd À-¤D²’sHd)Kjh²6—eq–ÅD¢J­W%‹Iç&‹Is‘Å(‹ Éb”ÅDYúI-Êbi)5i ¤)ˆÒô®’´Ò@eéKúŒ=.Òô$­%MÓH^@/.мììIS•4=iê;{ÒÔÓô¤©(~JÒö¤IºÅž4qÇ,0SØ“*éž4‰jR’ÅY€v4˜%Ȳ5I ŠrŸK¨Ic•¢’ Á&y’p¤([“„©ÿ–D‹zIdY#URO*ýå$H9á“QÏ(ˆå}) p?TäMéŒji2ç@-Ç ùry©-Gy_$E¦i*“ô$GDË:’šäOÎR—4”³Œ_úÉŒÂ_Ö±œ#é&—>vYW%¯TŸ[Ÿª¬k¥>õL½†À©ôóå 1y%EQR°R÷#‘å4qíG*3„;É ‚¾’v2é:9÷¥Ð“¼bi9öÆ%T9 ýUÜsHÁ•[¯ÄP[Ÿy.ýpë‘”ºrW–ÞréÊ…B+š·äN=ÁÓ?<}ÿ÷×//2ö(WÝ_Ÿ¾ÿI8”íå_žß^¾}èÁÛ~{ù¯^˜úÛÏ/ÿûm¨¿ýøãQ Ø´p©•ŽZz៴কÚJ+•A+;Z©šV^já •<-2­´ÒŠí¨u­rÒ2öqÉ>ì“Ç>û¸döÑcoì¡­Ø÷»îQËa­šV^j ìcò´È´VìûÝß´ö*'­{¨e©•-‡=T6-\jÙïUNZƾ,Ù—2hy싱/Kö-}1öeÉžöÅc_Œ=/ÙóÀ¾xìÙØó’=ìÙcÏÆ>/Ùç={ì³±ÏKöy`Ïûlìó’= ì³Ç>{Z²§}öØ“±§%{ؓǞŒ=.ÙãÀž<öhìqÉöä±GcKö0°G½y-,½¯ÏkÁ¼–^ ƒ×‚çµ`^ K¯…ÁkÁóZ0¯…¥×Âàµày-˜×ÂÒkaðZð¼Ìkaéµ0x-x^ æµ°[å­xÔò¼v;þIöV_ëºÛ¡vî»iܽçÖJúS·iÑ>¤?½¾½X™~~ïÁëÒ?üö»è|Ê4çD˧œtÌY«SñnͲ{·Í;»÷OŽV6-Xjçp§.›C©+­Âƒ9ZŦRh©ƒVô´ŒvYÒæzÔºV9j±±ç%{Ø={^²ç={ìÙØç%û<°g}6öyÉ>ìÙcŸ}^²§}öØ“±§%{Øg={Z²§=yìÉØã’=ìÉcÆ—ìq`O{4ö¸d{ô؃±‡%{أnj=,ÙÃÀ<ö`ìÓ’}؃Ç>û´dŸöà±OÆ>-ÙÇ}òØGcï°¿îöhG£=;íà­7¯ÜíV˜:Ún»£ÌYns˜,x¿®µÛý†±§ CŠZGÓLeLa>=Ùt9>/x&gn;™í°³Ñœ9˜íN®;Ê'âUcŽ;î(sç¶f¶“׿êy«Yëä¬ût´€2Œªoöƒ²?ÝLò7Ú“Ùòƒ yNkF;ùl>bòüËlurÕqç°g©æ¨“¡¥{˜fðÖô˜€÷ç+`œäo€'G-t¿¸7ý½hÕ/¼Ò7›]6ß>†w‚ƒË¶8<ïn7½”ýæ1å»Íkvâ1ß±;χ͆'>ÞãçO¢˜®ß?ü"½~}ý˜Š8møe®x\]q›Ñ¼{hûviQmôÊ=T«âËöÏŽN¨\úÃy™;ÕzÚö ½ªºí”’M”mWÊ)ÖAƒk]²™ä·ß¿þS†ö)}Þ¦ãD¢†ôW/çm^ÇÚ±›S“Rqc ±êÆPc~}ò”£ËKN“SÅçÚd•7mѺ±ª1rcMcÙ‹)té€ßüzÑ%_œºXv¦3Aw&:rG¢9\§‡%—¨ÝîS÷;ìÅžö=hP§Aî4tä c»¬ˆÑ é0 ¸1ÝWü}:àíùú~‹óõ¨û–-¦ëÅSüFtR…Ý˜Žª7¦³*Õé°JscÊ{ûlp¯Ë­âóÆGmê¼jòÒ*[&7¤óªî¼¶Ó_ÝyjV_S‰oÛ]¶‹³ñA— «îÀ@VݬºXu:°ݘoiÝf¯ø¼ñA› Ý#‹:°æ u`ÍêÀš;0Ô|Íϧį»<ŸXÙ÷¨IWsç…:¯æÎ u^ÍöyåèÎ ›ÆÜ+D›äGMæËy߃&µGv{ÔÙmQ;d·C=ÅmPÏc9÷Ûs<8©SwðàY7U<_‹¨7pÜ?o°Í‰lO°çÆhËã#mnË-ó´kÕmå: µ|Ç/éZ~v"úüí<’žpIP]ô¥UΛÖmUmNº¦ÏìóÆÞ¹7æ Róö(¤v>‡¼©5\¶5y> endobj 1577 0 obj << /Type /ObjStm /N 68 /First 687 /Length 2888 /Filter /FlateDecode >> stream xÚ•ZËn%7Ýû+j¶Dê 4Lv“I‚y¬‚^¸#0Ð/Äîó÷ÃC•nJE]¹gcДJçðèA–êú˜Óæ6sÙÈWu Ä(nK)Àð[)†¸½Sož ºù7„+n>¹KþäoÄ’Ç*1|2¾+@’LjVuçaùbF¥rÁx•7ªE[ÃÆ>c”7挚6ޱ†<Æ9j¿²qUîU‚ðÌ ä¶À”`ù-D_`Ѳ÷°x Õi¿°EWµ_Ü"•Ò‚‰¡ø±òSÖ~e“øÔª›<…~^”"E“ASP O¢bKž·”!IòaK5UXqË>XiËÌGòyË‘t”²åì §ÌHVí¹­¸L°d:(¢•h+!8X¼ü+VÉÂ%Š[u:ž„_)’`PÞj(Ú¯l5;¹n"3Æ“I©•À…¸s2±üq> 'Bt08ËÇÍ+º „X¢t1± ±4¹”AŽE—³vÁ]Q1ÀÛUõu…Å‘éeÉÀ4OI;˜%ß¼~}s÷Ãý‡‡§íçoÞ?=üòáÝÃﯾͯüíæY&ÍmÿÜŽ-¥µÈJ;·Tm nÚ⴩Κô)?}Š´É£éíÍÝ÷ŸgDÍSooÞ¼¹št`–fˆA›xÖµ)Ìš’6ÅYSÖ¦4k*Ú”Ñ5®æ±«:n™@ªžÎÏZt‚ül‚H'ˆfD: MT‘‰ÖÑÕ[ûØ::Ò¢Ù ‘ÎÍfˆt†h6C¤3D³"!ʳ&™Ê2|zøåùñÓÇ¿àTá³K&¿zãoHó= ¸±«¡zhÈ-¨.&²~]Q=”ƒ_WJ×þàÞ„édª¤ü>ÓiýW1dŒV ˆÌu¨6„ ·¡„ÝЇøÑõÑ9Úsd4ö_Ðǽá‰3>zËS”Žd)E¸m n# ÎüÈ|==÷‡þ«Dè,¥ ·¡„•åLXÈ1¢È1mbZ¬Í CÿœÓ1Y¢P:YJPÚîä‹hW:rF,Fä ¼V_ @sÇД¶{Õhd“h·2J²KY%õjéà–±“w þõöÔÁ‡}òæPA®I½Š8¸EéÔ놃;Ãm™ŠÒ©—w…;\@ÐÐN?oPpÀ0ü”†h€ÒÑ P:m$‹å:ÿ‘~Kú:E„N–(„Ζ(„Ζ(„ÎFdo¿8AAiè¾ ¡s1ô#t®†g„ÎÕðŒÐ¹Zž¢cvvìwºÎœÆþ«Dèì-Ó ·eZà¶LEèl׿Ef·'¡—y±Aiì¿àŸ/)ԔδQšR:ÓFeJyA{ÈHqu¢ š¥|¦b–Š¡ e‹¡e‹¡eëyQ‘R½^ Ç!ÅU"BËîL/"çm‰–ÍVCÙÇîL;éamÞBP‰²¿.x2PZ% T¯lv ŠW6;µ«½CCÁÇd(Š´l6*Pæë‚§!õ¤Õ»+*ßóq€š•ÍDÉÊf¢Ìc³Qyr<ÓFáÉñ:íá<É«WVÔªl6 JU6› •*›Í†ò޳aa;A¥uý²,¯ª«Å •ÍþCÊfÿ¡>e³ÿPÓ±Ù(3¹Ö¸{u×ßñÊçË*Ï£2Ý?ÄÁ½«;/Ô¥Á\g¢˜ æÚEçy_£¸Ü?ÎiY¾®²< ÒÀg*¨GXQ6˜KLÔpÁÜa¢ª æ²Eeˆ××I²|]ey¢!ŠP6ŠPöt{ùÊëO üÙ‰c°žºU¯³>lJí|µ4ê2‡g9¦â"§ÚéUÐoªgÎA»ž^ÅËð^+•¡·eýåÝA,•«ƒ[-—·Ç±EÓõ´EóŠiá†ã´¯ðöm°´­Bã6¿y©¡yÇó&Ü©].iNm¸X»\+œÚ]yN™¸EŒ;ÝɃë Iï;óåVïÔ†—ÀÉLiÞ£Ÿ·áu°ÌõÑ4W‰ý1Í„¯}ð¥(q]¾æŒM8’Êœ¾L¸y“~š+ƒ/qÞ„Ï õ¥'ê¿?þ*ÏÈËiËpbÔÝØ³ºß¿ïÀ n˜Ãõ»O¿|û¯çûߟÇ_x÷*[¬½2ðý[©¹ý¸:Ž_Nˆþø¡º#æKî±äËþIŒÎ*¯~("ãÇ1ÍäÌ= ÜãÈ]×Ò¹”ΥЋ¿g3¿Lé@¥Ó/~颖ΡtQKyáÇ%€Î»vÞµó®]ÃÚIÔð5_ÂÑ:Lí¬kg];ëÚ%Ü3•þâôk?x/Ìw¬þuM¥ºÜÐØôÕwÛÇ›± Vîã”nô(ö ú“ÖÝ ¯½Å v[%߃ð=߃Ø_ˆô°»Q¾òõ¶L€zÔ# uE©“¡ðBá{5v8êQP‚òÿŸ»êdà>%Ôâ÷€¸ÄË3o¹¸÷ï›í‡¼»ÑCá>!ÜÉp}iq¿Xèa¿$}u ïž¤·ß îý÷??lw»¾ÿé·›»Ÿdk?!cht7w?~y~ÿø±¹bsµô©?4ÖÿñÈ?>ýúp÷Ÿ§‡Þ]ßß¿{xÿôúõÝ_><ýìÄøéûæWëQ,jLÞMءو/O%µH¬¬‹UÔ bUµ"Fo@ fCÊ0TÙ *̆$õ˜Ø;”2l`h¾Áyàùèè¤&íÁ”¨*5TXj°¸Ôp ¸Ôp ¸´ \j¸\j¸\j¸\n¸\n¸\n¸\n¸\n¸ \n¸ \n¸ \Þå.7\.7\nh¸ ÜÐp¸¡á2pßÊ»ûñóÃÇ¿êjÜ*]ÖÝÿ^x¬ endstream endobj 1646 0 obj << /Type /XRef /Index [0 1647] /Size 1647 /W [1 3 1] /Root 1644 0 R /Info 1645 0 R /ID [<9C9EAB7B8074A87214AAF415F1EABA50> <9C9EAB7B8074A87214AAF415F1EABA50>] /Length 3814 /Filter /FlateDecode >> stream xÚ%ÙIˆ%_Zð¸™YseeUVdÍcVUVEÍsUÖ°Ÿv‚v=ÒNÒΠ‡ÀaÚ)ÚñC…7–xƒw[ao…½_JÛ‚ {+ì}4¾o…½ö>À&TØ[aï#Ä2Ó {+ìý*ÚTØ[aï§iòÖ {+ìýjÚ:TØ[aï£Èj+ì­°÷3´5¨°·ÂÞ¯¡Éy ìýZ‰Æ{¿‘ÿ-~\ØûhÓh\¸°÷›iñ“qÓÂÞo¡­DcÁ„½ßJ‹Ëµâ­°÷ÑdV ªÕ {M&nU¥[aï£É,G¹¤ö~ŽËÄa­°÷»iKQîl…½&KÌÙ­°÷m1*­°÷Ñx¢<Õ {g!*­°÷‡hQZIn…½&3 {+ì}4™°…°·ÂÞG“C…½ö>šLXJØ[aïïÑ TØ[aïG)[X†'…½öþí3*ì­°÷iüÜ {+ìýSÚGTØ[aïŸÑd¡öVØûç´÷¨°·ÂÞk¥µÂÞ {¯y”oQao…½×DÇÁM)  ÀBšRÞGÅ4Í£¼‡.KišGY£ËÀršæQÞE'Á šæQÞA§ÀJšÝCy]¦ivå-t5(išGykhšGy] ÖÑ4ò:ºl iå5t#ØDÓ<Ê«èf°…f—Q^A·‚m4»Œò2ºÌÒÖÓ.¡;ÀNšP^Dw9šP^@wƒ=4  Ïiv-åú¼¤]¥…^×´k´èð–vöyÞÓìxÊYôˆ°ß¤…õ>û-Ú6tö±w£°/ÿÉûmol¥%a¿CÛ‚Žƒ{tàÍèa¼ ]"ìÑ7¢K@„ýmº DØÓÖ£“ Âþ„¶öèÞkÑU Âx ºDأϠöèÀ%_aÿ-~<Âx °GŽŸŒ›Ž°G^‰Æ‚EØ£ÇånöèÀ+Ð@ØëèÀq«»€°×¶oårt7öÚö­Œeª€°×Ñ|—¢û€°×Ñ|c‰a¯£ù.Fa¯£ùFyŽa¯£ù.Da¯£ùFiOa¯£ùN §€°×Ñ|Ãó@Øëh¾cèY ìµ£[–:„½Žæ[ °×Š7 O^Â^ÏÐ>£W°× :ü„^Â^¯¥}Doa¯yø½ „½^O{ÞÂ^+üðz{­È÷è ìõVÚôöZᇯÑ'@Øëí´Wè3 ì53 _¢/€°×;hAãÃÂ^3Èð9?$ì53 ã«â"…½®hOѸAa¯d—‹#ìõ>Úct´°ãs£°o˜öšs†h {}€öÂ^sÓðº{Í9Ãûè" ìõ1Ú=t öš›†5º {}‚vÂ^sØð:„½>E»®Â^sÝðºðKÍaÛè àú<íºðAÍuÃëèz æõEÚ5t#öš‡WÑÍ€êË´+èV æ5w/£ÛúÖWi—Ð Â~“vÝ"ìÜ9¼€îöÛ´óh"ì;<‡îö´³èaB;ƒvNΣG@„놧Ñc ÂþŠv =”"GHN¢ñ†eÏQßøH|iüÚÙTLÿš ÐÜåT¬üZ¼Š[E {隸­¸™¸Ë¸ýXç(@T&JµŒ"GõÃá—0R8,¬ž ³†‹ƒ'à)xžƒà%x^ƒ7à-xÞƒà#ø>™ébÄ%$Ýn0z·°wY¸’Š™oÆÿXণµÄrn‘î¹TìýøÈr Ý ÀüÝJÀòÝ4`ô®ìÝ­LÝ­¬Üm ÜmlÛmÌÚm,ÚÍÆìvvìæv{€u{Ãuû›usu‡KuG#uÇotLÓ1MÇf«t¬Òql§úêwªß±J'.uÖqDÇGtÑqDÇGtÑqDÇGtÑqDÇGtÑqDÇGt,б@Ç t Ò-NÅì®( ÓtlѱEÇ[tlѱE7²ÅDcÒ$`ÒŒƒf0îhCŽf 0Úh–fc4SÀð¢YŒ,šÕÀ ¢™ÆÍZ`(ѬFÍF`ÑlÆÍV`ØÐlF Í`°ÐìÆ Ín`ˆÐTÀè Ù š¨ùËTÌýLÜêT̯Œ š#Àp 9ŒšÀ  9ÿ›yàÐßœŽúÍyà€ß\ŽõÍeà0ß\ŽðÍuààÞÜŽëÍmàÞÜŽæÍ=à@Þ<ŽáÍ#àðÝ<ŽÜÍ3à Ý¼Ž×Í+àPݼŽÒÍ;àÝ|fdÍ'`2Ö¨þÀB TÓnÕÄŒ[õQøC©8þÝøÜÂTüòõxÅEŒ wu©¿ÄC5ø{9Œ FLÕw0Š»ßŠÿÆ >ÄÞgˆ©®š6¤âÉ·ãsÌ0PßÁ–T<ýãÐâKc,|ŒºÏû?7T ðƒø*…ÄX8汇SñµùøœJe0ê÷_þUhñW7~ÍZ ®¥â{ã %¨Ö þÈÅx[É1DU²AŒN•lS%ĘTÉ1U²AŒD•l`5t}},ñûTüÜíøµ©ø§ÿ©¤0õ Rñí© 1m—~E*~ýËФ¬ƒ~]*~ëqhÖ¹‘ÙÎTüî²Ð¬_ï¦û}©øÎÖÐb‚æïê0^Å`ëh*þä÷ã] è]}?ŸŠöWB‹9Ë÷1Ýbù>fZ,ßÇ$‹åû˜_YÝ>¦VÖ¯Y•EìcBeýú˜KY¿>¦Q–½œŠ¿ýø5;€YvìG‹ó÷—BûÒþ_ŒƒÝKÅ¿üXÐqàïÑìÊTüû†¶ Ä h#°ÚÎú>;rÓÿc|Ž›fíf÷¤âû?š½èlá§´àë¡ÙæÍÞSšü›ÐâDÿ Ô)•?Ú=ðÛgFÊv¾Ù±%Ûïf&ÌÌ•"[œ|$¥¯¾W`[›íc3Ã娽:9䨳*hv,ÍJ–5­ìl™5ÛýgžÌö÷Ù†>ÛÁgåÎתf;ø¬›e{ôlSžU:«tVé¬ÒÙÎ<³E~¢Òr™mÅ3d,+w¶ÏöÞYá³Âcu>+|Vø¬ðYá³Âg…iâ·ÿ,þt¨þPõ‡ª?Týaì;U¨úÃÅ)ý§øð’”þü7ãÕÒ4¶ô‹xµ,Ú¯–§±Wÿ¯&ÓØþO¼Z‘Æþù¿âÕT_4ˆW+Óxõ ^­Jãw'âÕtÿæ÷âÕê4þ«ãU™Æ»¯âÕLš˜ú¥xµ&M|q?^­M?1¯âQ‹Se3ާ‰ïüüè.ÿèïâI—ùЌЌ ÐŒýŒ ýL<Ï‹xñÄ.ÑÅ3¹x÷¼@<„‹§nñ˜Í`¦ýÆ_þ€ß¨Ì_*—ʲÊ@¼2ü® \*–ÊH¥2C© M*S’ÊX¤2© >*“ŽÊ­Ê¬ÊÔ¹2é¨Ì•+ƒäÊä¸2*®Ì†+ÃàÊc§Ês¦Êƒ¥Ê“¤Ê££Ê³¢Êl¸2 ®L+ãÞÊ|·2ЭLp+Ï{*x*Ot*ÜÊȶ2£­ e+SØÊص2g­ V+“ÔÊÃâʃ›Ê“šÊ$µ2:­ÌJ+ÃÑÊ4´2þ¬Ì;+ÎÊ3ÜÊCÛÊSÚÊ€³2Ѭ®¦‰¿Ø9ZØïm.þíjG endstream endobj startxref 316957 %%EOF cqueues-rel-20161214/doc/cqueues.tex000066400000000000000000002404241302435770500171630ustar00rootroot00000000000000\documentclass[11pt, oneside]{memoir} \usepackage{fullpage} \usepackage{xspace} \usepackage{makeidx} \usepackage{listings} \usepackage{multicol} \usepackage{graphicx} \usepackage[colorlinks=true, linkcolor=blue]{hyperref} \setlength{\parindent}{0pt} \nonzeroparskip % add padding to ctabular tables \renewcommand{\arraystretch}{1.2} \makeindex % % COMMANDS % \newcommand*{\cqueues}[0]{\texttt{cqueues}\xspace} \newcommand*{\key}[1]{#1\index{#1}\xspace} \newcommand*{\syscall}[1]{\texttt{#1}\xspace} \newcommand*{\routine}[1]{\texttt{#1}\xspace} \newcommand*{\fn}[1]{\texttt{#1}\xspace} \newcommand*{\method}[1]{\texttt{#1}\xspace} \newcommand*{\module}[1]{\texttt{#1}\xspace} \newcommand*{\errno}[1]{\texttt{#1}\xspace} \newcommand*{\crlf}[0]{$\backslash$r$\backslash$n\xspace} \newcommand*{\lf}[0]{$\backslash$n\xspace} \newcommand*{\true}[0]{\texttt{true}\xspace} \newcommand*{\false}[0]{\texttt{false}\xspace} \newcommand*{\nil}[0]{\texttt{nil}\xspace} % % ENVIRONMENTS % \lstdefinelanguage{lua}{ morekeywords={break,goto,do,end,while,repeat,until,if,then,elseif,else,for,in,function,local,nil,false,true,and,or,not}, sensitive=true, morestring=[b]" } \lstnewenvironment{code}[1]{ \lstset{language=#1} }{ } \lstnewenvironment{example}[1]{ \lstset{language=#1,numbers=left,numberstyle=\tiny,stepnumber=2,tabsize=4} \ttfamily\small }{ } \newcounter{toccols} \setcounter{toccols}{2} \newenvironment{Module}[1]{ \subsection{\texttt{#1}} \addtocontents{toc}{ \protect\begin{multicols}{\value{toccols}} %\renewcommand*{\cftsubsubsectiondotsep}{\cftnodots}% } }{ \addtocontents{toc}{\protect\end{multicols}} } \lstdefinelanguage{lua}{morekeywords={break,goto,do,end,while,repeat,until,if,then,elseif,else,for,in,function,local,nil,false,true,and,or,not},sensitive=true,morestring=[b]"} \begin{document} %\pagestyle{empty} \title{ \HUGE\sffamily The \cqueues User Guide \\ %\vspace*{20pt} %\hrule \vspace*{10pt} \LARGE for composing \\ \vspace*{10pt} \HUGE Socket, Signal, Thread, \& File Change Messaging \\ %\vspace*{20pt} %\hrule \vspace*{10pt} \LARGE on \\ \vspace*{10pt} \HUGE Linux, OS X, Solaris, \\ FreeBSD, NetBSD, \& OpenBSD \vspace*{10pt} \LARGE with \\ \vspace*{10pt} % ps2pdf -dEPSCrop lua.ps {\includegraphics[scale=0.10]{art/lua.pdf}} \vspace*{30pt} \hrule } \date{\today} \author{William Ahern} %\setlength{\droptitle}{85pt} \maketitle \thispagestyle{empty} \clearpage \maxtocdepth{subsubsection} \setsecnumdepth{subsection} \setcounter{page}{1} \pagenumbering{roman} \tableofcontents \clearpage \setcounter{page}{1} \pagenumbering{arabic} \chapterstyle{section} \chapter{Dependencies} \section{Operating Systems} \cqueues heavily relies on a modern POSIX environment. But the fundamental premise is to build on the new but non-standard polling facilities provided by contemporary Unix environments. Specifically, BSD \syscall{kqueue}, Linux \syscall{epoll}, and Solaris Event Ports. \cqueues should work on recent versions of Linux, OS X, Solaris, NetBSD, FreeBSD, OpenBSD, and derivatives. The only other possible candidate is AIX, if and when support for AIX's \syscall{pollset} interface is added to the embedded ``kpoll'' library. \subsection{$\lnot$ Microsoft Windows} Microsoft Windows support is basically out of the question\footnote{I have been toying with the idea of using an fd\_set in-place of a pollable descriptor on Windows, and taking the union of all fd\_sets when polling.}, for far too many reasons to put here. Aside from the more technical reasons, Windows I/O and networking programming interfaces have a fundamentally different character than on Unix. Unix historically relies on readiness polling, while Windows uses event completion callbacks. There are strengths and weaknesses to each approach. Trying to paper over the chasm between the two approaches invariably results in a framework with the strengths of neither and the weaknesses of both. The purpose of \cqueues is to leverage the strengths of polling as well as address the weaknesses. \section{Libraries} \subsection{LuaJIT, Lua 5.2, Lua 5.3} \cqueues principally targets Lua 5.2 and above. Some semantics are not fully portable to Lua 5.1 due to 5.1 not supporting yielding from metamethods and iterators. LuaJIT does not have this handicap. \subsection{OpenSSL} The \cqueues \module{socket} module provides seamless SSL/TLS support using OpenSSL. Comprehensive bindings for certificate and key management are provided in the \href{http://25thandClement.com/~william/projects/luaossl.html}{companion \module{openssl} module, \texttt{luaossl}}. \subsection{pthreads} \cqueues provides an optional threading module, using POSIX threads.\footnote{Building without threading enabled is not well tested.} Internally it consistently uses thread-safe routines when built with either the \_REENTRANT or \_THREAD\_SAFE feature macros, such as \syscall{pthread\_sigmask} instead of \syscall{sigprocmask}. Thread support is enabled by default. \paragraph{Linking} Note that on some systems, such as NetBSD and FreeBSD, the loading application must be linked against pthreads (using -lpthread or -pthread). It is not enough for the \cqueues module to pull in the dependency at load time. In particular, if using the stock Lua interpreter, it must have been linked against pthreads at build time. Add the appropriate linker flag to MYLIBS in lua-5.2.x/src/Makefile. \paragraph{OpenBSD} OpenBSD 5.1 threading is completely \emph{fubar}, especially with regard to signals, because of OpenBSD's transition to kernel threading. If using OpenBSD, be sure to compile \emph{without} the thread-safe macros predefined, especially if using \module{cqueues.signal}. \section{Compilers} The source code is mostly ISO C99 compliant, and even more so with regards to ISO C11. But regardless of standards conformance, it aims to build cleanly with the native compiler for each targeted platform. It currently builds with recent versions of GCC, clang, and SunPro. Patches are welcome to silence compiler diagnostics. \section{GNU Make} The Makefile requires GNU Make, usually installed as gmake on platforms other than Linux or OS X. The actual \texttt{Makefile} proxies to \texttt{GNUmakefile}. As long as \texttt{gmake} is installed on non-GNU systems you can invoke your system's \texttt{make}. \chapter{Installation} Refer to the included \texttt{README.md} for up-to-date build and installation instructions. \chapter{Usage} \section{Conventions} \subsection{Polling} \cqueues works through a simple protocol. When a coroutine yields to its parent \cqueues controller, it can pass one or more objects. These objects are introspected for three methods: \method{:pollfd}, \method{:events}, and \method{:timeout}. These methods generate the parameters for installing descriptor and timeout events. When one of these events fires, \cqueues will resume the coroutine, passing the relevant objects which were interested in the triggered event. It's analogous to calling Unix \syscall{poll}, and in fact the routine \routine{cqueues.poll} is provided as a wrapper for \routine{coroutine.yield}.\footnote{This wrapper can also detect if the current coroutine was resumed by a controller, and if not chain yield calls---with the cooperation of a \routine{cqueues.resume}---until a controller is reached.} \subsubsection[\method{object:pollfd}]{\method{:pollfd()}} The \method{:pollfd} method should return a descriptor integer or nil. This descriptor must remain in existence until the owner object is garbage collected, \routine{cqueues.cancel} is used, the coroutine executes one additional yield/resume cycle (so the old descriptor is expired from the descriptor queue), or until after the coroutine exits. If the descriptor is closed prematurely, the kernel will remove it from the internal descriptor queue, bringing it out of sync with the controller, and probably causing \method{cqueues:step} to return EBADF or ENOENT errors. Alternatively, \method{:pollfd} may return a condition variable object, or the member field may itself be a condition variable instead of a function. Similarly, the \texttt{.pollfd} member field may be an integer descriptor. This permits user code to create \textit{ad hoc} pollable objects. \subsubsection[\method{object:events}]{\method{:events()}} The \method{:events} method should return a string or nil. \cqueues searches the string for the flags `r' and `w', which describe the events to associate with the descriptor---respectively, \texttt{POLLIN} and \texttt{POLLOUT}. The flag `p' may also be specified, describing \texttt{POLLPRI}. However, \texttt{POLLPRI} is not supported for kqueue-based environments.\footnote{OS X's \texttt{EV\_OOBAND} is only useable as an output flag. DragonflyBSD's \texttt{EVFILT\_EXCEPT} maps well and will be supported in a future release.} Alternatively, the events may be a literal integer value of the logical-OR of the system event values \texttt{POLLIN}, \texttt{POLLOUT}, \texttt{POLLPRI}, etc. However, specifying any events beyond the three discussed is not currently supported and may lead to unexpected behavior. \subsubsection[\method{object:timeout}]{\method{:timeout()}} The \method{:timeout} should return a number or nil. This schedules an independent timeout event. To effect a simple one second timeout, you can do \begin{code}{perl} cqueues.poll({ timeout = function() return 1.0 end }) \end{code} which is equivalent to the shortcut \begin{code}{perl} cqueues.poll(1.0) \end{code} Instantiated \cqueues objects implement all three methods.\footnote{\method{:pollfd} returns the internal \syscall{kqueue}, \syscall{epoll}, or Ports descriptor; \method{:events} returns ``r''; and \method{:timeout} returns the time to the next internal timeout event.} In particular, this means that you can stack \cqueues, or poll on a \cqueues object using some other event loop library. Each \cqueues object is entirely self-contained, without any global state. \subsection{$\lnot$ Globals} Like the core controller module, other \cqueues modules adhere to a \emph{no global side effects} discipline. In particular, this means \begin{itemize} \item no global process variables; \item no signal handling gimmicks---like the pipe trick---which could conflict with other components of your application\footnote{The \module{cqueues.thread} module ensures threads are started with a filled signal mask.}; \item consistent use of thread-safe function variants; and \item consistent use of O\_CLOEXEC and similar flags to eliminate or reduce \syscall{fork} $+$ \syscall{exec} races in threaded applications. \end{itemize} \subsection{Errors} The usual behavior is for errors to be returned directly. But see \routine{socket.onerror}. If a routine is specified to return an object or string, nil is returned; if a boolean, false is returned. In both cases, these are usually followed by a numeric error code. Thus, if a routine is specified to return two values on success, then on error three values are returned, the first two nil or false, and the third an error code. \cqueues is a relatively low-level component library. In almost all cases errors will be system errors, returned as numeric error codes for easy and efficient comparison. For example, attempting to create a UNIX domain socket with \routine{socket.listen} in a directory without sufficient permissions might return `nil, \errno{EACCES}'. \subsubsection{\texttt{EAGAIN}} \cqueues modules are implemented in both C and Lua. The C routines never yield, and always return recoverable errors directly. Most C routines are wrapped---and methods interposed---with Lua functions. These Lua functions usually poll when \errno{EAGAIN} is encountered and retry the C routine on resumption. Few methods will return \errno{EAGAIN} directly. \subsubsection{\texttt{ETIMEDOUT}} This error value is usually seen when a timeout is specified by the caller of a logically synchronous method. The method will normally yield and poll if the operation cannot be completed immediately, but if the timeout expires then it will return a failure with \errno{ETIMEDOUT}. \subsubsection{\texttt{EPIPE}} In Unix \errno{EPIPE} is only encountered when attempting to write to a closed pipe or socket. In \cqueues \errno{EPIPE} is used to signal both EOF and a closed output stream.\footnote{In some situations, such as with SSL/TLS, a read attempt might require a write, anyhow. Expanding the scope of EPIPE simplifies the logic required to handle various I/O failures.} The low-level I/O method \method{socket:recv}, for example, returns \errno{EPIPE} on EOF. In other cases, as with \method{socket:read}, EOF is not an error condition. \subsubsection{\texttt{EBADF}} This error commonly occurs in asynchronous applications, which are especially prone to bugs related to their complex state management. With Lua code using the \cqueues APIs, \errno{EBADF} should never be encountered. When it does occur, it's a sure sign of a bug somewhere in the parent application or an extension module and---hopefully---not \cqueues. \subsubsection{The Future} The idiomatic protocol for returning errors in Lua is a string representation followed by the integer errno number. This is how Lua's \fn{io} and \fn{file} modules behave. The original concern was that this would be too wasteful for a networking library, where ``errors'' like EAGAIN, ETIMEDOUT, and EPIPE are common and not very exceptional. Copying even small strings into the Lua VM is somewhat costly. However, in the future the API may be configurable to use the Lua-idiomatic protocol by default, using upvalue memoization to minimize the cost of returning string representations. In the meantime, the auxiliary routines \fn{auxlib.assert} and \fn{auxlib.fileresult} can be used to explicitly achieve the idiomatic behavior. \section{Modules} \begin{Module}{\cqueues} \subsubsection{\routine{cqueues.VENDOR}} String describing the vendor, e.g.\ william@25thandClement.com. If you fork this project please change this string so I don't receive unwarranted scorn or praise. \subsubsection{\routine{cqueues.VERSION}} Number describing the running version, formatted as YYYYMMDD. Official releases are tagged in the git repo as rel-YYYYMMDD. \subsubsection{\routine{cqueues.COMMIT}} Git commit hash string of HEAD. \subsubsection[\routine{cqueues.type}]{\routine{cqueues.type(obj)}} Return the string ``controller'' if $obj$ is a controller object, or $nil$ otherwise. \subsubsection[\routine{cqueues.interpose}]{\routine{cqueues.interpose(name, function)}} Add or interpose a \cqueues controller class method. Returns the previous method, if any. \subsubsection[\routine{cqueues.monotime}]{\routine{cqueues.monotime()}} Return the system's monotonic clock time, usually clock\_gettime(CLOCK\_MONOTONIC). \subsubsection[\routine{cqueues.cancel}]{\routine{cqueues.cancel(fd)}} Cancels the specified descriptor, $fd$, for all controllers. If $fd$ is an object, the descriptor is obtained by calling the \method{:pollfd} method. Any coroutine polling on the canceled descriptor is placed on its controller's pending queue. To simplify error and exit paths in application code, canceling a descriptor that isn't installed is a no-op. Similarly, $fd$ may be -1. However, if $fd$ was installed with any controller but the descriptor has already been closed, then this is an error. Cancellation must be done before closing a descriptor\footnote{Unless the application knows the descriptor isn't currently installed. But note that \cqueues persists descriptor events for at least one yield/resume cycle. When \method{cqueues.poll} returns, for example, the descriptor is still installed. It won't be uninstalled until the coroutine yields again without requesting any events for the descriptor.}, otherwise controller state becomes corrupted. Closing a descriptor automatically removes the descriptor from the kernel's internal polling data structures, but not the user-land data structures. When the process then attempts to modify or remove the descriptor, the operation will fail with \texttt{EBADF}. Some event loops silently suppress such errors because it's very common for applications to close a descriptor before destroying an event handle. But such ordering issues aren't always so benign. If a new socket object was created between the close and cancel operations which happens to have the same descriptor number, then the controller erroneously believes the descriptor is already installed, or if not previously installed than the cancel stalls some other thread. Such bugs can be extremely difficult to track down; they're much easier to discover if the library bubbles up \texttt{EBADF} when canceling an already closed descriptor. \cqueues objects---controller, notify, resolver, signal, socket, etc---automatically call cancel before closing any descriptor. Normally only extension libraries and modules need to explicitly cancel descriptors. \subsubsection[\routine{cqueues.poll}]{\routine{cqueues.poll($\ldots$)}} Takes a series of objects obeying the polling protocol and yields control to the parent \cqueues controller. On an event the coroutine is resumed and \fn{.poll} returns the objects which polled ready. A number value is interpreted as a simple timeout, \emph{not} a file descriptor. This routine is intended to be behave much like POSIX \texttt{poll(2)}. Think of each object as a \texttt{struct pollfd} object. Then \begin{example}{lua} local ready = { assert(cqueues.poll(socket1, socket2, 10)) } for i=1,#ready do ... end \end{example} looks and behaves like \begin{example}{c} struct pollfd pollset[2]; pollset[0].fd = fd1; pollset[0].events = POLLIN; pollset[1].fd = fd2; pollset[1].events = POLLIN; int n = poll(pollset, 2, 10 * 1000); assert(n != -1); for (i = 0; i < n; i++) { ... } \end{example} \subsubsection[\routine{cqueues.sleep}]{\routine{cqueues.sleep(number)}} Yields to the parent \cqueues controller and schedules a wakeup for `number' seconds in the future. \subsubsection[\routine{cqueues.running}]{\routine{cqueues.running()}} Returns two values: the immediate controller currently executing, if any, or nil; and a boolean---true if the caller's coroutine is the same coroutine resumed by the controller. \subsubsection[\routine{cqueues.resume}]{\routine{cqueues.resume(co)}} See \fn{auxlib.resume}. \subsubsection[\routine{cqueues.wrap}]{\routine{cqueues.wrap(f)}} See \fn{auxlib.wrap}. \subsubsection[\routine{cqueues.new}]{\routine{cqueues.new()}} Create a new cqueues object. \subsubsection[\routine{cqueues:attach}]{\routine{cqueue:attach(coroutine)}} Attach and manage the specified coroutine. Returns the controller. \subsubsection[\routine{cqueues:wrap}]{\routine{cqueue:wrap(function)}} Execute function inside a new coroutine managed by the controller. Returns the controller. \subsubsection[\routine{cqueues:step}]{\routine{cqueue:step([timeout])}} Step once through the event queue. Unless the timeout is explicitly specified as \texttt{0}, or unless the current thread of execution is a \cqueues managed coroutine, \emph{it suspends the process indefinitely or for the specified timeout} until a descriptor event or timeout fires. Returns true on success. Otherwise returns false, an error message, and additional context: a numeric error code (possibly $nil$), a Lua thread object (possibly $nil$), an object that was polled (possibly $nil$), and an integer file descriptor (possibly $nil$). :step can be called again after errors. If embedding \cqueues within an existing application, the top-level :step invocation should always specify a 0 timeout. A controller is a pollable object, and the descriptor returned by the :pollfd method can be used with third-party event libraries, whether written in Lua, C, or some other language. Don't forget to also schedule a timeout using the value from :timeout. %[However, at the moment the controller cannot recover from synchronization errors with the kernel queue. In the future, internal, unrecoverable errors should be thrown and only coroutine errors returned directly.] \subsubsection[\routine{cqueues:loop}]{\routine{cqueue:loop([timeout])}} Invoke \method{cqueues:step} in a loop, exiting on error, timeout, or if the event queue is empty. Returns same values as \method{cqueues:step}. \subsubsection[\routine{cqueues:errors}]{\routine{cqueue:errors([timeout])}} Returns an iterator function over errors returned from \routine{cqueues:loop}. If \routine{cqueues:loop} returns successfully because of an empty event queue, or if the timeout expires, returns nothing, which terminates any for-loop. `timeout' is cumulative over the entire iteration, not simply passed as-is to each invocation of \routine{cqueues:loop}. \subsubsection[\routine{cqueues:empty}]{\routine{cqueue:empty()}} Returns true if there are no more descriptor or timeout events queued, false otherwise. \subsubsection[\routine{cqueues:count}]{\routine{cqueue:count()}} Returns a count of managed coroutines. \subsubsection[\routine{cqueues:cancel}]{\routine{cqueue:cancel(fd)}} Cancel the specified descriptor for that controller. See cqueues.cancel. \subsubsection[\routine{cqueues:pause}]{\routine{cqueue:pause(signal [, signal $\ldots$ ])}} A wrapper around \syscall{pselect} which \emph{suspends execution of the process} until the controller polls ready or a signal is delivered. This interface is provided as a very basic least common denominator for simple slave process controller loops and similar scenarios, where immediate response to signal delivery is required on platforms like Solaris without a proper signal polling primitive. (\routine{signal.listen} on Solaris merely periodically queries the pending set.) Much better alternatives are possible for Solaris, but require global process state or an LWP thread helper. \end{Module} \begin{Module}{cqueues.socket} The socket bindings provide built-in DNS, SSL/TLS, buffering, and line translation. DNS happens transparently, and SSL/TLS can be initiated with the \method{socket:starttls} method. The default I/O mode is ``tl''---text translation and line buffering. This makes sockets work intuitively with the most common protocols on the Internet, like SMTP and HTTP, which require CRLF and use line delimited framing. \subsubsection[\fn{socket[]}]{\fn{socket[]}} A table mapping socket related system identifier names to number codes, including AF\_UNSPEC, AF\_INET, AF\_INET6, AF\_UNIX, SOCK\_STREAM, and SOCK\_DGRAM. \subsubsection[\routine{socket.type}]{\routine{socket.type(obj)}} Return the string ``socket'' if $obj$ is a socket object, or $nil$ otherwise. \subsubsection[\fn{socket.interpose}]{\fn{socket.interpose(name, function)}} Add or interpose a socket class method. Returns the previous method, if any. \subsubsection[\fn{socket.connect}]{\fn{socket.connect(host, port [, family] [, type])}} Return a new socket immediately ready for reading or writing. DNS lookup and TCP connection handling are handled transparently. \subsubsection[\fn{socket.connect}]{\fn{socket.connect\{ $\ldots$ \}}} Like \fn{socket.connect} with list arguments, but takes a table of named arguments: \begin{ctabular}{r | c | p{4.5in}} field & type:default & description\\\hline .host & string:nil & IP address or host domain name \\ .port & string:nil & host port \\ .path & string:nil & UNIX domain socket path \\ .family & number & protocol family---AF\_INET (default), AF\_INET6, AF\_UNIX (default if .path specified)\\ .type & number & protocol type---SOCK\_STREAM (default) or SOCK\_DGRAM\\ .mode & string:nil & fchmod or chmod socket after creating UNIX domain socket %NOTE: There's a race between bind and the following chmod. fchmod is attempted before the bind, however it fails on BSD derivatives. Not all platforms obey UNIX domain socket permissions (e.g. Solaris). Check peer credentials, instead, to be portable. \\ .mask & string:nil & set and restore umask when binding UNIX domain sockets %NOTE: Not all platforms obey UNIX domain socket permissions. Check peer credentials, instead, to be portable. \\ .unlink & boolean:false & unlink socket path before binding \\ .reuseaddr & boolean:true & SO\_REUSEADDR socket option \\ .reuseport & boolean:false & SO\_REUSEPORT socket option \\ .nodelay & boolean:false & TCP\_NODELAY IP option \\ .nopush & boolean:false & TCP\_NOPUSH, TCP\_CORK, or equivalent IP option \\ .v6only & boolean:nil & enables or disables IPV6\_V6ONLY IPv6 option, otherwise the system default is left as-is \\ .oobinline & boolean:false & SO\_OOBLINE socket option \\ .nonblock & boolean:true & O\_NONBLOCK descriptor flag \\ .cloexec & boolean:true & O\_CLOEXEC descriptor flag \\ .nosigpipe & boolean:true & O\_NOSIGPIPE, SO\_NOSIGPIPE, MSG\_NOSIGNAL, or equivalent descriptor flag \\ .verify & boolean:false & require SSL certificate verification \\ .sendname & boolean:true & send connect host as TLS SNI host name \\ & string:nil & send specified string as TLS SNI host name \\ .time & boolean:true & track elapsed time for statistics \\ \end{ctabular} \subsubsection[\fn{socket.listen}]{\fn{socket.listen(host, port)}} Return a new socket immediately ready for accepting connections. \subsubsection[\fn{socket.listen}]{\fn{socket.listen\{ $\ldots$ \}}} Like \fn{socket.listen} with list arguments, but takes a table of named arguments. See also \fn{socket.connect\{\}}. \subsubsection[\fn{socket.pair}]{\fn{socket.pair([type])}} Returns two bound sockets. Type should be the system type number, e.g. \fn{socket.SOCK\_STREAM} or \fn{socket.SOCK\_DGRAM}. \subsubsection[\fn{socket.pair}]{\fn{socket.pair\{ $\ldots$ \}}} Like single argument form of \fn{socket.listen}, but takes a table of named arguments. See also \fn{socket.connect\{\}}. \subsubsection[\fn{socket.setvbuf}]{\fn{socket.setvbuf(mode [, size])}} Set the default output buffering mode for all new sockets. See \fn{socket:setvbuf}. \subsubsection[\fn{socket.setmode}]{\fn{socket.setmode([input] [, output])}} Set the default I/O modes for all new sockets. See \fn{socket:setmode}. \subsubsection[\fn{socket.setbufsiz}]{\fn{socket.setbufsiz([input] [, output])}} Set the default I/O buffer sizes for all new sockets. See \fn{socket:setbufsiz}. \subsubsection[\fn{socket.setmaxline}]{\fn{socket.setmaxline([input] [, output])}} Set the default I/O line-buffering limits for all new sockets. See \fn{socket:setmaxline}. \subsubsection[\fn{socket.settimeout}]{\fn{socket.settimeout([timeout])}} Set the default timeout for all new sockets. See \fn{socket:settimeout}. \subsubsection[\fn{socket.setmaxerrs}]{\fn{socket.setmaxerrs([which,][limit])}} Set the default error limit for all new sockets. See \fn{socket:setmaxerrs}. \subsubsection[\fn{socket.onerror}]{\fn{socket.onerror([function])}} Set the default error handler for all new sockets. See \fn{socket:onerror}. \subsubsection[\fn{socket:connect}]{\fn{socket:connect([timeout])}} Wait for connection establishment to succeed. You do not need to wait before proceeding to perform read or write calls, but waiting may ease diagnosing connection problems in your code and allows you to separate connect phase from I/O phase timeouts. \subsubsection[\fn{socket:listen}]{\fn{socket:listen([timeout])}} Wait for socket binding to succeed. You do not need to wait before proceeding to call \fn{:accept}, but waiting may ease diagnosing binding problems in your code and allows you to separate listen phase from accept phase timeouts. Socket binding may not occur immediately if you provided a host address that required DNS resolution over the network. This is uncommon for listening sockets but supported nonetheless; the symmetry simplifies internal code. Also, socket object instantiation with \fn{socket.listen} and \fn{socket.connect} only return errors regarding user data object construction; address lookup and binding errors are detected later, when initiated by subsequent method calls. \subsubsection[\fn{socket:accept}]{\fn{socket:accept([options] [, timeout])}} Wait for and return an incoming client socket on a listening object. Optionally takes a table of named arguments. See also \fn{socket.connect\{\}}. \subsubsection[\fn{socket:clients}]{\fn{socket:clients([options] [, timeout])}} Iterator over \method{socket:accept}: \texttt{for con in srv:clients() do ... end}. %\subsection[\fn{socket:certify}]{\fn{socket:certify(certificate)}} % Associate a certificate for subsequent :starttls operation. % [UNFINISHED] \subsubsection[\fn{socket:starttls}]{\fn{socket:starttls([context][, timeout])}} Place socket into TLS mode, optionally using the \module{openssl.ssl.context} object as the configuration prototype, and wait for the handshake to complete.\footnote{Prior to 2014-04-30, if no timeout was specified then the routine returned immediately.} Returns true on success, false and an error code on failure. \subsubsection[\fn{socket:checktls}]{\fn{socket:checktls()}} If in TLS mode, returns an \module{openssl.ssl} object, otherwise nil. If the openssl module cannot be loaded, returns nil and an error string. \subsubsection[\fn{socket:setvbuf}]{\fn{socket:setvbuf(mode [, size])}} Same as Lua \fn{file:setvbuf}. Analogous to ``n'', ``l'', and ``f'' mode flags. Returns the previous output mode and output buffer size. \subsubsection[\fn{socket:setmode}]{\fn{socket:setmode([input] [, output])}} Sets the the input and output buffering and translation modes, which mirror C's stdio semantics. Either mode can be nil or none, in which case the mode is left unchanged. A mode is specified as a string containing one or more of the following flags \begin{ctabular}{c | c | p{5in}} flag & default & description \\\hline t & default & text mode---input or output undergoes LF/CRLF translation \\ b & & binary mode---no LF/CRLF translation \\ n & & no output buffering---output buffer is always flushed completely after every write operation \\ l & default & line buffered output---output buffer is flushed up to and including the last LF after every write operation \\ f & & fully buffered output---all buffer-sized blocks are flushed after every write operation (see \fn{socket:setbufsiz} and \fn{socket:setvbuf}) \\ a & default & enable autoflush---every read operation attempts to flush the output buffer as-if an explicit unbuffered flush were performed with a 0-second timeout; when initiating SSL/TLS, all pending data is fully flushed before proceeding with negotiation \\ A & & disable autoflush \\ p & default & enable pushback---when initiating SSL/TLS, any data in the input buffer is virtually pushed back to the socket so that it will be processed as part of the SSL/TLS handshake \\ P & & disable pushback \\ \end{ctabular} Returns the previous input and output modes as fixed-sized strings. At present the first character is one of ``t'' or ``b'', and the second character one of ``n'', ``l'', ``f'', or ``-'' (for in the input mode). \subsubsection[\fn{socket:setbufsiz}]{\fn{socket:setbufsiz([input] [, output])}} Sets the input and output buffer size. Either size can be nil or none, in which case the size is left unchanged. These are not hard limits for SOCK\_STREAM sockets. The input buffer argument simply sets a minimum for input buffering, to reduce syscalls. The output buffer argument is the same as provided to \method{:setvbuf}, and effectively changes when flushing occurs for full- or line-buffered output modes. For SOCK\_DGRAM sockets, the input buffer sets a hard limit on the size of datagram messages. Any message over this size will be truncated, unless a previous block- or line-buffered read operation forced the buffer to be reallocated to a larger size. Returns the previous input and output buffer sizes, or throws an error if the buffers could not be reallocated. \subsubsection[\fn{socket:setmaxline}]{\fn{socket:setmaxline([input] [, output])}} Sets the maximum input and output length for line-buffered operations. Either size can be nil or none, in which case the size is left unchanged. These are hard limits. For line-buffered input operations, if a \lf character is not found within this limit then the data is processed as-if EOF was reached at this boundary. For line-buffered output, a chunk is always flushed at this boundary. Returns the previous input and output sizes. \subsubsection[\fn{socket:settimeout}]{\fn{socket:settimeout([timeout])}} Sets the default timeout period for I/O. If nil or none, then clears any default timeout. If a timeout is cleared, any operation which polls will wait indefinitely until completion or an error occurs. Sockets are instantiated without a default timeout. \subsubsection[\fn{socket:setmaxerrs}]{\fn{socket:setmaxerrs([which,] limit)}} Set the maximum number of times an error will be returned to a caller before throwing an error, instead. $which$ specifies which I/O channel limit to set---``r'' for the input channel, ``w'' for the output channel, or ``rw'' for both. $which$ defaults to ``rw''. $limit$ is an integer limit. The initial $limit$ is 100. Returns the previous channel limits in the order specified. Note that \fn{socket:clearerr} will clear the error counters as well as any errors. \paragraph{Unchecked error loops} The default error handler will throw on most errors. However, EPIPE and ETIMEDOUT are returned directly as they're common errors that normally need to be handled explicitly in correct applications. Furthermore, errors will be repeated until cleared. If errors were not repeated then unchecked transient errors could lead to difficult to detect loss of data bugs by giving the illusion of successful forward progress.\footnote{This is especially true of Lua's for-loop iterator pattern.} Code which loops and fails to check the success of I/O calls could enter an infinite loop which never yields to the controller and stalls the process. This is a fail-safe mechanism to catch such code. \subsubsection[\fn{socket:onerror}]{\fn{socket:onerror([function])}} Set the error handler. The error handler is passed four arguments: socket object, method name, error number, and stack level of caller. The handler is expected to either throw an error or return an error code---to be returned to the caller as part of the documented return interface. The default error handler returns \errno{EPIPE} and \errno{ETIMEDOUT} directly, and throws everything else. \errno{EAGAIN} is handled internally for logically synchronous calls. Returns the previous error handler, if any. \subsubsection[\fn{socket:error}]{\fn{socket:error([which])}} Returns the saved error conditions for the input and output channels. $which$ is a string containing one or more of the characters `r' and `w', which return the input and output channel errors respectively and in the order specified. $which$ defaults to the string ``rw''. \subsubsection[\fn{socket:clearerr}]{\fn{socket:clearerr([which])}} Clears the error conditions and counters for the specified I/O channels and returns any previous errors. $which$ is a string containing one or more of the characters ``r'' and ``w'', which clears the input and output channel errors respectively, and returns the previous error numbers (or nil) in the order specified. $which$ defaults to the string ``rw''. \subsubsection[\fn{socket:read}]{\fn{socket:read(...)}} Similar to Lua's \fn{file:read}, with additional formats. \begin{tabular}{c | l} format & description\\\hline {*n} & unsupported \\ {*a} & read until EOF \\ {*l} & read the next line, trimming the EOL marker \\ {*L} & read the next line, keeping the EOL marker \\ {*h} & read and unfold MIME compliant header \\ {*H} & read MIME compliant header, keeping EOL markers \\ {\texttt{--}$marker$} & read multipart MIME entity chunk delineated by MIME boundary $marker$ \\ $number$ & read $number$ bytes or until EOF \\ $-number$ & read 1 to $number$ bytes, immediately returning if possible \\ \end{tabular} For SOCK\_DGRAM sockets, each message is treated as-if EOF was reached. The slurp operation returns a single datagram, and line-buffered operations will return the remaining text in a message even without a terminating \lf. Datagrams will be truncated if the message is larger than the input buffer size. The MIME entity reader allows efficient reading of large MIME-encoded bodies, such as with HTTP POST file uploads. The format will return chunks until the boundary is reached. The last chunk will have any trailing EOL marker removed, regardless of input mode, as this is part of the boundary token. In binary mode chunks are sized according to the current input channel buffer size, except that the last chunk will probably be short. In text mode chunks will not exceed the maximum of the current input channel buffer size or maximum line size; and in addition to EOL translation, chunks are broken along line boundaries with multiple lines aggregated into a single chunk. Both the MIME header and MIME entity reader require a proper terminating condition. In particular, \emph{EOF is not a terminating condition}. Applications must be careful to handle truncation if the stream was prematurely closed. When looping over one of these input formats, the application should read the next line of input after the loop terminates. If the next next line does not match the terminating condition, then the stream is invalid and the application should abort processing the stream. For MIME headers the next line should be non-$nil$ and should not appear to be a prefix of a header. \begin{example}{lua} local function isbreak(ln) -- requires *L, not *l return find(ln, "\n", #ln, true) and not match(ln, "[%w%-_]+%s*:") end \end{example} For MIME entities the next line should begin with the boundary text. \begin{example}{lua} local function isboundary(marker, ln) local p, pe = find(ln, marker, 1, true) if p == 1 then if find(ln, "^\r?\n?$", pe + 1) then return "begin" elseif find(ln, "^--\r?\n?$", pe + 1) then return "end" end end return false end \end{example} \subsubsection[\fn{socket:xread}]{\fn{socket:xread(format[, mode][, timeout])}} Like \method{socket:read}, but only takes a single format instead of a list of formats, and permits specifying an input mode and timeout. $mode$ should be in the format described at \method{socket:setmode}. $mode$ and $timeout$ are used only for the current read operation; they do not change the default mode and timeout for the socket. \subsubsection[\fn{socket:lines}]{\fn{socket:lines(...)}} Returns an iterator function over \method{socket:read}. \subsubsection[\fn{socket:xlines}]{\fn{socket:xlines([format][, mode][, timeout])}} Returns an iterator function over \method{socket:xread}. \subsubsection[\fn{socket:write}]{\fn{socket:write(...)}} Same as Lua \fn{file:write}. \subsubsection[\fn{socket:xwrite}]{\fn{socket:xwrite(string[, mode][, timeout])}} Like \method{socket:write}, but only takes a single string, and permits specifying an output mode and timeout. $mode$ should be in the format described at \method{socket:setmode}. $mode$ and $timeout$ are used only for the current write operation; they do not change the default mode and timeout for the socket. \subsubsection[\fn{socket:flush}]{\fn{socket:flush([mode][, timeout])}} Flushes the output buffer. Mode is one of the ``nlf'' flags described in \method{socket:setmode}. A nil mode implies ``n'', i.e.\ no buffering and effecting a full flush. An empty string mode resolves to the configured output buffering mode. \subsubsection[\fn{socket:fill}]{\fn{socket:fill(size[, timeout])}} Fills the input buffer with `size' bytes. Returns true on success, false and an error code on failure. \subsubsection[{\fn{socket:unget}}]{\fn{socket:unget(string)}} Writes `string' to the head of the socket input buffer. \subsubsection[{\fn{socket:pending}}]{\fn{socket:pending()}} Returns two numbers---the counts of buffered bytes in the input and output streams. This does not include the bytes in the kernel's buffer. \subsubsection[\fn{socket:uncork}]{\fn{socket:uncork()}} Disables TCP\_NOPUSH, TCP\_CORK, or equivalent socket option. \subsubsection[\fn{socket:recv}]{\fn{socket:recv(format [, mode])}} Similar to \method{socket:read}, except takes only a single format and returns immediately without polling. On success returns the string or number. On failure returns nil and a numeric error code--usually EAGAIN or EPIPE. Does not use error handler. `mode' is as described in \fn{socket.connect}, and defaults to the configured input mode. \subsubsection[\fn{socket:send}]{\fn{socket:send(string, i, j [, mode])}} Write out the slice `string'[i,j]. Similar to passing \fn{string:sub(i, j)}, but without instantiating a new string object. Immediately returns two values: count of bytes written (0 to j-i+1), and numerical error code, if any (usually EAGAIN or EPIPE). \subsubsection[\fn{socket:recvfd}]{\fn{socket:recvfd([prepbufsiz][, timeout])}} Receive an ancillary socket message with accompanying descriptor. `prepbufsiz' specifies the maximum message size to expect. This routine bypasses I/O buffering. Returns message-string, socket-object on success; nil, nil, error-integer on failure. On success socket-object may still be nil. Message truncation is treated as an error condition. \subsubsection[\fn{socket:sendfd}]{\fn{socket:sendfd(msg, socket[, timeout])}} Send an ancillary socket message with accompanying descriptor. `msg' should be a non-zero-length string, which some platforms require. `socket' should be a Lua file handle, \cqueues socket, integer descriptor, or nil. This routine bypasses I/O buffering. Returns true on success; false and an error code on failure. \subsubsection[\fn{socket:shutdown}]{\fn{socket:shutdown(how)}} Simple binding to \syscall{shutdown(2)}. `how' is a string containing one or both of the flags ``r'' or ``w''. \begin{tabular}{c | l} flag & description \\\hline r & analagous to \syscall{shutdown(SHUT\_RD)} \\ w & analagous to \syscall{shutdown(SHUT\_WR)} \\ \end{tabular} \subsubsection[\fn{socket:eof}]{\fn{socket:eof([which])}} Returns boolean values representing whether EOF has been received on the input channel, and whether the output channel has signaled closure (e.g. \errno{EPIPE}). $which$ is a string containing one or more of the characters ``r'' and ``w'', which return the state of the input or output channel, respectively, in the order specified. $which$ defaults to ``rw''. Note that \fn{socket:shutdown} does not change the state of these values. They are set only upon receiving the condition after I/O is attempted. \subsubsection[\fn{socket:peername}]{\fn{socket:peername()}} Returns one, two, or three values. On success, returns three values for AF\_INET and AF\_INET6 sockets---the address family number, IP address string, and IP port. For AF\_UNIX sockets, returns the address family and either the file path or nil (e.g. for an unnamed socket). If the socket is not yet connected, returns the address family AF\_UNSPEC, usually numeric 0. On failure returns nil and a numeric error code. \subsubsection[\fn{socket:peereid}]{\fn{socket:peereid()}} Queries the effective UID and effective GID of an AF\_UNIX, SOCK\_STREAM peer as cached by the kernel when the stream initially connected. Returns two numbers representing the UID and GID, respectively, on success, otherwise nil and a numeric error code. \subsubsection[\fn{socket:peerpid}]{\fn{socket:peerpid()}} Queries the PID of a AF\_UNIX, SOCK\_STREAM peer as cached by the kernel when the stream initially connected. This capability is unsupported on OS X and FreeBSD; they only provide \fn{getpeereid}, which cannot provide the PID. Returns a number representing the PID on success, otherwise nil and a numeric error code. \subsubsection[\fn{socket:localname}]{\fn{socket:localname()}} Identical to \fn{socket:peername}, but returns the local address of the socket. \subsubsection[\fn{socket:stat}]{\fn{socket:stat()}} Returns a table containing two subtables, `sent' and `rcvd', which each have three fields---.count for the number of bytes sent or received, a boolean .eof signaling whether input or output has been shutdown, and .time logging the last send or receive operation. \subsubsection[\fn{socket:close}]{\fn{socket:close()}} Explicitly and immediately close all internal descriptors. This routine ensures all descriptors are properly cancelled. \end{Module} \begin{Module}{cqueues.errno} \subsubsection[\fn{errno[]}]{\fn{errno[]}} A table mapping all system error string macros to numerical error codes, and all numerical error codes to system error string macros. Thus, \texttt{errno.EAGAIN} evaluates to a numeric error code, and \texttt{errno[errno.EAGAIN]} evaluates to the string ``EAGAIN''. \subsubsection[\fn{errno.strerror}]{\fn{errno.strerror(code)}} Returns string returned by strerror(3). %[FUTURE: Will also handle embedded socket.c and dns.c library error codes.] \end{Module} \begin{Module}{cqueues.signal} \subsubsection{\fn{signal[]}} A table mapping signal string macros to numerical signal codes. In all likelihood, \texttt{signal.SIGKILL} evaluates to the number 9. \subsubsection[\fn{signal.strsignal}]{\fn{signal.strsignal(code)}} Returns string returned by strsignal(3). \subsubsection[\fn{signal.ignore}]{\fn{signal.ignore(signal [, signal $\ldots $ ])}} Set the signal handler to SIG\_IGN for the specified signals. \subsubsection[\fn{signal.default}]{\fn{signal.default(signal [, signal $\ldots$ ])}} Set the signal handler to SIG\_DFL for the specified signals. \subsubsection[\fn{signal.discard}]{\fn{signal.discard(signal [, signal $\ldots$ ])}} Set the signal handler to a builtin ``noop'' handler for the specified signals. Use this is you want signals to interrupt syscalls. \subsubsection[\fn{signal.block}]{\fn{signal.block(signal [, signal $\ldots$ ])}} Block the specified signals. \subsubsection[\fn{signal.unblock}]{\fn{signal.unblock(signal [, signal $\ldots$ ])}} Unblock the specified signals. \subsubsection[\fn{signal.raise}]{\fn{signal.raise(signal [, signal $\ldots$ ])}} raise(3) the specified signals. \subsubsection[\routine{signal.type}]{\routine{signal.type(obj)}} Return the string ``signal listener'' if $obj$ is a signal listener object, or $nil$ otherwise. \subsubsection[\fn{signal.interpose}]{\fn{signal.interpose(name, function)}} Add or interpose a signal listener class method. Returns the previous method, if any. \subsubsection[\fn{signal.listen}]{\fn{signal.listen(signal [, signal $\ldots$ ])}} Returns a signal listener object for the specified signals. Semantics differ between platforms: \paragraph{kqueue} BSD \syscall{kqueue} provides the most intuitive behavior. All listeners will detect a signal sent to the process irrespective of whether the signal is ignored, blocked, or delivered. However, EVFILT\_SIGNAL is edge-triggered, which means no notification of delivery of a pending signal upon being unblocked. \paragraph{signalfd} Linux \syscall{signalfd} will not detect ignored or delivered signals, and only one signalfd object will poll ready per signal. \paragraph{sigtimedwait} Solaris provides no signal polling kernel primitive. Instead, the pending set is periodically queried using \syscall{sigtimedwait}. See \method{signal:settimeout}. Like Linux, only one listener can notify per interrupt. To be portable the application must block the relevant signals. See \fn{signal.block}. Otherwise, neither Linux nor Solaris will be able to detect the interrupt. Any signal should be assigned to one listener only, although any listener may query multiple signals. Alternatively, applications may start a dedicated thread to field incoming signals, and send notifications over a socket. In the future this may be provided as an optional listener implementation. See also \routine{cqueue:pause} for another, if crude, alternative. \subsubsection[\fn{signal:wait}]{\fn{signal:wait([timeout])}} Polls for the signal set passed to the constructor. Returns the signal number, or nil on timeout. \subsubsection[\fn{signal:settimeout}]{\fn{signal:settimeout(timeout)}} Set the polling interval for implementations such as Solaris which lack a signal polling kernel primitive. On such systems signal:wait merely queries the pending set every `timeout' seconds. \end{Module} \begin{Module}{cqueues.thread} \subsubsection[\routine{thread.type}]{\routine{thread.type(obj)}} Return the string ``thread'' if $obj$ is a thread object, or $nil$ otherwise. \subsubsection[\fn{thread.self}]{\fn{thread.self()}} Returns the LWP thread object for the running Lua instances. Threads not started via thread.start return nil. \subsubsection[\fn{thread.start}]{\fn{thread.start(function [, string [, string $\ldots$ ]])}} Generates a socket pair, starts a POSIX LWP thread, initializes a new Lua VM instance, preloads the \cqueues library, and loads and executes the specified function from the new LWP thread and Lua instance. The function receives as the first parameter one end of the socket pair---instantiated as a \module{cqueues.socket} object---followed by the string parameters passed to thread.start. The new LWP thread starts with all signals blocked. Returns a thread object and a socket object---the other end of the socket pair. The thread object is pollable, and readiness signals that the LWP thread has exited, or is imminently about to exit. On error returns two nils and an error code. \subsubsection[\fn{thread:join}]{\fn{thread.join([timeout])}} Wait for the thread to terminate. Calling the equivalent of thread.self():join() is disallowed. Returns a boolean and error value. If false, error value is an error code describing a local error, usually \errno{EAGAIN} or \errno{ETIMEDOUT}. If true, error value is 1) an error code describing a system error which the thread encountered, 2) an error message string returned by the new Lua instance, or 3) nil if completed successfully. \end{Module} \begin{Module}{cqueues.notify} \subsubsection[\fn{notify[]}]{\fn{notify[]}} A table mapping bitwise flags to names, and vice-versa. \begin{tabular}{c | l} name & description \\\hline CREATE & file creation event \\ ATTRIB & metadata change event \\ MODIFY & modification to file contents or directory entries \\ REVOKE & permission revoked \\ DELETE & file deletion event \\ ALL & bitwise-or of CREATE, DELETE, ATTRIB, MODIFY, and REVOKE %\hline %INOTIFY & foo \\ %FEN & foo \\ %KQUEUE & foo \\ %KQUEUE1 & foo \\ %OPENAT & foo \\ %FDOPENDIR & foo \\ %O\_CLOEXEC & foo \\ %IN\_CLOEXEC & foo \\ \end{tabular} \subsubsection[\fn{notify.flags}]{\fn{notify.flags(bitset[, bitset $\ldots$ ])}} Returns an iterator over the flags in the specified bitwise change sets. Thus, \texttt{notify.flags(bit32.xor(notify.CREATE, notify.DELETE), notify.MODIFY)} returns an iterator returning all three flags. \subsubsection[\routine{notify.type}]{\routine{notify.type(obj)}} Return the string ``file notifier'' if $obj$ is a notification object, or $nil$ otherwise. \subsubsection[\fn{notify.opendir}]{\fn{notify.opendir(path[, changes ])}} Returns a notification object associated with the specified directory. Directory change events are limited to the set, `changes', or to notify.ALL if nil. \subsubsection[\fn{notify:add}]{\fn{notify:add(name[, changes ])}} Track the specified file name within the notification directory. `changes' defaults to notify.ALL if nil. \subsubsection[\fn{notify:get}]{\fn{notify:get([timeout])}} Returns a bitwise change set and a filename on success. \subsubsection[\fn{notify:changes}]{\fn{notify:changes([timeout])}} Returns an iterator over the \method{notify:get} method. \end{Module} \begin{Module}{cqueues.dns} As the internal DNS implementation has no global state, \module{cqueues.dns} is mostly a convenience wrapper around other facilities. \subsubsection[\fn{dns.version}]{\fn{dns.version()}} Returns the release, ABI, and API version numbers of the internal DNS implementation as three numbers. \subsubsection[\fn{dns.query}]{\fn{dns.query(name[, type][, class][, timeout])}} Proxies the \fn{resolvers:query} method of the internal resolver pool. If no resolver pool has been set with \fn{dns:setpool}, creates a new stub resolver pool. \subsubsection[\fn{dns.setpool}]{\fn{dns.setpool(pool)}} Sets the internal resolver pool for use by subsequent calls to \fn{dns.query} to $pool$. \subsubsection[\fn{dns.getpool}]{\fn{dns.getpool()}} Returns the internal resolver pool. This routine should never return nil, as it will automatically create a new resolver pool if none has been set yet. \end{Module} \begin{Module}{cqueues.dns.record} DNS resource record objects are implemented within \module{cqueues.dns.record}. The global tables and shared methods are documented below. The type-specific accessory methods are quite numerous. Until documented please confer with cqueues/src/dns.c. Also, the accessory method names are usually equivalent to the structure member names in cqueues/src/lib/dns.h, which in return usually reflect the member names in the relevant RFC. The \fn{\_\_tostring} metamethod returns a representation of the record data only, excluding the name, type, ttl, etc. For an A record, it's equivalent to string.format(``\%s'', \fn{rr:addr()}). For MX---which has multiple members---it's string.format(``\%d \%s'', rr:preference(), rr:host()). \subsubsection[\fn{record.type[]}]{\fn{record.type[]}} A table mapping DNS record type string identifiers to number values, and vice-versa. So, \method{record.type.A} evaluates to 1, the IANA numeric record type. String identifiers are only provided for record types which are directly parseable and composable by the library. Currently supported types include A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, SRV, OPT, SSHFP, and SPF. Other record types can be instantiated, but the numeric type must be used and the only methods available operate on the raw rdata. \subsubsection[\fn{record.class[]}]{\fn{record.class[]}} A table mapping DNS record class string identifiers to number values, and vice-versa. At present the only class included is IN. \subsubsection[\fn{record.sshfp[]}]{\fn{record.sshfp[]}} A table mapping DNS SSHFP record string identifiers to the number values---RSA, DSA, and SHA1. \subsubsection[\routine{record.type}]{\routine{record.type(obj)}} Return the string ``dns record'' if $obj$ is a record object, or $nil$ otherwise. \subsubsection[\fn{record:section}]{\fn{record:section()}} Returns the section identifier from whence the record came, if derived from a packet. Specifically, QUESTION, ANSWER, AUTHORITY, or ADDITIONAL. See \module{cqueues.dns.packet.section[]}. \subsubsection[\fn{record:name}]{\fn{record:name()}} Returns the uncompressed record domain name as a string. \subsubsection[\fn{record:type}]{\fn{record:type()}} Returns the numeric record type. If `rr' holds an AAAA record, then the return value of rr:type() will compare equal to \module{record.type.AAAA}. \subsubsection[\fn{record:class}]{\fn{record:class()}} Returns the numeric record class. See \module{record.class[]}. \subsubsection[\fn{record:ttl}]{\fn{record:ttl()}} Returns the record TTL. \end{Module} \begin{Module}{cqueues.dns.packet} DNS packets are stored in a simple structure encapsulating the raw packet data. One consequence is that packets are append only. Because a packet is composed of four adjacent sections, when building a packet all the information necessary should be at-hand so that records can be appended in order. The \fn{\_\_tostring} metamethod composes a string similar to the output of the venerable dig utility. \subsubsection[\fn{packet.section[]}]{\fn{packet.section[]}} A table mapping packet section string identifiers to number values, and vice-versa. A packet is composed of only four sections: QUESTION, ANSWER, AUTHORITY, and ADDITIONAL. \subsubsection[\fn{packet.opcode[]}]{\fn{packet.opcode[]}} A table mapping packet opcode string identifiers to number values, and vice-versa. The currently mapped opcodes are QUERY, IQUERY, STATUS, NOTIFY, and UPDATE. \subsubsection[\fn{packet.rcode[]}]{\fn{packet.rcode[]}} A table mapping packet rcode string identifiers to number values, and vice-versa. The currently mapped rcodes are NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMP, REFUSED, YXDOMAIN, YXRRSET, NXRRSET, NOTAUTH, and NOTZONE. \subsubsection[\routine{packet.type}]{\routine{packet.type(obj)}} Return the string ``dns packet'' if $obj$ is a packet object, or $nil$ otherwise. \subsubsection[\fn{packet.interpose}]{\fn{packet.interpose}} Add or interpose a packet class method. Returns the previous method, if any. \subsubsection[\fn{packet.new}]{\fn{packet.new([prepbufsiz])}} Instantiate a new packet object. `prepbufsiz' is the maximum space available for appending compressed records. For constructing a packet with a single question, the most space possibly necessary is 260---256 bytes for the name, and 2 bytes each for the type and class (a QUESTION record has no TTL or rdata section). \subsubsection[\fn{packet:qid}]{\fn{packet:qid()}} Returns the 16-bit QID value. \subsubsection[\fn{packet:flags}]{\fn{packet:flags()}} Returns a table of packet header flags. \begin{tabular}{ c | c | l } field & type & description\\\hline .qr & integer & specifies whether the packet is a query (0) or response (1)\\ .opcode & number & specifies the query type\\ .aa & boolean & signals an authoritative answer\\ .tc & boolean & signals packet truncation\\ .rd & boolean & signals ``recursion desired''\\ .ra & boolean & signals ``recursion available''\\ .z & boolean & reserved by RFC 1035 and used by other RFCs\\ .rcode & integer & specifies the response disposition \end{tabular} \subsubsection[\fn{packet:count}]{\fn{packet:count([sections])}} Returns a count of records in the sections specified by the bitwise parameter `sections'. Defaults to \texttt{packet.section.ALL}, which is the XOR of all four sections. \subsubsection[\fn{packet:grep}]{\fn{packet:grep\{ $\ldots$ \}}} Returns a record iterator over the packet according to all the criteria specified by the optional table parameter. \begin{tabular}{ c | l } field & description\\\hline .section & select records by bitwise AND with the specified sections\\ .type & select records of this type (not bitwise)\\ .class & selects records of this class (not bitwise)\\ .name & select records with this name \end{tabular} \end{Module} \begin{Module}{cqueues.dns.config} The traditional BSD /etc/resolv.conf file is the prototype for this module, although it's also capable of parsing /etc/nsswitch.conf. \module{cqueues.dns.config} objects are used when instantiating new resolver objects, and provide the general options controlling a resolver. The \fn{\_\_tostring} metamethod composes a string adhering to /etc/resolv.conf syntax, with /etc/nsswitch.conf alternatives as comments. \subsubsection[\fn{config[]}]{\fn{config[]}} A table mapping flag identifiers to number values. \begin{tabular}{ c | l } field & description\\\hline TCP\_ENABLE & fall back to TCP when truncation detected (default)\\ TCP\_ONLY & only use TCP when querying\\ TCP\_DISABLE & do not fall back to TCP\\ RESOLV\_CONF & specifies BSD /etc/resolv.conf input syntax\\ NSSWITCH\_CONF & specifies Solaris /etc/nsswitch.conf input syntax \end{tabular} \subsubsection[\routine{config.type}]{\routine{config.type(obj)}} Return the string ``dns config'' if $obj$ is a config object, or $nil$ otherwise. \subsubsection[\fn{config.interpose}]{\fn{config.interpose(name, function)}} Add or interpose a config class method. Returns the previous method, if any. \subsubsection[\fn{config.new}]{\fn{config.new\{ $\ldots$ \}}} Returns a new config object, optionally initialized according to the specified table values. \begin{ctabular}{ c | c | p{5in} } field & type & description\\\hline .nameserver & table & list of IP address strings to use for stub resolvers\\ .search & table & list of domain suffixes to append to query names\\ .lookup & table & order of lookup methods---``file'' and ``bind''\\ .options & table & canonical location for .edns0, .ndots, .timeout, .attempts, .rotate, .recurse, .smart, and .tcp options\\ ..edns0 & boolean & enable EDNS0 support\\ ..ndots & number & if query name has fewer labels than this, reverse suffix search order\\ ..timeout & number & timeout between query retries\\ ..attempts & number & maximum number of attempts per nameserver\\ ..rotate & boolean & randomize nameserver selection\\ ..recurse & boolean & query recursively instead of as a simple stub resolver\\ ..smart & boolean & for NS, MX, SRV and similar record queries, resolve the A record if not included as glue in the initial answer\\ ..tcp & number & see TCP\_ENABLE, TCP\_ONLY, TCP\_DISABLE in \fn{config[]}\\ .interface & string & IP address to bind to when querying (e.g. [192.168.1.1]:1234) \end{ctabular} \subsubsection[\fn{config.stub}]{\fn{config.stub\{ $\ldots$ \}}} Returns a config object initialized for a stub resolver by loading the relevant system files; e.g. /etc/resolv.conf and /etc/nsswitch.conf. Takes optional initialization values like \fn{config.new}. \subsubsection[\fn{config.root}]{\fn{config.root\{ $\ldots$ \}}} Returns a config object initialized for a recursive resolver. Takes optional initialization values like \fn{config.new}. \subsubsection[\fn{config:loadfile}]{\fn{config:loadfile(file[, syntax])}} Parse the Lua file object `file'. `syntax' describes the format, which should be RESOLV\_CONF (default), or NSSWITCH\_CONF. \subsubsection[\fn{config:loadpath}]{\fn{config:loadpath(path[, syntax])}} Like :loadfile, but takes a file path. \subsubsection[\fn{config:get}]{\fn{config:get()}} Returns the configuration as a Lua table structure. See \fn{config.new} for a description of the values. \subsubsection[\fn{config:set}]{\fn{config:set\{ $\ldots$ \}}} Apply the defined configuration values. The table should have the same structure as described for \fn{config.new}. \end{Module} \begin{Module}{cqueues.dns.hosts} The traditional BSD /etc/hosts file is the prototype for this module, and provides resolvers the data source for the ``file'' lookup method. The \fn{\_\_tostring} metamethod composes a string adhering to /etc/hosts syntax. \subsubsection[\routine{hosts.type}]{\routine{hosts.type(obj)}} Return the string ``dns hosts'' if $obj$ is a hosts object, or $nil$ otherwise. \subsubsection[\fn{hosts.interpose}]{\fn{hosts.interpose(name, function)}} Add or interpose a hosts class method. Returns the previous method, if any. \subsubsection[\fn{hosts.new}]{\fn{hosts.new()}} Returns a new hosts object. \subsubsection[\fn{hosts.stub}]{\fn{hosts.stub()}} Returns a host object initialized for a stub resolver by loading the relevant system files; e.g. /etc/hosts. \subsubsection[\fn{hosts.root}]{\fn{hosts.root()}} Returns a hosts object initialized for a recursive resolver. \subsubsection[\fn{hosts:loadfile}]{\fn{hosts:loadfile(file)}} Parse the Lua file object `file' for host entries. \subsubsection[\fn{hosts:loadpath}]{\fn{hosts:loadpath(path)}} Like :loadfile, but takes a file path. \subsubsection[\fn{hosts:insert}]{\fn{hosts:insert(address, name[, alias])}} Inserts a new hosts entry. `address' should be an IPv4 or IPv6 address string, `name' the domain name, and `alias' a boolean---true if `name' is canonical and a valid response for a reverse address lookup. \end{Module} \begin{Module}{cqueues.dns.hints} The internal DNS library is implemented as a recursive resolver. No matter whether configured as a stub or recursive resolver, when a query is submitted it consults a ``hints'' database for the initial name servers to contact. In stub mode these would usually be the local recursive, caching name servers, derived from the \module{cqueues.dns.config} object; in recursive mode, the root IANA name servers. The \fn{\_\_tostring} metamethod composes a multi-line string indexing SOA zone names and addresses. \subsubsection[\routine{hints.type}]{\routine{hints.type(obj)}} Return the string ``dns hints'' if $obj$ is a hints object, or $nil$ otherwise. \subsubsection[\fn{hints.interpose}]{\fn{hints.interpose(name, function)}} Add or interpose a hints class method. Returns the previous method, if any. \subsubsection[\fn{hints.new}]{\fn{hints.new([resconf])}} Returns a new hints object. `resconf' is an optional \module{cqueues.dns.config} object which in the future may be used to initialize database behavior. Currently it's unused, and \emph{does not} pre-load the name server list. \subsubsection[\fn{hints.stub}]{\fn{hints.stub([resconf])}} Returns a hints object initialized for a stub resolver. If provided, the initial hints are taken from the \module{cqueues.dns.config} object, `resconf'. Otherwise, the hints are derived from a temporary ``stub'' config object internally. \subsubsection[\fn{hints.root}]{\fn{hints.root([resconf])}} Returns a hints object initialized for a recursive resolver. The root name servers are initialized from an internal database compiled into the module. See \fn{hints.new} for the function of the optional `resconf'. \subsubsection[\fn{hints:insert}]{\fn{hints:insert(zone, address|resconf[, priority])}} Inserts a new hints entry. `zone' is the domain name which anchors the SOA (e.g. ``.'', or ``com.''), and `address' the IPv4 or IPv6 of the nameserver. Alternatively, in lieu of a string address a \module{cqueues.dns.config} object can be specified, and the addresses taken from the nameserver list property. `priority' is used for ordering nameservers in each zone. IPv4 and IPv6 addresses can optionally contain a port component, e.g. ``[2001:503:ba3e::2:30]:123'' or ``[198.41.0.4]:53''. \end{Module} \begin{Module}{cqueues.dns.resolver} This module implements a comprehensive DNS resolution algorithm, capable of working in both stub and recursive modes, and automatically querying for missing glue records. The resolver implementation only supports one outstanding query per resolver, with a 1:1 mapping between resolvers and sockets. This is intended to promote both simplicity and security---it maximizes port number and QID entropy to mitigate spoofing. An additional module, \module{cqueues.dns.resolvers}, implements a resolver pool to assist with bulk querying. \subsubsection[\routine{resolver.type}]{\routine{resolver.type(obj)}} Return the string ``dns resolver'' if $obj$ is a resolver object, or $nil$ otherwise. \subsubsection[\fn{resolver.interpose}]{\fn{resolver.interpose(name, function)}} Add or interpose a resolver class method. Returns the previous method, if any. \subsubsection[\fn{resolver.new}]{\fn{resolver.new([resconf][,hosts][,hints])}} Returns a new resolver object, configured according to the specified config, hosts, and hints objects. `resconf' can be either an object, or a table suitable for passing to \fn{config.new}. `hosts' and `hints', if nil, are instantiated according to the mode---recursive or stub---of the config object. \subsubsection[\fn{resolver.stub}]{\fn{resolver.stub\{ $\ldots$ \}}} Returns a stub resolver, optionally initialized to the defined config parameters, which should have a structure suitable for passing to \fn{cqueues.dns.config.new}. \subsubsection[\fn{resolver.root}]{\fn{resolver.root\{ $\ldots$ \}}} Returns a recursive resolver, optionally initialized to the defined config parameters, which should have a structure suitable for passing to \fn{cqueues.dns.config.new}. \subsubsection[\fn{resolver:query}]{\fn{resolver:query(name[, type][, class][, timeout])}} Query for the DNS resource record with the specified type and class. $name$ is the fully-qualified or prefix domain name string. $type$ and $class$ corresponding to the IANA-assigned numeric or string identifier for the type of answer desired, and default to A (0x01) and IN (0x01), respectively. $timeout$ is the total elapsed time for resolution, irrespective of the $.attempts$ and $.timeout$ configuration values.\footnote{The \texttt{resolv.conf} $.timeout$ controls the time to wait on each query to a nameserver, while $.attempts$ controls how many times to query each nameserver in the nameserver list. Thus in the absence of an overall timeout, the effective timeout is $.timeout$ x $.attempts$ x number of nameservers.} Returns a \module{cqueues.dns.packet} answer packet on success, or nil and a numeric error code on failure. The answer may not actually have anything in the ANSWERS section; e.g. if the RCODE is NXDOMAIN. This routine is a simple wrapper around \fn{resolver:submit} and \fn{resolver:fetch}. \subsubsection[\fn{resolver:submit}]{\fn{resolver:submit(name[, type][, class])}} Resets the query state and submits a new query. Returns true on success, or false and an error number on failure. This routine does not poll. \subsubsection[\fn{resolver:fetch}]{\fn{resolver:fetch()}} Process a previously submitted query. Returns a \module{dns.packet} object on success, or nil and an error number on failure---usually \texttt{EAGAIN}. This routine does not poll. \subsubsection[\fn{resolver:stat}]{\fn{resolver:stat()}} Returns a table of statistics for the resolver instance. \begin{ctabular}{ c | p{5in}} field & description\\\hline .queries & number of queries submitted \\ .udp.sent.count & number of UDP packets sent \\ .udp.sent.bytes & number of UDP bytes sent \\ .udp.rcvd.count & number of UDP packets received \\ .udp.rcvd.bytes & number of UDP bytes received \\ .tcp.sent.count & number of TCP packets sent \\ .tcp.sent.bytes & number of TCP bytes sent \\ .tcp.rcvd.count & number of TCP packets received \\ .tcp.rcvd.bytes & number of TCP bytes received \\ \end{ctabular} \subsubsection[\fn{resolver:close}]{\fn{resolver:close()}} Explicitly destroy the resolver object, immediately closing all internal descriptors. This routine ensures all descriptors are properly cancelled. \end{Module} \begin{Module}{cqueues.dns.resolvers} A resolver pool is both a factory and container for resolver objects. When a resolver is requested it attempts to pull one from the internal queue. If none is available and the $.hiwat$ mark has not been reached, a new resolver is created, otherwise the calling coroutine waits on a conditional variable until a resolver becomes available, or the request times-out. When a resolver is placed back into the queue it is cached if the number of cached resolvers is below $.lowat$, otherwise it is closed and discarded. \subsubsection[\routine{resolvers.type}]{\routine{resolvers.type(obj)}} Return the string ``dns resolver pool'' if $obj$ is a resolver pool object, or $nil$ otherwise. \subsubsection[\fn{resolvers.new}]{\fn{resolvers.new([resconf][,hosts][,hints])}} Behaves similar to \fn{resolver:new}. Returns a new resolver pool object. \subsubsection[\fn{resolvers.stub}]{\fn{resolvers.stub\{ $\ldots$ \}}} Returns a stub resolver pool, with each resolver optionally initialized to the defined config parameters, which should have a structure suitable for passing to \fn{cqueues.dns.config.new}. \subsubsection[\fn{resolvers.root}]{\fn{resolvers.root\{ $\ldots$ \}}} Returns a recursive resolver pool, with each resolver optionally initialized to the defined config parameters, which should have a structure suitable for passing to \fn{cqueues.dns.config.new}. \subsubsection[\fn{resolvers:query}]{\fn{resolvers:query(name[, type][, class][, timeout])}} Behaves similar to \fn{resolver:query}, except that $timeout$ is inclusive of the time spent waiting for a resolver to become available in the pool. \subsubsection[\fn{resolvers:get}]{\fn{resolvers:get([timeout])}} Return a resolver from the pool. If $timeout$ is expires, returns nil and ETIMEDOUT. \subsubsection[\fn{resolvers:put}]{\fn{resolvers:put(resolver)}} Returns $resolver$ back to the pool. Any waiting coroutines are woken. \end{Module} \begin{Module}{cqueues.condition} This module implements a condition variable. A condition variable can be used to queue multiple Lua threads to await a user-defined event. Unlike some condition variable implementations, this one does not implement the monitor pattern directly. A monitor uses both a mutex and a condition variable. However, a full monitor will usually be unnecessary as coroutines do not run in parallel. Monitors are more a necessity in pre-emptive threading environments. The condition variable primitive can be used to implement mutexes, semaphores, and monitors. \subsubsection[\fn{condition.type}]{\fn{condition.type(obj)}} Returns the string ``condition'' if $obj$ is a condition variable, or $nil$ otherwise. \subsubsection[\fn{condition.interpose}]{\fn{condition.interpose(name, function)}} Add or interpose a condition class method. Returns the previous method, if any. \subsubsection[\fn{condition.new}]{\fn{condition.new([lifo])}} Returns a new condition variable object. If `lifo' is \texttt{true}, waiting threads are woken in LIFO order, otherwise in FIFO order. Note that the \cqueues scheduler might schedule execution of multiple woken threads in a different order. The LIFO/FIFO behavior is most useful when implementing a mutex and for whatever reason you wish to select the thread which has waited either the longest or shortest amount of time. \subsubsection[\fn{condition:wait}]{\fn{condition:wait([$\ldots$])}} Wait on the condition variable. Additional arguments are yielded to the \cqueues controller for polling. Passing an integer, for example, allows you to effect a timeout. Passing a socket allows you to wait on both the condition variable and the socket. Returns true if the thread was woken by the condition variable, and false otherwise. Additional values are returned if they polled as ready. It's possible that both the condition variable and, e.g., a socket object poll ready simultaneously, in which case two values are returned---true and the socket object. You can also directly yield a condition variable, along with other condition variables, timeouts, or pollable objects, to the \cqueues controller with \fn{cqueues.poll}. \subsubsection[\fn{condition:signal}]{\fn{condition:signal([n])}} Signal a condition, wakening one or more waiting threads. If specified, a maximum of `n' threads are woken, otherwise all threads are woken. \end{Module} \begin{Module}{cqueues.promise} This module implements the promise/future pattern. It most closely resembles the C++11 std::promise and std::future APIs rather than the JavaScript Promise API. JavaScript lacks coroutines, so JavaScript Promises are overloaded with complex functionality intended to mitigate the problems with lacking such a primitive. The typical usage of promises/futures with C++11's threading model mirrors how they would be typically used in \cqueues' thread--like model. The promise object uses a condition variable to wakeup any coroutines waiting inside \fn{promise:wait} or \fn{promise:get}. \subsubsection[\fn{promise.type}]{\fn{promise.type(obj)}} Returns the string ``promise'' if $obj$ is a promise, or $nil$ otherwise. \subsubsection[\fn{promise.new}]{\fn{promise.new([$f$[, $\ldots$]])}} Returns a new promise object. $f$ is an optional function to run asynchronously, to which any subsequent arguments are passed. $f$ is called using \fn{pcall}, and the return values of \fn{pcall} are passed directly to \fn{promise:set}. \subsubsection[\fn{promise:status}]{\fn{promise:status()}} Returns ``pending'' if the promise is yet unresolved, ``fulfilled'' if the promise has been resolved (\fn{promise:get} will return the values), or ``rejected'' if the promise failed (\fn{promise:get} will throw an error). \subsubsection[\fn{promise:set}]{\fn{promise:set($ok$[, $\ldots$])}} Resolves the state of the promise object. If $ok$ is \true then any subsequent arguments will be returned to \fn{promise:get} callers. If $ok$ is \false then an error will be thrown to \fn{promise:get} callers, with the error value taken from the first subsequent argument, if any. \fn{promise:set} can only be called once. Subsequent invocations will throw an error. \subsubsection[\fn{promise:get}]{\fn{promise:get([$timeout$])}} Wait for resolution of the promise object (if unresolved) and either return the resolved values directly or, if the promise was ``rejected'', throw an error. If $timeout$ is specified, returns nothing if the promise is not resolved within the timeout. \subsubsection[\fn{promise:wait}]{\fn{promise:wait([$timeout$])}} Wait for resolution of the promise object or until $timeout$ expires. Returns promise object if the status is no longer pending (i.e. ``fulfilled'' or ``rejected''), otherwise \nil. \subsubsection[\fn{promise:pollfd}]{\fn{promise:pollfd()}} Returns a condition variable suitable for polling which is used to signal resolution of the promise to any waiting threads.\footnote{To improve performance of the scheduler the pollfd member is itself the condition variable, but it can be called as a function because condition variables support the \_\_call metamethod.} \end{Module} \begin{Module}{cqueues.auxlib} The auxiliary module exposes some convenience interfaces, including some interfaces to help with application integration or for dealing with quirky behavior that hasn't yet been changed because of API stability concerns. \subsubsection[\fn{auxlib.assert}]{\fn{auxlib.assert($v$ [$\ldots$])}} Similar to Lua's built-in \fn{assert}, except that when $v$ is false searches the argument list for the first non-nil, non-false value to use as the message. If the message is an integer, applies \fn{errno.strerror} to derive a human readable string. This routine can be explicitly monkey patched to be the global \fn{assert}. Most \cqueues interfaces return a single integer error rather than the Lua-idiomatic string followed by an integer error. The original concern was that most ``errors'' would be EAGAIN, ETIMEDOUT, or EPIPE, which occur very often and would be costly to continually copy onto the stack as strings, especially given that they'd normally be discarded. In the future the plan is to revert to the idiomatic return protocol used by Lua's \fn{file} API, but memoize the more common errno string representations using upvalues so they can be efficiently returned. \subsubsection[\fn{auxlib.fileresult}]{\fn{auxlib.fileresult($v$ [$\ldots$])}} Serves a similar purpose as \fn{auxlib.assert}, except on error returns $v$ (\nil or \false) followed by the string message and any integer error. For example, in \begin{example}{lua} local v, why, syserr = fileresult(false, nil, EPERM) \end{example} $v$ is \false, $why$ is ``Operation not permitted'', and $syserr$ is EPERM. Whereas with \begin{example}{lua} local v, why, syserr = fileresult(nil, ``No such file or directory'') \end{example} $v$ is \nil, $why$ is ``No such file or directory'', and $syserr$ is \nil. \subsubsection[\fn{auxlib.resume}]{\fn{auxlib.resume($co$ [$\ldots$])}} Similar to Lua's built-in \fn{coroutine.resume}, except that when coroutines yield using \fn{cqueues.poll} recursively yields up the stack until the controller is reached, and then silently restart the coroutine when the poll operation completes. This permits creating iterators which can transparently yield. The application must be careful to ensure that this wrapper is used at every point in a yield/resume chain to get the automatic behavior. This routine can be explicitly monkey patched to be \fn{coroutine.resume}. \subsubsection[\fn{auxlib.tostring}]{\fn{auxlib.tostring($v$)}} Similar to Lua's built-in \fn{tostring}, except supports yielding of \_\_tostring metamethods. This routine can be explicitly monkey patched to be the global \fn{tostring}. \subsubsection[\fn{auxlib.wrap}]{\fn{auxlib.wrap($f$)}} Similar to Lua's built-in \fn{coroutine.wrap}, except uses \fn{auxlib.resume} when resuming coroutines. This routine can be explicitly monkey patched to be \fn{coroutine.wrap}. Note that unlike \fn{cqueues:wrap}, the created coroutine is not attached to a controller. \end{Module} \chapter{Examples} \section{HTTP SSL Request} \begin{example}{lua} local cqueues = require"cqueues" local socket = require"cqueues.socket" local http = socket.connect("google.com", 443) local cq = cqueues.new() cq:wrap(function() http:starttls() http:write("GET / HTTP/1.0\n") http:write("Host: google.com:443\n\n") local status = http:read() print("!", status) for ln in http:lines"*h" do print("|", ln) end local empty = http:read"*L" print"~" for ln in http:lines"*L" do io.stdout:write(ln) end http:close() end) assert(cq:loop()) \end{example} \clearpage \section{Multiplexing Echo Server} \begin{example}{lua} local cqueues = require"cqueues" local socket = require"cqueues.socket" local bind, port, wait = ... local srv = socket.listen(bind or "127.0.0.1", tonumber(port or 8000)) local cq = cqueues.new() cq:wrap(function() for con in srv:clients(wait) do cq:wrap(function() for ln in con:lines("*L") do cq:write(ln) end cq:shutdown("w") end) end end) assert(cq:loop()) \end{example} \clearpage \section{Thread Messaging} \begin{example}{lua} local cqueues = require"cqueues" local thread = require"cqueues.thread" -- we start a thread and pass two parameters--`0' and '9' local thr, con = thread.start(function(con, i, j) -- the `cqueues' upvalue defined above is gone local cqueues = require"cqueues" local cq = cqueues.new() cq:wrap(function() for n = tonumber(i), tonumber(j) do io.stdout:write("sent ", n, "\n") con:write(n, "\n") -- sleep so our stdout writes don't mix cqueues.sleep(0.1) end end) assert(cq:loop()) end, 0, 9) local cq = cqueues.new() cq:wrap(function() for ln in con:lines() do io.stdout:write(ln, " rcvd", "\n") end local ok, why = thr:join() if ok then print(why or "OK") else error(require"cqueues.errno".strerror(why)) end end) assert(cq:loop()) \end{example} \appendix \printindex \end{document} cqueues-rel-20161214/examples/000077500000000000000000000000001302435770500160325ustar00rootroot00000000000000cqueues-rel-20161214/examples/chat.srv000066400000000000000000000203721302435770500175110ustar00rootroot00000000000000#!/usr/bin/env lua -- -- Simple chat server that broadcasts user messages to everybody connected, -- and accepts simple commands. -- -- Provides simple example of combining sockets, condition variables, and a -- basic flow control methodology. -- -- Also provides an example of signal management and a dead-man switch using -- pre-emptive threads. -- local cqueues = require"cqueues" local errno = require"cqueues.errno" local socket = require"cqueues.socket" local signal = require"cqueues.signal" local thread = require"cqueues.thread" local condition = require"cqueues.condition" local EAGAIN = errno.EAGAIN local EPIPE = errno.EPIPE local poll = cqueues.poll local sleep = cqueues.sleep local monotime = cqueues.monotime local port = socket.listen("0.0.0.0", ... or 6667) -- IRC port :) local loop = cqueues.new() local MaxIdle = 300 local Streams = { } -- socket -> user mapping local Users = { } -- nickname -> user mapping -- -- simple fifo module -- -- ------------------------------------------------------------------------ local fifo = {} function fifo.new() local self = { condvar = condition.new(), count = 0 } return setmetatable(self, { __index = fifo }) end -- fifo.new function fifo:put(msg) local tail = { data = msg } if self.tail then self.tail.next = tail self.tail = tail else self.head = tail self.tail = tail end self.count = self.count + 1 self:signal() end -- fifo:put function fifo:get() if self.head then local head = self.head self.head = head.next if not self.head then self.tail = nil end assert(self.count > 0) self.count = self.count - 1 return head.data end assert(self.count == 0) end -- fifo:get function fifo:signal() self.condvar:signal() end -- fifo:signal function fifo:getcv() return self.condvar end -- fifo:getcv -- -- simple chat session object -- -- ------------------------------------------------------------------------ local chat = {} function chat.new(con) local self = { } local _, ip, port = con:peername() self.socket = con self.from = string.format("[%s]:%d", ip, port) self.msgs = fifo.new() self.eof = false --> flag to tell loop to exit self.lastt = monotime() --> last transmission time return setmetatable(self, { __index = chat }) end -- chat.new function chat:greet() self.socket:write"# What's your nickname?\n" self.nick = assert(self.socket:read"*l") self.socket:write(string.format("# Hello %s\n", self.nick)) self.socket:write"# commands: /die /quit /who\n" return self.nick end -- chat:greet function chat:pollfd() return self.socket:pollfd() end -- chat:pollfd function chat:events() -- NOTE: this would need to be changed to something more -- sophisticated if we changed our flow control protocol in -- chat:loop. return self.socket:events() end -- chat:events function chat:tryrecv() repeat local msg, why = self.socket:recv"*L" if msg then local cmd = string.match(msg, "^/(%w+)") if cmd then cmd = string.lower(cmd) if cmd == "quit" then self.socket:write"# goodbye\n" self:exit() return true elseif cmd == "who" then for nick, user in pairs(Users) do self.socket:write(string.format("# %s from %s\n", nick, user.from)) end elseif cmd == "die" then signal.raise(signal.SIGTERM) end else for _, user in pairs(Users) do user:put(msg) end end self.lastt = monotime() elseif why ~= EAGAIN and why ~= EPIPE then return false, why end until not msg return true end -- chat:tryrecv function chat:trysend(flush) repeat local msg = self.msgs:get() if msg then self.socket:send(msg, 1, #msg) end until not msg -- -- Use a yielding flush (blocking our immediate coroutine) to the -- socket to simplify things. -- -- If not, we'll have to synthesis an event mask by combining the -- event states after both :recv and :send, which would normally be -- either "r" if send flushed, or "rw" if send didn't flush. But -- note that if using SSL, read attempts can trigger writes, and -- vice-versa, if the transport channel does a key exchange in the -- middle of the stream. We would then have to be careful about -- always adding the "r" event, because a malicious client could -- send data while OpenSSL's state machine is only trying to write -- data (never reading from the socket), which means we'd constantly -- be waking up. This is an issue lots of software is probably -- susceptible to, actually. Could make for a cool presentation at a -- hacker conference ;) -- -- Ultimately, if the client isn't reading our data fast enough, why -- should we read his? It would complicate our flow control logic, -- and make SSL event management a headache. -- if flush then local ok, why = self.socket:flush() if not ok and why ~= EAGAIN and why ~= EPIPE then return false, why end end return true end -- chat:trysend() function chat:loop() local nick = self:greet() Streams[self.socket] = self if Users[self.nick] then Users[self.nick]:put(string.format("# nick %s taken by %s\n", nick, self.from)) Users[self.nick]:exit() end Users[self.nick] = self repeat -- -- Do :tryrecv last so we poll on the proper events. -- :trysend won't return until it's flushed, so we don't -- care about send events. -- -- Loop because :tryrecv may have enqueued a message to -- ourselves. -- repeat assert(self:trysend(true)) assert(self:tryrecv()) until self.msgs.count == 0 or self.eof local eof, gone = self.socket:eof() if eof or gone then self:exit() end if monotime() - self.lastt > MaxIdle then self:exit() end if not self.eof then poll(self, self.msgs.condvar, 10) end until self.eof local _, gone = self.socket:eof() if not gone then self:trysend(false) --> send any goodbye messages end end -- chat:loop function chat:exit(force) self.eof = true self.msgs:signal() if force then self.socket:shutdown"rw" end end -- chat:exit function chat:close() Streams[self.socket] = nil if Users[self.nick] == self then Users[self.nick] = nil end self.socket:close() --> descriptors are a precious resource end -- chat:close function chat:put(msg) self.msgs:put(msg) end -- chat:put -- -- Our core block which spawns a new coroutine for each client. -- -- ------------------------------------------------------------------------ loop:wrap(function() for con in port:clients() do loop:wrap(function() local chat = chat.new(con) -- don't let buggy code take down whole server local ok, why = pcall(chat.loop, chat) if not ok then io.stderr:write(string.format("%s: %s\n", chat.from, why)) end chat:close() end) end end) -- -- Try to handle our signals cleanly by doing managed shutdown. -- -- Do signal management in the same POSIX thread as our client loop because -- the /die command raises SIGTERM, and raise(2) sends the signal to the -- issuing thread, regardless of signal masks. Linux signalfd and Solaris -- sigtimedwait won't detect signals sent to another thread. -- -- ------------------------------------------------------------------------ loop:wrap(function() signal.block(signal.SIGTERM, signal.SIGHUP, signal.SIGINT) local sigs = signal.listen(signal.SIGTERM, signal.SIGHUP, signal.SIGINT) sigs:wait() for _, user in pairs(Users) do user:put"# server exiting\n" user:exit() end local deadline = monotime() + 1 while next(Users) and deadline > monotime() do sleep(0.05) end os.exit(true) end) -- -- Implement a dead-man switch in case a bug in our code causes the main -- loop to stall. -- -- ------------------------------------------------------------------------ loop:wrap(function() local thr, pipe = thread.start(function(pipe) local cqueues = require"cqueues" local sleep = cqueues.sleep local poll = cqueues.poll local loop = cqueues.new() loop:wrap(function() sleep(10) -- try to drain socket so we don't get stale alive -- tokens on successive iterations. while pipe:recv(-32) do poll(pipe, 10) end io.stderr:write"main thread unresponsive\n" os.exit(false) end) local ok, why = loop:loop() io.stderr:write(string.format("dead-man thread failed: %s\n", why or "unknown error")) os.exit(false) end) loop:wrap(function() while true do sleep(5) assert(pipe:write"!\n") end end) end) -- start our main loop assert(loop:loop()) cqueues-rel-20161214/examples/condition.lua000066400000000000000000000017051302435770500205260ustar00rootroot00000000000000#!/usr/bin/env lua -- -- Test for inter-controller signaling. If it works, the inner loops should -- terminate as soon as we signal the condition variable. Otherwise, they -- shouldn't terminate till each timeouts in turn. -- local cqueues = require"cqueues" local cond = require"cqueues.condition" local sleep = cqueues.sleep local monotime = cqueues.monotime local loop = cqueues.new() local cv = cond.new() local function printf(...) print(string.format(...)) end local success = true for i=1,5 do loop:wrap(function() local loop = cqueues.new() loop:wrap(function() local began = monotime() local okay = cv:wait(3) if not okay then success = false end printf("thread %d woke in %.1fs: %s", i, monotime() - began, (okay and "OK") or "FAIL") end) assert(loop:loop()) end) end loop:wrap(function() print"sleeping..." sleep(2) print"signaling..." cv:signal() end) assert(loop:loop()) print((success and "OK") or "FAIL") cqueues-rel-20161214/examples/dig.lua000077500000000000000000000030371302435770500173060ustar00rootroot00000000000000#!/usr/bin/env lua local cqueues = require"cqueues" local resolver = require"cqueues.dns.resolver" local getopt = loadfile"getopt.lua"() local usage = string.gsub([[ dig -rsh [name] [type] -r recursive lookup beginning from root nameservers -s smart resolve NS, MX, etc to A records if not present -h print this usage message Report bugs to ]], "^[\t\n]", ""):gsub("\n[\t]", "\n") local args = {} local opts = {} for optnam, optval in getopt("rsh", ...) do if optnam then if optnam == "r" then opts.r = true elseif optnam == "s" then opts.s = true elseif optnam == "h" then io.stdout:write(usage) os.exit() else if optnam == "?" then io.stderr:write("invalid option: " .. optval .. "\n") end io.stderr:write(usage) os.exit(false) end else args[#args + 1] = optval end end local name = args[1] or "google.com" local type = args[2] or "A" assert(cqueues.new():wrap(function() local init = (opts.r and resolver.root) or resolver.stub local res = init{ smart = opts.s } print(tostring(assert(res:query(name, type)))) local st = res:stat() print(string.format(";; queries: %d", st.queries)) print(string.format(";; udp sent: %d in %d bytes", st.udp.sent.count, st.udp.sent.bytes)) print(string.format(";; udp rcvd: %d in %d bytes", st.udp.rcvd.count, st.udp.rcvd.bytes)) print(string.format(";; tcp sent: %d in %d bytes", st.tcp.sent.count, st.tcp.sent.bytes)) print(string.format(";; tcp rcvd: %d in %d bytes", st.tcp.rcvd.count, st.tcp.rcvd.bytes)) end):loop()) cqueues-rel-20161214/examples/echo.srv000077500000000000000000000011001302435770500174770ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua -- -- echo.srv [bind-address [[bind-port] [wait-timeout]]] -- local cqueues = require("cqueues") local socket = require("cqueues.socket") local bind, port, wait = ... local srv = socket.listen(bind or "127.0.0.1", tonumber(port or 8000)) local loop = cqueues.new() loop:wrap(function() for con in srv:clients(wait) do loop:wrap(function() for ln in con:lines("*L") do con:write(ln) end con:shutdown("w") end) end end) while not loop:empty() do local ok, err = loop:step() if not ok then error("loop.step: " .. err) end end cqueues-rel-20161214/examples/getopt.lua000066400000000000000000000021321302435770500200350ustar00rootroot00000000000000-- -- getopt(":a:b", ...) -- works just like getopt(3). -- -- Send bug reports to william@25thandClement.com. -- local function getopt(optstring, ...) local opts = { } local args = { ... } for optc, optv in optstring:gmatch"(%a)(:?)" do opts[optc] = { hasarg = optv == ":" } end return coroutine.wrap(function() local yield = coroutine.yield local i = 1 while i <= #args do local arg = args[i] i = i + 1 if arg == "--" then break elseif arg:sub(1, 1) == "-" then for j = 2, #arg do local opt = arg:sub(j, j) if opts[opt] then if opts[opt].hasarg then if j == #arg then if args[i] then yield(opt, args[i]) i = i + 1 elseif optstring:sub(1, 1) == ":" then yield(':', opt) else yield('?', opt) end else yield(opt, arg:sub(j + 1)) end break else yield(opt, false) end else yield('?', opt) end end else yield(false, arg) end end for i = i, #args do yield(false, args[i]) end end) end return getopt cqueues-rel-20161214/examples/httpd.srv000077500000000000000000000052631302435770500177220ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua -- -- Very simple HTTP server. -- -- httpd.srv [bind-address [bind-port]] -- local cqueues = require"cqueues" local socket = require"cqueues.socket" local errno = require"cqueues.errno" local bind, port = ... local verbose = false --true local function fmt(...) return string.format(...) end local info if verbose then info = function(...) io.stderr:write(fmt(...), "\n") end else info = function() return true end end local function warn(...) io.stderr:write(fmt(...), "\n") end local mime = { html = "text/html", xml = "application/xml", css = "text/css", js = "text/javascript", txt = "text/plain", gif = "image/gif", png = "image/png", jpg = "image/jpeg", } local srv = socket.listen(bind or "127.0.0.1", tonumber(port or 8000)) local loop = cqueues.new() loop:wrap(function() local count = 0 for con in srv:clients() do count = count + 1 local id = count loop:wrap(function() local _, ip, port = con:peername() info("%s:%d: connected", ip, port) local ok, why = pcall(function() con:setmode("tl", "tf") local get, why = con:read"*l" if not get then warn("%s:%d: no request (%s)", ip, port, errno.strerror(why)) return end info("%s:%d: %s", ip, port, get) local hdr = {} for h in con:lines"*h" do local f, b = string.match(h, "^([^:%s]+)%s*:%s*(.*)$") hdr[f] = b end con:read"*l" -- discard header/body break local path = string.match(get, "^%w+%s+/?([^%s]+)") or "/dev/null" if not path then warn("%s:%d: no path specified", ip, port) return end local file = io.open(path, "r") if file then con:write"HTTP/1.0 200 OK\n" con:write"Connection: close\n" local ext = string.match(path, "(%w+)$") or "" local ctype = mime[string.lower(ext)] or "application/octet-stream" con:write("Content-Type: ", ctype, "\n\n") if not string.match(ctype, "^text/") then con:setmode(nil, "bf") end for blk in file:lines(1024) do con:write(blk) end file:close() else con:write"HTTP/1.0 404 Not Found\n" con:write"Connection: close\n\n" end local ok, why = con:flush() if not ok then warn("%s:%d: cannot flush (%s)", ip, port, errno.strerror(why)) end con:shutdown"w" local junk = con:read(1) -- wait for EOF if junk then warn("%s:%d: spurious data", ip, port) end con:shutdown"r" end) if ok then info("%s:%d: disconnected", ip, port) else warn("%s:%d: %s", ip, port, why) end con:close() end) end end) while not loop:empty() do local ok, err = loop:step() if not ok then error("loop.step: " .. err) end end cqueues-rel-20161214/examples/notify.lua000077500000000000000000000015701302435770500200530ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua local cqueues = require("cqueues") local notify = require("cqueues.notify") local path = ... or "/tmp" -- -- list kernel capabilities -- -- local f = {} -- -- for flag in notify.flags(notify.FEATURES) do -- f[#f + 1] = notify[flag] -- end -- -- io.stderr:write("using ", table.concat(f, ", "), "\n") -- -- initialize our directory notifier -- local nfy = notify.opendir(path, notify.ALL) local function addall(name, ...) if name then nfy:add(name, notify.ALL) addall(...) end end addall(select(2, ...)) -- -- create controller and loop over file change notifications -- local cq = cqueues.new() cq:wrap(function() for flags, name in nfy:changes() do for flag in notify.flags(flags) do print(name, notify[flag]) end end end) while not cq:empty() do local okay, why = cq:step() if not okay then error("cqueue: " .. why) end end cqueues-rel-20161214/examples/peereid.lua000066400000000000000000000010551302435770500201530ustar00rootroot00000000000000#!/usr/bin/env lua local cqueues = require"cqueues" local socket = require"cqueues.socket" local path = "./peereid.sock" local unix = socket.listen{ path = path, unlink = true } local loop = cqueues.new() loop:wrap(function() local con = unix:accept() local pid, uid, gid = con:peerpid(), con:peereid() print(string.format("pid:%s uid:%s gid:%s", pid, uid, gid)) end) loop:wrap(function() local con = socket.connect{ path = path } con:connect() end) -- make sure we delete the socket local ok, why = loop:loop() os.remove(path) assert(ok, why) cqueues-rel-20161214/examples/promise.lua000077500000000000000000000007431302435770500202220ustar00rootroot00000000000000#!/usr/bin/env lua local cqueues = require"cqueues" local promise = require"cqueues.promise" local resolver = require"cqueues.dns.resolver" local auxlib = require"cqueues.auxlib" local host, type = ... assert(cqueues.new():wrap(function () -- use fully recursive resolver to make sure it takes awhile local pkt = promise.new(function (host, type) return resolver.root():query(host, type) end, host or "parliament.uk", type or "MX") print(auxlib.tostring(pkt)) end):loop()) cqueues-rel-20161214/examples/signal.pause000077500000000000000000000042061302435770500203530ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua -- -- Solaris has no analog to *BSD EVFILT_SIGNAL or Linux signalfd. Rather -- than using tricks (signal handlers + pipes, or thread + sigwait + pipe) -- which create troublesome global state, especially on fork, the default -- signal listener implementation just uses a short timeout for Solaris. -- -- Most of the time signals are used for simple process management. The most -- portable solution for immediate signal response I can come up with is to -- use two signals. One signal will be used to interrupt the process, and -- another for queueing and retrieval. (Note that you can't do both. Neither -- blocked nor ignored signals guarantee a syscall interrupt.) We install a -- dummy handler for the interrupt signal, and block both signals. Then we -- use pselect (cqueue:pause()) to wait around for either I/O or our -- interrupt. When we wake up, we check for any queued signals and process -- any I/O. -- -- There's one more caveat, however. pselect is broken on OS X. Therefore, -- for the most portable solution we use both methods: polling a signal -- listener descriptor, and a loop using pselect(). -- -- If all is working well, then the following script should print "sent -- SIGTERM" and "rcvd SIGTERM" in rapid succession on all supported -- platforms. -- local cqueues = require("cqueues") local signal = require("cqueues.signal") local cq = cqueues.new() local sl = signal.listen(signal.SIGTERM, signal.SIGINT) signal.discard(signal.SIGINT) signal.block(signal.SIGTERM, signal.SIGINT) cq:wrap(function() -- Change the default Solaris timeout so we can confirm we're waking -- up from an interrupt, and not just timing out. sl:settimeout(30) while true do sl:wait() end end) cq:wrap(function() local i = 0 while true do cqueues.sleep(2) i = i + 1 if i > 2 then os.exit() end signal.raise(signal.SIGTERM) print("sent", signal.SIGTERM, signal[signal.SIGTERM]) signal.raise(signal.SIGINT) end end) while not cq:empty() do local signo = sl:wait(0.0) if signo then print("rcvd", signo, signal[signo]) end local ok, err = cq:step(0.0) if not ok then error(err) end cq:pause(signal.SIGINT) end cqueues-rel-20161214/examples/signal.wait000077500000000000000000000011531302435770500202000ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua local cqueues = require("cqueues") local signal = require("cqueues.signal") local cq = cqueues.new() local sl = signal.listen(signal.SIGTERM, signal.SIGINT) --signal.ignore(signal.SIGTERM) signal.block(signal.SIGTERM, signal.SIGINT) cq:wrap(function() local signo while true do signo = sl:wait(0.5) print(signo, signal[signo]) if signo == signal.SIGINT then os.exit(true) end end end) cq:wrap(function() while true do cqueues.sleep(1) signal.raise(signal.SIGTERM) end end) while not cq:empty() do local ok, err = cq:step() if not ok then error(err) end end cqueues-rel-20161214/examples/simple.get000077500000000000000000000014331302435770500200300ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua local cqueues = require("cqueues") local socket = require("cqueues.socket") local host, port = ... host = host or "google.com" port = tonumber(port or 80) local http = socket.connect(host, port) if port == 443 then http:starttls() end local cq = cqueues.new() cq:wrap(function() http:write("GET / HTTP/1.0\n") http:write(string.format("Host: %s:%d\n\n", host, port)) local status = http:read() print("!", status) for ln in http:lines"*h" do print("|", ln) end local empty = http:read"*L" if empty and empty:match"^[^\r\n]" then http:unget(empty) end print"~" for ln in http:lines"*L" do io.stdout:write(ln) end http:close() end) while not cq:empty() do local ok, err = cq:step() if not ok then error("cqueue: " .. err) end end cqueues-rel-20161214/examples/socket.pair000077500000000000000000000007051302435770500202040ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua local cqueues = require("cqueues") local socket = require("cqueues.socket") local snd, rcv = socket.pair("stream") local cq = cqueues.new() cq:wrap(function() for i = 1, 10 do snd:write(tostring(i), "\n") end snd:shutdown("w") end) cq:wrap(function() for i in rcv:lines() do print(tostring(i)) end end) while not cq:empty() do local ok, err = cq:step() if not ok then error("cqueue: " .. err) end end cqueues-rel-20161214/examples/socket.xchg000077500000000000000000000015071302435770500202030ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua local cqueues = require("cqueues") local socket = require("cqueues.socket") local snd, rcv = socket.pair("stream") local cq = cqueues.new() local NLOOP = 1 cq:wrap(function() for i = 1, NLOOP do local con, s = socket.pair("stream") local ok, err = snd:sendfd(os.date(), s) if not ok then error(err) end for i = 1, 10 do con:pack(i, 32) end con:flush() con:shutdown"w" -- Handle OS X bug cq:wrap(function() con:read() end) end end) cq:wrap(function() for i = 1, NLOOP do local msg, con, err = rcv:recvfd(512) if not msg then error(err) end print(msg) for i in function() return con:unpack(32) end do print(tostring(i)) end con:shutdown"w" end end) while not cq:empty() do local ok, err = cq:step() if not ok then error("cqueue: " .. err) end end cqueues-rel-20161214/examples/thread.count000077500000000000000000000014261302435770500203610ustar00rootroot00000000000000#!/usr/local/lua52/bin/lua local cqueues = require"cqueues" local thread = require"cqueues.thread" local thr, con = thread.start(function(con, i, j) local cqueues = require"cqueues" local cq = cqueues.new() cq:wrap(function() for n = tonumber(i), tonumber(j) do io.stderr:write("sent ", n, "\n") con:write(n, "\n") cqueues.sleep(0.1) end end) while not cq:empty() do local ok, why = cq:step() if not ok then error(why) end end end, 0, 9) local cq = cqueues.new() cq:wrap(function() for ln in con:lines() do io.stdout:write(ln, " rcvd", "\n") end local ok, why = thr:join() if ok then print(why or "OK") else error(require"cqueues.errno".strerror(why)) end end) while not cq:empty() do local ok, err = cq:step() if err then print(err) end end cqueues-rel-20161214/mk/000077500000000000000000000000001302435770500146235ustar00rootroot00000000000000cqueues-rel-20161214/mk/changelog000077500000000000000000000025071302435770500165040ustar00rootroot00000000000000#!/bin/sh set -e # strict errors set -u # don't expand unbound variables set -f # disable pathname expansion set -C # noclobber \unalias -a # no command surprises export LC_ALL=C # no locale headaches unset IFS # no field splitting surprises RELPATH=${0%/*} : RELPATH=${RELPATH:-.} CHANGELOG=${RELPATH}/../debian/changelog ROOTDIR=${RELPATH}/.. GIT="$(command -v git)" changelog() { if [ ! -f ${CHANGELOG} ]; then printf -- "${CHANGELOG}: No such file\n" >&2 exit 1 fi cat ${CHANGELOG} } usage() { cat <<-EOF usage: ${0##*/} [-h] version|author|commit -h print this usage message version most recent package version number author author of most recent log message commit Git hash of most recent commit Report bugs to EOF } while getopts h OPT; do case "${OPT}" in h) usage exit 0 ;; *) usage >&2 exit 1 ;; esac done shift $(($OPTIND - 1)) case "${1:-version}" in version) changelog | sed -ne ' s/.*(\([1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\).*).*/\1/ t Found d :Found p q ' ;; author) changelog | sed -ne ' s/.*<\([^>]*@[^>]*\)>.*/\1/ t Found d :Found p q ' ;; commit) test -n "${GIT}" -a -d ${ROOTDIR}/.git || exit 1 cd ${ROOTDIR} ${GIT} show --pretty='%H' HEAD 2>/dev/null | sed -n '1p' ;; *) usage >&2 exit 1 ;; esac exit 0 cqueues-rel-20161214/mk/errno.ls000077500000000000000000000033151302435770500163150ustar00rootroot00000000000000#!/bin/sh # # List all EXXXX macros # set -e : ${CC:=cc} : ${xflag:=} : ${ELAST=no} usage() { cat <<-EOF usage: $(basename $0) -lxh -l print or compute ELAST value -x expand macros -h print this usage message Report bugs to EOF } while getopts lxh OPT; do case "${OPT}" in l) if [ -n "${xflag}" ]; then echo "$0: -l and -x are exclusive" >&2 usage >&2 exit 1 fi ELAST=yes ;; x) if [ "${ELAST}" != "no" ]; then echo "$0: -l and -x are exclusive" >&2 usage >&2 exit 1 fi xflag="-x" ;; h) usage exit 0 ;; *) usage >&2 exit 1 ;; esac done posix() { cat <<-EOF | tr " " "\n" E2BIG EACCES EADDRINUSE EADDRNOTAVAIL EAFNOSUPPORT EAGAIN EWOULDBLOCK EALREADY EBADF EBADMSG EBUSY ECANCELED ECHILD ECONNABORTED ECONNREFUSED ECONNRESET EDEADLK EDESTADDRREQ EDOM EDQUOT EEXIST EFAULT EFBIG EHOSTUNREACH EIDRM EILSEQ EINPROGRESS EINTR EINVAL EIO EISCONN EISDIR ELOOP EMFILE EMLINK EMSGSIZE EMULTIHOP ENAMETOOLONG ENETDOWN ENETRESET ENETUNREACH ENFILE ENOBUFS ENODATA ENODEV ENOENT ENOEXEC ENOLCK ENOLINK ENOMEM ENOMSG ENOPROTOOPT ENOSPC ENOSR ENOSTR ENOSYS ENOTCONN ENOTDIR ENOTEMPTY ENOTRECOVERABLE ENOTSOCK ENOTSUP EOPNOTSUPP ENOTTY ENXIO EOPNOTSUPP ENOTSUP EOVERFLOW EOWNERDEAD EPERM EPIPE EPROTO EPROTONOSUPPORT EPROTOTYPE ERANGE EROFS ESPIPE ESRCH ESTALE ETIME ETIMEDOUT ETXTBSY EWOULDBLOCK EAGAIN EXDEV EOF } errno_h() { posix | env PATH="$(dirname $0):${PATH}" CC="${CC}" macros.ls -i "errno.h" -m "^E" -s - ${xflag} } if [ "${ELAST}" = "yes" ]; then xflag="" (echo "#include "; errno_h) | ${CC} -E - | \ sed -ne 's/^[ ]*\([0123456789][0123456789]*\).*$/\1/p' | sort -n | tail -1 else errno_h fi cqueues-rel-20161214/mk/luapath000077500000000000000000001025641302435770500162170ustar00rootroot00000000000000#!/bin/sh # # This script is used to derive compiler flags and filesystem paths # necessary to utilize Lua, LuaJIT, and particular versions thereof in both # simple and mixed installation environments. # # For usage help information use the -h switch. # # This script attempts to adhere strictly to POSIX shell specifications. The # known non-POSIX features used are the path of the shell at the very first # line of this script, the default compiler command name of `cc' instead of # `c99', and the use of /dev/urandom for generating a random sandbox # directory suffix. All of these can be override. For any other issues # please contact the author. # # WARNING: When searching for a Lua interpreter this script may execute # various utilities in an attempt to deduce their fitness and release # version. By default this script will search for and execute utilities # using the glob patterns luac* and lua*. But this script CANNOT GUARANTEE # that executing such utilities, or any other utilities, either wittingly or # unwittingly, will not result in your COMPUTER EXPLODING. You have been # warned. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Changelog: # # * 2013-08-02 - Published. Derived from an earlier script, lua.path, # written for the cqueues project. # # * 2013-08-05 - Redirect stdin from /dev/null when probing so we don't # freeze if a utility tries to read from stdin. # # chdir to a read-only directory by default to try to prevent creation # of temporary files. These features address the issues of LuaTeX # reading from stdin and creating a luatex.out file in the current # working directory. By default a directory with a random suffix # generated from /dev/urandom is placed in TMPDIR and removed on exit. # # If CPPFLAGS is empty and no -I options directly specified then set # INCDIRS to "/usr/include:/usr/local/include". # # * 2013-08-07 - Add pkg-config support and refactor header probing to delay # recursive searching. # # * 2013-09-09 - NetBSD's sh gets upset over the noclobber option and # redirection to /dev/null, so use append operator. And check $# # before iterating over a null parameter set with `do X; ... done` # when `set -u` is enabled--it complains about $@ being unset. # # * 2013-10-22 - Initial ldflags detection. # # * 2014-01-26 - Migrate CC vendor detection from external script. # # * 2014-09-29 - Add ldir and cdir modes which print install path by parsing # package.path and package.cpath. # # * 2014-12-18 - Add -e GLOB option. # # Deprecate ldir and cdir modes. # # Add package.path and package.cpath to replace ldir and dir modes. # Optional arguments to the new modes are preferred install paths, # rather than globs for finding the lua utility path (use the new -e # option, instead). # # * 2014-12-19 - Fix pkg-config version matching. The --modversion of # the lua package might be stale. For example, it's 5.2.0 on Ubuntu # 14.04 even though the Lua release is 5.2.3. # # Use the interpreter path as a reference point when searching for # headers. $(dirname ${LUA_PATH})/../include is a very likely location # as bindir and includedir have the same prefix in most installations. # # * 2015-01-15 - Quote more command names and arguments. Still need to # handle space characters in code that employs command substitution. I # think we could handle all whitespace characters, including newlines, # by using a control character in IFS and using --exec printf "%s\1" {} # rather than -print with find(1). # # * 2015-01-19 - Add fix for LuaJIT's default package.cpath, which tends to # hardcode /usr/local/lib/lua/5.1, ordered before the LuaJIT # installation prefix. # # * 2015-07-14 - Add recursive glob function implemented in shell code # and use instead of find(1). # # * 2016-03-18 - Fix bug in tryluac where a continue statement was used # instead of return 0. # # * 2016-03-25 - Support ${CC} values with trailing flags, which invoke # the compiler through env(1), or which otherwise are intended to # expand as multiple words. # # OpenBSD 5.8 sh does not suppress strict errors within an eval # invoked from an if condition compound-list. Workaround by changing # trylua to return 0 on matching failure, like tryluainclude and # tryluac do. # # Undeprecate ldir and cdir. The names are more intuitive and # convenient as evidenced by the fact that I keep using them instead # of package.path and package.cpath. Try to maintain backwards # compatibility by using a simple heuristic to differentiate lua # interpreter glob patterns from preferred install directory # string.match expressions. # # * 2016-10-10 - Fix issue with passing empty CPPFLAGS to ${CC}. /usr/bin/cc # in NetBSD 7.0.1 does not tolerate an empty string argument. This # exposed a bug in NetBSD's and FreeBSD's /bin/sh, triggered by how we # pass CPPFLAGS (see evalmacro and runcc routines, below). # # Some Ash variants (confirmed /bin/sh in NetBSD 7.0.1 and FreeBSD # 10.1) will expand unquoted ${UNSET-} and ${UNSET:-} as an empty # string rather than eliding it during argument processing. That is, # # nargs() { printf "%d\n" "$#"; } # nargs ${UNSET} 2 3 # nargs ${UNSET-} 2 3 # # prints "2" and "3", whereas every other shell tested prints "2" and # "2" (confirmed dash in Ubuntu Xenial; bash 4.3 in Ubuntu Xenial; # pdksh in FreeBSD 10.1, NetBSD 7.0, OS X 10.1, OpenBSD 6.0; ksh93 in # Solaris 11.3 and AIX 7.1; ksh88 in AIX 7.1). # # A workaround in set -u mode (where unbound variable expansion aborts # execution) is to substitute a known empty value. E.g. # # EMPTY= # nargs ${UNSET-$EMPTY} # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (C) 2012-2016 William Ahern # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # set -e # strict errors set -u # don't expand unbound variables set -f # disable pathname expansion set -C # noclobber \unalias -a # no command surprises export LC_ALL=C # no locale headaches unset IFS # no field splitting surprises : ${TMPDIR:=/tmp} # sane TMPDIR : ${CC:=cc} unset LUA_PATH || true # interferes search for module install directory unset LUA_CPATH || true MYVERSION=20161010 MYVENDOR="william@25thandClement.com" EMPTY= # empty string for parameter expansion workaround for Ash bug DEVRANDOM=/dev/urandom SANDBOX="${TMPDIR}/${0##*/}-" CPPDIRS= # -I directories from CPPFLAGS INCDIRS= LDDIRS= # -L directories from LDFLAGS LIBDIRS= BINDIRS= RECURSE=no MAXDEPTH=5 # maximum recursion depth SHORTEST= # continue searching until shortest pathname found PKGCONFIG= # path to pkg-config, found by `command -v` when -k option invoked GLOB= # -e GLOB expression for lua, luac, ldir, and cdir GLOB_LUA="lua:lua[5-9]*:lua-[5-9]*:luajit*" GLOB_LUAC="luac:luac[5-9]*:luac-[5-9]*" API_MIN=500 API_MAX=999 API_VER= API_DIR= JIT_REQ= JIT_MIN=20000 JIT_MAX=99999 JIT_VER= JIT_DIR= LIBLUA_VER= LIBLUA_DIR= LIBLUA_LIB= LIBJIT_VER= LIBJIT_DIR= LIBJIT_LIB= LUAC_PATH= LUAC_VER= LUA_PATH= LUA_VER= # # warn FORMAT [...] # # Print message to original stderr. # exec 9>&2 warn() { printf "%s: %.0s${1}\n" "${0##*/}" "$@" >&9 } # # panic FORMAT [...] # # Print message to original stderr, then exit with failure. # panic() { warn "$@" exit 1 } # # parse CPPFLAGS -I or LDFLAGS -L directories # xdirs() { OPTC="${1:-I}" DIRS= set -- ${2:-} while [ $# -gt 0 ]; do case "${1}" in -${OPTC}) shift if [ -n "${1:-}" ]; then DIRS="${DIRS}${DIRS:+:}${1}" fi ;; -${OPTC}*) if [ "${1}" != "-${OPTC}" ]; then DIRS="${DIRS}${DIRS:+:}${1#-${OPTC}}" fi ;; esac shift done printf -- "${DIRS}" } idirs() { xdirs "I" "${1:-}" } ldirs() { xdirs "L" "${1:-}" } # count ":"-delimited substrings count() { IFS=: set -- ${1:-} unset IFS printf "$#" } # append to ":"-delimited string variable append() { NAME=${1} eval VALUE="\${${NAME}}" shift IFS=: TMP="$*" IFS="\n" read -r "${NAME}" <<-EOF ${VALUE:-}${VALUE:+:}${TMP} EOF unset IFS } # # glob PATTERN [MAXDEPTH] [EXEC-COMMAND] [INTERNAL:GLOB-COUNT] # glob() { glob_N="${4:-0}" IFS= set +f for F in ${1}; do [ -e "${F}" ] || continue if eval "${3:-printf '%s\\n'} \"\${F}\""; then glob_N=$((${glob_N} + 1)) fi done set -f unset IFS if [ "${2-0}" -gt 0 ]; then glob "${1%/*}/*/${1##*/}" "$((${2} - 1))" "${3:-}" "${glob_N}" || : fi [ "${glob_N}" -gt 0 ] } # glob # # runcc [...] # # Wrapper for invoking ${CC}. Some build system include flags in ${CC}, # invoke the compiler through env(1), or employ other hacks. # # TODO: Optionally handle unescaping of words in a manner similar to how # ${CC} would be evaluated from a make rule--typically by being passed # through system(3). # runcc() { (unset IFS; exec ${CC} "$@") } # # evalmacro PATH MACRO [REGEX] [SUBST] # # PATH Header identifier--#include # MACRO Macro identifier # REGEX Optional regex pattern to match macro evaluation result # SUBST Optional replacement expression # evalmacro() { printf "#include <$1>\n[===[$2]===]\n" \ | runcc ${CPPFLAGS:-${EMPTY}} -E - 2>>/dev/null \ | sed -ne " s/^.*\\[===\\[ *\\(${3:-.*}\\) *\\]===\\].*$/${4:-\\1}/ t Found d :Found p q " } # # testsym PATH NAME # # Test whether global symbol NAME exists in object file at PATH. Exits with # 0 (true) when found, non-0 (false) otherwise. # testsym() { # NOTE: No -P for OpenBSD nm(1), but the default output format is # close enough. Section types always have a leading and trailing # space. U section type means undefined. On AIX [VWZ] are weak # global symbols. Solaris and OS X have additional symbol types # beyond the canonical POSIX/BSD types, all of which are uppercase # and within [A-T]. (nm -Pg ${1} 2>>/dev/null || nm -g 2>>/dev/null) \ | sed -ne '/ [A-T] /p' \ | grep -q "${2}" } tryluainclude() { V="$(evalmacro "${1}" LUA_VERSION_NUM '[0123456789][0123456789]*')" : ${V:=0} if [ "${1%/*}" != "${1}" ]; then D="${1%/*}" # cleanup after Solaris directory prune trick if [ "${D##*/./}" != "${D}" ]; then D="${D%%/./*}/${D##*/./}" else D="${D%/.}" fi else D= fi [ "$V" -gt 0 -a "$V" -ge "${API_VER:-0}" ] || return 0 [ "$V" -gt "${API_VER:-0}" -o "${#D}" -lt "${#API_DIR}" -o \( "${JIT_REQ}" = "yes" -a "${JIT_VER:-0}" -lt "${JIT_MAX}" \) ] || return 0 [ "$V" -ge "${API_MIN}" -a "$V" -le "${API_MAX}" ] || return 0 if [ -n "${JIT_REQ}" ]; then J="$(evalmacro "${1%%lua.h}luajit.h" LUAJIT_VERSION_NUM '[0123456789][0123456789]*')" : ${J:=0} if [ "${JIT_REQ}" = "skip" ]; then [ "${J}" -eq 0 ] || return 0 elif [ "${JIT_REQ}" = "yes" ]; then [ "$J" -ge "${JIT_VER:-0}" ] || return 0 [ "$J" -gt "${JIT_VER:-0}" -o "${#D}" -lt "${#JIT_DIR}" ] || return 0 [ "$J" -ge ${JIT_MIN} ] || return 0 [ "$J" -le "${JIT_MAX}" ] || return 0 JIT_VER="$J" JIT_DIR="$D" fi fi API_VER="$V" API_DIR="$D" } # # foundversion # # true if found the best (maximum) possible version, false otherwise # foundversion() { if [ "${API_VER:-0}" -lt "${API_MAX}" ]; then return 1 fi if [ "${JIT_REQ}" = "yes" -a "${JIT_VER:-0}" -lt "${JIT_MAX}" ]; then return 1 fi if [ "${SHORTEST}" = "yes" ]; then return 1 fi return 0 } # # luapc # # wrapper around `pkg-config ... LIB`, where LIB is derived by # searching for all libraries with "lua" in the name that have a # --modversion equal to the release version printed by ${LUA_PATH} -v. # LUAPC_LIB= luapc() { [ -n "${LUA_PATH}" ] || return 0 [ -n "${PKGCONFIG}" ] || return 0 # find pkg-config library name if [ -z "${LUAPC_LIB}" ]; then V="$("${LUA_PATH}" -v &1 | head -n1 | sed -ne 's/^Lua[^ ]* \([0123456789][0123456789]*\(\.[0123456789][0123456789]*\)*\).*/\1/p')" [ -n "${V}" ] || return 0 V_N=$(mmp2num "${V}") for LIB in $("${PKGCONFIG}" --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p'); do M="$("${PKGCONFIG}" --modversion ${LIB} || true)" # break immediately on exact match if [ "${V}" = "${M}" ]; then LUAPC_LIB="${LIB}" break fi # # NOTE: On Ubuntu 14.04 pkg-config --modversion # lua5.2 prints 5.2.0 even though the release # version is 5.2.3 (what lua5.2 -v prints). # # If the major.minor components match, then # tentatively use that package name. # M_N=$(mmp2num "${M}" 0 0 0) if [ "$((${V_N} / 100))" -eq "$((${M_N} / 100))" ]; then LUAPC_LIB="${LIB}" fi done [ -n "${LUAPC_LIB}" ] || return 0 fi ${PKGCONFIG} "$@" "${LUAPC_LIB}" >/dev/null || true } # # findinstalldir package.path|package.cpath [preferred-path ...] # findinstalldir() { V_DIR=$((${LUA_VER} / 100 % 100)).$((${LUA_VER} % 100)) if [ "${1}" = "package.cpath" -o "${1}" = "cdir" ]; then ARRAY="package.cpath" DIR="$(luapc --variable INSTALL_CMOD)" [ -n "${DIR}" ] && set -- "$@" "${DIR}" DIR="$(luapc --variable INSTALL_LIB)" [ -n "${DIR}" ] && set -- "$@" "${DIR}/lua/${V_DIR}" DIR="$(luapc --variable libdir)" [ -n "${DIR}" ] && set -- "$@" "${DIR}/lua/${V_DIR}" DIR="$(luapc --variable prefix)" [ -n "${DIR}" ] && set -- "$@" "${DIR}/lib/lua/${V_DIR}" # LuaJIT installations tend to include # /usr/local/lib/lua/5.1 as one of the first paths, ordered # before the LuaJIT installation prefix, and regardless of # whether there exists a /usr/local/lib/lua/5.1. set -- "$@" "${LUA_PATH}/../../lib/lua/${V_DIR}" set -- "$@" "${LUA_PATH}/../../lib/*/lua/${V_DIR}" # e.g. lib/x86_64-linux-gnu else ARRAY="package.path" DIR="$(luapc --variable INSTALL_LMOD)" [ -n "${DIR}" ] && set -- "$@" "${DIR}" DIR="$(luapc --variable prefix)" [ -n "${DIR}" ] && set -- "$@" "${DIR}/share/lua/${V_DIR}" # See above LuaJIT note. Although the built-in package.path # usually orders the LuaJIT installation prefix first. set -- "$@" "${LUA_PATH}/../../share/lua/${V_DIR}" fi shift if [ $# -eq 0 ]; then set -- "/nonexistent" # cannot expand empty $@ on some implementations fi "${LUA_PATH}" - "$@" <<-EOF -- -- actual pkg-config variable on Ubuntu 14.04 -- -- /usr//share/lua/5.1 -- local function fixpath(path) local stack = { path:match"^/" and "" or "." } for ent in path:gmatch"([^/]+)" do if ent == ".." and #stack > 1 then stack[#stack] = nil elseif ent ~= "." then stack[#stack + 1] = ent end end return table.concat(stack, "/") end local function topattern(path) if string.match(path, "*") then path = string.gsub(path, "%%", "%%") return string.gsub(path, "*", "[^/]+") end end local dirs = { } for dir in ${ARRAY}:gmatch"([^;?]+)/" do dir = fixpath(dir) if dir ~= "." then dirs[#dirs + 1] = dir end end for _, arg in ipairs{ ... } do arg = fixpath(arg) local pat = topattern(arg) for _, dir in ipairs(dirs) do if arg == dir then print(dir) os.exit(0) elseif pat and string.match(dir, pat) then print(dir) os.exit(0) end end end if dirs[1] then print(dirs[1]) os.exit(0) else os.exit(1) end EOF } # # findversion # findversion() { tryluainclude "lua.h" if foundversion; then return 0 fi # iterate through CPPFLAGS to probe different precedence if [ "${API_VER:-0}" -lt "${API_MAX}" ]; then IFS=: set -- ${CPPDIRS} unset IFS if [ $# -gt 0 ]; then for D; do tryluainclude "${D}/lua.h" if foundversion; then return 0 fi done fi fi if [ -n "${PKGCONFIG}" ]; then PKGFLAGS="$("${PKGCONFIG}" --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p' | xargs -- ${PKGCONFIG} --cflags 2>>/dev/null | cat)" PKGDIRS="$(idirs "${PKGFLAGS}")" IFS=: set -- ${PKGDIRS} unset IFS if [ $# -gt 0 ]; then for D; do tryluainclude "${D}/lua.h" if foundversion; then return 0 fi done fi fi IFS=: set -- ${INCDIRS} unset IFS if [ $# -gt 0 ]; then for D; do tryluainclude "${D}/lua.h" if foundversion; then return 0 fi done fi if [ "${RECURSE}" != "yes" ]; then [ "${API_VER:-0}" -gt 0 ] return $? fi # recurse into CPPDIRS IFS=: set -- ${CPPDIRS} unset IFS if [ $# -gt 0 ]; then for D; do glob "${D}/lua.h" "${MAXDEPTH}" tryluainclude || : if foundversion; then return 0 fi done fi # recurse into INCDIRS IFS=: set -- ${INCDIRS} unset IFS if [ $# -gt 0 ]; then for D; do glob "${D}/lua.h" "${MAXDEPTH}" tryluainclude || : if foundversion; then return 0 fi done fi # if we can find the lua interpreter, use it as a reference for # header locations. if findlua; then D="${LUA_PATH%/*}" D="${D%/*}/include" if [ -d "${D}" ]; then glob "${D}/lua.h" "${MAXDEPTH}" tryluainclude || : if foundversion; then return 0 fi fi fi [ "${API_VER:-0}" -gt 0 ] } # # Unlike API version checking, this is less likely to be accurately forward # compatible. # trylib() { testsym "${1}" "lua_newstate" || return 1 # exclude C++ [ "${1#*++}" = "${1}" ] || return 1 V=0 J=0 D= F="${1##*/}" L= if [ "${1%/*}" != "${1}" ]; then D="${1%/*}" # cleanup after Solaris directory prune trick if [ "${D##*/./}" != "${D}" ]; then D="${D%%/./*}/${D##*/./}" else D="${D%/.}" fi fi L="${F#lib}" L="${L%.so}" L="${L%.a}" L="${L%.dylib}" # FIXME: need more versioning tests if testsym "${1}" "lua_getfenv"; then V=501 elif testsym "${1}" "lua_yieldk"; then if testsym "${1}" "lua_getctx"; then V=502 else V=503 fi else return 1 fi [ "$V" -gt 0 -a "$V" -ge "${LIBLUA_VER:-0}" ] || return 1 [ "$V" -gt "${LIBLUA_VER:-0}" -o "${#D}" -lt "${#LIBLUA_DIR}" -o \( "${JIT_REQ}" = "yes" -a "${LIBJIT_VER:-0}" -lt "${JIT_MAX}" \) ] || return 1 [ "$V" -ge "${API_MIN}" -a "$V" -le "${API_MAX}" ] || return 1 if [ -n "${JIT_REQ}" ]; then # FIXME: need more versioning tests if testsym "${1}" "luaopen_jit"; then J=20000 fi if [ "${JIT_REQ}" = "skip" ]; then [ "${J}" -eq 0 ] || return 1 elif [ "${JIT_REQ}" = "yes" ]; then [ "$J" -ge "${LIBJIT_VER:-0}" ] || return 1 [ "$J" -gt "${LIBJIT_VER:-0}" -o "${#D}" -lt "${#LIBJIT_DIR}" ] || return 1 [ "$J" -ge ${JIT_MIN} ] || return 1 [ "$J" -le "${JIT_MAX}" ] || return 1 LIBJIT_VER="$J" LIBJIT_DIR="$D" LIBJIT_LIB="$L" fi fi LIBLUA_VER="$V" LIBLUA_DIR="$D" LIBLUA_LIB="$L" } # # foundlib # # true if found the best (maximum) possible version, false otherwise # foundlib() { if [ "${LIBLUA_VER:-0}" -lt "${API_MAX}" ]; then return 1 fi if [ "${JIT_REQ}" = "yes" -a "${LIBJIT_VER:-0}" -lt "${JIT_MAX}" ]; then return 1 fi if [ "${SHORTEST}" = "yes" ]; then return 1 fi return 0 } findlib() { if [ -n "${PKGCONFIG}" ]; then PKGFLAGS="$("${PKGCONFIG}" --list-all >/dev/null | sed -ne 's/^\(lua[^ ]*\).*/\1/p' | xargs -- ${PKGCONFIG} --libs 2>>/dev/null | cat)" PKGDIRS="$(ldirs "${PKGFLAGS}")" PKGDIRS="${PKGDIRS}${PKGDIRS:+:}/lib:/usr/lib:/usr/local/lib" NUMDIRS="$(count "${PKGDIRS}")" PKGLIBS="$(xdirs "l" "${PKGFLAGS}")" NUMLIBS="$(count "${PKGLIBS}")" ALLDIRS="${PKGDIRS}${PKGLIBS:+:}${PKGLIBS}" IFS=: set -- ${ALLDIRS} unset IFS I=1 while [ $I -le ${NUMDIRS} ]; do K=$((1 + ${NUMDIRS})) while [ $K -le $# ]; do findlib_L=$(eval "printf \${$I}") findlib_l=$(eval "printf \${$K}") #printf -- "I=$I K=$K $findlib_L/lib$findlib_l*.*\n" glob "${findlib_L}/lib${findlib_l}*.*" 0 trylib || : if foundlib; then return 0; fi glob "${findlib_L}/lib${findlib_l}*.*" ${MAXDEPTH} trylib || : if foundlib; then return 0; fi K=$(($K + 1)) done I=$(($I + 1)) done fi ALLDIRS="${LDDIRS}${LDDIRS:+:}${LIBDIRS}" IFS=: set -- ${ALLDIRS} unset IFS for findlib_D; do glob "${findlib_D}/liblua*.*" "${MAXDEPTH}" trylib || : if foundlib; then return 0 fi done # if we can find the lua interpreter, use it as a reference for # library locations. if findlua; then findlib_D="${LUA_PATH%/*}" findlib_D="${findlib_D%/*}/lib" if [ -d "${findlib_D}" ]; then glob "${findlib_D}/liblua*.*" "${MAXDEPTH}" trylib || : if foundlib; then return 0 fi fi fi } # check setuid and setgid mode safeperm() { [ -f "$1" -a ! -u "$1" -a ! -g "$1" ] } tryluac() { tryluac_F="${1}" [ -x "${tryluac_F}" ] && safeperm "${tryluac_F}" || return 0 tryluac_V="$("${tryluac_F}" -v &1 | head -n1 | sed -ne 's/^Lua \([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" : ${tryluac_V:=0} tryluac_V="$((${tryluac_V%%.*} * 100 + ${tryluac_V##*.} % 100))" [ "${tryluac_V}" -gt 0 -a "${tryluac_V}" -ge "${LUAC_VER:-0}" ] || return 0 [ "${tryluac_V}" -gt "${LUAC_VER:-0}" -o "${#tryluac_F}" -lt "${#LUAC_PATH}" ] || return 0 [ "${tryluac_V}" -ge "${API_MIN}" -a "${tryluac_V}" -le "${API_MAX}" ] || return 0 printf "return true" 2>>/dev/null | ${tryluac_F} -p - >/dev/null 2>&1 || return 0 LUAC_PATH="${tryluac_F}" LUAC_VER="${tryluac_V}" } # # foundluac # # true if found the best (maximum) possible version, false otherwise # foundluac() { if [ "${LUAC_VER:-0}" -lt "${API_MAX}" ]; then return 1 fi if [ "${SHORTEST}" = "yes" ]; then return 1 fi return 0 } findluac() { if [ $# -eq 0 ]; then IFS=: set -- ${GLOB:-${GLOB_LUAC}} unset IFS fi for findluac_G; do IFS=: for findluac_D in ${PATH}; do unset IFS glob "${findluac_D}/${findluac_G}" 0 tryluac || : if foundluac; then return 0 fi done IFS=: for findluac_D in ${BINDIRS}; do unset IFS glob "${findluac_D}/${findluac_G}" "${MAXDEPTH}" tryluac || : if foundluac; then return 0 fi done unset IFS done [ "${LUAC_VER:-0}" -gt 0 ] && [ "${#LUAC_PATH}" -gt 0 ] } isinteger() { I="${1}" [ "${#I}" -gt 0 ] || return 1 while [ "${#I}" -gt 0 ]; do if [ "${I##[0123456789]}" = "${I}" ]; then return 1 fi I=${I##[0123456789]} done return 0 } checkints() { while [ $# -gt 0 ]; do if ! isinteger "${1}"; then warn "%s: not a number" "${1}" return 1 fi shift done } # Only major.minor for matching LUA_VERSION_NUM in lua.h. Also, _VERSION # only includes major.minor. lua2num() { M=0 m="${2:-0}" IFS=. set -- ${1} unset IFS M=${1:-${M}} m=${2:-${m}} checkints $M $m printf "$((${M} * 100 + ${m}))\n" } # All major.minor.patch for matching LUAJIT_VERSION_NUM in luajit.h. jit2num() { M=0 m="${2:-0}" p="${3:-0}" IFS=. set -- ${1} unset IFS M=${1:-${M}} m=${2:-${m}} p=${3:-${p}} checkints $M $m $p printf "$((${M} * 10000 + ${m} * 100 + ${p}))\n" } mmp2num() { M="${2:-0}" m="${3:-0}" p="${4:-0}" IFS=".+-_" set -- ${1} unset IFS if isinteger "${1:-}"; then M=${1} fi if isinteger "${2:-}"; then m=${2} fi if isinteger "${3:-}"; then p=${3} fi checkints $M $m $p printf "$((${M} * 10000 + ${m} * 100 + ${p}))\n" } trylua() { trylua_F="${1}" [ -x "${trylua_F}" ] && safeperm "${trylua_F}" || return 0 trylua_V="$("${trylua_F}" -e 'print(string.match(_VERSION, [[[%d.]+]]))' >/dev/null | head -n1 | sed -ne 's/^\([0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" : ${trylua_V:=0} trylua_V="$((${trylua_V%%.*} * 100 + ${trylua_V##*.} % 100))" [ "${trylua_V}" -gt 0 -a "${trylua_V}" -ge "${LUA_VER:-0}" ] || return 0 [ "${trylua_V}" -gt "${LUA_VER:-0}" -o "${#trylua_F}" -lt "${#LUA_PATH}" ] || return 0 [ "${trylua_V}" -ge "${API_MIN}" -a "${trylua_V}" -le "${API_MAX}" ] || return 0 if [ -n "${JIT_REQ}" ]; then J="$("${trylua_F}" -v &1 | head -n1 | sed -ne 's/^LuaJIT \([0123456789][0123456789]*\.[0123456789][0123456789]*\.[0123456789][0123456789]*\).*/\1/p')" J="$(jit2num ${J:-0})" if [ "${JIT_REQ}" = "skip" ]; then [ "${J}" -eq 0 ] || return 0 elif [ "${JIT_REQ}" = "yes" ]; then [ "${J}" -gt 0 ] || return 0 [ "${J}" -ge "${JIT_MIN}" ] || return 0 [ "${J}" -le "${JIT_MAX}" ] || return 0 fi fi LUA_PATH="${trylua_F}" LUA_VER="${trylua_V}" } # # foundlua # # true if found the best (maximum) possible version, false otherwise # foundlua() { if [ "${LUA_VER:-0}" -lt "${API_MAX}" ]; then return 1 fi if [ "${SHORTEST}" = "yes" ]; then return 1 fi return 0 } findlua() { if [ $# -eq 0 ]; then IFS=: set -- ${GLOB:-${GLOB_LUA}} unset IFS fi for findlua_G; do IFS=: for findlua_D in ${PATH}; do unset IFS glob "${findlua_D}/${findlua_G}" 0 trylua || : if foundlua; then return 0 fi done IFS=: for findlua_D in ${BINDIRS}; do unset IFS glob "${findlua_D}/${findlua_G}" "${MAXDEPTH}" trylua || : if foundlua; then return 0 fi done unset IFS done [ "${LUA_VER:-0}" -gt 0 ] && [ "${#LUA_PATH}" -gt 0 ] } ccname() { runcc -E - <<-EOF | awk '/sunpro/||/clang/||/gcc/||/other/{ print $1; exit; }' #if defined __SUNPRO_C sunpro #elif defined __clang__ clang #elif defined __GNUC__ gcc #else other #endif EOF } usage() { cat <<-EOF usage: ${0##*/} [-I:L:P:d:De:krm:xsv:j:JVh] cppflags|version|lua|luac|... -I PATH additional search directory for includes -L PATH additional search directory for libraries -P PATH additional search directory for binaries -d PATH use PATH as sandbox directory; a random 16 byte suffix is generated from /dev/urandom and the directory removed on exit unless a trailing "/" is present (default sandbox is \$TMPDIR/${0##*/}-XXXXXXXXXXXXXXXX) -D do not create a sandbox -e GLOB glob pattern for finding utilities (lua, luac, etc) -k query pkg-config if available -r recursively search directories -m MAXDEPTH limit recursion to MAXDEPTH -s find shortest pathname, otherwise print first best match -v VERSION require specific Lua version or range (e.g. "5.1" or "5.1-5.2") -j VERSION require specific LuaJIT version or range (e.g. "2.0.1"; empty ranges like "-" force any LuaJIT version) -J skip LuaJIT if encountered -V print this script's version information -h print this usage message cppflags print derived additional CPPFLAGS necessary version print derived Lua API version from cppflags discovery ldflags print derived additional LDFLAGS necessary (TODO) libs print derived additional LIBS necessary (TODO) libversion print derived Lua API version from ldflags/libs discovery luac print path to luac utility ($(printf "${GLOB_LUA}" | tr ':' ' ')) lua print path to lua interpreter ($(printf "${GLOB_LUAC}" | tr ':' ' ')) package.path print preferred module install path package.cpath print preferred C module install path ccname print CC name (e.g. sunpro, clang, gcc) evalmacro run internal macro evaluator for debugging testsym run internal library symbol reader for debugging This utility is used to derive compiler flags and filesystem paths necessary to utilize Lua, LuaJIT, and particular versions thereof. On success it prints the requested information and exits with 0, otherwise it fails with an exit status of 1. Note that cppflags may not print anything if no additional flags are required to compile against the requested API version. When searching, the highest Lua version is preferred. Searching stops once the highest version in the allowable range is found unless the -s flag is specified. LuaJIT is treated like any other Lua installation. If an explicit LuaJIT version or range is specified, then only LuaJIT installations will match. To exclude LuaJIT entirely use the -J switch. This utility processes the environment variables CC, CPPFLAGS, LDFLAGS, and PATH if present. If recursion is requested, then directories specified in CPPFLAGS, LDFLAGS, and PATH are also recursed. If the environment variable CPPFLAGS is empty and no -I options are specified directly, then /usr/include and /usr/local/include are used when probing for cppflags and API version. Report bugs to EOF } version() { cat <<-EOF luapath $MYVERSION vendor $MYVENDOR release $MYVERSION EOF } while getopts I:L:P:d:De:krm:xsv:j:JVh OPT; do case "${OPT}" in I) INCDIRS="${INCDIRS:-}${INCDIRS:+:}${OPTARG}" ;; L) LIBDIRS="${LIBDIRS:-}${LIBDIRS:+:}${OPTARG}" ;; P) BINDIRS="${BINDIRS:-}${BINDIRS:+:}${OPTARG}" ;; d) SANDBOX="${OPTARG}" ;; D) SANDBOX= ;; e) GLOB="${GLOB:-}${GLOB:+:}${OPTARG}" ;; k) PKGCONFIG="$(command -v pkg-config || true)" ;; r) RECURSE=yes ;; m) if [ "${#OPTARG}" -eq 0 -o -n "${OPTARG##[0123456789]}" ]; then panic "%s: invalid maxdepth" "${OPTARG}" fi MAXDEPTH="${OPTARG}" ;; x) # # NOTE: This option was # # -x do not cross device mounts when recursing # # but is currently unsupported as our built-in glob function # does not implement this functionality. Previously this # option caused -xdev to be added to invocations of find(1). ;; s) SHORTEST=yes ;; v) MIN=${OPTARG%%[,:-]*} MAX=${OPTARG##*[,:-]} API_MIN="$(lua2num ${MIN:-0} 0)" API_MAX="$(lua2num ${MAX:-99} 99)" if [ "${API_MIN}" -gt "${API_MAX}" ]; then panic "%s: invalid version range" "${OPTARG}" fi ;; j) MIN=${OPTARG%%[,:-]*} MAX=${OPTARG##*[,:-]} JIT_MIN="$(jit2num ${MIN:-0} 0 0)" JIT_MAX="$(jit2num ${MAX:-99} 99 99)" if [ "${JIT_MIN}" -gt "${JIT_MAX}" ]; then panic "%s: invalid version range" "${OPTARG}" fi JIT_REQ=yes ;; J) JIT_REQ=skip ;; V) version exit 0 ;; h) usage exit 0 ;; *) usage >&2 exit 1 ;; esac done shift $(($OPTIND - 1)) [ "${RECURSE}" = "yes" ] || MAXDEPTH=0 for U in "${CC}" grep od rm rmdir sed xargs; do ! command -v "${U}" >>/dev/null 2>&1 || continue # ${CC} might have trailing flags or invoke the compiler through env ! command -v "${U%% *}" >>/dev/null 2>&1 || continue warn "%s: command not found" "${U}" done if [ -n "${SANDBOX}" ]; then if [ "${SANDBOX}" = "${SANDBOX%/}" ]; then if [ ! -c "${DEVRANDOM}" ]; then # TODO: expand DEVRANDOM into set of different possibilities to check panic "%s: no character random device available" "${DEVRANDOM}" fi TMP="${SANDBOX}$(od -An -N8 -tx1 < ${DEVRANDOM} 2>>/dev/null | tr -d ' ')" if [ ${#TMP} -ne $((${#SANDBOX} + 16)) ]; then panic "%s: unable to generate random suffix" "${SANDBOX}" fi SANDBOX="${TMP}" trap "cd .. && rm -f -- ${SANDBOX}/* && rmdir -- ${SANDBOX}" EXIT fi if [ ! -d "${SANDBOX}" ]; then OMASK="$(umask)" umask 0777 mkdir -m0550 -- "${SANDBOX}" || exit 1 umask ${OMASK} fi cd ${SANDBOX} fi CPPDIRS="$(idirs "${CPPFLAGS:-}")" if [ -z "${CPPDIRS}" -a -z "${INCDIRS}" ]; then INCDIRS="/usr/include:/usr/local/include" fi LDDIRS="$(ldirs "${LDFLAGS:-}")" if [ -z "${LDDIRS}" -a -z "${LIBDIRS}" ]; then LIBDIRS="/lib:/usr/lib:/usr/local/lib" fi case "${1:-}" in cppflags) findversion || exit 1 [ "${API_VER:-0}" -gt 0 ] || exit 1 [ -z "${API_DIR:-}" ] || printf -- "-I${API_DIR}\n" ;; version) findversion || exit 1 printf "$(((${API_VER} / 100) % 100)).$((($API_VER) % 100))\n" ;; ldflags) findlib [ "${LIBLUA_VER:-0}" -gt 0 ] || exit 1 if [ "${#LIBLUA_DIR}" -gt 0 ]; then printf -- "-L%s\n" "${LIBLUA_DIR}" fi ;; libs) findlib [ "${LIBLUA_VER:-0}" -gt 0 ] || exit 1 printf -- "-l%s\n" "${LIBLUA_LIB}" ;; libv*) findlib [ "${LIBLUA_VER:-0}" -gt 0 ] || exit 1 printf "$(((${LIBLUA_VER} / 100) % 100)).$((($LIBLUA_VER) % 100))\n" ;; luac) shift if [ $# -gt 0 ]; then append GLOB $* fi findluac || exit 1 printf -- "${LUAC_PATH}\n" ;; lua) shift if [ $# -gt 0 ]; then append GLOB $* fi findlua || exit 1 printf -- "${LUA_PATH}\n" ;; ldir|cdir) # # ldir and cdir were deprecated on 2014-12-18. On 2016-03-25 they # were revived because their names are more intuitive than # package.path and package.cpath. For now try to support the # semantics of both by assuming interpreter glob patterns only match # file names, while preferred install directory string.match # expressions have directory components. # if true; then MODE="${1}" # move command to end; rotates to ${1} after loop set -- "$@" "${1}" shift cdir_I=0 cdir_N="$(($# - 1))" while [ "${cdir_I}" -lt "${cdir_N}" ]; do if [ "${1#*/}" = "${1}" ]; then append GLOB "${1}" warn "%s: passing glob patterns to %s is deprecated" "${1}" "${MODE}" else set -- "$@" "${1}" fi shift cdir_I=$((${cdir_I} + 1)) done fi findlua || exit 1 findinstalldir "$@" || exit 1 ;; package.path|package.cpath) findlua || exit 1 findinstalldir "$@" || exit 1 ;; ccname) ccname ;; evalmacro) shift evalmacro $* ;; testsym) shift if testsym $*; then printf "found\n" exit 0 else printf "not found\n" exit 1 fi ;; *) if [ -n "${1:-}" ]; then warn "%s: unknown command" "${1}" else warn "no command specified" fi exit 1 ;; esac exit 0 cqueues-rel-20161214/mk/macros.ls000077500000000000000000000041371302435770500164570ustar00rootroot00000000000000#!/bin/sh # # List specified macros # set -e : ${CC:=cc} : ${CPPFLAGS:=} : ${MACRO:=.} : ${VENDOR:=} : ${INCLUDE:=} : ${TMPFILE:=.$(basename $0).c} : ${PREPEND:=} : ${EXPAND:=no} NL=" " usage() { cat <<-EOF usage: $(basename $0) -m:v:i:t:xh -i INCLUDE include file (e.g. ) -m REGEX regular expression macro name filter -s PATH path to supplemental list, one per line -t TMPFILE compiler intermediate tmp file -v VENDOR compiler vendor name (e.g. gcc, clang, sunpro) -x expand macros -h print this usage message Report bugs to EOF } while getopts i:m:s:t:v:xh OPT; do case "${OPT}" in i) case "${OPTARG}" in \<*) INCLUDE="${INCLUDE}#include ${OPTARG}${NL}" ;; \"*) INCLUDE="${INCLUDE}#include ${OPTARG}${NL}" ;; *) INCLUDE="${INCLUDE}#include <${OPTARG}>${NL}" ;; esac ;; m) MACRO="${OPTARG}" ;; s) PREPEND="${OPTARG}" ;; t) TMPFILE="${OPTARG}" ;; v) VENDOR="${OPTARG}" ;; x) EXPAND=yes ;; h) usage exit 0 ;; *) usage >&2 exit 1 ;; esac done vendor() { SCRIPT="$(dirname $0)/vendor.cc" if [ -n "${SCRIPT}" -a -x "${SCRIPT}" ]; then env CC="${CC}" ${SCRIPT} else ${CC} -E - <<-EOF | awk '/sunpro/||/clang/||/gcc/||/other/{ print $1; exit; }' #if defined __SUNPRO_C sunpro #elif defined __clang__ clang #elif defined __GNUC__ gcc #else other #endif EOF fi } filter() { awk "\$1~/^#define/ && \$2~/${MACRO}/{ print \$2 }" } macros() { if [ -n "${PREPEND}" ]; then cat "${PREPEND}" fi case "${VENDOR:-$(vendor)}" in *sunpro*) trap "rm -f ${TMPFILE}" EXIT echo "${INCLUDE}" >| ${TMPFILE} ${CC} ${CPPFLAGS} -xM ${TMPFILE} | awk '/\.h$/{ print $2 }' | sort -u | xargs cat | filter rm ${TMPFILE} ;; *) echo "${INCLUDE}" | ${CC} ${CPPFLAGS} -dM -E - | filter ;; esac } expand() { if [ "${EXPAND}" = "yes" ]; then (echo "${INCLUDE}"; awk '{ print "\"<<<< "$1" >>>>\" "$1 }') | ${CC} ${CPPFLAGS} -E - | awk '$1~/^"<<<>>>") + 5) }' else cat fi } macros | sort -u | expand cqueues-rel-20161214/mk/runlua000077500000000000000000000477061302435770500160750ustar00rootroot00000000000000#!/bin/sh # # Copyright (C) 2015-2016 William Ahern # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # ---------------------------------------------------------------------------- # DESCRIPTION # # runlua is a POSIX shell script for locating and invoking specific Lua # interpreter versions. For example, the environment's Lua 5.1 interpreter # might be named lua, lua5.1, lua51, luajit, luajit2.0.2, etc. runlua # automates this difficult task in a safe, portable manner. runlua is # regularly tested on Linux, OS X, Solaris, AIX, FreeBSD, NetBSD, and # OpenBSD. And runlua safely handles all special characters encountered in # option arguments, directory and command paths, and shell variables. # # To execute a simple statement in either a Lua 5.2 or 5.3 interpreter: # # runlua -r5.2-5.3 -e "print(_VERSION)" # # The command-line options to runlua are a superset of the standard Lua # interpreter. Run `runlua -h` for a description of each option. # # Shebang (#!) Execution # # In addition to explicit invocation, runlua supports two modes of shebang # execution: # # #!/path/to/runlua -r5.2 # print"running Lua code!" # # and # # #!/bin/sh # _=[[ # echo "running shell code!" # exec runlua -r5.2 "$0" "$@" # ]] # print "running Lua code!" # # Only Linux and OS X support the first mode. The second is portable in # practice--although POSIX does not require sh to be located at # /bin/sh, it nonetheless can be invoked from that location on all the # environments I've tested. And POSIX effectively requires shells to parse # and execute scripts command-by-command, so no shell should read past the # exec line. # # Also, the first mode requires a fully qualified path name, whereas with # the second mode the shell code in the header could locate runlua # dynamically. For example, a regression or example script in a project # repository might have a header like # # #!/bin/sh # _=[[ # PATH="${PATH}:$(dirname "$0")/../bin" # exec "$(dirname "$0")/../contrib/runlua" "$0" "$@" # ]] # local mymodule = require"mymodule" # -- ... # # which will work regardless of the current working directory when invoking # the script. # ---------------------------------------------------------------------------- # PORTING NOTES # # unset) On NetBSD (confirmed up to 6.1.5) unset NAME will exit with a # failure status if no such variable is set. If errexit (set -e) is # enabled then the shell will exit. See NetBSD PR 49595. # # #!) Linux and OS X permit recursive shebang execution, which some users # might wish to take advantage of. However, neither will field-split # interpreter arguments, instead passing the remainder of the shebang line # as a single positional argument. So we manually field-split any first # argument. # # Solaris (confirmed 11.1), AIX (confirmed 7.1), OpenBSD (confirmed 5.5), # NetBSD (confirmed 5.1.2, 6.1.1), and FreeBSD (confirmed 9.0) will search # for the interpreter recursively, following shebang interpreter paths # until a binary interpreter is found. But they will not add each # intervening interpreter path to the positional argument list. If you # don't know the paths you cannot execute them recursively. # # $@) On some BSD shells (confirmed NetBSD 5.1.2, 6.1.1, OpenBSD 5.5) # expansion of an empty $@ will wrongly trigger an error if nounset (set # -u) is in effect. # # noclobber) On some BSD shells (confirmed NetBSD 5.1.2, 6.1.1) the # noclobber (set -C) option will wrongly cause redirection to /dev/null # using the redirect operator (">") to fail. Use the appending redirect # operator (">>") as a workaround. # # trap EXIT) ksh88 (confirmed AIX 7.1) wrongly executes an EXIT trap when # the calling function returns, rather than when the shell exits. Note # ksh93 does not exhibit this bug. # # $@ and null IFS) ksh88 (confirmed AIX 7.1) pdksh (confirmed pdksh 5.2.14) # and pdksh derivatives (confirmed OpenBSD 5.6 ksh, NetBSD 6.1 ksh) will # expand $@ as a single field if IFS is null (set but empty). As a # workaround we set IFS to a control character when juggling paths. ksh93, # bash, and ash correctly expand $@ when IFS is null. # set -e # strict error set -u # don't expand unbound variable set -f # disable pathname expansion set -C # noclobber unset IFS "unalias" -a _LC_ALL="${LC_ALL+X}${LC_ALL-}" export LC_ALL=C : ${PATH:=$(command -p getconf PATH)} : ${TMPDIR:=/tmp} : ${RUNLUA_E:=} : ${RUNLUA_R:=1-} : ${RUNLUA_J:=0-} : ${RUNLUA_M:=6} : ${RUNLUA_S:=} : ${RUNLUA_T:=} : ${RUNLUA_D:=} : ${RUNLUA_P:=} MYVERSION=20160816 MYVENDOR="william@25thandClement.com" TMPWD= warn() { if [ "${RUNLUA_D#!}" -gt 0 -a -t 2 ]; then printf "\033[0;31m%s: %.0s${1}\033[0m\n" "${0##*/}" "$@" >&2 else printf "%s: %.0s${1}\n" "${0##*/}" "$@" >&2 fi } panic() { warn "$@" exit 1 } debug() { if [ "${RUNLUA_D#!}" -gt 0 ]; then printf "%s: %.0s${1}\n" "${0##*/}" "$@" >&2 fi } dump() { if [ "${RUNLUA_D#!}" -gt 1 ]; then printf "%s: %.0s${1}\n" "${0##*/}" "$@" >&2 fi } # see porting note "$@ and null IFS" null_ifs() { IFS= set -- x y z set -- "$@" unset IFS [ $# -gt 1 ] || printf "\2" } # glob PATTERN [MAXDEPTH] [EXEC-COMMAND] [INTERNAL:GLOB-COUNT] # # Recursive tree descending pathname generator combining features of glob(3) # and find(3). # glob() { glob_N="${4:-0}" IFS= set +f for glob_F in ${1}; do [ -e "${glob_F}" ] || continue if eval "${3:-printf '%s\\n'} \"\${glob_F}\""; then glob_N=$((${glob_N} + 1)) fi done set -f unset IFS if [ "${2-0}" -gt 0 ]; then glob "${1%/*}/*/${1##*/}" "$((${2} - 1))" "${3:-}" "${glob_N}" || : fi [ "${glob_N}" -gt 0 ] } tempnam() { printf "%s-%s\n" "${1}" "$(od -An -N8 -tx1 -v /dev/urandom 2>>/dev/null | tr -cd '0123456789abcdef')" } # chomp VARIABLE SUFFIX chomp() { chomp_K="${1}" chomp_C="${2}" eval "chomp_V=\"\${${chomp_K}}\"" while [ "${chomp_V%${chomp_C}}" != "${chomp_V}" ]; do chomp_V="${chomp_V%${chomp_C}}" done eval "${chomp_K}=\"\${chomp_V}\"" } # istrue STRING istrue() { case "${1#!}" in [TtYy1]*) return 0 ;; *) return 1 ;; esac } # isinteger STRING isinteger() { I="${1}" [ "${#I}" -gt 0 ] || return 1 while [ "${#I}" -gt 0 ]; do [ "${I##[0123456789]}" != "${I}" ] || return 1 I="${I##[0123456789]}" done } # isdir PATH isdir() { [ "${#1}" -gt 0 ] && [ -d "${1}" ] } # ver2num STRING [MAJOR] [MINOR] [PATCH] ver2num() { M="${2:-0}" m="${3:-0}" p="${4:-0}" IFS="." set -- ${1} unset IFS if isinteger "${1:-}"; then M=${1} fi if isinteger "${2:-}"; then m=${2} fi if isinteger "${3:-}"; then p=${3} fi printf "$((${M} * 10000 + ${m} * 100 + ${p}))\n" } num2ver() { M=$((${1} / 10000 % 100)) m=$((${1} / 100 % 100)) p=$((${1} % 100)) printf "${M}.${m}.${p}\n" } api2num() { n="$(ver2num "$@")" printf "$((${n} / 100))\n" } num2api() { printf "$((${1} / 100 % 100)).$((${1} % 100))\n" } getapi() { if [ -x "${1}" ]; then api2num "$(noenv; cdwd; "${1}" -e 'print(string.match(_VERSION, [[[%d.]+]]))' >/dev/null || true)" fi } getrel() { if [ -x "${1}" ]; then ver2num "$(noenv; cdwd; "${1}" -v &1 | sed -ne 's/^Lua[^ ]* \([0-9][0-9\.]*\).*/\1/p' | head -n1)" fi } trypth() { trypth_LUA="${1}" shift 1 for L; do dump "testing LD_PRELOAD=\"%s\" %s" "${L}" "${trypth_LUA}" # glibc's ld.so ignores LD_PRELOAD errors. It does emit a # diagnostic to stderr, so we redirect stderr to stdout so # that it corrupts our expected output. # # NB: musl's ld.so ignores LD_PRELOAD errors, but never # issues a diagnostic. There's no way to distinguish success # and failure. TMP="$(noenv; cdwd; LD_PRELOAD="${L}" "${trypth_LUA}" -e 'print"OK"' &1 || true)" [ "${TMP}" = "OK" ] && printf "%s\n" "${L}" && return 0 dump "unable to preload %s (expected 'OK', got '%s')" "${L}" "$(printf "%s" "${TMP}" | tr '\n' ' ')" done return 1 } getpth() { case "${getpth_UNAME:=$(uname -s)}" in Linux) # Linux/glibc supports loading libpthread through a DSO, but # glibc has several bugs, including a race in libdl. # # Linux/musl unifies libpthread with libc. But it's a noop # to preload libpthread.so.0 so we don't bother trying to # distinguish musl. set +f trypth "${1}" "libpthread.so.0" /lib/*/libpthread.so* /lib/libpthread.so* && return 0 set -f ;; FreeBSD|NetBSD|OpenBSD) # Neither FreeBSD, NetBSD, nor OpenBSD support loading # libpthread through a DSO. NetBSD requires a full path. set +f trypth "${1}" "libpthread.so" /usr/lib/libpthread.so* && return 0 set -f ;; *BSD*) # Assume other BSDs are similar and make a best effort. set +f trypth "${1}" "libpthread.so" /usr/lib/libpthread.so* && return 0 set -f debug "skipping libpthread preloading (unknown operating system: %s)" "${getpth_UNAME}" return 0 ;; Darwin) # libpthread.dylib integrated with libSystem.dylib return 0 ;; SunOS) # libpthread integrated with libc since Solaris 10 getpth_V="$(ver2num "$(uname -v)")" getpth_M="$(ver2num "10")" [ ${getpth_V} -ge ${getpth_M} ] && return 0 trypth "${1}" "libpthread.so" && return 0 ;; oops) # for regression testing ;; *) debug "skipping libpthread preloading (unknown operating system: %s)" "${getpth_UNAME}" return 0 ;; esac panic "unable to locate libpthread" } cdwd() { if [ ${#TMPWD} -gt 0 ]; then cd "${TMPWD}" fi } mkwd() { if [ -d "/dev" -a ! -w "/dev" ]; then TMPWD="/dev" debug "reusing /dev as non-writable working directory" else TMPWD="$(tempnam "${TMPDIR}/${0##*/}")" debug "creating non-writable working directory %s" "${TMPWD}" mkdir -m0500 "${TMPWD}" fi } trap "rmwd" EXIT # see portability note "trap EXIT" rmwd() { if [ ${#TMPWD} -gt 0 -a "${TMPWD}" != "/dev" -a -d "${TMPWD}" ]; then debug "removing working directory %s" "${TMPWD}" rmdir -- "${TMPWD}" TMPWD= fi } # set LUA_API and LUA_PRELOAD if LUA was specified from environment checklua() { case "${LUA_API:-}" in [0123456789].[0123456789]) ;; *) # need absolute path if [ "${LUA#*/}" = "${LUA}" ]; then LUA="$(command -v "${LUA}" || panic "%s: cannot find path" "${LUA}")" debug "using %s" "${LUA}" fi V="$(getapi "${LUA}")" LUA_API="$(num2api ${V:-0})" debug "%s API is %s" "${LUA}" "${LUA_API}" ;; esac if istrue "${RUNLUA_T}"; then if ! istrue "${LUA_PRELOAD+true}"; then LUA_PRELOAD="$(getpth "${LUA}")" [ -z "${LUA_PRELOAD}" ] || debug "preloading %s" "${LUA_PRELOAD}" fi fi } findlua() { if [ -n "${LUA:-}" ]; then checklua printf "%s\n" "${LUA}" return 0 fi mkwd TMP="${RUNLUA_R#!}" find_API_MIN="$(api2num "${TMP%%[,:-]*}" 1 0)" find_API_MAX="$(api2num "${TMP##*[,:-]}" 99 99)" TMP="${RUNLUA_R#!}" find_REL_MIN="$(ver2num "${TMP%%[,:-]*}" 1 0 0)" find_REL_MAX="$(ver2num "${TMP##*[,:-]}" 99 99 99)" TMP="${RUNLUA_J#!}" find_JIT_MIN="$(ver2num "${TMP%%[,:-]*}" 1 0 0)" find_JIT_MAX="$(ver2num "${TMP##*[,:-]}" 99 99 99)" found_PATH= found_API=0 found_REL=0 # leverage shell pathname expansion to locate interpreter by # iterating over $PATH directories and letting shell do the search IFS=: set -- ${PATH} unset IFS for D; do # get abspath because getapi and getrel cd to working directory D="$(! cd "${D}" 2>>/dev/null || pwd)" [ ${#D} -gt 0 ] || continue set -- "false" # see porting note "$@" IFS="${NULL_IFS="$(null_ifs)"}" set +f if [ ! $find_JIT_MIN -gt 0 ]; then # PUC Lua set -- "$@" ${D}/lu[a] ${D}/lua5* ${D}/lua-5* fi if [ $find_JIT_MAX -gt 0 ]; then # LuaJIT set -- "$@" ${D}/luajit* fi set -f unset IFS for F; do [ "${F}" != "false" ] || continue dump "testing %s" "${F}" # NB: match only a narrow range of basenames so we # don't execute something like luatex or lua-tool B="${F##*/}" # strip pre-release suffix from basename (luajit-2.1.0-alpha) B="${B%[._-]alpha*}" B="${B%[._-]beta*}" B="${B%[._-]dev*}" # strip version suffix from basename (lua51, luajit-2.0.3) while [ "${B}" != "${B%%[0123456789._-]}" ]; do B="${B%%[0123456789._-]}" done # skip if basename isn't "lua" or "luajit" [ "${B}" = "lua" ] || [ "${B}" = "luajit" ] || continue V="$(getapi "${F}")" R="$(getrel "${F}")" : ${V:=0} : ${R:=0} debug "%s is version %s (%s API)" "${F}" "$(num2ver "${R}")" "$(num2api "${V}")" # does it meet our API range criteria? [ ${V} -ge ${find_API_MIN} -a ${V} -le ${find_API_MAX} ] || continue # does it meet our release range criteria? if [ ${R} -lt ${find_REL_MIN} -o ${R} -gt ${find_REL_MAX} -o ${find_JIT_MIN} -gt 0 ]; then # try luajit release range [ "${B}" = "luajit" ] || continue [ ${R} -ge ${find_JIT_MIN} ] || continue [ ${R} -le ${find_JIT_MAX} ] || continue fi # is it a better fit than what we already found? [ ${V} -gt 0 -a ${V} -ge ${found_API} -a ${R} -gt ${found_REL} ] || continue # does it work if we preload libpthread? if istrue "${RUNLUA_T}"; then found_PTH="$(getpth "${F}")" else found_PTH="" fi found_PATH="${F}" found_API=${V} found_REL=${R} done done rmwd LUA="${found_PATH}" LUA_API="$(num2api "${found_API}")" LUA_PRELOAD="${found_PTH-}" [ -n "${LUA}" ] || panic "unable to locate Lua interpreter" debug "using %s" "${LUA}" [ -n "${LUA_PRELOAD}" ] && debug "preloading %s" "${LUA_PRELOAD}" printf "%s\n" "${LUA}" } # scan_api2key PREFIX VERSION scan_api2key() { case "${2}" in 5.1) printf "%s" "${1}" ;; *) printf "%s_%s" "${1}" "$(printf "%s" "${2}" | tr '.' '_')" ;; esac } scan_addenv() { [ "${#1}" -gt 0 ] || return 0 addenv_K="$(scan_api2key "${2}" "${3}")" debug "adding %s to %s" "${1}" "${addenv_K}" # NB: append left to right (see note in scan routine) eval "addenv_V=\"\${${addenv_K}:-;;}\"" addenv_V="${1};${addenv_V#;}" eval "export ${addenv_K}=\"\${addenv_V}\"" } # callback from glob for paths matching */${LUA_API} scan__add() { isdir "${1}" || return 0 # NB: test in subshell as our glob routine isn't reentrant if (glob "${1}/*.lua" ${RUNLUA_M} ":"); then scan_addenv "${1}/?.lua" LUA_PATH "${LUA_API}" fi if (glob "${1}/*.so" ${RUNLUA_M} ":"); then scan_addenv "${1}/?.so" LUA_CPATH "${LUA_API}" fi } scan() { # NB: process from right to left and append left to right while [ ${#RUNLUA_S} -gt 0 ]; do scan_D="${RUNLUA_S##*;}" if isdir "${scan_D}"; then debug "scanning %s" "${scan_D}" glob "${scan_D}/${LUA_API}" "${RUNLUA_M}" scan__add || : fi RUNLUA_S="${RUNLUA_S%${scan_D}}" chomp RUNLUA_S ";" done } noenv() { for F in $(env | sed -ne 's/^\(LUA_C\{0,1\}PATH[_0123456789]*\).*$/\1/p' -e 's/^\(LUA_INIT[_0123456789]*\).*$/\1/p'); do unset "$F" || true # see porting note "unset" done } setopt() { setopt_VAR="${1}" setopt_NVAL="${2:-}" eval "setopt_OVAL=\"\${${setopt_VAR}:-}\"" # don't set if value prefixed with "!" [ "${setopt_OVAL}" = "${setopt_OVAL#!}" ] || return 0 eval "${setopt_VAR}=\"\${setopt_NVAL}\"" } usage() { cat <<-EOF Usage: ${0##*/} [-e:il:vEr:j:Jms:tdpVh] [PATH [...]] -e STRING execute statement -i enter interactive mode after executing PATH -l STRING require package -v print interpreter version information -E ignore environment variables -r RANGE run specific Lua version -j RANGE run specific LuaJIT version -J exclude LuaJIT from candidate interpreters -m NUMBER limit recursion to NUMBER -s PATH scan directory tree for Lua modules -t preload POSIX threading library -d enable debug logging (twice for even more logging) -p print path of Lua interpreter -V print runlua version information -h print this usage message Examples: -r5.2.1 only run PUC Lua 5.2.1 interpreter -r5.1 run any Lua 5.1 interpreter, including LuaJIT -r5.2-5.3 run any Lua 5.2 or 5.3 interpreter -r5.2- run any Lua 5.2 or later interpreter -j2.1 only run LuaJIT 2.1 interpreter Environment: RUNLUA_E=BOOLEAN same as -E if TRUE RUNLUA_R=RANGE same as -r (use !RANGE to override -r) RUNLUA_J=RANGE same as -j (use !RANGE to override -j or -J) RUNLUA_M=NUMBER same as -m RUNLUA_T=BOOLEAN same as -t if TRUE RUNLUA_D=NUMBER set debug level (e.g. same -dd if 2) RUNLUA_P=BOOLEAN same as -p if TRUE Examples: RUNLUA_R=5.2 runlua run any Lua 5.2 interpreter RUNLUA_R=5.3 runlua -r5.2 same as above (-r has higher precedence) RUNLUA_R='!5.3' runlua -r5.2 run any Lua 5.3 interpreter (! overrides -r) BNF: ::= ::= | [VERSION] "-" [VERSION] ::= ["." ["." ]] ::= | ::= "yes" | "true" | "1" ::= "no" | "false" | "0" | "" Report bugs to EOF } version() { cat <<-EOF runlua $MYVERSION vendor $MYVENDOR release $MYVERSION EOF } # # Field-split first argument. See porting note "#!". # # If we only have one argument than it's either a script path or we're not # running as a shebang interpreter. If we have zero then we don't want to # accidentally field-split the script path becaues it might contain spaces. # if [ $# -ge 2 ]; then TMP="${1}" shift 1 IFS=" " set -- ${TMP} "$@" unset IFS fi ARGC=0 pusharg() { eval "ARG${ARGC}=\"\${1}\"" ARGC=$((${ARGC} + 1)) } while getopts "e:il:vEr:j:Js:tdpVh" OPTC; do case "${OPTC}" in e) pusharg "-e" pusharg "${OPTARG}" ;; i) pusharg "-i" ;; l) pusharg "-l" pusharg "${OPTARG}" ;; v) pusharg "-v" ;; E) setopt RUNLUA_E yes ;; r) setopt RUNLUA_R "${OPTARG}" ;; j) setopt RUNLUA_J "${OPTARG}" ;; J) setopt RUNLUA_J "0-0" ;; m) setopt RUNLUA_M "${OPTARG}" ;; s) if [ "${RUNLUA_S}" = "${RUNLUA_S#!}" ]; then MAIN_S="${MAIN_S-}${MAIN_S:+;}${OPTARG}" fi ;; t) setopt RUNLUA_T yes ;; d) MAIN_D="$((${MAIN_D:-0} + 1))" ;; p) setopt RUNLUA_P yes ;; V) version exit 0 ;; h) usage exit 0 ;; *) usage >&2 exit 1 ;; esac done shift $((${OPTIND} - 1)) # # Prepend our argument stack to the positional list. # if [ ${ARGC} -gt 0 ]; then # first append to our positional list I=0 while [ ${I} -lt ${ARGC} ]; do eval "ARG=\"\${ARG${I}}\"" if [ $# -gt 0 ]; then set -- "$@" "${ARG}" else set -- "${ARG}" fi I=$(($I + 1)) done # then rotate left I=0 N=$(($# - ${ARGC})) while [ ${I} -lt ${N} ]; do set -- "$@" "${1}" shift 1 I=$(($I + 1)) done fi RUNLUA_M="${RUNLUA_M#!}" isinteger "${RUNLUA_M}" || panic "%s: not an integer" "${RUNLUA_M}" RUNLUA_S="${MAIN_S-}${MAIN_S:+;}${RUNLUA_S#!}" chomp RUNLUA_S ";" setopt RUNLUA_D "${MAIN_D:-0}" isinteger "${RUNLUA_D#!}" || panic "%s: not an integer" "${RUNLUA_D#!}" if istrue "${RUNLUA_E}"; then noenv fi findlua >>/dev/null if istrue "${RUNLUA_P}"; then printf "%s\n" "${LUA}" exit 0 fi scan if [ ${#_LC_ALL} -gt 0 ]; then LC_ALL="${_LC_ALL#X}" else unset LC_ALL fi if [ -n "${LUA_PRELOAD-}" ]; then export LD_PRELOAD="${LD_PRELOAD-}${LD_PRELOAD:+:}${LUA_PRELOAD}" fi # see portability note "$@" if [ $# -gt 0 ]; then exec "${LUA}" "$@" else exec "${LUA}" fi cqueues-rel-20161214/mk/vendor.cc000077500000000000000000000003461302435770500164350ustar00rootroot00000000000000#!/bin/sh set -e : ${CC:=cc} ${CC} -E - <<-EOF | awk '/sunpro/||/clang/||/gcc/||/other/{ print $1; exit; }' #if defined __SUNPRO_C sunpro #elif defined __clang__ clang #elif defined __GNUC__ gcc #else other #endif EOF cqueues-rel-20161214/mk/vendor.os000077500000000000000000000000241302435770500164620ustar00rootroot00000000000000#!/bin/sh uname -s cqueues-rel-20161214/regress/000077500000000000000000000000001302435770500156665ustar00rootroot00000000000000cqueues-rel-20161214/regress/102-getlookup-getsearch.lua000077500000000000000000000007331302435770500226530ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local config = require"cqueues.dns.config" local txt = io.tmpfile() assert(txt:write([[ search google.com yahoo.com wikipedia.org nameserver 8.8.8.8 ]])) local resconf = config.new() resconf:loadfile(txt) for i,dn in ipairs(resconf:getsearch()) do info("search[%d]: %s", i, dn) end for i,how in ipairs(resconf:getlookup()) do info("lookup[%d]: %s", i, how) end say("OK") cqueues-rel-20161214/regress/125-thread.current-not-cleared-on-error.lua000077500000000000000000000006731302435770500255730ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local cq = cqueues.new() local is_ok = false cq:wrap(function() cq:wrap(function() is_ok = true end) error("an error", 0) end) -- First step should signal an error local ok, err = cq:step() check(not ok and err == "an error", err) -- Second step should succeed check(cq:step()) check(is_ok, "second thread not run") say("OK") cqueues-rel-20161214/regress/130-segfault-on-null-cstack.lua000077500000000000000000000043211302435770500233370ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" runlua "$0" "$@" RC="$?" if [ ${RC} -eq 0 ]; then case ${REGRESS_VERBOSE} in 0) ;; 1) printf "\n%s: OK\n" "${REGRESS_PROGNAME}" ;; *) printf "%s: OK\n" "${REGRESS_PROGNAME}" ;; esac fi exit ${RC} ]] require"regress".export".*" -- -- Issue #130 -- Segfault due to passing NULL cstack to cstack_isrunning from -- condition variable __gc -- -- cqueue_destroy only cleaned up threads on the polling queue, not the -- pending queue. If a thread was waiting on a condition variable, but was -- made pending for another reason (e.g. timeout or other event), then the -- condition variable <-> controller association would still be installed, -- holding a reference to the controller. If the controller was garbage -- collected with the thread still on the pending queue and before the -- condition variable was collected (as it could be if both were destroyed -- in the same cycle, like when both survive until Lua VM destruction time), -- then the condition variable destructor cond__gc would attempt to access a -- pointer to a defunct controller object via the wakecb structure. In this -- particular case, one of the results was a NULL pointer dereference. -- -- The fix was to walk both the thread.polling _and_ thread.pending queues, -- calling thread_del. thread_del breaks any wakecb associations. -- -- stop GC to ensure everything destroyed on exit in same cycle info"stopping GC" collectgarbage"stop" -- instantiate first so cond__gc invoked after cqueue__gc (only required for -- 5.2 and 5.3). local cv1 = check(condition.new()) local cv2 = check(condition.new()) local cq = cqueues.new() local ready = false cq:wrap(function() info"starting thread 1" ready = true -- one condvar to wake us; another to ensure we still -- have a wakecb installed info"thread 1 polling" cqueues.poll(cv1, cv2) end) check(cq:step(), "thread 1 failed to start") cq:wrap(function() info"starting thread 2" -- put 1st thread in pending state info"putting thread 1 into pending state" check(ready, "thread 1 not ready") cv1:signal() -- short-circuit loop so 2nd thread is never run info"short-circuiting loop" error"oops" end) check(not cq:loop(), "loop was expected to fail") cqueues-rel-20161214/regress/141-segfault-on-accept.lua000077500000000000000000000013101302435770500223530ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] -- -- Issue #141 -- Lua/C accept method binding doesn't check whether the -- socket has already been closed, passing a NULL socket object to low-level -- so_accept routine. Fix was to use the lso_checkself utility routine like -- every other method, which will throw an error when passed a closed -- socket. -- require"regress".export".*" info"opening listening socket" local con = socket.listen("localhost", 0) info"calling close" con:close(); -- bug caused NULL dereference in so_clear, invoked from lso_accept info"calling accept" local ok = pcall(con.accept, con, 0); check(not ok, "con:accept didn't throw error as expected") say("OK") cqueues-rel-20161214/regress/145-assertion-on-dead.lua000077500000000000000000000007461302435770500222260ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local co = coroutine.create(function() coroutine.yield() end) coroutine.resume(co) -- kick off coroutine coroutine.resume(co) -- resume a yield with no arguments local status = coroutine.status(co) check(status == "dead", "expected dead coroutine (got %q)", status) local cq = require"cqueues".new() cq:attach(co) cq:step() -- previously would trigger C assert and abort process say"OK" cqueues-rel-20161214/regress/152-thread-integer-passing.lua000077500000000000000000000055261302435770500232550ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua -r5.3- "$0" "$@" ]] require"regress".export".*" local function runtest(testf, ...) -- check that the test runs locally info("running test in main thread") local err = packerror(testf(...)) if err then return nil, err:unpack() end -- check test when running from a thread info("running test in separate thread") local err, thr = packerror(thread.start(function (_, testf, ...) require"regress".export".*" testf = check(load(testf)) local err = packerror(testf(...)) check(not err, "%s", tostring(err)) end, string.dump(testf), ...)) if err then return nil, err:unpack() end local function joinresult(ok, ...) if not ok then return nil, ... elseif (...) then return fileresult(nil, ...) else return true end end return joinresult(fileresult(thr:join(5))) end local function checktest(...) local err = packerror(runtest(...)) check(not err, "%s", tostring(err)) return true end -- -- Assuming (among other things) ones' complement, two's complement, or -- sign-and-magnitude (the only 3 possibilities permissible in a C -- environment) and that lua_Integer has more value bits than lua_Number -- has significand bits, then math.maxinteger shouldn't be representable -- as a lua_Number. -- do local function tofloat(i) check("integer" == math.type(i), "number not an integer (%s)", tostring(i)) local n = i + 0.0 check("float" == math.type(n), "number not a float (given %s, got %s)", tostring(i), tostring(n)) return n end check(math.maxinteger ~= tofloat(math.maxinteger), "math.maxinteger unexpectedly representable as a float") local function test(i, n) if not thread.self() then info("checking integer (%s) and float (%s) equivalence", tostring(i), tostring(n)) end check(i == math.maxinteger, "integer argument does not equal math.maxinteger (%s, %s)", tostring(i), tostring(math.maxinteger)) check(i ~= n, "integer argument unexpectedly equals floating point argument (%s, %s)", tostring(i), tostring(n)) return true end checktest(test, math.maxinteger, tofloat(math.maxinteger)) end -- -- Check that we didn't break floating point in other odd ways. -- do local function ftoa(n) return string.format("%a", n) end local function test(...) local t = pack(...) local function ftoa(n) return string.format("%a", n) end for i=1,t.n,2 do local n = t[i] local s = t[i + 1] if not thread.self() then info("checking float (%s) and string (%s)", tostring(n), tostring(s)) end check(ftoa(n) == s, "floating point test failed (%s, %q)", tostring(n), tostring(s)) check(tonumber(s) == n or math.abs(n) == math.huge, "floating point test failed (%s, %q)", tostring(n), tostring(s)) end return true end checktest(test, 1.1, ftoa(1.1), 1/3, ftoa(1/3), math.huge, ftoa(math.huge), -math.huge, ftoa(-math.huge)) end say"OK" cqueues-rel-20161214/regress/153-dns-resolvers.lua000077500000000000000000000004641302435770500215140ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local main = cqueues.new() collectgarbage "stop" assert(main:wrap(function() local pool = dns.getpool() for _=1, pool.hiwat + 1 do check(pool:query("google.com", 'AAAA', 'IN', 1)) end end):loop()) say"OK" cqueues-rel-20161214/regress/157-interposed-methods-lost.lua000077500000000000000000000024161302435770500235050ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] -- load dns.config first so that subsequent loads of DNS modules overwrite -- the config metatable local config = require"cqueues.dns.config" local resolver = require"cqueues.dns.resolver" -- ensure config mt reloaded require"regress".export".*" -- config.new would fail because the :set method it depended upon is an -- interposed Lua function added to the userdata metatable. When subsequent -- modules which depended on the metatable definition were loaded, they -- called dnsL_loadall. dnsL_loadall defined all the internal metatables, -- which since commit 4d66661 had the effect of replacing any pre-existing -- __index field with a new table containing only the original C-defined -- methods. Previously cqs_newmetatable short-circuited when a metatable -- existed. The purpose of 4d66661 was to permit forced reloading of all Lua -- modules by clearing package.loaded; the original behavior resulted in -- modules interposing the same functions multiple times. info"creating resolver config object" local cfg = config.new{ nameserver = { "1.2.3.4" }, lookup = { "file", "bind", "cache" }, options = { edns0 = true }, } info("resolv.conf:") for ln in tostring(cfg):gmatch("[^\n]+") do info(" %s", ln) end say"OK" cqueues-rel-20161214/regress/174-cq-close.lua000077500000000000000000000025241302435770500204160ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local mainloop = cqueues.new() local cv = condition.new() local cq = cqueues.new() local closed = false local polling, polled = false, false local tests = 0 local okays = 0 local function test(...) tests = tests + 1 mainloop:wrap(function (f, ...) f(...) okays = okays + 1 end, ...) end test(function () local function checkresult(ready) info"awoke on controller readiness" polling = false polled = true check(closed == true, "spurious wakeup") check(ready == cq, "cqueue not cancelled") check(cqueues.type(cq), "cqueue not closed") end cv:signal() polling = true info"polling controller" checkresult(cqueues.poll(cq, 3)) end) test(function () check(not polled, "polling thread unexpectedly finished") if not polling then info"waiting on polling thread" cv:wait(5) check(polling, "expected polling thread") end closed = true info"closing controller" cq:close() check(not pcall(cq.wrap, cq), "cqueue should not allow new threads when closed") -- previously would trigger a segfault, should now error info"closing controller again" cq:close() info("cq:close() was safely called a second time") end) ok, why = mainloop:loop() check(ok, "%s", why) check(tests == okays, "expected %d okays, got %d", tests, okays) say"OK" cqueues-rel-20161214/regress/20-listen-dgram.lua000077500000000000000000000004411302435770500212000ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local main = cqueues.new() main:wrap(function () local con = socket.listen{ host = "127.0.0.1", port = 0, type = socket.SOCK_DGRAM } check(con:listen()) end) check(main:loop()) say("OK") cqueues-rel-20161214/regress/22-client-dtls.lua000077500000000000000000000056231302435770500210450ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local context = require"openssl.ssl.context" local function exists(path) local fh = io.open(path, "r") if fh then fh:close() return true else return false end end -- return integer version of openssl(1) command-line tool at path local function openssl_version(path) local fh = io.popen(string.format("%s version", path), "r") local ln = (fh and fh:read()) or "" if fh then fh:close() end local M, m, p if ln:match"LibreSSL" then p, M, m = 0, ln:match("(%d+)%.(%d+)") else M, m, p = ln:match("(%d+)%.(%d+)%.(%d+)") end if M then return (tonumber(M) * 268435456) + (tonumber(m) * 1048576) + (tonumber(p) * 4096) end end -- find most recent version of openssl(1) command-line tool local function openssl_path() local paths = check(os.getenv"PATH", "no PATH in environment") local path = nil local version = 0 for D in paths:gmatch("[^:]+") do local tmp_path = D .. "/openssl" local tmp_version = exists(tmp_path) and openssl_version(tmp_path) if tmp_version and tmp_version > version then info("found %s (%x)", tmp_path, tmp_version) path = tmp_path version = tmp_version end end return version > 0 and path end local function openssl_popen(path) local key, crt = genkey() local tmpname = os.tmpname() local tmpfile = check(io.open(tmpname, "w")) check(tmpfile:write(key:toPEM"private")) check(tmpfile:write(tostring(crt))) check(tmpfile:flush()) tmpfile:close() local perl_main = [[ use POSIX; use strict; my ($openssl, $key) = @ARGV; my $pid = fork; die "$!" unless defined $pid; # exec openssl in child exec $openssl, "s_server", "-quiet", "-dtls1", "-key", $key, "-cert", $key or die "$!" if $pid == 0; ; # wait for EOF unlink $key; while ($pid != (my $rpid = waitpid($pid, WNOHANG))) { die "$!" if $rpid == -1; #print STDERR "killing $pid\n"; kill 9, $pid; sleep 1; } #print STDERR "reaped $pid\n"; 1; EOF ]] local perl_begin = ([[ my @code; while () { last if m/^\s+EOF\s+$/; push @code, $_; } eval join("", @code) or die $@; ]]):gsub("%s+", " ") local function quote(txt) return string.format("'%s'", txt:gsub("'", "'\"'\"'")) end local perl_cmd = string.format("perl -e %s %s %s", quote(perl_begin), quote(path), quote(tmpname)) local fh = check(io.popen(perl_cmd, "w")) fh:write(perl_main) fh:flush() cqueues.sleep(1) -- wait for server to begin listening return fh end local main = cqueues.new() assert(main:wrap(function () -- spin up DTLS server using openssl(1) command-line utility local fh = openssl_popen(check(openssl_path(), "no openssl command-line utility found")) -- create client socket local con = socket.connect{ host="localhost", port=4433, type=socket.SOCK_DGRAM }; check(fileresult(con:starttls(context.new"DTLSv1", 3))) fh:close() end):loop()) say"OK" cqueues-rel-20161214/regress/23-eventfd.lua000077500000000000000000000014351302435770500202540ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] -- -- Simple test of controller alerts to check whether our eventfd support -- works and didn't break anything else. A controller triggers its alert -- event when adding a new coroutine and it's not currently looping. -- require"regress".export".*" local main = cqueues.new() local sub = cqueues.new() local sub_islooping = false local sub_alerted = false assert(main:wrap(function() sub_islooping = true assert(sub:loop()) end)) assert(main:step(0)) assert(sub_islooping) assert(main:wrap(function () assert(not sub_alerted) -- this should cause the main loop to resume the sub loop assert(sub:wrap(function() sub_alerted = true end)) end)) assert(main:loop(3)) check(sub_alerted, "sub loop never woke up") say"OK" cqueues-rel-20161214/regress/30-starttls-completion.lua000077500000000000000000000027121302435770500226450ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] -- -- Originally socket:starttls did not poll waiting for handshake completion, -- but instead simply placed the socket into TLS handshake mode and -- immediately returned. As part of a larger refactoring to improve the -- consistency of timeouts across the API, socket:starttls was changed to -- accept a timeout parameter and to poll for completion before returning. -- An explicit timeout of 0 seconds is necessary to return immediately. -- -- However, early TLS example code called socket:starttls outside of any -- event loop. So as to not break such code, if no timeout was provided -- socket:starttls attempted to detect whether it was running inside an -- event loop, and if not exhibited the original behavior of returning -- immediately rather than polling indefinitely. -- -- Since then, cqueues.poll has become capable of polling outside of an -- event loop, albeit with the side-effect of blocking the application. To -- improve consistency the backward's compatibility behavior has been -- removed. socket:starttls now unconditionally polls for completion of the -- handshake, up to any specified timeout. -- require"regress".export".*" local so = socket.connect("google.com", 443) local ok, why = auxlib.fileresult(so:starttls()) check(ok, "STARTTLS failed: %s", why) local ssl = check(so:checktls(), "no SSL object") local crt = ssl:getPeerCertificate() check(crt ~= nil, "bug not fixed") say("OK") cqueues-rel-20161214/regress/44-resolvers-gc.lua000077500000000000000000000010451302435770500212340ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua -j- "$0" "$@" ]] require"regress".export".*" local resolvers = require"cqueues.dns.resolvers" local function caughtleak() local pool = resolvers.stub() local gotleak pool:onleak(function () gotleak = true end) pool:get() for i=1,10 do collectgarbage"collect" end return gotleak end check(caughtleak(), "resolver leak not detected") if jit then _VERSION = "Lua 5.2" -- pretend that we support __gc on tables check(not caughtleak(), "expected not to catch leak") end say("OK") cqueues-rel-20161214/regress/51-join-defunct-thread.lua000077500000000000000000000040161302435770500224520ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua -t -j- "$0" "$@" ]] require"regress".export".*" check(jit, "LuaJIT required") check(errno.EOWNERDEAD, "EOWNERDEAD not defined") local thr, con = assert(thread.start(function (con) local errno = require"cqueues.errno" local ffi = require"ffi" require"regress".export".*" -- -- NOTE: On musl-libc the parent process deadlocks on flockfile as -- apparently the thread dies when writing to stderr. Log to our -- communications socket instead, which doesn't hold any locks. -- local function info(...) con:write(string.format(...), "\n") con:flush() end info"calling prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT)" local PR_SET_SECCOMP = 22 local SECCOMP_MODE_STRICT = 1 ffi.cdef"int prctl(int, unsigned long, unsigned long, unsigned long, unsigned long)" local ok, rv = pcall(function () return ffi.C.prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0) end) if not ok then local rv = string.match(rv, "[^:]+:[^:]+$") or rv info("prctl call failed: %s", rv) elseif rv ~= 0 then info("prctl call failed: %s", errno.strerror(ffi.errno())) else info"attempting to open /nonexistant" io.open"/nonexistant" -- should cause us to be killed info"prctl call failed: still able to open files" end info"calling pthread_exit" ffi.cdef"void pthread_exit(void *);" ffi.C.pthread_exit(nil) info"pthread_exit call failed: thread still running" end)) local main = check(cqueues.new()) -- read thread's log lines from pcall because socket:read will throw an -- error when the socket is closed asynchronously below. check(main:wrap(pcall, function () for ln in con:lines() do info("%s", ln) end end)) check(main:wrap(function () local ok, why, code = auxlib.fileresult(thr:join(5)) check(not ok, "thread unexpectedly joined (%s)", why or "no error") check(code == errno.EOWNERDEAD or code == errno.ETIMEDOUT, "unexpected error: %s", why) check(code == errno.EOWNERDEAD, "robust mutex strategy not supported on this system") con:close() end)) check(main:loop()) say"OK" cqueues-rel-20161214/regress/59-catch-special-only.lua000077500000000000000000000013341302435770500223070ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local cq = cqueues.new() local MAXSTACK = 1000000 + 10 local co = coroutine.create(function(...) cq:wrap(function (...) for i=1,MAXSTACK do if 0 == i % math.floor(MAXSTACK / 100) then info("%d", i) end cqueues.poll(0) local n = coroutine.yield(i) check(n == i + 1) end end, ...) local ok, why, errno, thr = cq:loop() if not ok then io.stderr:write(debug.traceback(thr)) check(false) end return "done" end) for i=1,MAXSTACK do local ok, j = coroutine.resume(co, i) check(i == j) end check("done" == select(2, coroutine.resume(co, MAXSTACK + 1))) check(coroutine.status(co) == "dead") say("OK")cqueues-rel-20161214/regress/61-multiwriters.lua000077500000000000000000000031751302435770500214000ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local main = cqueues.new() local _cache = {} local function megarep(s) if not _cache[s] then _cache[s] = string.rep(string.rep(s, 1024), 1024) end return _cache[s] end local function test(bufsiz) local loop = cqueues.new() local rd, wr = check(socket.pair()) wr:setvbuf("full", bufsiz) local sem = { count = 0, condvar = condition.new() } local function sem_get() while sem.count < 1 do sem.condvar:wait() end sem.count = sem.count - 1 sem.condvar:signal() cqueues.sleep(0) end local function sem_put(n) sem.count = sem.count + (n or 1) sem.condvar:signal() cqueues.sleep(0) end for i=0,3 do loop:wrap(function () sem_get() local ch = string.char(string.byte"A" + i) for i=1,10 do check(wr:write(megarep(ch))) end check(wr:flush()) sem_put() end) end loop:wrap(function () sem_put(4) repeat sem.condvar:wait() until sem.count == 4 wr:shutdown"rw" end) local interleaved = false loop:wrap(function () while true do local buf = rd:read(1024 * 1024) if buf == nil then break end local ch = string.sub(buf, 1, 1) local uniform = not buf:match(string.format("[^%s]", ch)) interleaved = interleaved or not uniform info("read %d bytes (interleaved:%s)", #buf, tostring(not uniform)) end end) check(loop:loop()) return interleaved end info"begin control test" check(test(4096) == true, "expected control test to interleave") info"control test OK" info"begin test case" check(test(-1) == false, "test case interleaved") info"test case OK" say("OK") cqueues-rel-20161214/regress/62-noname.lua000077500000000000000000000015671302435770500201070ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local fileresult = auxlib.fileresult check(cqueues.new():wrap(function () local so, ok, why, error so, why, error = fileresult(socket.connect("nothing.nothing", 80)) info("socket.connect -> %s error:%d (%s)", tostring(so), error or 0, tostring(why)) check(so, "failed to create socket: %s", tostring(why)) so:onerror(function (so, op, why, lvl) return why end) ok, why, error = fileresult(so:connect(10)) info("socket:connect -> %s error:%d (%s)", tostring(ok), error or 0, tostring(why)) check(not ok and error and error ~= 0, "socket:connect shouldn't have succeeded") check(error ~= errno.ENOENT, "socket:connect shouldn't return ENOENT anymore") check(error < 0, "socket:connect should have returned DNS_ENONAME but got %d (%s)", error, tostring(why)) end):loop()) say"OK" cqueues-rel-20161214/regress/63-timeouts-early.lua000077500000000000000000000020201302435770500215770ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] -- -- Timeouts are stored as deadlines--absolute clock values of when we should -- resume the thread. Also, timeouts are stored and computed in floating -- point and converted to integers only when neeed. If the difference -- between the nearest deadline and the current time is less than the -- resolution of the system call timeout parameter (1 millisecond for -- epoll_wait), truncation will cause us to compute a timeout value of 0. -- The fix was to use ceil(3) to round up fractional seconds after shifting -- but before integer conversion. Additionally, subnormal floating point -- values are now also rounded up to the minimum resolution (e.g. 1 -- millisecond for epoll_wait, 1 nanosecond for kevent). -- require"regress".export".*" local cqueues = require"cqueues" local cq = cqueues.new() cq:wrap(function () cqueues.sleep(0.1) end) local i = 0 while not cq:empty() do assert(cq:step()) i = i + 1 end check(i == 2, "loop waking up too early") say("OK") cqueues-rel-20161214/regress/71-empty-cqueue.lua000077500000000000000000000045761302435770500212600ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" -- -- Issue #71A -- After the addition of alerts, cqueue:loop waited indefinitely -- on an empty queue when the original, more desirable behavior was that it -- should immediately return. -- -- Issue #71B -- cqueue:step did not clear an alert, causing the cqueue to -- continually poll as ready even after calling cqueue:step. -- local function check_71A() info"testing issue 71A" -- run loop from top-level so we're not testing the nested :step logic local grace = 3 local fh = check(io.popen(string.format([[ GRACE=%d run_and_wait() { . "${CQUEUES_SRCDIR}/regress/regress.sh" || exit 1; runlua - <<-EOF & require"regress".export".*" assert(cqueues.new():loop()) io.stdout:write"OK\n" EOF PID="$!" sleep ${GRACE} set +e # disable strict errors kill -9 "${PID}" 2>>/dev/null wait "${PID}" RC="$?" printf "RC=%%d\n" "${RC}" } exec %gms", timeout, ms) check(ms / 1000 >= timeout, "conversion lost time (%g > %dms)", timeout, ms) local ts = dbg.f2ts(timeout) info("%g -> { %d, %d }", timeout, ts.tv_sec, ts.tv_nsec) check(ts.tv_sec + (ts.tv_nsec * 1000000000) >= timeout, "conversion lost time (%g > { %d, %d })", timeout, ts.tv_sec, ts.tv_nsec) end local main = cqueues.new() local function checksleep(timeout, noloop) local start, elapsed info("sleeping for %gs (noloop:%s)", timeout, noloop and "yes" or "no") if noloop then local start = cqueues.monotime() cqueues.poll(timeout) elapsed = cqueues.monotime() - start else check(main:wrap(function () local start = cqueues.monotime() cqueues.poll(timeout) elapsed = cqueues.monotime() - start end):loop()) end info("%gs elapsed", elapsed) check(elapsed >= timeout, "sleep too short (%g < %g)", elapsed, timeout) end for _, noloop in ipairs{ false, true } do for _, timeout in ipairs{ 0.1, 1e-4, 1e-5, 1e-10, 1e-11, 0.9999, 0.999999999, 0.9999999999, 0.9, 1.0, 1.1, 2.0 } do if not noloop then checkconv(timeout) end checksleep(timeout, noloop) end end local INT_MAX = dbg.INT_MAX for _, timeout in ipairs{ INT_MAX + 1, INT_MAX * 2, INT_MAX + 0.9999 } do local ms, is_int_max = dbg.f2ms(timeout) info("%g -> %d", timeout, ms) check(is_int_max, "%g didn't clamp", timeout) end for _, timeout in ipairs{ 2^63 } do local ts, is_long_max = dbg.f2ts(timeout) info("%g -> { %g, %g }", timeout, ts.tv_sec, ts.tv_nsec) check(is_long_max, "%g didn't clamp", timeout) end say("OK") cqueues-rel-20161214/regress/82-localname-garbage.lua000077500000000000000000000025461302435770500221530ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" local function uname() return check(check(io.popen("uname -a", "r")):read"*a") end local function localpath(s) local family, pathname = check(fileresult(s:localname())) check(family == socket.AF_UNIX, "wrong address family (%s)", tostring(family)) return pathname end info"creating socket pair" local a, b = check(socket.pair(socket.AF_UNIX)) info"check that pathname is nil" check(localpath(a) == nil, "pathname of AF_UNIX socket pair not nil (%q)", localpath(a)) a:close() b:close() if uname():find"Linux" then local pathname = "\00082-localname-garbage.sock" info("creating abstract socket at %q", pathname) local srv = check(socket.listen{ path = pathname }) local a, b info("checking for abstract socket pathname") check(localpath(srv) == pathname, "bad pathname (%q)", localpath(srv)) local main = cqueues.new() main:wrap(function () info("accepting connection at %q", pathname) a = check(srv:accept()) end) main:wrap(function () info("connecting to %q", pathname) b = check(socket.connect{ path = pathname }) end) check(main:loop()) check(localpath(a) == pathname, "pathname of connected AF_UNIX socket wrong (%q)", localpath(a)) check(localpath(b) == nil, "pathname of connected AF_UNIX socket not nil (%q)", localpath(b)) end say("OK") cqueues-rel-20161214/regress/85-cancel-issues.lua000077500000000000000000000021471302435770500213700ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" -- -- Run our test. We don't have a way to force failure with versions of the -- openssl module that have been fixed. -- local main = check(cqueues.new()) local a, b = check(socket.pair(socket.SOCK_STREAM)) local cv = check(condition.new()) check(main:wrap(function () local subloop = check(cqueues.new()) subloop:wrap(function () local event = { pollfd = a:pollfd(), events = "rp" } check(cv:signal()) info"subloop: entering poll state" local ready, errmsg = fileresult(cqueues.poll(event)) info"subloop: awoke from poll" check(ready or not errmsg, "subloop: poll error (%s)", tostring(errmsg)) check(ready == event, "subloop: cancelled event did not poll ready") cv:signal() end) check(subloop:loop()) end)) check(main:wrap(function () info"main loop: waiting on subloop to poll" cv:wait() info"main loop: cancelling socket" cqueues.cancel(a) info"main loop: waiting on subloop signal" check(cv:wait(3), "main loop: timeout before cancelled event polled ready") end)) check(main:loop()) say"OK" cqueues-rel-20161214/regress/87-alpn-disappears.lua000077500000000000000000000052051302435770500217150ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" -- -- Acquire reference to internal openssl loader so we can reinvoke to -- trigger the bug. The bug is that ex_newstate overwrites the registry -- index where we stored our exdata state object from a previous invocation -- of ex_newstate, causing it to be garbage collected. -- -- When the state object is garbage collected, it invalidates all the -- outstanding data attached to any extant object. The state object is an -- interpreter-scoped singleton which should persist for the lifetime of the -- Lua interpreter. Garbage collection is supposed to signal that the Lua -- interpreter is being destroyed, and any data belonging to that -- interpreter and attached to OpenSSL objects is invalid. (NB: The parent -- application and any OpenSSL objects created from the interpreter can -- persist after our Lua interpreter. For example in a multithreaded -- application. Likewise, there can be multiple interpreters, each of which -- must have its own exdata state.) -- -- Because ex_newdata is run from initall, and initall is invoked whenever -- any openssl submodule is loaded, this bug can be triggered by loading a -- new submodule after installing exdata. Or, as here, it can be triggered -- by explicitly reinvoking the loader for a submodule. -- check(package.searchpath, "package.searchpath not defined") local file, why = package.searchpath("_openssl", package.cpath) check(file, "module _openssl required") local reinit = check(package.loadlib(file, "luaopen__openssl_compat")) local function ticklebug() reinit() for i=1,2 do collectgarbage"collect" end end -- -- Run our test. We don't have a way to force failure with versions of the -- openssl module that have been fixed. -- local main = check(cqueues.new()) check(main:wrap(function () local alpn_attempts, alpn_issued = 0, 0 local cli_ctx, srv_ctx cli_ctx = getsslctx("TLSv1", false, false) check(cli_ctx.setAlpnProtos, "ALPN support not available") cli_ctx:setAlpnProtos{ "http2.0", "http" } srv_ctx = getsslctx("TLSv1", true) srv_ctx:setAlpnSelect(function (ssl, list, ...) info("onAlpnSelect: %s", table.concat(list, " ")) alpn_issued = alpn_issued + 1 return list[1] end) for i=1,100 do local srv, cli = check(socket.pair(socket.SOCK_STREAM)) check(main:wrap(function () check(cli:starttls(cli_ctx)) end)) check(srv:starttls(srv_ctx)) alpn_attempts = alpn_attempts + 1 ticklebug() end info("%d ALPN callbacks issued in %d SSL connections", alpn_issued, alpn_attempts) check(alpn_attempts == alpn_issued, "wrong number of ALPN callbacks") end)) check(main:loop()) say"OK" cqueues-rel-20161214/regress/93-remove-family-field.lua000077500000000000000000000010711302435770500224610ustar00rootroot00000000000000#!/bin/sh _=[[ . "${0%%/*}/regress.sh" exec runlua "$0" "$@" ]] require"regress".export".*" -- -- Simple test to check whether we broke initializing a socket object with a -- pipe descriptor. -- info"creating FILE handle attached to printf invocation" local fh = check(io.popen("printf 'OK\\n'", "r")) info"creating socket object attached to pipe (via FILE handle)" local con = check(fileresult(socket.dup(fh))) info"reading printed string from pipe" local what = check(fileresult(con:read"*l")) check(what == "OK", "expected 'OK' but got '%q'", what) say("OK") cqueues-rel-20161214/regress/GNUmakefile000066400000000000000000000016001302435770500177350ustar00rootroot00000000000000# non-recursive prologue sp := $(sp).x dirstack_$(sp) := $(d) d := $(abspath $(lastword $(MAKEFILE_LIST))/..) ifeq ($(origin GUARD_$(d)), undefined) GUARD_$(d) := 1 include $(d)/../GNUmakefile .PHONY: $(d)/check check $(d)/check: @for V in 5.1 5.2 5.3; do \ printf "Building $${V}... "; \ if (cd $(@D) && ./regress.sh -r"$${V}" build >/dev/null 2>&1); then \ printf "OK\n"; \ else \ printf "FAIL\n"; \ fi; \ done @cd $(@D); P=0; F=0; for T in ./[123456789]*.lua; do \ if "./$$T" -B; then P=$$(($$P + 1)); else F=$$(($$F + 1)); fi; \ done; \ printf "PASS=%d FAIL=%d\n" "$$P" "$$F"; \ test $$F -eq 0; check: $(d)/check .PHONY: $(d)/clean $(d)/clean~ $(d)/clean: $(RM) -fr $(@D)/.local $(d)/clean~: $(d)/clean $(RM) -f $(@D)/*~ clean: $(d)/clean clean~: $(d)/clean~ endif # include guard # non-recursive epilogue d := $(dirstack_$(sp)) sp := $(basename $(sp)) cqueues-rel-20161214/regress/Makefile000066400000000000000000000001161302435770500173240ustar00rootroot00000000000000.POSIX: all: +gmake -f GNUmakefile all .DEFAULT: +gmake -f GNUmakefile $< cqueues-rel-20161214/regress/regress.lua000066400000000000000000000117671302435770500200570ustar00rootroot00000000000000local require = require -- may be overloaded by regress.require local auxlib = require"cqueues.auxlib" local regress = { cqueues = require"cqueues", dns = require "cqueues.dns", socket = require"cqueues.socket", thread = require"cqueues.thread", errno = require"cqueues.errno", condition = require"cqueues.condition", auxlib = auxlib, assert = auxlib.assert, fileresult = auxlib.fileresult, pack = table.pack or function (...) local t = { ... } t.n = select("#", ...) return t end, unpack = table.unpack or unpack, } local emit_progname = os.getenv"REGRESS_PROGNAME" or "regress" local emit_verbose = tonumber(os.getenv"REGRESS_VERBOSE" or 1) local emit_info = {} local emit_ll = 0 local function emit(fmt, ...) local msg = string.format(fmt, ...) for txt, nl in msg:gmatch("([^\n]*)(\n?)") do if emit_ll == 0 and #txt > 0 then io.stderr:write(emit_progname, ": ") emit_ll = #emit_progname + 2 end io.stderr:write(txt, nl) if nl == "\n" then emit_ll = 0 else emit_ll = emit_ll + #txt end end end -- emit local function emitln(fmt, ...) if emit_ll > 0 then emit"\n" end emit(fmt .. "\n", ...) end -- emitln local function emitinfo() for _, txt in ipairs(emit_info) do emitln("%s", txt) end end -- emitinfo function regress.say(...) emitln(...) end -- say function regress.panic(...) emitinfo() emitln(...) os.exit(1) end -- panic function regress.info(...) if emit_verbose > 1 then emitln(...) else emit_info[#emit_info + 1] = string.format(...) if emit_verbose > 0 then if emit_ll > 78 then emit"\n." else emit"." end end end end -- info function regress.check(v, ...) if v then return v, ... else regress.panic(...) end end -- check function regress.export(...) for _, pat in ipairs{ ... } do for k, v in pairs(regress) do if string.match(k, pat) then _G[k] = v end end end return regress end -- export function regress.require(modname) local ok, module = pcall(require, modname) regress.check(ok, "module %s required", modname) return module end -- regress.require function regress.genkey(type) local pkey = regress.require"openssl.pkey" local x509 = regress.require"openssl.x509" local name = regress.require"openssl.x509.name" local altname = regress.require"openssl.x509.altname" local key type = string.upper(type or "RSA") if type == "EC" then key = regress.check(pkey.new{ type = "EC", curve = "prime192v1" }) else key = regress.check(pkey.new{ type = type, bits = 1024 }) end local dn = name.new() dn:add("C", "US") dn:add("ST", "California") dn:add("L", "San Francisco") dn:add("O", "Acme, Inc.") dn:add("CN", "acme.inc") local alt = altname.new() alt:add("DNS", "acme.inc") alt:add("DNS", "localhost") local crt = x509.new() crt:setVersion(3) crt:setSerial(47) crt:setSubject(dn) crt:setIssuer(crt:getSubject()) crt:setSubjectAlt(alt) local issued, expires = crt:getLifetime() crt:setLifetime(issued, expires + 60) crt:setBasicConstraints{ CA = true, pathLen = 2 } crt:setBasicConstraintsCritical(true) crt:setPublicKey(key) crt:sign(key) return key, crt end -- regress.genkey local function getsubtable(t, name, ...) name = name or false -- cannot be nil if not t[name] then t[name] = {} end if select('#', ...) > 0 then return getsubtable(t[name], ...) else return t[name] end end -- getsubtable function regress.newsslctx(protocol, accept, keytype) local context = regress.require"openssl.ssl.context" local ctx = context.new(protocol, accept) if keytype or keytype == nil then local key, crt = regress.genkey(keytype) ctx:setCertificate(crt) ctx:setPrivateKey(key) end return ctx end -- require.newsslctx local ctxcache = {} function regress.getsslctx(protocol, accept, keytype) local keycache = getsubtable(ctxcache, protocol, accept) if keytype == nil then keytype = "RSA" end local ctx = keycache[keytype] if not ctx then ctx = regress.newsslctx(protocol, accept, keytype) keycache[keytype] = ctx end return ctx end -- regress.getsslctx -- test 87-alpn-disappears relies on package.searchpath function regress.searchpath(name, paths, sep, rep) sep = (sep or "."):gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") rep = (rep or "/"):gsub("%%", "%%%%") local nofile = {} for path in paths:gmatch("([^;]+)") do path = path:gsub("%?", (name:gsub(sep, rep):gsub("%%", "%%%%"))) local fh = io.open(path, "rb") if fh then fh:close() return path end nofile[#nofile + 1] = string.format("no file '%s'", path) end return nil, table.concat(nofile, "\n") end -- regress.searchpath package.searchpath = package.searchpath or regress.searchpath local Error = {} Error.__index = Error function Error:__tostring() return self.text end function Error:unpack() return regress.unpack(self, 1, self.n) end function regress.packerror(v, ...) if not v then local err = regress.pack(select(2, auxlib.fileresult(nil, ...))) err.text = err[1] or "?" err.code = err[2] return setmetatable(err, Error) else return nil, v, ... end end return regress cqueues-rel-20161214/regress/regress.sh000077500000000000000000000057321302435770500177060ustar00rootroot00000000000000#!/bin/sh set -e # strict error set -f # disable pathname expansion set -C # noclobber unset IFS : ${CQUEUES_SRCDIR:="$(cd "${0%%/*}/.." && pwd -L)"} PATH="${PATH:-$(command -p getconf PATH)}:${CQUEUES_SRCDIR}/mk" : ${REGRESS_VERBOSE:=1} : ${REGRESS_REBUILD:=1} : ${REGRESS_PROGNAME:=${0##*/}} REGRESS_PROGNAME="${REGRESS_PROGNAME%.lua}" export CQUEUES_SRCDIR REGRESS_VERBOSE REGRESS_REBUILD REGRESS_PROGNAME SHORTOPTS="Br:j:Jqvh" usage() { cat <<-EOF Usage: ${0##*/} [-${SHORTOPTS}] -B do not try to rebuild modules -r RANGE run specific Lua version -j RANGE run specific LuaJIT version -J exclude LuaJIT from candidate interpreters -q do not emit informational messages -v emit verbose informational messages -h print this usage message Report bugs to EOF } while getopts "${SHORTOPTS}" OPTC; do case "${OPTC}" in B) REGRESS_REBUILD=0 ;; r) export RUNLUA_R="${OPTARG}" ;; j) export RUNLUA_J="${OPTARG}" ;; J) export RUNLUA_J="0-0" ;; q) REGRESS_VERBOSE=0 ;; v) REGRESS_VERBOSE=2 ;; h) usage exit 0 ;; ?) usage >&2 exit 1 ;; esac done shift $((${OPTIND} - 1)) lua51path="${CQUEUES_SRCDIR}/regress/.local/share/5.1" lua51cpath="${CQUEUES_SRCDIR}/regress/.local/lib/5.1" lua52path="${CQUEUES_SRCDIR}/regress/.local/share/5.2" lua52cpath="${CQUEUES_SRCDIR}/regress/.local/lib/5.2" lua53path="${CQUEUES_SRCDIR}/regress/.local/share/5.3" lua53cpath="${CQUEUES_SRCDIR}/regress/.local/lib/5.3" export LUA_PATH="${lua51path}/?.lua;${CQUEUES_SRCDIR}/regress/?.lua;${LUA_PATH:-;}" export LUA_CPATH="${lua51cpath}/?.so;${LUA_CPATH:-;}" export LUA_PATH_5_2="${lua52path}/?.lua;${CQUEUES_SRCDIR}/regress/?.lua;${LUA_PATH_5_2:-;}" export LUA_CPATH_5_2="${lua52cpath}/?.so;${LUA_CPATH_5_2:-;}" export LUA_PATH_5_3="${lua53path}/?.lua;${CQUEUES_SRCDIR}/regress/?.lua;${LUA_PATH_5_3:-;}" export LUA_CPATH_5_3="${lua53cpath}/?.so;${LUA_CPATH_5_3:-;}" if [ "${0##*/}" = "regress.sh" ]; then case "${1:-build}" in build) LUA_API="$(runlua -e "print(_VERSION:match'%d.%d')")" unset MAKEFLAGS (cd "${CQUEUES_SRCDIR}" && make -s "install${LUA_API}" \ lua51path="${lua51path}" lua51cpath="${lua51cpath}" \ lua52path="${lua52path}" lua52cpath="${lua52cpath}" \ lua53path="${lua53path}" lua53cpath="${lua53cpath}") exit $? ;; *) printf "%s: %s: unknown command\n" "${0##*/}" "${1:-\'\'}" >&2 exit 1 ;; esac else if [ ${REGRESS_REBUILD} -eq 1 ]; then export REGRESS_REBUILD=0 # disable for recursive invocations (cd "${CQUEUES_SRCDIR}" && make -s install \ lua51path="${lua51path}" lua51cpath="${lua51cpath}" \ lua52path="${lua52path}" lua52cpath="${lua52cpath}" \ lua53path="${lua53path}" lua53cpath="${lua53cpath}") fi if [ ! -d "${CQUEUES_SRCDIR}/regress/.local/lib/5.3" ] || ! runlua -e 'require"_cqueues"' >>/dev/null 2>&1; then export RUNLUA_R="${RUNLUA_R:=5.1-5.2}" fi case "$(uname -s)" in OpenBSD) : ${RUNLUA_T:=1} export RUNLUA_T ;; esac fi cqueues-rel-20161214/src/000077500000000000000000000000001302435770500150035ustar00rootroot00000000000000cqueues-rel-20161214/src/GNUmakefile000066400000000000000000000156671302435770500170740ustar00rootroot00000000000000# non-recursive prologue sp := $(sp).x dirstack_$(sp) := $(d) d := $(abspath $(lastword $(MAKEFILE_LIST))/..) ifeq ($(origin GUARD_$(d)), undefined) GUARD_$(d) := 1 # # E N V I R O N M E N T C O N F I G U R A T I O N # include $(d)/../GNUmakefile include $(d)/lib/GNUmakefile # # C O M P I L A T I O N F L A G S # VENDOR_$(d) = $(or $(CQUEUES_VENDOR),$(shell $(| $@.tmp mv $@.tmp $@ $(d)/config.h: $(abspath $(d)/..)/config.h $(CP) $< $@ define BUILD_$(d) $$(d)/$(1)/cqueues.so: $$(addprefix $$(d)/$(1)/, $$(OBJS_$(d))) $$(d)/lib/libnonlua.a $$(CC) -o $$@ $$^ $$(SOFLAGS_$$(abspath $$(@D)/..)) $$(LDFLAGS_$$(abspath $$(@D)/..)) $$(LIBS_$$(abspath $$(@D)/..)) $$(d)/$(1)/%.o: $$(d)/%.c $$(d)/cqueues.h $$(d)/compat52.h $$(d)/config.h $$(MKDIR) -p $$(@D) $$(CC) $$(CFLAGS_$$(" help: $(d)/help endif # include guard # non-recursive epilogue d := $(dirstack_$(sp)) sp := $(basename $(sp)) cqueues-rel-20161214/src/Makefile000066400000000000000000000001161302435770500164410ustar00rootroot00000000000000.POSIX: all: +gmake -f GNUmakefile all .DEFAULT: +gmake -f GNUmakefile $< cqueues-rel-20161214/src/auxlib.lua000066400000000000000000000064271302435770500170030ustar00rootroot00000000000000local loader = function(loader, ...) local cqueues = require"_cqueues" local errno = require"_cqueues.errno" local auxlib = require"_cqueues.auxlib" local coroutine = require"coroutine" -- -- auxlib.tostring -- -- Yieldable tostring. Implemented in C for Lua 5.2 and above. -- -- Lua 5.1 API doesn't allow us to implement a yieldable tostring -- routine. Fortunately, LuaJIT's tostring permits yielding. -- auxlib.tostring = auxlib.tostring or tostring -- -- auxlib.resume -- auxlib.wrap -- -- Wrappers for multilevel coroutine management to allow I/O polling -- of coroutine-wrapped code. The code checks for a special value -- returned by cqueues.poll, and will propogate a yield on I/O. -- Everything else should behave as usual. -- local _POLL = cqueues._POLL -- magic internal value local l_resume = coroutine.resume -- take reference to permit monkey patching local l_yield = coroutine.yield local function c_resume(co, ok, arg1, ...) if ok and arg1 == _POLL then return auxlib.resume(co, l_yield(_POLL, ...)) else return ok, arg1, ... end end -- c_resume function auxlib.resume(co, ...) return c_resume(co, l_resume(co, ...)) end -- auxlib.resume local function c_wrap(co, ok, ...) if ok then return ... else error((...), 0) end end -- c_wrap function auxlib.wrap(f) local co = coroutine.create(f) return function(...) return c_wrap(co, c_resume(co, l_resume(co, ...))) end end -- auxlib.wrap -- -- auxlib.assert -- -- If condition is false, locate and use the first non-false, -- non-nil value as the reason. If an integer value, attempt to -- convert to string using strerror. -- -- RATIONALE: Many routines return only an integer error number. -- Errors like EAGAIN are very common and constantly pushing a new -- string on the stack from C would be inefficient. -- -- Also, unlike the standard Lua idiom, the failure mode for some -- routines will return multiple false or nil values preceding the -- error number so user code doesn't need to use variable names like -- "value_or_error" for routines which can be expected to fail -- regularly in the normal course of operation and where simply -- using the assert idiom would create spaghetti code. -- local tostring = auxlib.tostring local function findwhy(v, ...) if v then if type(v) == "number" then -- return string and number for auxlib.fileresult return (errno.strerror(v) or tostring(v)), v else return tostring(v), ... end elseif select("#", ...) > 0 then return findwhy(...) else return end end function auxlib.assert(c, ...) if c then return c, ... end return error(findwhy(...), 2) end -- auxlib.assert -- -- auxlib.assert[23456789] -- -- Like auxlib.assert, but use error(message, level) where level is -- the numeric suffix of the assert function name. -- local function makeassert(level) return function (c, ...) if c then return c, ... end return error(findwhy(...), level) end end for n=2,9 do auxlib[string.format("assert%d", n)] = makeassert(n) end -- -- auxlib.fileresult -- function auxlib.fileresult(c, ...) if c then return c, ... else return c, findwhy(...) end end -- auxlib.fileresult auxlib.loader = loader return auxlib end -- loader return loader(loader, ...) cqueues-rel-20161214/src/compat52.h000066400000000000000000000107751302435770500166200ustar00rootroot00000000000000/* ========================================================================== * compat52.h - Routines for Lua 5.2 compatibility * -------------------------------------------------------------------------- * Copyright (c) 2012 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #if LUA_VERSION_NUM < 502 #define LUA_OK 0 static void luaL_setmetatable(lua_State *L, const char *tname) { luaL_getmetatable(L, tname); lua_setmetatable(L, -2); } /* luaL_setmetatable() */ static int lua_absindex(lua_State *L, int idx) { return (idx > 0 || idx <= LUA_REGISTRYINDEX)? idx : lua_gettop(L) + idx + 1; } /* lua_absindex() */ static void *luaL_testudata(lua_State *L, int arg, const char *tname) { void *p = lua_touserdata(L, arg); int eq; if (!p || !lua_getmetatable(L, arg)) return 0; luaL_getmetatable(L, tname); eq = lua_rawequal(L, -2, -1); lua_pop(L, 2); return (eq)? p : 0; } /* luaL_testudate() */ static void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) { int i, t = lua_absindex(L, -1 - nup); for (; l->name; l++) { for (i = 0; i < nup; i++) lua_pushvalue(L, -nup); lua_pushcclosure(L, l->func, nup); lua_setfield(L, t, l->name); } lua_pop(L, nup); } /* luaL_setfuncs() */ #define luaL_newlibtable(L, l) \ lua_createtable(L, 0, (sizeof (l) / sizeof *(l)) - 1) #define luaL_newlib(L, l) \ (luaL_newlibtable((L), (l)), luaL_setfuncs((L), (l), 0)) static int luaL_getsubtable(lua_State *L, int index, const char *tname) { lua_getfield(L, index, tname); if (lua_istable(L, -1)) return 1; lua_pop(L, 1); index = lua_absindex(L, index); lua_newtable(L); lua_pushvalue(L, -1); lua_setfield(L, index, tname); return 0; } /* luaL_getsubtable() */ static void luaL_requiref(lua_State *L, const char *modname, lua_CFunction openf, int glb) { lua_pushcfunction(L, openf); lua_pushstring(L, modname); lua_call(L, 1, 1); luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); lua_pushvalue(L, -2); lua_setfield(L, -2, modname); lua_pop(L, 1); if (glb) { lua_pushvalue(L, -1); lua_setglobal(L, modname); } } /* luaL_requiref() */ #define lua_resume(L, from, nargs) lua_resume((L), (nargs)) static void lua_rawgetp(lua_State *L, int index, const void *p) { index = lua_absindex(L, index); lua_pushlightuserdata(L, (void *)p); lua_rawget(L, index); } /* lua_rawgetp() */ static void lua_rawsetp(lua_State *L, int index, const void *p) { index = lua_absindex(L, index); lua_pushlightuserdata(L, (void *)p); lua_pushvalue(L, -2); lua_rawset(L, index); lua_pop(L, 1); } /* lua_rawsetp() */ #ifndef LUA_UNSIGNED #define LUA_UNSIGNED unsigned #endif typedef LUA_UNSIGNED lua_Unsigned; static void lua_pushunsigned(lua_State *L, lua_Unsigned n) { lua_pushnumber(L, (lua_Number)n); } /* lua_pushunsigned() */ static lua_Unsigned luaL_checkunsigned(lua_State *L, int arg) { return (lua_Unsigned)luaL_checknumber(L, arg); } /* luaL_checkunsigned() */ static lua_Unsigned luaL_optunsigned(lua_State *L, int arg, lua_Unsigned def) { return (lua_Unsigned)luaL_optnumber(L, arg, (lua_Number)def); } /* luaL_optunsigned() */ #ifndef LUA_FILEHANDLE /* Not defined by earlier LuaJIT releases */ #define LUA_FILEHANDLE "FILE*" #endif /* * Lua 5.1 userdata is a simple FILE *, while LuaJIT is a struct with the * first member a FILE *, similar to Lua 5.2. */ typedef struct luaL_Stream { FILE *f; } luaL_Stream; #define lua_rawlen(...) lua_objlen(__VA_ARGS__) #endif /* LUA_VERSION_NUM < 502 */ cqueues-rel-20161214/src/condition.lua000066400000000000000000000005471302435770500175020ustar00rootroot00000000000000local loader = function(loader, ...) local core = require"_cqueues.condition" local function check(self, why, ...) if self == why then return true, ... else return false, why, ... end end local wait; wait = core.interpose("wait", function (self, ...) return check(self, wait(self, ...)) end) return core end return loader(loader, ...) cqueues-rel-20161214/src/cqueues.c000066400000000000000000002052721302435770500166310ustar00rootroot00000000000000/* ========================================================================== * cqueues.c - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012-2015 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include /* INT_MAX LONG_MAX */ #include /* FLT_RADIX */ #include /* va_list va_start va_end */ #include /* NULL offsetof() size_t */ #include /* malloc(3) free(3) */ #include /* memset(3) */ #include /* sigprocmask(2) pthread_sigmask(3) */ #include /* struct timespec clock_gettime(3) */ #include /* FP_* NAN fmax(3) fpclassify(3) isfinite(3) signbit(3) islessequal(3) isgreater(3) ceil(3) modf(3) */ #include /* errno */ #include /* assert */ #include /* LIST_* TAILQ_* */ #include /* struct timeval */ #include /* pselect(3) */ #include /* close(2) */ #include /* F_SETFD FD_CLOEXEC fcntl(2) */ #include /* POLLIN POLLOUT POLLPRI */ #include #include #include "lib/llrb.h" #include "cqueues.h" /* * V E R S I O N I N T E R F A C E S * * If forking change CQUEUES_VENDOR to avoid confusion. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef CQUEUES_VENDOR #define CQUEUES_VENDOR "william@25thandClement.com" #endif #ifndef CQUEUES_VERSION #define CQUEUES_VERSION 20161214L #endif /* * D E B U G R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if !defined SAY #define SAY_(file, func, line, fmt, ...) \ fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__) #define SAY(...) SAY_(__FILE__, __func__, __LINE__, __VA_ARGS__, "\n") #define HAI SAY("hai") #endif #if __GNUC__ #define NOTUSED __attribute__((unused)) #else #define NOTUSED #endif #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) || __GNUC__ > 4 || __clang__ #define NOTREACHED __builtin_unreachable() #else #define NOTREACHED (void)0 #endif #if __GNUC__ #define NONNULL(...) __attribute__((nonnull (__VA_ARGS__))) #else #define NONNULL(...) #endif #if __GNUC__ #define luaL_error(...) __extension__ ({ int tmp = luaL_error(__VA_ARGS__); NOTREACHED; tmp; }) #endif /* * U T I L I T Y R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ inline static int setcloexec(int fd) { if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC)) return errno; return 0; } /* setcloexec() */ /* * T I M E & C L O C K R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * clock_gettime() * * OS X didn't implement the clock_gettime() POSIX interface until macOS * 10.12 (Sierra). But it did provide a monotonic clock through * mach_absolute_time(). On i386 and x86_64 architectures this clock is in * nanosecond units, but not so on other devices. mach_timebase_info() * provides the conversion parameters. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if __APPLE__ #include /* errno EINVAL */ #include /* struct timespec */ #include /* TIMEVAL_TO_TIMESPEC struct timeval gettimeofday(3) */ #include /* mach_timebase_info_data_t mach_timebase_info() mach_absolute_time() */ #if !HAVE_DECL_CLOCK_REALTIME enum { CLOCK_REALTIME = 0 }; #endif #if !HAVE_DECL_CLOCK_MONOTONIC enum { CLOCK_MONOTONIC = 6 }; #endif #if !HAVE_CLOCKID_T typedef int clockid_t; #endif #if HAVE_CLOCK_GETTIME && !HAVE_DECL_CLOCK_GETTIME extern int (clock_gettime)(clockid_t, struct timespec *); #endif static mach_timebase_info_data_t clock_timebase = { .numer = 1, .denom = 1, }; /* clock_timebase */ static int compat_clock_gettime(clockid_t clockid, struct timespec *ts) { switch (clockid) { case CLOCK_REALTIME: { struct timeval tv; if (0 != gettimeofday(&tv, 0)) return -1; TIMEVAL_TO_TIMESPEC(&tv, ts); return 0; } case CLOCK_MONOTONIC: { unsigned long long abt; abt = mach_absolute_time(); abt = abt * clock_timebase.numer / clock_timebase.denom; ts->tv_sec = abt / 1000000000UL; ts->tv_nsec = abt % 1000000000UL; return 0; } default: errno = EINVAL; return -1; } /* switch() */ } /* clock_gettime() */ #define clock_gettime(clockid, ts) clock_gettime_p((clockid), (ts)) static int (*clock_gettime_p)(clockid_t, struct timespec *) = &compat_clock_gettime; void clock_gettime_init(void) __attribute__((constructor)); void clock_gettime_init(void) { #if HAVE_CLOCK_GETTIME /* * NB: clock_gettime is implemented as a weak symbol which autoconf * tests will always positively identify when compiling with XCode * 8.0 or above, regardless of -mmacosx-version-min. Similarly, it * will always be declared by XCode 8.0 or above. */ if (&(clock_gettime)) { clock_gettime_p = &(clock_gettime); return; } #endif if (mach_timebase_info(&clock_timebase) != KERN_SUCCESS) __builtin_abort(); clock_gettime_p = &compat_clock_gettime; } /* clock_gettime_init() */ #endif /* __APPLE__ */ static inline int f2ms(const double f) { double ms; switch (fpclassify(f)) { case FP_NORMAL: if (signbit(f)) return 0; ms = ceil(f * 1000); return (ms > INT_MAX)? INT_MAX : ms; case FP_SUBNORMAL: return 1; case FP_ZERO: return 0; case FP_INFINITE: case FP_NAN: default: return -1; } } /* f2ms() */ static inline struct timespec *f2ts_(struct timespec *ts, const double f) { double s, ns; switch (fpclassify(f)) { case FP_NORMAL: if (signbit(f)) return ts; ns = modf(f, &s); ns = ceil(ns * 1000000000); if (ns >= 1000000000) { s++; ns = 0; } cqs_static_assert(FLT_RADIX == 2, "FLT_RADIX != 2"); cqs_static_assert(cqs_ispowerof2((unsigned long)LONG_MAX + 1), "LONG_MAX + 1 not a power of 2"); if (s >= (unsigned long)LONG_MAX + 1) { ts->tv_sec = LONG_MAX; ts->tv_nsec = 0; } else { ts->tv_sec = s; ts->tv_nsec = ns; } return ts; case FP_SUBNORMAL: ts->tv_sec = 0; ts->tv_nsec = 1; return ts; case FP_ZERO: return ts; case FP_INFINITE: case FP_NAN: default: return NULL; } } /* f2ts_() */ #define f2ts(f) f2ts_(&(struct timespec){ 0, 0 }, (f)) static inline double ts2f(const struct timespec *ts) { return ts->tv_sec + (ts->tv_nsec / 1000000000.0); } /* ts2f() */ static inline double tv2f(const struct timeval *tv) { return tv->tv_sec + (tv->tv_usec / 1000000.0); } /* tv2f() */ static inline double monotime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts2f(&ts); } /* monotime() */ static inline double abstimeout(double timeout) { return (isfinite(timeout))? monotime() + fmax(timeout, 0) : NAN; } /* abstimeout() */ static inline double reltimeout(double timeout) { double curtime; if (!isfinite(timeout)) return NAN; curtime = monotime(); return (islessequal(timeout, curtime))? 0.0 : timeout - curtime; } /* reltimeout() */ static inline double mintimeout(double a, double b) { if (islessequal(a, b) || !isfinite(b)) return a; else if (islessequal(b, a) || !isfinite(a)) return b; else return NAN; } /* mintimeout() */ /* * M E M O R Y M A N A G M E N T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static void *make(size_t size, int *error) { void *p; if (!(p = malloc(size))) *error = errno; return p; } /* make() */ struct pool { size_t size, count; void *head; }; /* pool */ static void pool_init(struct pool *P, size_t size) { P->size = MAX(size, sizeof (void **)); P->count = 0; P->head = NULL; } /* pool_init() */ static void pool_destroy(struct pool *P) { void *p; while ((p = P->head)) { P->head = *(void **)p; free(p); P->count--; } } /* pool_destroy() */ static void pool_put(struct pool *P, void *p) { *(void **)p = P->head; P->head = p; } /* pool_put() */ static int pool_grow(struct pool *P, size_t n) { void *p; int error; while (n--) { if (P->count + 1 == 0) return ENOMEM; if (!(p = make(P->size, &error))) return error; P->count++; pool_put(P, p); } return 0; } /* pool_grow() */ static void *pool_get(struct pool *P, int *_error) { void *p; int error; if (!(p = P->head)) { error = pool_grow(P, MAX(1, P->count)); if (!(p = P->head)) { *_error = error; return NULL; } } P->head = *(void **)p; return p; } /* pool_get() */ /* * K P O L L ( K Q U E U E / E P O L L ) R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if ENABLE_EPOLL #include /* struct epoll_event epoll_create(2) epoll_ctl(2) epoll_wait(2) */ #elif ENABLE_PORTS #include #elif ENABLE_KQUEUE #include /* EVFILT_READ EVFILT_WRITE EV_SET EV_ADD EV_DELETE struct kevent kqueue(2) kevent(2) */ #else #error "No polling backend available" #endif #if HAVE_SYS_EVENTFD_H #include /* eventfd(2) */ #endif #define KPOLL_FOREACH(ke, kp) for (ke = (kp)->pending.event; ke < &(kp)->pending.event[(kp)->pending.count]; ke++) #define KPOLL_MAXWAIT 32 #if ENABLE_EPOLL typedef struct epoll_event kpoll_event_t; #elif ENABLE_PORTS typedef port_event_t kpoll_event_t; #elif ENABLE_KQUEUE /* NetBSD uses intptr_t, others use void *, for .udata */ #define KP_P2UDATA(p) ((__typeof__(((struct kevent *)0)->udata))(p)) #define KP_UDATA2P(udata) ((void *)(udata)) #define KP_SET(ev, a, b, c, d, e, f) EV_SET((ev), (a), (b), (c), (d), (e), KP_P2UDATA(f)) typedef struct kevent kpoll_event_t; #endif struct kpoll { int fd; struct { kpoll_event_t event[KPOLL_MAXWAIT]; size_t count; } pending; struct { int fd[2]; short state; int pending; } alert; }; /* struct kpoll */ static void kpoll_preinit(struct kpoll *kp) { kp->fd = -1; kp->pending.count = 0; for (size_t i = 0; i < countof(kp->alert.fd); i++) kp->alert.fd[i] = -1; kp->alert.state = 0; kp->alert.pending = 0; } /* kpoll_preinit() */ static int kpoll_ctl(struct kpoll *, int, short *, short, void *); static int alert_rearm(struct kpoll *); static int alert_init(struct kpoll *kp) { #if ENABLE_PORTS (void)kp; return 0; #elif HAVE_EVENTFD if (kp->alert.fd[0] != -1) return 0; if (-1 == (kp->alert.fd[0] = eventfd(0, O_CLOEXEC|O_NONBLOCK))) return errno; return alert_rearm(kp); #else int error; if (kp->alert.fd[0] != -1) return 0; if ((error = cqs_pipe(kp->alert.fd, O_CLOEXEC|O_NONBLOCK))) return error; return alert_rearm(kp); #endif } /* alert_init() */ static void alert_destroy(struct kpoll *kp, int (*closefd)(int *, void *), void *cb_udata) { #if ENABLE_PORTS (void)kp; #else for (size_t i = 0; i < countof(kp->alert.fd); i++) closefd(&kp->alert.fd[i], cb_udata); #endif } /* alert_destroy() */ static int alert_rearm(struct kpoll *kp) { #if ENABLE_PORTS return 0; #else return kpoll_ctl(kp, kp->alert.fd[0], &kp->alert.state, POLLIN, &kp->alert); #endif } /* alert_rearm() */ static int kpoll_init(struct kpoll *kp) { int error; #if ENABLE_EPOLL #if defined EPOLL_CLOEXEC (void)error; if (-1 == (kp->fd = epoll_create1(EPOLL_CLOEXEC))) return errno; #else if (-1 == (kp->fd = epoll_create(32))) return errno; if ((error = setcloexec(kp->fd))) return error; #endif #elif ENABLE_PORTS if (-1 == (kp->fd = port_create())) return errno; if ((error = setcloexec(kp->fd))) return error; #elif ENABLE_KQUEUE if (-1 == (kp->fd = kqueue())) return errno; if ((error = setcloexec(kp->fd))) return error; #endif return alert_init(kp); } /* kpoll_init() */ static void kpoll_destroy(struct kpoll *kp, int (*closefd)(int *, void *), void *cb_udata) { alert_destroy(kp, closefd, cb_udata); closefd(&kp->fd, cb_udata); kpoll_preinit(kp); } /* kpoll_destroy() */ static inline void *kpoll_udata(const kpoll_event_t *event) { #if ENABLE_EPOLL return event->data.ptr; #elif ENABLE_PORTS return event->portev_user; #elif ENABLE_KQUEUE return KP_UDATA2P(event->udata); #endif } /* kpoll_udata() */ static inline short kpoll_pending(const kpoll_event_t *event) { #if ENABLE_EPOLL return event->events; #elif ENABLE_PORTS return event->portev_events; #elif ENABLE_KQUEUE return (event->filter == EVFILT_READ)? POLLIN : (event->filter == EVFILT_WRITE)? POLLOUT : 0; #endif } /* kpoll_pending() */ static inline short kpoll_diff(const kpoll_event_t *event NOTUSED, short ostate NOTUSED) { #if ENABLE_PORTS /* Solaris Event Ports aren't persistent. */ return 0; #else return ostate; #endif } /* kpoll_diff() */ static int kpoll_ctl(struct kpoll *kp, int fd, short *state, short events, void *udata) { #if ENABLE_EPOLL struct epoll_event event; int op; if (*state == events) return 0; op = (!*state)? EPOLL_CTL_ADD : (!events)? EPOLL_CTL_DEL : EPOLL_CTL_MOD; memset(&event, 0, sizeof event); event.events = events; event.data.ptr = udata; if (0 != epoll_ctl(kp->fd, op, fd, &event)) return errno; *state = events; return 0; #elif ENABLE_PORTS if (*state == events) return 0; if (!events) { if (0 != port_dissociate(kp->fd, PORT_SOURCE_FD, fd)) return errno; } else { if (0 != port_associate(kp->fd, PORT_SOURCE_FD, fd, events, udata)) return errno; } *state = events; return 0; #elif ENABLE_KQUEUE struct kevent event; if (*state == events) return 0; if (events & POLLIN) { if (!(*state & POLLIN)) { KP_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, udata); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) return errno; *state |= POLLIN; } } else if (*state & POLLIN) { KP_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) return errno; *state &= ~POLLIN; } if (events & POLLOUT) { if (!(*state & POLLOUT)) { KP_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, udata); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) return errno; *state |= POLLOUT; } } else if (*state & POLLOUT) { KP_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) return errno; *state &= ~POLLOUT; } return 0; #endif } /* kpoll_ctl() */ static int kpoll_alert(struct kpoll *kp) { int error; if (kp->alert.pending) return 0; /* initialization may have been delayed */ if ((error = alert_init(kp))) return error; #if ENABLE_PORTS if (0 != port_send(kp->fd, POLLIN, &kp->alert)) { if (errno != EBUSY) return errno; } #elif HAVE_EVENTFD static const uint64_t one = 1; while (-1 == write(kp->alert.fd[0], &one, sizeof one)) { if (errno == EAGAIN) { break; } else if (errno != EINTR) { return errno; } } #else while (-1 == write(kp->alert.fd[1], "!", 1)) { if (errno == EAGAIN) { break; } else if (errno != EINTR) { return errno; } } #endif if ((error = alert_rearm(kp))) return error; kp->alert.pending = 1; return 0; } /* kpoll_alert() */ static int kpoll_calm(struct kpoll *kp) { int error; #if ENABLE_PORTS /* each PORT_SOURCE_USER event is discrete */ #elif HAVE_EVENTFD uint64_t n; while (-1 == read(kp->alert.fd[0], &n, sizeof n)) { if (errno == EAGAIN) { break; } else if (errno != EINTR) { return errno; } } #else for (;;) { char buf[64]; ssize_t n; if (-1 == (n = read(kp->alert.fd[0], buf, sizeof buf))) { if (errno == EAGAIN) { break; } else if (errno != EINTR) { return errno; } } else if (n == 0) { return EPIPE; /* somebody closed our fd! */ } } #endif if ((error = alert_rearm(kp))) return error; kp->alert.pending = 0; return 0; } /* kpoll_calm() */ static inline short kpoll_isalert(struct kpoll *kp, const kpoll_event_t *event) { #if ENABLE_PORTS return event->portev_source == PORT_SOURCE_USER; #else return kpoll_udata(event) == &kp->alert; #endif } /* kpoll_isalert() */ static int kpoll_wait(struct kpoll *kp, double timeout) { #if ENABLE_EPOLL int n; if (-1 == (n = epoll_wait(kp->fd, kp->pending.event, (int)countof(kp->pending.event), f2ms(timeout)))) return (errno == EINTR)? 0 : errno; kp->pending.count = n; return 0; #elif ENABLE_PORTS kpoll_event_t *ke; uint_t n = 1; kp->pending.count = 0; if (0 != port_getn(kp->fd, kp->pending.event, countof(kp->pending.event), &n, f2ts(timeout))) return (errno == ETIME || errno == EINTR)? 0 : errno; kp->pending.count = n; return 0; #elif ENABLE_KQUEUE int n; if (-1 == (n = kevent(kp->fd, NULL, 0, kp->pending.event, (int)countof(kp->pending.event), f2ts(timeout)))) return (errno == EINTR)? 0 : errno; kp->pending.count = n; return 0; #endif } /* kpoll_wait() */ /* * A U X I L I A R Y L I B R A R Y R O U T I N E S * * Routines which can be used to improve integration, including extending * Lua's support for implicit yielding. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if LUA_VERSION_NUM >= 502 #if LUA_VERSION_NUM >= 503 static int auxlib_tostringk(lua_State *L NOTUSED, int status NOTUSED, lua_KContext ctx NOTUSED) { #else static int auxlib_tostringk(lua_State *L NOTUSED) { #endif if (luaL_getmetafield(L, 1, "__tostring")) { lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1)); } else { luaL_tolstring(L, 1, NULL); } return 1; } /* auxlib_tostringk() */ static int auxlib_tostring(lua_State *L) { luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, "__tostring")) { lua_insert(L, 1); lua_settop(L, 2); lua_callk(L, 1, 1, 0, &auxlib_tostringk); #if LUA_VERSION_NUM >= 503 return auxlib_tostringk(L, LUA_OK, 0); #else return auxlib_tostringk(L); #endif } else { luaL_tolstring(L, 1, NULL); return 1; } } /* auxlib_tostring() */ #endif static const luaL_Reg auxlib_globals[] = { #if LUA_VERSION_NUM >= 502 { "tostring", &auxlib_tostring }, #endif { NULL, NULL } }; /* auxlib_globals[] */ int luaopen__cqueues_auxlib(lua_State *L) { luaL_newlib(L, auxlib_globals); return 1; } /* luaopen__cqueues_auxlib() */ /* * C O N D I T I O N V A R I A B L E R O U T I N E S * * FIXME: Add logic to the scheduler that prevents two coroutines from * continually placing each other onto the pending queue within the same * resume iteration. Otherwise cqueue_process could loop forever, starving * other contexts. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define wakecb_init(cb, ...) wakecb_init4((cb), __VA_ARGS__, NULL, NULL) #define wakecb_init4(cb, _fn, a, b, ...) do { \ (cb)->cv = NULL; \ (cb)->fn = (_fn); \ (cb)->arg[0] = (a); \ (cb)->arg[1] = (b); \ } while (0) struct wakecb { struct condition *cv; cqs_error_t (*fn)(struct wakecb *); void *arg[3]; TAILQ_ENTRY(wakecb) tqe; }; /* struct wakecb */ struct condition { _Bool lifo; TAILQ_HEAD(, wakecb) waiting; }; /* struct condition */ static void wakecb_del(struct wakecb *cb) { if (cb->cv) { TAILQ_REMOVE(&cb->cv->waiting, cb, tqe); cb->cv = NULL; } } /* wakecb_del() */ static void wakecb_add(struct wakecb *cb, struct condition *cv) { if (cv->lifo) { TAILQ_INSERT_HEAD(&cv->waiting, cb, tqe); cb->cv = cv; } else { TAILQ_INSERT_TAIL(&cv->waiting, cb, tqe); cb->cv = cv; } } /* wakecb_add() */ static struct condition *cond_testself(lua_State *L, int index) { return cqs_testudata(L, index, 1); } /* cond_testself() */ static struct condition *cond_checkself(lua_State *L, int index) { return cqs_checkudata(L, index, 1, CQS_CONDITION); } /* cond_checkself() */ static int cond_type(lua_State *L) { if (cond_testself(L, 1)) { lua_pushstring(L, "condition"); } else { lua_pushnil(L); } return 1; } /* cond_type() */ static int cond_interpose(lua_State *L) { return cqs_interpose(L, CQS_CONDITION); } /* cond_interpose() */ static int cond_new(lua_State *L) { _Bool lifo = lua_toboolean(L, 1); struct condition *cv; cv = lua_newuserdata(L, sizeof *cv); cv->lifo = lifo; TAILQ_INIT(&cv->waiting); luaL_setmetatable(L, CQS_CONDITION); return 1; } /* cond_new() */ static int cond__call(lua_State *L NOTUSED) { lua_settop(L, 1); return 1; } /* cond__call() */ static int cond__gc(lua_State *L) { struct condition *cv = cond_checkself(L, 1); int empty = TAILQ_EMPTY(&cv->waiting); struct wakecb *cb; while ((cb = TAILQ_FIRST(&cv->waiting))) { wakecb_del(cb); /* * NOTE: We drop wakeup callback errors. Throwing from a * __gc metamethod seems less than useful. Applications can * and should check errors when explicitly signaling. That * we signal on GC is just a backstop for code that is * already probably buggy. */ cb->fn(cb); } /* * XXX: Check can fail when lua_State is destroyed (e.g. script * terminates) and there are coroutines still waiting. Condition * variables are usually younger than the coroutines and objects * waiting on them, resources are collected in reverse order of * creation during each cycle, and in this case everything is * collected in the same cycle. * * Note that even if luaL_error triggers, oddly the Lua interpreter * will only show the message if the script terminated with an * error, and not if it terminated normally. The order of * destruction is the same either way. * * Is there a way to detect that the lua_State is being destroyed? */ if (0 && !empty) return luaL_error(L, "invariant failure: condition variable wait queue not empty on __gc"); return 0; } /* cond__gc() */ static int cond_wait(lua_State *L) { cond_checkself(L, 1); lua_pushlightuserdata(L, CQUEUE__POLL); lua_insert(L, 1); return lua_yield(L, lua_gettop(L)); } /* cond_wait() */ static int cond_signal(lua_State *L) { struct condition *cv = cond_checkself(L, 1); int i, n = luaL_optint(L, 2, INT_MAX); struct wakecb *cb; int error; for (i = 0; i < n && !TAILQ_EMPTY(&cv->waiting); i++) { cb = TAILQ_FIRST(&cv->waiting); wakecb_del(cb); if ((error = cb->fn(cb))) goto error; } lua_pushinteger(L, i); return 1; error: lua_pushnil(L); lua_pushstring(L, cqs_strerror(error)); lua_pushinteger(L, error); return 3; } /* cond_signal() */ static int cond_pollfd(lua_State *L) { cond_checkself(L, 1); lua_settop(L, 1); return 1; } /* cond_pollfd() */ static int cond_events(lua_State *L NOTUSED) { return 0; } /* cond_events() */ static int cond_timeout(lua_State *L NOTUSED) { return 0; } /* cond_timeout() */ static const luaL_Reg cond_methods[] = { { "wait", &cond_wait }, { "signal", &cond_signal }, { "pollfd", &cond_pollfd }, { "events", &cond_events }, { "timeout", &cond_timeout }, { NULL, NULL } }; /* cond_methods[] */ static const luaL_Reg cond_metatable[] = { { "__call", &cond__call }, { "__gc", &cond__gc }, { NULL, NULL } }; /* cond_metatable[] */ static const luaL_Reg cond_globals[] = { { "new", &cond_new }, { "type", &cond_type }, { "interpose", &cond_interpose }, { NULL, NULL } }; /* cond_globals[] */ int luaopen__cqueues_condition(lua_State *L) { lua_pushnil(L); /* initial upvalue */ cqs_newmetatable(L, CQS_CONDITION, cond_methods, cond_metatable, 1); lua_pushvalue(L, -1); /* push self as replacement upvalue */ cqs_setmetaupvalue(L, -2, 1); /* insert self as 1st upvalue */ /* capture metatable here, too. */ luaL_newlibtable(L, cond_globals); lua_pushvalue(L, -2); luaL_setfuncs(L, cond_globals, 1); return 1; } /* luaopen__cqueues_condition() */ /* * C O N T I N U A T I O N Q U E U E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define CQUEUE_CLASS "Continuation Queue" const char *cqueue__poll = "poll magic"; // signals multilevel yield typedef int auxref_t; struct event; struct thread; struct fileno; struct cqueue; struct event { int fd; short events; double timeout; _Bool pending; int index; /* on .thread->L stack */ struct thread *thread; TAILQ_ENTRY(event) tqe; struct fileno *fileno; LIST_ENTRY(event) fle; struct wakecb *wakecb; }; /* struct event */ struct fileno { int fd; short state; LIST_HEAD(, event) events; LLRB_ENTRY(fileno) rbe; LIST_ENTRY(fileno) le; }; /* struct fileno */ struct timer { double timeout; LLRB_ENTRY(timer) rbe; }; /* struct timer */ struct thread { lua_State *L; /* only for coroutines */ TAILQ_HEAD(, event) events; unsigned count; struct threads *threads; LIST_ENTRY(thread) le; double mintimeout; struct timer timer; }; /* struct thread */ #define timer2thread(timer) ((struct thread *)((char *)(timer) - offsetof(struct thread, timer))) struct cqueue { struct kpoll kp; struct { LLRB_HEAD(table, fileno) table; LIST_HEAD(, fileno) polling, outstanding, inactive; } fileno; struct { struct pool wakecb, fileno, event; } pool; struct { LIST_HEAD(threads, thread) polling, pending; struct thread *current; unsigned count; } thread; LLRB_HEAD(timers, timer) timers; struct cstack *cstack; LIST_ENTRY(cqueue) le; }; /* struct cqueue */ static inline int fileno_cmp(const struct fileno *const a, const struct fileno *const b) { return a->fd - b->fd; } /* fileno_cmp() */ LLRB_GENERATE_STATIC(table, fileno, rbe, fileno_cmp) static inline int timer_cmp(const struct timer *const a, const struct timer *const b) { return (a->timeout < b->timeout)? -1 : (a->timeout > b->timeout)? 1 : (a < b)? -1 : (a > b)? 1 : 0; } /* timer_cmp() */ LLRB_GENERATE_STATIC(timers, timer, rbe, timer_cmp) struct stackinfo { struct cqueue *Q; /* actual cqueue object */ lua_State *L; /* stack holding cqueue object reference (i.e. thread calling :step) */ int self; /* stack index in L of cqueue object */ lua_State *T; /* running thread */ struct stackinfo *running; /* next running cqueue object in call stack */ }; /* struct stackinfo */ static void cstack_push(struct cstack *, struct stackinfo *); static void cstack_pop(struct cstack *); static _Bool cstack_isrunning(const struct cstack *, const struct cqueue *); #define CALLINFO_INITIALIZER { 0, { 0, 0, 0, 0, -1 } } struct callinfo { cqs_index_t self; /* stack index of cqueue object */ struct { cqs_index_t value; int code; cqs_index_t thread; cqs_index_t object; int fd; } error; }; /* struct callinfo */ static struct cqueue *cqueue_checkvalid(lua_State *L, int index, struct cqueue *Q) { luaL_argcheck(L, !!Q->cstack, index, "cqueue closed"); return Q; } /* cqueue_checkvalid() */ static struct cqueue *cqueue_checkself(lua_State *L, int index) { return cqueue_checkvalid(L, index, cqs_checkudata(L, index, 1, CQUEUE_CLASS)); } /* cqueue_checkself() */ static struct cqueue *cqueue_enter_nothrow(lua_State *L, struct callinfo *I, int index, struct cqueue *Q) { I->self = lua_absindex(L, index); I->error.value = 0; I->error.code = 0; I->error.thread = 0; I->error.object = 0; I->error.fd = -1; return Q; } /* cqueue_enter_nothrow() */ static struct cqueue *cqueue_enter(lua_State *L, struct callinfo *I, int index) { return cqueue_enter_nothrow(L, I, index, cqueue_checkself(L, index)); } /* cqueue_enter() */ static cqs_error_t cqueue_tryalert(struct cqueue *Q) { if (!cstack_isrunning(Q->cstack, Q) || LIST_EMPTY(&Q->thread.pending)) { return kpoll_alert(&Q->kp); } else { return 0; } } /* cqueue_tryalert() */ static void err_setvfstring(lua_State *L, struct callinfo *I, const char *fmt, va_list ap) { lua_pushvfstring(L, fmt, ap); I->error.value = lua_gettop(L); } /* err_setvfstring() */ static void err_setfstring(lua_State *L, struct callinfo *I, const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_setvfstring(L, I, fmt, ap); va_end(ap); } /* err_setfstring() */ static void err_setcode(lua_State *L, struct callinfo *I, int code) { I->error.code = code; if (!I->error.value) err_setfstring(L, I, "%s", cqs_strerror(code)); } /* err_setcode() */ static void err_setthread(lua_State *L, struct callinfo *I, struct thread *T) { lua_pushthread(T->L); lua_xmove(T->L, L, 1); I->error.thread = lua_gettop(L); } /* err_setthread() */ static void err_setobject(lua_State *L, struct callinfo *I, cqs_index_t index) { if (index) I->error.object = lua_absindex(L, index); } /* err_setobject() */ static void err_setfd(lua_State *L NOTUSED, struct callinfo *I, int fd) { I->error.fd = fd; } /* err_setfd() */ static void err_setinfo(lua_State *L, struct callinfo *I, int code, struct thread *T, int object, const char *fmt, ...) { /* set object first in case it's a relative index */ if (object) err_setobject(L, I, object); if (T) err_setthread(L, I, T); if (fmt) { va_list ap; va_start(ap, fmt); err_setvfstring(L, I, fmt, ap); va_end(ap); } /* * set code after we set any string so we don't unnecessarily * instantiate a string description */ if (code) err_setcode(L, I, code); } /* err_setinfo() */ static _Bool err_onstack(lua_State *L NOTUSED, struct callinfo *I) { return I->error.value || I->error.thread || I->error.object; } /* err_onstack() */ static void err_corrupt(lua_State *L, int index, const char *type) { luaL_error(L, "corrupt error stack: expected %s, got %s at index %d", type, luaL_typename(L, index), index); } /* err_corrupt() */ static void err_checktype(lua_State *L, int index, int type) { if (lua_type(L, index) != type) err_corrupt(L, index, lua_typename(L, type)); } /* err_checktype() */ static const char *err_pushvalue(lua_State *L, struct callinfo *I) { if (I->error.value) { lua_pushvalue(L, I->error.value); } else { lua_pushstring(L, "no error message"); } return lua_tostring(L, -1); } /* err_pushvalue() */ static cqs_nargs_t err_pushinfo(lua_State *L, struct callinfo *I) { int nargs = 0; luaL_checkstack(L, 5, "too many arguments"); err_pushvalue(L, I); nargs = 1; if (I->error.code) { lua_pushinteger(L, I->error.code); nargs = 2; } if (I->error.thread) { lua_settop(L, lua_gettop(L) + (2 - nargs)); err_checktype(L, I->error.thread, LUA_TTHREAD); lua_pushvalue(L, I->error.thread); nargs = 3; } if (I->error.object) { lua_settop(L, lua_gettop(L) + (3 - nargs)); if (lua_isnone(L, I->error.object)) err_corrupt(L, I->error.object, "any"); lua_pushvalue(L, I->error.object); nargs = 4; } if (I->error.fd != -1) { lua_settop(L, lua_gettop(L) + (4 - nargs)); lua_pushinteger(L, I->error.fd); nargs = 5; } return nargs; } /* err_pushinfo() */ static void err_error(lua_State *L, struct callinfo *I) { err_pushvalue(L, I); lua_error(L); } /* err_error() */ static void cqueue_preinit(struct cqueue *Q) { memset(Q, 0, sizeof *Q); kpoll_preinit(&Q->kp); Q->thread.current = NULL; pool_init(&Q->pool.wakecb, sizeof (struct wakecb)); pool_init(&Q->pool.fileno, sizeof (struct fileno)); pool_init(&Q->pool.event, sizeof (struct event)); } /* cqueue_preinit() */ static void cstack_add(lua_State *, struct cqueue *); static void cqueue_init(lua_State *L, struct cqueue *Q, int index) { int error; index = lua_absindex(L, index); if ((error = kpoll_init(&Q->kp))) luaL_error(L, "unable to initialize continuation queue: %s", cqs_strerror(error)); /* * give ourselves an empty table of threads */ lua_newtable(L); cqs_setuservalue(L, index); /* * associate ourselves with global continuation stack */ cstack_add(L, Q); } /* cqueue_init() */ static void thread_del(lua_State *, struct cqueue *, struct callinfo *, struct thread *); static int fileno_del(struct cqueue *, struct fileno *, _Bool); static void cstack_del(struct cqueue *); static int cstack_onclosefd(int *, void *); /* * NOTE: Q->cstack can be NULL if cqueue_init() OOM'd. See cstack_closefd() * and cstack_del(). */ static void cqueue_destroy(lua_State *L, struct cqueue *Q, struct callinfo *I) { struct cstack *cstack = Q->cstack; struct thread *thread; struct fileno *fileno; void *next; cstack_del(Q); Q->thread.current = NULL; while ((thread = LIST_FIRST(&Q->thread.pending))) { thread_del(L, Q, I, thread); } while ((thread = LIST_FIRST(&Q->thread.polling))) { thread_del(L, Q, I, thread); } for (fileno = LLRB_MIN(table, &Q->fileno.table); fileno; fileno = next) { next = LLRB_NEXT(table, &Q->fileno.table, fileno); fileno_del(Q, fileno, 0); } kpoll_destroy(&Q->kp, &cstack_onclosefd, cstack); pool_destroy(&Q->pool.event); pool_destroy(&Q->pool.fileno); pool_destroy(&Q->pool.wakecb); } /* cqueue_destroy() */ static int cqueue_new(lua_State *L) { struct cqueue *Q; Q = lua_newuserdata(L, sizeof *Q); cqueue_preinit(Q); luaL_getmetatable(L, CQUEUE_CLASS); lua_setmetatable(L, -2); cqueue_init(L, Q, -1); return 1; } /* cqueue_new() */ static int cqueue__gc(lua_State *L) { struct callinfo I; struct cqueue *Q = cqs_checkudata(L, 1, 1, CQUEUE_CLASS); cqueue_enter_nothrow(L, &I, 1, Q); cqueue_destroy(L, Q, &I); return 0; } /* cqueue__gc() */ static void thread_move(struct thread *T, struct threads *list) { if (T->threads != list) { LIST_REMOVE(T, le); LIST_INSERT_HEAD(list, T, le); T->threads = list; } } /* thread_move() */ static struct fileno *fileno_find(struct cqueue *Q, int fd) { struct fileno key; key.fd = fd; return LLRB_FIND(table, &Q->fileno.table, &key); } /* fileno_find() */ static struct fileno *fileno_get(struct cqueue *Q, int fd, int *error) { struct fileno *fileno; if (!(fileno = fileno_find(Q, fd))) { if (!(fileno = pool_get(&Q->pool.fileno, error))) return NULL; fileno->fd = fd; fileno->state = 0; LIST_INIT(&fileno->events); LIST_INSERT_HEAD(&Q->fileno.inactive, fileno, le); LLRB_INSERT(table, &Q->fileno.table, fileno); } return fileno; } /* fileno_get() */ static cqs_error_t fileno_signal(struct cqueue *Q, struct fileno *fileno, short events) { struct event *event; int error = 0, _error; LIST_FOREACH(event, &fileno->events, fle) { /* XXX: If POLLPRI should we always mark as pending? */ if (event->events & events) event->pending = 1; thread_move(event->thread, &Q->thread.pending); if ((_error = cqueue_tryalert(Q))) error = _error; } return error; } /* fileno_signal() */ static int fileno_ctl(struct cqueue *Q, struct fileno *fileno, short events) { int error; if ((error = kpoll_ctl(&Q->kp, fileno->fd, &fileno->state, events, fileno))) return error; /* XXX: Should we call fileno_signal? */ LIST_REMOVE(fileno, le); if (fileno->state) LIST_INSERT_HEAD(&Q->fileno.polling, fileno, le); else LIST_INSERT_HEAD(&Q->fileno.inactive, fileno, le); return 0; } /* fileno_ctl() */ static int fileno_update(struct cqueue *Q, struct fileno *fileno) { struct event *event; short events = 0; LIST_FOREACH(event, &fileno->events, fle) { events |= event->events; } return fileno_ctl(Q, fileno, events); } /* fileno_update() */ static int fileno_del(struct cqueue *Q, struct fileno *fileno, _Bool update) { struct event *event; int error = 0; while ((event = LIST_FIRST(&fileno->events))) { event->fileno = NULL; LIST_REMOVE(event, fle); } if (update) error = fileno_update(Q, fileno); LLRB_REMOVE(table, &Q->fileno.table, fileno); LIST_REMOVE(fileno, le); pool_put(&Q->pool.fileno, fileno); return error; } /* fileno_del() */ static cqs_error_t wakecb_wakeup(struct wakecb *cb) { struct cqueue *Q = cb->arg[0]; struct event *event = cb->arg[1]; event->pending = 1; thread_move(event->thread, &Q->thread.pending); return cqueue_tryalert(Q); } /* wakecb_wakeup() */ #define object_pcall(L, I, T, index, field, ...) object_pcall((L), (I), (T), (index), (field), ((int[]){ __VA_ARGS__ }), countof(((int[]){ __VA_ARGS__ }))) NONNULL(1, 2, 5) static cqs_status_t (object_pcall)(lua_State *L, struct callinfo *I, struct thread *T, int index, const char *field, int rtype[], int n) { int type, i, status; index = lua_absindex(L, index); lua_getfield(L, index, field); if (lua_isfunction(L, -1)) { lua_pushvalue(L, index); if (LUA_OK != (status = lua_pcall(L, 1, 1, 0))) { err_setinfo(L, I, 0, T, index, "error calling method %s: %s", field, lua_tostring(L, -1)); return status; } } type = lua_type(L, -1); for (i = 0; i < n; i++) { if (type == rtype[i]) return LUA_OK; } err_setinfo(L, I, 0, T, index, "error loading field %s: %s expected, got %s", field, lua_typename(L, rtype[0]), luaL_typename(L, -1)); return LUA_ERRRUN; } /* object_pcall() */ NONNULL(1, 2, 3, 6) static int object_getcv(lua_State *L, struct cqueue *Q, struct callinfo *I, struct thread *T, int index, struct event *event) { struct condition *cv = lua_touserdata(L, index); int error; if (!(event->wakecb = pool_get(&Q->pool.wakecb, &error))) { err_setinfo(L, I, error, T, index, "unable to wait on conditional variable: %s", cqs_strerror(error)); return LUA_ERRRUN; } wakecb_init(event->wakecb, &wakecb_wakeup, Q, event); wakecb_add(event->wakecb, cv); return LUA_OK; } /* object_getcv() */ NONNULL(1, 2, 3, 6) static cqs_status_t object_getinfo(lua_State *L, struct cqueue *Q, struct callinfo *I, struct thread *T, int index, struct event *event) { int status; /* optimize simple timeout */ if (lua_isnumber(T->L, index)) { event->timeout = abstimeout(lua_tonumber(T->L, index)); return LUA_OK; } /* * push onto our local stack so we don't dirty the thread stack and * also to allow fast upvalue comparisons */ lua_pushvalue(T->L, index); lua_xmove(T->L, L, 1); if (cqs_testudata(L, -1, 2)) { event->fd = cqs_socket_pollfd(L, -1); event->events = cqs_socket_events(L, -1); event->timeout = abstimeout(cqs_socket_timeout(L, -1)); } else if (cqs_testudata(L, -1, 3)) { if ((LUA_OK != (status = object_getcv(L, Q, I, T, -1, event)))) goto oops; } else { if (LUA_OK != (status = object_pcall(L, I, T, -1, "pollfd", LUA_TNUMBER, LUA_TUSERDATA, LUA_TNIL))) goto oops; if (lua_isuserdata(L, -1) && cqs_testudata(L, -1, 3)) { if ((LUA_OK != (status = object_getcv(L, Q, I, T, -1, event)))) goto oops; } else { event->fd = luaL_optinteger(L, -1, -1); event->fd = MAX(event->fd, -1); } lua_pop(L, 1); /* pop fd or condvar */ if (LUA_OK != (status = object_pcall(L, I, T, -1, "events", LUA_TNUMBER, LUA_TSTRING, LUA_TNIL))) goto oops; if (lua_isnumber(L, -1)) { event->events = (POLLIN|POLLOUT|POLLPRI) & lua_tointeger(L, -1); } else { const char *mode = luaL_optstring(L, -1, ""); event->events = 0; while (*mode) { if (*mode == 'r') event->events |= POLLIN; else if (*mode == 'w') event->events |= POLLOUT; else if (*mode == 'p') event->events |= POLLPRI; mode++; } } lua_pop(L, 1); /* pop event mode */ if (LUA_OK != (status = object_pcall(L, I, T, -1, "timeout", LUA_TNUMBER, LUA_TNIL))) goto oops; event->timeout = abstimeout(luaL_optnumber(L, -1, event->timeout)); lua_pop(L, 1); /* pop timeout */ } lua_pop(L, 1); /* pop object */ return LUA_OK; oops: return status; } /* object_getinfo() */ static void event_init(struct event *event, struct thread *T, int index) { memset(event, 0, sizeof *event); event->fd = -1; event->timeout = NAN; event->index = index; event->thread = T; TAILQ_INSERT_TAIL(&T->events, event, tqe); T->count++; } /* event_init() */ static cqs_status_t event_add(lua_State *L, struct cqueue *Q, struct callinfo *I, struct thread *T, int index) { struct event *event; struct fileno *fileno; int error, status; if (!(event = pool_get(&Q->pool.event, &error))) goto error; event_init(event, T, index); if (LUA_OK != (status = object_getinfo(L, Q, I, T, index, event))) return status; if (event->fd >= 0 && event->events) { if (!(fileno = fileno_get(Q, event->fd, &error))) goto error; LIST_INSERT_HEAD(&fileno->events, event, fle); event->fileno = fileno; LIST_REMOVE(fileno, le); LIST_INSERT_HEAD(&Q->fileno.outstanding, fileno, le); } return LUA_OK; error: err_setinfo(L, I, error, T, index, "unable to add event: %s", cqs_strerror(error)); return LUA_ERRRUN; } /* event_add() */ static void event_del(struct cqueue *Q, struct event *event) { if (event->wakecb) { wakecb_del(event->wakecb); pool_put(&Q->pool.wakecb, event->wakecb); } if (event->fileno) { LIST_REMOVE(event->fileno, le); LIST_INSERT_HEAD(&Q->fileno.outstanding, event->fileno, le); LIST_REMOVE(event, fle); } TAILQ_REMOVE(&event->thread->events, event, tqe); assert(event->thread->count > 0); event->thread->count--; pool_put(&Q->pool.event, event); } /* event_del() */ static void timer_init(struct timer *timer) { timer->timeout = NAN; } /* timer_init() */ static void timer_del(struct cqueue *Q, struct timer *timer) { if (isfinite(timer->timeout)) { LLRB_REMOVE(timers, &Q->timers, timer); timer->timeout = NAN; } } /* timer_del() */ static void timer_add(struct cqueue *Q, struct timer *timer, double timeout) { timer_del(Q, timer); if (isfinite(timeout)) { timer->timeout = timeout; LLRB_INSERT(timers, &Q->timers, timer); } } /* timer_add() */ static void timer_destroy(struct cqueue *Q, struct timer *timer) { timer_del(Q, timer); } /* timer_destroy() */ static double thread_timeout(struct thread *T) { double timeout = NAN; struct event *event; TAILQ_FOREACH(event, &T->events, tqe) { timeout = mintimeout(timeout, event->timeout); } return timeout; } /* thread_timeout() */ static void thread_add(lua_State *L, struct cqueue *Q, struct callinfo *I, int index) { struct thread *T; index = lua_absindex(L, index); T = lua_newuserdata(L, sizeof *T); memset(T, 0, sizeof *T); TAILQ_INIT(&T->events); timer_init(&T->timer); /* anchor new lua_State to our thread context */ lua_pushvalue(L, index); cqs_setuservalue(L, -2); T->L = lua_tothread(L, index); /* anchor thread context to cqueue object */ cqs_getuservalue(L, I->self); lua_pushvalue(L, -2); lua_rawsetp(L, -2, T); lua_pop(L, 2); LIST_INSERT_HEAD(&Q->thread.pending, T, le); T->threads = &Q->thread.pending; Q->thread.count++; } /* thread_add() */ static void thread_del(lua_State *L, struct cqueue *Q, struct callinfo *I, struct thread *T) { struct event *event; while ((event = TAILQ_FIRST(&T->events))) { event_del(Q, event); } timer_destroy(Q, &T->timer); LIST_REMOVE(T, le); Q->thread.count--; /* * XXX: These lua operations are documented as able to longjmp on * OOM. However, inspection of the lua source suggests that when used * as below they won't throw. * - In lua5.1 pushing a lightuserdata doesn't allocate (they're * stack allocated) * - rawset doesn't allocate if the key already exists in the table * (which it always does for this function) */ cqs_getuservalue(L, I->self); /* set thread's uservalue (it's thread) to nil */ lua_rawgetp(L, -1, T); lua_pushnil(L); cqs_setuservalue(L, -2); lua_pop(L, 1); T->L = NULL; /* remove thread from cqueues's thread table */ lua_pushnil(L); lua_rawsetp(L, -2, T); lua_pop(L, 1); } /* thread_del() */ static cqs_status_t cqueue_update(lua_State *L, struct cqueue *Q, struct callinfo *I, struct thread *T) { struct fileno *fileno, *next; struct event *event; int error; for (fileno = LIST_FIRST(&Q->fileno.outstanding); fileno; fileno = next) { next = LIST_NEXT(fileno, le); if ((error = fileno_update(Q, fileno))) goto error; } return LUA_OK; error: LIST_FOREACH(event, &fileno->events, fle) { if (event->thread != T) continue; lua_pushvalue(T->L, event->index); lua_xmove(T->L, L, 1); err_setobject(L, I, lua_gettop(L)); break; } err_setfd(L, I, fileno->fd); err_setinfo(L, I, error, T, 0, "unable to update event disposition: %s (fd:%d)", cqs_strerror(error), fileno->fd); return LUA_ERRRUN; } /* cqueue_update() */ static cqs_error_t cqueue_reboot(struct cqueue *Q, _Bool stop, _Bool restart) { if (stop) { struct fileno *fileno; struct thread *thread; while ((fileno = LIST_FIRST(&Q->fileno.polling))) { LIST_REMOVE(fileno, le); LIST_INSERT_HEAD(&Q->fileno.outstanding, fileno, le); } LIST_FOREACH(fileno, &Q->fileno.outstanding, le) { fileno->state = 0; } while ((thread = LIST_FIRST(&Q->thread.polling))) { thread_move(thread, &Q->thread.pending); } kpoll_destroy(&Q->kp, &cstack_onclosefd, Q->cstack); } if (restart) { int error; if ((error = kpoll_init(&Q->kp))) return error; } return 0; } /* cqueue_reboot() */ static _Bool auxL_xcopy(lua_State *from, lua_State *to, int count) { int index; if (!lua_checkstack(from, count) || !lua_checkstack(to, count + LUA_MINSTACK)) return 0; for (index = 1; index <= count; index++) lua_pushvalue(from, index); lua_xmove(from, to, count); return 1; } /* auxL_xcopy() */ static cqs_status_t cqueue_resume(lua_State *L, struct cqueue *Q, struct callinfo *I, struct thread *T) { int otop = lua_gettop(L), nargs, status, tmp_status, index; struct event *event; status = lua_status(T->L); if (status == LUA_YIELD && lua_islightuserdata(T->L, 1) && lua_topointer(T->L, 1) == CQUEUE__POLL) { /* * Preserve any previously yielded objects on another stack * until we can call cqueue_update() because when * lua_resume() returns the thread stack will only contain * new objects. Pausing the GC isn't a viable option because * we don't know if or when lua_resume() will return. */ if (!auxL_xcopy(T->L, L, lua_gettop(T->L))) goto nospace; nargs = 0; if (!lua_checkstack(T->L, T->count + LUA_MINSTACK)) goto nospace; while ((event = TAILQ_FIRST(&T->events))) { if (event->pending) { lua_pushvalue(T->L, event->index); nargs++; } event_del(Q, event); } } else { nargs = lua_gettop(T->L); if (status != LUA_YIELD) { if (nargs > 0) nargs -= 1; /* exclude function */ } } timer_del(Q, &T->timer); cstack_push(Q->cstack, &(struct stackinfo){ Q, L, I->self, T->L }); status = lua_resume(T->L, L, nargs); cstack_pop(Q->cstack); switch (status) { case LUA_YIELD: if (lua_islightuserdata(T->L, 1) && lua_topointer(T->L, 1) == CQUEUE__POLL) { for (index = 2; index <= lua_gettop(T->L); index++) { switch (lua_type(T->L, index)) { case LUA_TNIL: continue; default: if (LUA_OK != (status = event_add(L, Q, I, T, index))) goto defunct; } } if (LUA_OK != (status = cqueue_update(L, Q, I, T))) goto defunct; timer_add(Q, &T->timer, thread_timeout(T)); if (!TAILQ_EMPTY(&T->events) || isfinite(T->timer.timeout)) thread_move(T, &Q->thread.polling); } else { if (LUA_OK != (tmp_status = cqueue_update(L, Q, I, T))) { status = tmp_status; goto defunct; } break; } break; case LUA_OK: if (LUA_OK != (status = cqueue_update(L, Q, I, T))) goto defunct; thread_del(L, Q, I, T); break; default: if (LUA_OK != cqueue_update(L, Q, I, T)) goto defunct; lua_xmove(T->L, L, 1); /* move error message */ I->error.value = lua_gettop(L); err_setthread(L, I, T); defunct: thread_del(L, Q, I, T); break; } /* switch() */ /* discard objects preserved while resuming coroutine */ if (!err_onstack(L, I)) lua_settop(L, otop); return status; nospace: err_setinfo(L, I, 0, T, 0, "stack overflow"); status = LUA_ERRMEM; goto defunct; } /* cqueue_resume() */ static cqs_status_t cqueue_process_threads(lua_State *L, struct cqueue *Q, struct callinfo *I) { cqs_status_t status; struct thread *nxt; for (; Q->thread.current; Q->thread.current = nxt) { nxt = LIST_NEXT(Q->thread.current, le); if (LUA_OK != (status = cqueue_resume(L, Q, I, Q->thread.current))) { return status; } } return LUA_OK; } /* cqueue_process_threads() */ static cqs_status_t cqueue_process(lua_State *L, struct cqueue *Q, struct callinfo *I) { int onalert = 0; kpoll_event_t *ke; struct fileno *fileno; struct event *event; struct thread *T; struct timer *timer; double curtime; short events; int status; KPOLL_FOREACH(ke, &Q->kp) { if (kpoll_isalert(&Q->kp, ke)) { onalert = 1; continue; } fileno = kpoll_udata(ke); events = kpoll_pending(ke); fileno_signal(Q, fileno, events); fileno->state = kpoll_diff(ke, fileno->state); } curtime = monotime(); LLRB_FOREACH(timer, timers, &Q->timers) { if (isgreater(timer->timeout, curtime)) break; T = timer2thread(timer); TAILQ_FOREACH(event, &T->events, tqe) { if (islessequal(event->timeout, curtime)) event->pending = 1; } thread_move(T, &Q->thread.pending); } assert(NULL == Q->thread.current); Q->thread.current = LIST_FIRST(&Q->thread.pending); if (LUA_OK != (status = cqueue_process_threads(L, Q, I))) { return status; } if (onalert) { kpoll_calm(&Q->kp); } return LUA_OK; } /* cqueue_process() */ static double cqueue_timeout_(struct cqueue *Q) { struct timer *timer; double curtime; if (!(timer = LLRB_MIN(timers, &Q->timers))) return NAN; curtime = monotime(); return (islessequal(timer->timeout, curtime))? 0.0 : timer->timeout - curtime; } /* cqueue_timeout_() */ #if LUA_VERSION_NUM <= 502 static int cqueue_step_cont(lua_State *L) { #else static int cqueue_step_cont(lua_State *L, int status NOTUSED, lua_KContext ctx NOTUSED) { #endif int nargs = lua_gettop(L); struct callinfo I = CALLINFO_INITIALIZER; struct cqueue *Q = cqueue_checkself(L, 1); struct thread *T = Q->thread.current; if (!T) { luaL_error(L, "cqueue not yielded"); NOTREACHED; } if (lua_islightuserdata(L, 2) && lua_touserdata(L, 2) == CQUEUE__POLL) { luaL_error(L, "cannot resume a coroutine passing internal cqueues._POLL value as first parameter"); NOTREACHED; } /* move arguments onto resumed stack */ lua_xmove(L, T->L, nargs-1); cqueue_enter(L, &I, 1); switch(cqueue_process_threads(L, Q, &I)) { case LUA_OK: break; case LUA_YIELD: /* clear everything off the stack except for cqueue object; `I` now invalid */ lua_settop(L, 1); #if LUA_VERSION_NUM >= 502 /* move arguments onto 'main' stack to return them from this yield */ nargs = lua_gettop(Q->thread.current->L); lua_xmove(Q->thread.current->L, L, nargs); return lua_yieldk(L, nargs, 0, cqueue_step_cont); #else lua_pushliteral(L, "yielded"); /* move arguments onto 'main' stack to return them from this yield */ nargs = lua_gettop(Q->thread.current->L); lua_xmove(Q->thread.current->L, L, nargs); return nargs+1; #endif default: goto oops; } lua_pushboolean(L, 1); return 1; oops: Q->thread.current = NULL; lua_pushboolean(L, 0); return 1 + err_pushinfo(L, &I); } /* yield_cont() */ static int cqueue_step(lua_State *L) { struct callinfo I; struct cqueue *Q; double timeout; int error; int nargs; lua_settop(L, 2); Q = cqueue_enter(L, &I, 1); if (Q->thread.current) { return luaL_error(L, "cannot step live cqueue"); } if (Q->thread.count && LIST_EMPTY(&Q->thread.pending)) { timeout = mintimeout(luaL_optnumber(L, 2, NAN), cqueue_timeout_(Q)); } else { timeout = 0.0; } if ((error = kpoll_wait(&Q->kp, timeout))) { err_setfstring(L, &I, "error polling: %s", cqs_strerror(error)); err_setcode(L, &I, error); goto oops; } switch(cqueue_process(L, Q, &I)) { case LUA_OK: break; case LUA_YIELD: /* clear everything off the stack except for cqueue object; `I` now invalid */ lua_settop(L, 1); #if LUA_VERSION_NUM >= 502 /* move arguments onto 'main' stack to return them from this yield */ nargs = lua_gettop(Q->thread.current->L); lua_xmove(Q->thread.current->L, L, nargs); return lua_yieldk(L, nargs, 0, cqueue_step_cont); #else lua_pushliteral(L, "yielded"); /* move arguments onto 'main' stack to return them from this yield */ nargs = lua_gettop(Q->thread.current->L); lua_xmove(Q->thread.current->L, L, nargs); return nargs+1; #endif default: goto oops; } lua_pushboolean(L, 1); return 1; oops: Q->thread.current = NULL; lua_pushboolean(L, 0); return 1 + err_pushinfo(L, &I); } /* cqueue_step() */ static int cqueue_attach(lua_State *L) { struct callinfo I; struct cqueue *Q; int error; lua_settop(L, 2); Q = cqueue_enter(L, &I, 1); luaL_checktype(L, 2, LUA_TTHREAD); thread_add(L, Q, &I, 2); if ((error = cqueue_tryalert(Q))) goto error; lua_pushvalue(L, 1); /* return self */ return 1; error: lua_pushnil(L); lua_pushstring(L, cqs_strerror(error)); lua_pushinteger(L, error); return 3; } /* cqueue_attach() */ static int cqueue_wrap(lua_State *L) { struct callinfo I; struct cqueue *Q; struct lua_State *newL; int top, i, error; top = lua_gettop(L); Q = cqueue_enter(L, &I, 1); luaL_checktype(L, 2, LUA_TFUNCTION); newL = lua_newthread(L); lua_insert(L, 2); luaL_checkstack(newL, top - 1, "too many arguments"); lua_xmove(L, newL, top - 1); thread_add(L, Q, &I, -1); if ((error = cqueue_tryalert(Q))) goto error; lua_pushvalue(L, 1); /* return self */ return 1; error: lua_pushnil(L); lua_pushstring(L, cqs_strerror(error)); lua_pushinteger(L, error); return 3; } /* cqueue_wrap() */ static int cqueue_alert(lua_State *L) { struct cqueue *Q = cqueue_checkself(L, 1); int error; if ((error = kpoll_alert(&Q->kp))) goto error; lua_pushvalue(L, 1); return 1; error: lua_pushnil(L); lua_pushstring(L, cqs_strerror(error)); lua_pushinteger(L, error); return 3; } /* cqueue_alert() */ static int cqueue_empty(lua_State *L) { struct cqueue *Q = cqueue_checkself(L, 1); lua_pushboolean(L, !Q->thread.count); return 1; } /* cqueue_empty() */ static int cqueue_count(lua_State *L) { struct cqueue *Q = cqueue_checkself(L, 1); lua_pushinteger(L, Q->thread.count); return 1; } /* cqueue_count() */ static cqs_error_t cqueue_cancelfd(struct cqueue *Q, int fd) { struct fileno *fileno; int error = 0, _error; if (!(fileno = fileno_find(Q, fd))) return 0; if ((_error = fileno_signal(Q, fileno, POLLIN|POLLOUT|POLLPRI))) error = _error; if ((_error = fileno_ctl(Q, fileno, 0))) error = _error; return error; } /* cqueue_cancelfd() */ static int cqueue_checkfd(lua_State *L, struct callinfo *I, int index) { int fd; if (!lua_isnil(L, index) && !lua_isnumber(L, index)) { if (LUA_OK != object_pcall(L, I, NULL, index, "pollfd", LUA_TNUMBER)) err_error(L, I); fd = luaL_optint(L, -1, -1); lua_pop(L, 1); } else { fd = luaL_optint(L, index, -1); } return fd; } /* cqueue_checkfd() */ static int cqueue_cancel(lua_State *L) { struct callinfo I; int top = lua_gettop(L); struct cqueue *Q = cqueue_enter(L, &I, 1); int index; for (index = 2; index <= top; index++) cqueue_cancelfd(Q, cqueue_checkfd(L, &I, index)); return 0; } /* cqueue_cancel() */ static int cqueue_reset(lua_State *L) { struct cqueue *Q = cqueue_checkself(L, 1); int error; if ((error = cqueue_reboot(Q, 1, 1))) return luaL_error(L, "unable to reset continuation queue: %s", cqs_strerror(error)); return 0; } /* cqueue_reset() */ cqs_error_t cqs_sigmask(int how, const sigset_t *set, sigset_t *oset) { if (oset) sigemptyset(oset); #if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0 return pthread_sigmask(how, set, oset); #else return sigprocmask(how, set, oset)? errno : 0; #endif } /* cqs_sigmask() */ /* * cqs_pselect * * kqueue-backed pselect for OpenBSD and Apple. Though Apple provides * pselect in libc, it's a broken wrapper around select which doesn't solve * the race condition. * * Logical steps: * * 1) check for signals which will be unmasked and deliverable on select; * 2) if any are pending, allow delivery and return EINTR; otherwise, * 3) setup kqueue listener before we unblock; * 4) execute select with specified signal mask. * * NOTES: * o EVFILT_SIGNAL is an edge-triggered filter, which means that if a * signal is already pending when we add the listener, we won't be * notified when it's subsequently delivered. The solution is just to * check the pending set ahead of time. * * o This implementation doesn't try to minimize the signal disposition * race where the application doesn't use the proper mask/unmask * pattern. In particular, it calls sigpending earlier rather later. * In the future it might even optimize by not installing a filter * for signals already unblocked. */ static int cqs_pselect(int nfds, fd_set *_rfds, fd_set *wfds, fd_set *efds, const struct timespec *_timeout, const sigset_t *_mask, int *_error) { #if __OpenBSD__ || __APPLE__ || (__NetBSD__ && __NetBSD_Version__ < 600000000) fd_set rfds; struct timeval *timeout; sigset_t omask, mask, pending; int kq = -1, error; struct kevent event[NSIG]; unsigned nevent = 0; if (_rfds) FD_COPY(_rfds, &rfds); else FD_ZERO(&rfds); timeout = (_timeout)? &(struct timeval){ _timeout->tv_sec, _timeout->tv_nsec / 1000 } : NULL; if (_mask) mask = *_mask; else cqs_sigmask(SIG_SETMASK, NULL, &mask); sigpending(&pending); for (int i = 1; i < NSIG && nevent < countof(event); i++) { struct sigaction sa; if (i == SIGKILL || i == SIGSTOP) continue; if (sigismember(&mask, i)) continue; if (0 != sigaction(i, NULL, &sa)) goto syerr; if (sa.sa_handler == SIG_DFL || sa.sa_handler == SIG_IGN) continue; if (sigismember(&pending, i)) { /* allow signals to be delivered */ if ((error = cqs_sigmask(SIG_SETMASK, &mask, &omask))) goto error; cqs_sigmask(SIG_SETMASK, &omask, NULL); error = EINTR; goto error; } EV_SET(&event[nevent], i, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); nevent++; } if (nevent > 0) { if (-1 == (kq = kqueue())) goto syerr; if (0 != kevent(kq, event, nevent, 0, 0, 0)) goto syerr; if (kq >= (int)FD_SETSIZE) { error = EINVAL; goto error; } FD_SET(kq, &rfds); } if ((error = cqs_sigmask(SIG_SETMASK, &mask, &omask))) goto error; if (-1 == (nfds = select(MAX(nfds, kq + 1), &rfds, wfds, efds, timeout))) *_error = errno; cqs_sigmask(SIG_SETMASK, &omask, NULL); if (nfds > 0 && kq != -1) { if (FD_ISSET(kq, &rfds) && !--nfds) { error = EINTR; goto error; } FD_CLR(kq, &rfds); if (_rfds) FD_COPY(&rfds, _rfds); } cqs_closefd(&kq); return nfds; syerr: error = errno; error: cqs_closefd(&kq); *_error = error; return -1; #else if (-1 == (nfds = pselect(nfds, _rfds, wfds, efds, _timeout, _mask))) *_error = errno; return nfds; #endif } /* cqs_pselect() */ static int cqueue_pause(lua_State *L) { struct cqueue *Q = cqueue_checkself(L, 1); sigset_t block; fd_set rfds; int index, error; if ((error = cqs_sigmask(SIG_SETMASK, NULL, &block))) goto error; for (index = 2; index <= lua_gettop(L); index++) { sigdelset(&block, luaL_checkint(L, index)); } /* FD_SETSIZE unsigned on FreeBSD. */ if (Q->kp.fd < 0 || Q->kp.fd >= (int)FD_SETSIZE) return luaL_error(L, "cqueue:pause: fd %d outside allowable range 0..%d", Q->kp.fd, (int)(FD_SETSIZE - 1)); FD_ZERO(&rfds); FD_SET(Q->kp.fd, &rfds); if (-1 == cqs_pselect(Q->kp.fd + 1, &rfds, NULL, NULL, f2ts(cqueue_timeout_(Q)), &block, &error)) { if (error != EINTR) goto error; } return 0; error: return luaL_error(L, "cqueue:pause: %s", cqs_strerror(error)); } /* cqueue_pause() */ static int cqueue_pollfd(lua_State *L) { struct cqueue *Q = cqueue_checkself(L, 1); lua_pushinteger(L, Q->kp.fd); return 1; } /* cqueue_pollfd() */ static int cqueue_events(lua_State *L) { cqueue_checkself(L, 1); lua_pushliteral(L, "r"); return 1; } /* cqueue_events() */ static int cqueue_timeout(lua_State *L) { struct cqueue *Q = cqueue_checkself(L, 1); if (!LIST_EMPTY(&Q->thread.pending)) { lua_pushnumber(L, 0.0); } else { double timeout = cqueue_timeout_(Q); if (isfinite(timeout)) lua_pushnumber(L, timeout); else lua_pushnil(L); } return 1; } /* cqueue_timeout() */ static int cqueue_type(lua_State *L) { struct cqueue *Q; if ((Q = cqs_testudata(L, 1, 1))) { if (Q->cstack) { lua_pushstring(L, "controller"); } else { lua_pushstring(L, "closed controller"); } } else { lua_pushnil(L); } return 1; } /* cqueue_type() */ static int cqueue_interpose(lua_State *L) { return cqs_interpose(L, CQUEUE_CLASS); } /* cqueue_interpose() */ static int cqueue_monotime(lua_State *L) { lua_pushnumber(L, monotime()); return 1; } /* cqueue_monotime() */ /* * C O N T I N U A T I O N S T A C K R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #undef CS /* defined by Solaris in /usr/include/sys/crtctl.h */ struct cstack { LIST_HEAD(, cqueue) cqueues; struct stackinfo *running; }; /* struct cstack */ static struct cstack *cstack_self(lua_State *L) { static const int index = 47; struct cstack *CS; lua_rawgetp(L, LUA_REGISTRYINDEX, &index); CS = lua_touserdata(L, -1); lua_pop(L, 1); if (CS) return CS; CS = lua_newuserdata(L, sizeof *CS); memset(CS, 0, sizeof *CS); LIST_INIT(&CS->cqueues); lua_rawsetp(L, LUA_REGISTRYINDEX, &index); return CS; } /* cstack_self() */ static void cstack_add(lua_State *L, struct cqueue *Q) { Q->cstack = cstack_self(L); LIST_INSERT_HEAD(&Q->cstack->cqueues, Q, le); } /* cstack_add() */ static void cstack_del(struct cqueue *Q) { /* NB: Q->cstack can be NULL. See cqueue_destroy(). */ if (Q->cstack) { LIST_REMOVE(Q, le); Q->cstack = NULL; } } /* cstack_del() */ static void cstack_cancelfd(struct cstack *CS, int fd) { struct cqueue *Q; LIST_FOREACH(Q, &CS->cqueues, le) { cqueue_cancelfd(Q, fd); } } /* cstack_cancelfd() */ static void cstack_closefd(struct cstack *CS, int *fd) { /* NB: CS can be NULL. See cqueue_destroy(). */ if (CS) { cstack_cancelfd(CS, *fd); } cqs_closefd(fd); } /* cstack_closefd() */ /* NB: libevent-style prototype similar to dns.c and socket.c close handlers */ static int cstack_onclosefd(int *fd, void *CS) { cstack_closefd(CS, fd); return 0; } /* cstack_onclosefd() */ static int cstack_cancel(lua_State *L) { struct callinfo I = CALLINFO_INITIALIZER; struct cstack *CS = cstack_self(L); struct cqueue *Q; int index, fd; for (index = 1; index <= lua_gettop(L); index++) { fd = cqueue_checkfd(L, &I, index); cstack_cancelfd(CS, fd); } return 0; } /* cstack_cancel() */ void cqs_cancelfd(lua_State *L, int fd) { cstack_cancelfd(cstack_self(L), fd); } /* cqs_cancelfd() */ static int cstack_reset(lua_State *L) { struct cstack *CS = cstack_self(L); struct cqueue *Q; int error; LIST_FOREACH(Q, &CS->cqueues, le) { cqueue_reboot(Q, 1, 0); } LIST_FOREACH(Q, &CS->cqueues, le) { if ((error = cqueue_reboot(Q, 0, 1))) return luaL_error(L, "unable to reset continuation queue: %s", cqs_strerror(error)); } return 0; } /* cstack_reset() */ static void cstack_push(struct cstack *CS, struct stackinfo *info) { info->running = CS->running; CS->running = info; } /* cstack_push() */ static void cstack_pop(struct cstack *CS) { CS->running = CS->running->running; } /* cstack_push() */ static _Bool cstack_isrunning(const struct cstack *CS, const struct cqueue *Q) { struct stackinfo *info; for (info = CS->running; info; info = info->running) { if (info->Q == Q) return 1; } return 0; } /* cstack_isrunning() */ static int cstack_running(lua_State *L) { struct cstack *CS = cstack_self(L); if (CS->running) { lua_pushvalue(CS->running->L, CS->running->self); lua_xmove(CS->running->L, L, 1); lua_pushboolean(L, CS->running->T == L); } else { lua_pushnil(L); lua_pushboolean(L, 0); } return 2; } /* cstack_running() */ /* * C Q U E U E S M O D U L E L I N K A G E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static const luaL_Reg cqueue_methods[] = { { "step", &cqueue_step }, #if LUA_VERSION_NUM < 502 { "step_resume", &cqueue_step_cont }, #endif { "attach", &cqueue_attach }, { "wrap", &cqueue_wrap }, { "alert", &cqueue_alert }, { "empty", &cqueue_empty }, { "count", &cqueue_count }, { "cancel", &cqueue_cancel }, { "reset", &cqueue_reset }, { "pause", &cqueue_pause }, { "pollfd", &cqueue_pollfd }, { "events", &cqueue_events }, { "timeout", &cqueue_timeout }, { "close", &cqueue__gc }, { NULL, NULL } }; /* cqueue_methods[] */ static const luaL_Reg cqueue_metatable[] = { { "__gc", &cqueue__gc }, { NULL, NULL } }; /* cqueue_metatable[] */ static const luaL_Reg cqueues_globals[] = { { "new", &cqueue_new }, { "type", &cqueue_type }, { "interpose", &cqueue_interpose }, { "monotime", &cqueue_monotime }, { "cancel", &cstack_cancel }, { "reset", &cstack_reset }, { "running", &cstack_running }, { NULL, NULL } }; /* cqueues_globals[] */ int luaopen__cqueues(lua_State *L) { /* initialize our dependencies used for fast metatable lookup */ cqs_requiref(L, "_cqueues.socket", &luaopen__cqueues_socket, 0); cqs_requiref(L, "_cqueues.condition", &luaopen__cqueues_condition, 0); lua_pop(L, 2); /* push functions with shared upvalues for fast metatable lookup */ cqs_pushnils(L, 3); /* initial upvalues */ cqs_newmetatable(L, CQUEUE_CLASS, cqueue_methods, cqueue_metatable, 3); lua_pushvalue(L, -1); /* push self as replacement upvalue */ cqs_setmetaupvalue(L, -2, 1); /* insert self as 1st upvalue */ luaL_getmetatable(L, CQS_SOCKET); cqs_setmetaupvalue(L, -2, 2); /* insert socket as 2nd upvalue */ luaL_getmetatable(L, CQS_CONDITION); cqs_setmetaupvalue(L, -2, 3); /* insert condition as 3rd upvalue */ luaL_newlibtable(L, cqueues_globals); lua_pushvalue(L, -2); /* capture metatable as upvalue */ luaL_getmetatable(L, CQS_SOCKET); luaL_getmetatable(L, CQS_CONDITION); luaL_setfuncs(L, cqueues_globals, 3); /* add magic value used to accomplish multilevel yielding */ lua_pushlightuserdata(L, CQUEUE__POLL); lua_setfield(L, -2, "_POLL"); /* add our version constants */ lua_pushliteral(L, CQUEUES_VENDOR); lua_setfield(L, -2, "VENDOR"); lua_pushinteger(L, CQUEUES_VERSION); lua_setfield(L, -2, "VERSION"); #if defined CQUEUES_COMMIT if (sizeof CQUEUES_COMMIT > 1) { lua_pushliteral(L, CQUEUES_COMMIT); lua_setfield(L, -2, "COMMIT"); } #endif return 1; } /* luaopen__cqueues() */ /* * D E B U G & U N I T T E S T I N G R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int dbg_f2ms(lua_State *L) { int ms = f2ms(luaL_checknumber(L, 1)); lua_pushinteger(L, ms); lua_pushboolean(L, ms == INT_MAX); return 2; } /* dbg_f2ms() */ static int dbg_f2ts(lua_State *L) { struct timespec *ts = f2ts(luaL_checknumber(L, 1)); if (!ts) return 0; lua_createtable(L, 0, 2); lua_pushinteger(L, ts->tv_sec); lua_setfield(L, -2, "tv_sec"); lua_pushinteger(L, ts->tv_nsec); lua_setfield(L, -2, "tv_nsec"); lua_pushboolean(L, ts->tv_sec == LONG_MAX); return 2; } /* dbg_f2ts() */ static luaL_Reg dbg_globals[] = { { "f2ms", &dbg_f2ms }, { "f2ts", &dbg_f2ts }, { NULL, NULL } }; /* dbg_globals[] */ int luaopen__cqueues_debug(lua_State *L) { luaL_newlib(L, dbg_globals); lua_pushinteger(L, INT_MAX); lua_setfield(L, -2, "INT_MAX"); lua_pushinteger(L, LONG_MAX); lua_setfield(L, -2, "LONG_MAX"); return 1; } /* luaopen__cqueues_debug() */ cqueues-rel-20161214/src/cqueues.h000066400000000000000000000401051302435770500166260ustar00rootroot00000000000000/* ========================================================================== * cqueues.h - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012, 2014, 2015 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef CQUEUES_H #define CQUEUES_H #include /* sigset_t */ #include /* EOVERFLOW */ #include /* static_assert */ #include /* __NetBSD_Version__ OpenBSD __FreeBSD__version */ #include #include /* socketpair(2) */ #include /* close(2) pipe(2) */ #include /* F_GETFL F_SETFD F_SETFL FD_CLOEXEC O_NONBLOCK O_CLOEXEC fcntl(2) */ #include #include #include #if LUA_VERSION_NUM < 502 #include "compat52.h" #endif /* * F E A T U R E / E N V I R O N M E N T M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef __has_feature #define __has_feature(...) 0 #endif #ifndef __has_extension #define __has_extension(...) 0 #endif #ifndef __NetBSD_Prereq__ #define __NetBSD_Prereq__(M, m, p) 0 #endif #define GNUC_PREREQ(M, m) (defined __GNUC__ && ((__GNUC__ > M) || (__GNUC__ == M && __GNUC_MINOR__ >= m))) #define NETBSD_PREREQ(M, m) __NetBSD_Prereq__(M, m, 0) #define FREEBSD_PREREQ(M, m) (defined __FreeBSD_version && __FreeBSD_version >= ((M) * 100000) + ((m) * 1000)) #if defined __GLIBC_PREREQ #define GLIBC_PREREQ(M, m) (defined __GLIBC__ && __GLIBC_PREREQ(M, m) && !__UCLIBC__) #else #define GLIBC_PREREQ(M, m) 0 #endif #define UCLIBC_PREREQ(M, m, p) (defined __UCLIBC__ && (__UCLIBC_MAJOR__ > M || (__UCLIBC_MAJOR__ == M && __UCLIBC_MINOR__ > m) || (__UCLIBC_MAJOR__ == M && __UCLIBC_MINOR__ == m && __UCLIBC_SUBLEVEL__ >= p))) #ifndef ENABLE_EPOLL #define ENABLE_EPOLL HAVE_EPOLL_CREATE #endif #ifndef ENABLE_PORTS #define ENABLE_PORTS HAVE_PORT_CREATE #endif #ifndef ENABLE_KQUEUE #define ENABLE_KQUEUE HAVE_KQUEUE #endif #if __GNUC__ #define NOTUSED __attribute__((unused)) #define EXTENSION __extension__ #else #define NOTUSED #define EXTENSION #endif #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) || __GNUC__ > 4 || __clang__ #define NOTREACHED __builtin_unreachable() #else #define NOTREACHED (void)0 #endif /* * C L A S S I N T E R F A C E S / R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define cqs_index_t int /* for documentation purposes */ #define cqs_nargs_t int /* "" */ #define cqs_error_t int /* "" */ #define cqs_status_t int /* "" */ #define CQS_CQUEUE "Continuation Queue" #define CQS_SOCKET "CQS Socket" #define CQS_SIGNAL "CQS Signal" #define CQS_THREAD "CQS Thread" #define CQS_NOTIFY "CQS Notify" #define CQS_CONDITION "CQS Condition" #define CQUEUE__POLL ((void *)&cqueue__poll) const char *cqueue__poll; // signals multilevel yield cqs_nargs_t luaopen__cqueues(lua_State *); cqs_nargs_t luaopen__cqueues_errno(lua_State *); cqs_nargs_t luaopen__cqueues_socket(lua_State *); cqs_nargs_t luaopen__cqueues_signal(lua_State *); cqs_nargs_t luaopen__cqueues_thread(lua_State *); cqs_nargs_t luaopen__cqueues_notify(lua_State *); cqs_nargs_t luaopen__cqueues_condition(lua_State *); cqs_nargs_t luaopen__cqueues_dns_record(lua_State *); cqs_nargs_t luaopen__cqueues_dns_packet(lua_State *); cqs_nargs_t luaopen__cqueues_dns_config(lua_State *); cqs_nargs_t luaopen__cqueues_dns_hosts(lua_State *); cqs_nargs_t luaopen__cqueues_dns_hints(lua_State *); cqs_nargs_t luaopen__cqueues_dns_resolver(lua_State *); cqs_nargs_t luaopen__cqueues_dns(lua_State *); void cqs_cancelfd(lua_State *, int); struct so_options; cqs_error_t cqs_socket_fdopen(lua_State *, int, const struct so_options *); int cqs_socket_pollfd(lua_State *, int); int cqs_socket_events(lua_State *, int); double cqs_socket_timeout(lua_State *, int); static void cqs_requiref(lua_State *L, const char *modname, lua_CFunction openf, int glb) { luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); lua_getfield(L, -1, modname); lua_remove(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 1); luaL_requiref(L, modname, openf, glb); } } /* cqs_requiref() */ static void cqs_openlibs(lua_State *L) { int top = lua_gettop(L); cqs_requiref(L, "_cqueues", &luaopen__cqueues, 0); cqs_requiref(L, "_cqueues.errno", &luaopen__cqueues_errno, 0); cqs_requiref(L, "_cqueues.socket", &luaopen__cqueues_socket, 0); cqs_requiref(L, "_cqueues.signal", &luaopen__cqueues_signal, 0); cqs_requiref(L, "_cqueues.thread", &luaopen__cqueues_thread, 0); cqs_requiref(L, "_cqueues.notify", &luaopen__cqueues_notify, 0); #if 0 /* Make optional? */ cqs_requiref(L, "_cqueues.condition", &luaopen__cqueues_condition, 0); cqs_requiref(L, "_cqueues.dns.record", &luaopen__cqueues_dns_record, 0); cqs_requiref(L, "_cqueues.dns.packet", &luaopen__cqueues_dns_packet, 0); cqs_requiref(L, "_cqueues.dns.config", &luaopen__cqueues_dns_config, 0); cqs_requiref(L, "_cqueues.dns.hosts", &luaopen__cqueues_dns_hosts, 0); cqs_requiref(L, "_cqueues.dns.hints", &luaopen__cqueues_dns_hints, 0); cqs_requiref(L, "_cqueues.dns.resolver", &luaopen__cqueues_dns_resolver, 0); cqs_requiref(L, "_cqueues.dns", &luaopen__cqueues_dns, 0); #endif lua_settop(L, top); } /* cqs_openlibs() */ static inline int cqs_interpose(lua_State *L, const char *mt) { lua_settop(L, 2); luaL_getmetatable(L, mt); lua_getfield(L, -1, "__index"); lua_pushvalue(L, 1); /* push method name */ lua_gettable(L, -2); /* push old method */ lua_pushvalue(L, 1); /* push method name */ lua_pushvalue(L, 2); /* push new method */ lua_settable(L, -4); /* replace old method */ return 1; /* return old method */ } /* cqs_interpose() */ static inline void cqs_pushnils(lua_State *L, int n) { int i; luaL_checkstack(L, n, "too many arguments"); for (i = 0; i < n; i++) lua_pushnil(L); } /* cqs_pushnils() */ static inline int cqs_regcount(const luaL_Reg *l) { int i; for (i = 0; l[i].func; i++) ;; return i; } /* cqs_regcount() */ /* create new metatable, capturing upvalues for use by methods and metamethods */ static inline void cqs_newmetatable(lua_State *L, const char *name, const luaL_Reg *methods, const luaL_Reg *metamethods, int nup) { int i; luaL_newmetatable(L, name); for (i = 0; i < nup; i++) /* copy upvalues */ lua_pushvalue(L, -nup - 1); luaL_setfuncs(L, metamethods, nup); lua_createtable(L, 0, cqs_regcount(methods)); for (i = 0; i < nup; i++) /* copy upvalues */ lua_pushvalue(L, -nup - 2); luaL_setfuncs(L, methods, nup); lua_setfield(L, -2, "__index"); for (i = 0; i < nup; i++) /* remove the upvalues */ lua_remove(L, -2); } /* cqs_newmetatable() */ /* * set the n-th upvalue of every lua_CFunction in the table at tindex to the * value at the top of the stack */ static inline void cqs_setfuncsupvalue(lua_State *L, int tindex, int n) { tindex = lua_absindex(L, tindex); lua_pushnil(L); while (lua_next(L, tindex)) { if (lua_iscfunction(L, -1)) { lua_pushvalue(L, -3); lua_setupvalue(L, -2, n); } lua_pop(L, 1); /* pop field value (leaving key) */ } lua_pop(L, 1); /* pop upvalue */ } /* cqs_setfuncsupvalue() */ static inline void cqs_setmetaupvalue(lua_State *L, int tindex, int n) { tindex = lua_absindex(L, tindex); lua_pushvalue(L, -1); cqs_setfuncsupvalue(L, tindex, n); lua_getfield(L, tindex, "__index"); lua_pushvalue(L, -2); cqs_setfuncsupvalue(L, -2, n); lua_pop(L, 1); /* pop __index */ lua_pop(L, 1); /* pop upvalue */ } /* cqs_setmetaupvalue() */ /* test metatable against copy at upvalue */ static inline void *cqs_testudata(lua_State *L, int index, int upvalue) { void *ud = lua_touserdata(L, index); int eq; if (!ud || !lua_getmetatable(L, index)) return NULL; eq = lua_rawequal(L, -1, lua_upvalueindex(upvalue)); lua_pop(L, 1); return (eq)? ud : NULL; } /* cqs_testudata() */ static inline void *cqs_checkudata(lua_State *L, int index, int upvalue, const char *tname) { void *ud; if (!(ud = cqs_testudata(L, index, upvalue))) { index = lua_absindex(L, index); luaL_argerror(L, index, lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, index))); NOTREACHED; } return ud; } /* cqs_checkudata() */ struct cqs_macro { const char *name; int value; }; static inline void cqs_setmacros(lua_State *L, int index, const struct cqs_macro *macro, size_t count, _Bool swap) { index = lua_absindex(L, index); for (unsigned i = 0; i < count; i++) { lua_pushstring(L, macro[i].name); lua_pushinteger(L, macro[i].value); lua_rawset(L, index); } if (!swap) return; for (unsigned i = 0; i < count; i++) { lua_pushinteger(L, macro[i].value); lua_pushstring(L, macro[i].name); lua_rawset(L, index); } } /* cqs_setmacros() */ #if LUA_VERSION_NUM < 503 /* convert value at index to proxytable with value at t[2] */ static inline void cqs__toproxytable(lua_State *L, int index) { index = lua_absindex(L, index); lua_createtable(L, 2, 0); lua_pushlightuserdata(L, (void *)lua_topointer(L, -1)); lua_rawseti(L, -2, 1); /* set t[1] == pointer-to-t */ lua_pushvalue(L, index); lua_rawseti(L, -2, 2); /* set t[2] == value */ lua_replace(L, index); } /* cqs__toproxytable() */ /* check whether value at index is a proxytable */ static inline _Bool cqs__isproxytable(lua_State *L, int index) { const void *tp, *t1p; if (!lua_istable(L, index)) return 0; tp = lua_topointer(L, index); lua_rawgeti(L, index, 1); t1p = lua_topointer(L, -1); lua_pop(L, 1); return tp && tp == t1p; } /* cqs__isproxytable() */ #endif static inline void cqs_setuservalue(lua_State *L, int index) { #if LUA_VERSION_NUM >= 503 lua_setuservalue(L, index); #elif LUA_VERSION_NUM == 502 if (!lua_istable(L, -1) && !lua_isnil(L, -1)) cqs__toproxytable(L, -1); lua_setuservalue(L, index); #else if (!lua_istable(L, -1)) cqs__toproxytable(L, -1); lua_setfenv(L, index); #endif } /* cqs_setuservalue() */ static inline int cqs_getuservalue(lua_State *L, int index) { #if LUA_VERSION_NUM >= 503 return lua_getuservalue(L, index); #else #if LUA_VERSION_NUM == 502 lua_getuservalue(L, index); #else lua_getfenv(L, index); #endif if (cqs__isproxytable(L, -1)) { lua_rawgeti(L, -1, 2); lua_replace(L, -2); } return lua_type(L, -1); #endif } /* cqs_setuservalue() */ static inline void cqs_closefd(int *fd) { if (*fd != -1) { #if __APPLE__ /* Do we need bother with close$NOCANCEL$UNIX2003? */ extern int close$NOCANCEL(int); close$NOCANCEL(*fd); #else close(*fd); #endif *fd = -1; } } /* cqs_closefd() */ #if !defined O_CLOEXEC #if __NetBSD__ /* bad hack for NetBSD < 6.0 until we refactor flags code */ #define O_CLOEXEC 0x00400000 #endif #endif static inline int cqs_setfd(int fd, int flags) { if (flags & O_NONBLOCK) { int oflags = fcntl(fd, F_GETFL); if (-1 == oflags || -1 == fcntl(fd, F_SETFL, oflags|O_NONBLOCK)) return errno; } if (flags & O_CLOEXEC) { if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC)) return errno; } return 0; } /* cqs_setfd() */ static inline int cqs_pipe(int fd[2], int flags) { #if HAVE_PIPE2 if (0 != pipe2(fd, flags)) return errno; return 0; #else int error; if (0 != pipe(fd)) return errno; if ((error = cqs_setfd(fd[0], flags)) || (error = cqs_setfd(fd[1], flags))) return error; return 0; #endif } /* cqs_pipe() */ static inline int cqs_socketpair(int family, int type, int proto, int fd[2], int flags) { #if defined SOCK_NONBLOCK && defined SOCK_CLOEXEC if (flags & O_NONBLOCK) type |= SOCK_NONBLOCK; if (flags & O_CLOEXEC) type |= SOCK_CLOEXEC; if (0 != socketpair(family, type, proto, fd)) return errno; return 0; #else int error; if (0 != socketpair(family, type, proto, fd)) return errno; if ((error = cqs_setfd(fd[0], flags)) || (error = cqs_setfd(fd[1], flags))) return error; return 0; #endif } /* cqs_socketpair() */ #if HAVE_STATIC_ASSERT #define cqs_static_assert(cond, msg) static_assert(cond, msg) #elif HAVE__STATIC_ASSERT #define cqs_static_assert(cond, msg) EXTENSION _Static_assert(cond, msg) #else #define cqs_inline_assert(cond) (sizeof (int[1 - 2*!(cond)])) #define cqs_static_assert(cond, msg) extern char CQS_XPASTE(assert_, __LINE__)[cqs_inline_assert(cond)] #endif cqs_error_t cqs_strerror_r(cqs_error_t, char *, size_t); /* * NB: Compound literals have block scope in C. But g++ creates * list-initialized temporaries, which only have expression scope. */ #if !__cplusplus #define cqs_strerror(...) cqs_strerror_(__VA_ARGS__, (char [128]){ 0 }, 128, 0) #define cqs_strerror_(error, dst, lim, ...) (cqs_strerror)((error), (dst), (lim)) #endif const char *(cqs_strerror)(cqs_error_t, void *, size_t); cqs_error_t cqs_sigmask(int, const sigset_t *, sigset_t *); /* * A U X I L L A R Y R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef MIN #define MIN(a, b) (((a) < (b))? (a) : (b)) #endif #ifndef MAX #define MAX(a, b) (((a) > (b))? (a) : (b)) #endif #ifndef countof #define countof(a) (sizeof (a) / sizeof *(a)) #endif #ifndef endof #define endof(a) (&(a)[countof(a)]) #endif #define cqs_ispowerof2(x) (((x) != 0) && (0 == (((x) - 1) & (x)))) #define CQS_PASTE(x, y) x ## y #define CQS_XPASTE(x, y) CQS_PASTE(x, y) typedef int cqs_ref_t; static inline void cqs_unref(lua_State *L, cqs_ref_t *ref) { if (*ref != LUA_NOREF) { luaL_unref(L, LUA_REGISTRYINDEX, *ref); *ref = LUA_NOREF; } } /* cqs_unref() */ static inline void cqs_ref(lua_State *L, cqs_ref_t *ref) { cqs_unref(L, ref); *ref = luaL_ref(L, LUA_REGISTRYINDEX); } /* cqs_ref() */ static inline void cqs_getref(lua_State *L, cqs_ref_t ref) { if (ref != LUA_NOREF) lua_rawgeti(L, LUA_REGISTRYINDEX, ref); else lua_pushnil(L); } /* cqs_getref() */ static inline cqs_error_t cqs_addzu(size_t *r, size_t a, size_t b) { if (~a < b) return EOVERFLOW; *r = a + b; return 0; } /* cqs_addzu() */ /* * D E B U G M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if !defined SAY #define SAY_(file, func, line, fmt, ...) \ fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__) #define SAY(...) SAY_(__FILE__, __func__, __LINE__, __VA_ARGS__, "\n") #define HAI SAY("hai") #endif #include #include #include #if __sun #include #include #endif NOTUSED static void cqs_debugfd(int fd) { struct stat st; char descr[64] = ""; int pending = -1; if (0 != fstat(fd, &st)) goto syerr; if (S_ISSOCK(st.st_mode)) { int type; if (0 != getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &(socklen_t){ sizeof type })) goto syerr; if (type == SOCK_STREAM) strncat(descr, "stream socket", sizeof descr - 1); else if (type == SOCK_DGRAM) strncat(descr, "dgram socket", sizeof descr - 1); else strncat(descr, "other socket", sizeof descr - 1); } else { if (S_ISFIFO(st.st_mode)) strncat(descr, "fifo file", sizeof descr - 1); else if (S_ISREG(st.st_mode)) strncat(descr, "regular file", sizeof descr - 1); else strncat(descr, "other file", sizeof descr - 1); } ioctl(fd, FIONREAD, &pending); SAY("%d: %s (pending:%d)", fd, descr, pending); return; syerr: SAY("%d: %s", fd, strerror(errno)); } /* cqs_debugfd() */ #endif /* CQUEUES_H */ cqueues-rel-20161214/src/cqueues.lua000066400000000000000000000100111302435770500171510ustar00rootroot00000000000000local loader = function(loader, ...) local core = require"_cqueues" local errno = require"_cqueues.errno" local monotime = core.monotime local running = core.running local strerror = errno.strerror local unpack = assert(table.unpack or unpack) -- 5.1 compat -- lazily load auxlib to prevent circular or unused dependencies local auxlib = setmetatable({}, { __index = function (t, k) local v = require"cqueues.auxlib"[k] rawset(t, k, v) return v end }) -- load deprecated APIs into shadow table to keep hidden unless used local notsupp = {} setmetatable(core, { __index = notsupp }) -- -- core.poll -- -- Wrap the cqueues yield protocol to support polling across -- multilevel resume/yield. Requires explicit or implicit use -- (through monkey patching of coroutine.resume and coroutine.wrap) -- of auxlib.resume or auxlib.wrap. -- -- Also supports polling from outside of a running event loop using -- a cheap hack. NOT RECOMMENDED. -- local _POLL = core._POLL local yield = coroutine.yield local poller function core.poll(...) if running() then return yield(_POLL, ...) else local tuple poller = poller or auxlib.assert3(core.new()) poller:wrap(function (...) tuple = { core.poll(...) } end, ...) -- NOTE: must step twice, once to call poll and -- again to wake up auxlib.assert3(poller:step()) auxlib.assert3(poller:step()) return unpack(tuple or {}) end end -- core.poll -- -- core.sleep -- -- Sleep primitive. -- function core.sleep(timeout) core.poll(timeout) end -- core.sleep -- -- core:step -- -- Wrap the low-level :step interface to make managing event loops -- slightly easier. -- local step; step = core.interpose("step", function (self, timeout) if core.running() then core.poll(self, timeout) return step(self, 0.0) else return step(self, timeout) end end) -- -- Lua 5.1 doesn't allow continuations in C -- so we have to emulate them in lua. -- if _VERSION == "Lua 5.1" then local function checkstep(self, ok, ...) if ok == "yielded" then return checkstep(self, self:step_resume(coroutine.yield(...))) else return ok, ... end end local step_; step_ = core.interpose("step", function (self, timeout) return checkstep(self, step_(self, timeout)) end) end -- core:step -- -- core:loop -- -- Step until an error is encountered. -- local function todeadline(timeout) -- special case 0 timeout to avoid monotime call in totimeout return timeout and (timeout > 0 and monotime() + timeout or 0) or nil end -- todeadline local function totimeout(deadline) if not deadline then return nil, false elseif deadline == 0 then return 0, true else local curtime = monotime() if curtime < deadline then return deadline - curtime, false else return 0, true end end end -- totimeout core.interpose("loop", function (self, timeout) local function checkstep(self, deadline, ok, ...) local timeout, expired = totimeout(deadline) if not ok then return false, ... elseif expired or self:empty() then return true else return checkstep(self, deadline, self:step(timeout)) end end local deadline = todeadline(timeout) return checkstep(self, deadline, self:step(timeout)) end) -- core:loop -- -- core:errors -- -- Return iterator over core:loop. -- core.interpose("errors", function (self, timeout) local deadline = todeadline(timeout) return function () local timeout = totimeout(deadline) return select(2, self:loop(timeout)) end end) -- core:errors -- -- core.assert -- -- DEPRECATED. See auxlib.assert. -- function notsupp.assert(...) return auxlib.assert(...) end -- notsupp.assert -- -- core.resume -- -- DEPRECATED. See auxlib.resume. -- function notsupp.resume(...) return auxlib.resume(...) end -- notsupp.resume -- -- core.wrap -- -- DEPRECATED. See auxlib.wrap. -- function notsupp.wrap(...) return auxlib.wrap(...) end -- notsupp.wrap core.loader = loader return core end -- loader return loader(loader, ...) cqueues-rel-20161214/src/dns.c000066400000000000000000001565421302435770500157500ustar00rootroot00000000000000/* ========================================================================== * dns.c - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012, 2014 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include /* UINT_MAX */ #include /* offsetof */ #include /* free(3) */ #include /* tmpfile(3) fclose(3) */ #include /* memset(3) strcmp(3) */ #include #include #include /* AF_INET AF_INET6 */ #include /* struct sockaddr_in struct sockaddr_in6 */ #include /* INET_ADDSTRLEN INET6_ADDRSTRLEN inet_ntop(3) */ #include #include #include "lib/dns.h" #include "cqueues.h" #define RR_ANY_CLASS "DNS RR Any" #define RR_A_CLASS "DNS RR A" #define RR_NS_CLASS "DNS RR NS" #define RR_CNAME_CLASS "DNS RR CNAME" #define RR_SOA_CLASS "DNS RR SOA" #define RR_PTR_CLASS "DNS RR PTR" #define RR_MX_CLASS "DNS RR MX" #define RR_TXT_CLASS "DNS RR TXT" #define RR_AAAA_CLASS "DNS RR AAAA" #define RR_SRV_CLASS "DNS RR SRV" #define RR_OPT_CLASS "DNS RR OPT" #define RR_SSHFP_CLASS "DNS RR SSHFP" #define RR_SPF_CLASS "DNS RR SPF" #define PACKET_CLASS "DNS Packet" #define RESCONF_CLASS "DNS Config" #define HOSTS_CLASS "DNS Hosts" #define HINTS_CLASS "DNS Hints" #define RESOLVER_CLASS "DNS Resolver" static int optfint(lua_State *L, int t, const char *k, int def) { int i; lua_getfield(L, t, k); i = luaL_optint(L, -1, def); lua_pop(L, 1); return i; } /* optfint() */ static int optfbool(lua_State *L, int t, const char *k, _Bool def) { _Bool b; lua_getfield(L, t, k); b = (lua_isnil(L, -1))? def : lua_toboolean(L, -1); lua_pop(L, 1); return b; } /* optfbool() */ /* * R E S O U R C E R E C O R D B I N D I N G S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct rr { struct dns_rr attr; char *name; union dns_any data; }; /* struct rr */ static const struct rr_info { const char *tname; unsigned short bufsiz; } rrinfo[] = { [DNS_T_A] = { RR_A_CLASS, sizeof (struct dns_a) }, [DNS_T_NS] = { RR_NS_CLASS, sizeof (struct dns_ns) }, [DNS_T_CNAME] = { RR_CNAME_CLASS, sizeof (struct dns_cname) }, [DNS_T_SOA] = { RR_SOA_CLASS, sizeof (struct dns_soa) }, [DNS_T_PTR] = { RR_PTR_CLASS, sizeof (struct dns_ptr) }, [DNS_T_MX] = { RR_MX_CLASS, sizeof (struct dns_mx) }, [DNS_T_TXT] = { RR_TXT_CLASS, 0 }, [DNS_T_AAAA] = { RR_AAAA_CLASS, sizeof (struct dns_aaaa) }, [DNS_T_SRV] = { RR_SRV_CLASS, sizeof (struct dns_srv) }, [DNS_T_OPT] = { RR_OPT_CLASS, sizeof (struct dns_opt) }, [DNS_T_SSHFP] = { RR_SSHFP_CLASS, sizeof (struct dns_sshfp) }, [DNS_T_SPF] = { RR_SPF_CLASS, 0 }, }; static const struct rr_info *rr_info(int type) { return (type >= 0 && type < (int)countof(rrinfo))? &rrinfo[type] : 0; } /* rr_info() */ static const char *rr_tname(const struct dns_rr *rr) { const struct rr_info *info; if ((info = rr_info(rr->type)) && info->tname) return info->tname; else return RR_ANY_CLASS; } /* rr_tname() */ static size_t rr_bufsiz(const struct dns_rr *rr) { const struct rr_info *info; size_t minbufsiz = offsetof(struct dns_txt, data) + rr->rd.len + 1; if ((info = rr_info(rr->type)) && info->bufsiz) return MAX(info->bufsiz, minbufsiz); else return minbufsiz; } /* rr_bufsiz() */ static void rr_push(lua_State *L, struct dns_rr *any, struct dns_packet *P) { char name[DNS_D_MAXNAME + 1]; size_t namelen, datasiz; struct rr *rr; int error; namelen = dns_d_expand(name, sizeof name, any->dn.p, P, &error); datasiz = rr_bufsiz(any); rr = lua_newuserdata(L, offsetof(struct rr, data) + datasiz + namelen + 1); rr->attr = *any; rr->name = (char *)rr + offsetof(struct rr, data) + datasiz; memcpy(rr->name, name, namelen); rr->name[namelen] = '\0'; memset(&rr->data, '\0', datasiz); if (any->section != DNS_S_QD) { dns_any_init(&rr->data, datasiz); if ((error = dns_any_parse(&rr->data, any, P))) luaL_error(L, "dns.rr.parse: %s", cqs_strerror(error)); } luaL_setmetatable(L, rr_tname(any)); } /* rr_push() */ static struct rr *rr_toany(lua_State *L, int index) { luaL_checktype(L, index, LUA_TUSERDATA); luaL_argcheck(L, lua_rawlen(L, index) > offsetof(struct rr, data) + 4, index, "DNS RR userdata too small"); return lua_touserdata(L, index); } /* rr_toany() */ /* * ANY RR Bindings */ static int any_section(lua_State *L) { struct rr *rr = rr_toany(L, 1); lua_pushinteger(L, rr->attr.section); return 1; } /* any_section() */ static int any_name(lua_State *L) { struct rr *rr = rr_toany(L, 1); lua_pushstring(L, rr->name); return 1; } /* any_name() */ static int any_type(lua_State *L) { struct rr *rr = rr_toany(L, 1); lua_pushinteger(L, rr->attr.type); return 1; } /* any_type() */ static int any_class(lua_State *L) { struct rr *rr = rr_toany(L, 1); lua_pushinteger(L, rr->attr.class); return 1; } /* any_class() */ static int any_ttl(lua_State *L) { struct rr *rr = rr_toany(L, 1); lua_pushinteger(L, rr->attr.ttl); return 1; } /* any_ttl() */ static int any_rdata(lua_State *L) { struct rr *rr = rr_toany(L, 1); if (rr->attr.section == DNS_S_QD) return lua_pushliteral(L, ""), 1; lua_pushlstring(L, (char *)rr->data.rdata.data, rr->data.rdata.len); return 1; } /* any_rdata() */ static int any__tostring(lua_State *L) { struct rr *rr = rr_toany(L, 1); if (rr->attr.section == DNS_S_QD) return lua_pushliteral(L, ""), 1; if (luaL_testudata(L, 1, RR_ANY_CLASS)) { lua_pushlstring(L, (char *)rr->data.rdata.data, rr->data.rdata.len); } else { luaL_Buffer B; size_t len; luaL_buffinit(L, &B); len = dns_any_print(luaL_prepbuffer(&B), LUAL_BUFFERSIZE, &rr->data, rr->attr.type); luaL_addsize(&B, len); luaL_pushresult(&B); } return 1; } /* any__tostring() */ static const luaL_Reg any_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "rdata", &any_rdata }, { NULL, NULL } }; /* any_methods[] */ static const luaL_Reg any_metatable[] = { { "__tostring", &any__tostring }, { NULL, NULL } }; /* any_metatable[] */ /* * A RR Bindings */ static int a_addr(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_A_CLASS); char addr[INET_ADDRSTRLEN + 1] = ""; if (rr->attr.section != DNS_S_QD) inet_ntop(AF_INET, &rr->data.a.addr, addr, sizeof addr); lua_pushstring(L, addr); return 1; } /* a_addr() */ static const luaL_Reg a_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "addr", &a_addr }, { NULL, NULL } }; /* a_methods[] */ static const luaL_Reg a_metatable[] = { { "__tostring", &a_addr }, { NULL, NULL } }; /* a_metatable[] */ /* * NS, CNAME, PTR RR Bindings */ static int ns_host(lua_State *L) { struct rr *rr = rr_toany(L, 1); if (rr->attr.section == DNS_S_QD) return lua_pushliteral(L, ""), 1; lua_pushstring(L, rr->data.ns.host); return 1; } /* ns_host() */ static const luaL_Reg ns_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "host", &ns_host }, { NULL, NULL } }; /* ns_methods[] */ static const luaL_Reg ns_metatable[] = { { "__tostring", &ns_host }, { NULL, NULL } }; /* ns_metatable[] */ /* * SOA RR Bindings */ static int soa_mname(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SOA_CLASS); lua_pushstring(L, rr->data.soa.mname); return 1; } /* soa_mname() */ static int soa_rname(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SOA_CLASS); lua_pushstring(L, rr->data.soa.rname); return 1; } /* soa_rname() */ static int soa_serial(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SOA_CLASS); lua_pushinteger(L, rr->data.soa.serial); return 1; } /* soa_serial() */ static int soa_refresh(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SOA_CLASS); lua_pushinteger(L, rr->data.soa.refresh); return 1; } /* soa_refresh() */ static int soa_retry(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SOA_CLASS); lua_pushinteger(L, rr->data.soa.retry); return 1; } /* soa_retry() */ static int soa_expire(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SOA_CLASS); lua_pushinteger(L, rr->data.soa.expire); return 1; } /* soa_expire() */ static int soa_minimum(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SOA_CLASS); lua_pushinteger(L, rr->data.soa.minimum); return 1; } /* soa_minimum() */ static const luaL_Reg soa_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "mname", &soa_mname }, { "rname", &soa_rname }, { "serial", &soa_serial }, { "refresh", &soa_refresh }, { "retry", &soa_retry }, { "expire", &soa_expire }, { "minimum", &soa_minimum }, { NULL, NULL } }; /* soa_methods[] */ static const luaL_Reg soa_metatable[] = { { "__tostring", &any__tostring }, { NULL, NULL } }; /* soa_metatable[] */ /* * MX RR Bindings */ static int mx_host(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_MX_CLASS); lua_pushstring(L, rr->data.mx.host); return 1; } /* mx_host() */ static int mx_preference(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_MX_CLASS); lua_pushinteger(L, rr->data.mx.preference); return 1; } /* mx_preference() */ static const luaL_Reg mx_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "host", &mx_host }, { "preference", &mx_preference }, { NULL, NULL } }; /* mx_methods[] */ static const luaL_Reg mx_metatable[] = { { "__tostring", &any__tostring }, { NULL, NULL } }; /* mx_metatable[] */ /* * TXT RR Bindings */ static const luaL_Reg txt_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "data", &any_rdata }, { NULL, NULL } }; /* txt_methods[] */ static const luaL_Reg txt_metatable[] = { { "__tostring", &any__tostring }, { NULL, NULL } }; /* txt_metatable[] */ /* * AAAA RR Bindings */ static int aaaa_addr(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_AAAA_CLASS); char addr[INET6_ADDRSTRLEN + 1] = ""; if (rr->attr.section != DNS_S_QD) inet_ntop(AF_INET6, &rr->data.aaaa.addr, addr, sizeof addr); lua_pushstring(L, addr); return 1; } /* aaaa_addr() */ static const luaL_Reg aaaa_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "addr", &aaaa_addr }, { NULL, NULL } }; /* aaaa_methods[] */ static const luaL_Reg aaaa_metatable[] = { { "__tostring", &aaaa_addr }, { NULL, NULL } }; /* aaaa_metatable[] */ /* * SRV RR Bindings */ static int srv_priority(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SRV_CLASS); lua_pushinteger(L, rr->data.srv.priority); return 1; } /* srv_priority() */ static int srv_weight(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SRV_CLASS); lua_pushinteger(L, rr->data.srv.weight); return 1; } /* srv_weight() */ static int srv_port(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SRV_CLASS); lua_pushinteger(L, rr->data.srv.port); return 1; } /* srv_port() */ static int srv_target(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SRV_CLASS); lua_pushstring(L, rr->data.srv.target); return 1; } /* srv_target() */ static const luaL_Reg srv_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "priority", &srv_priority }, { "weight", &srv_weight }, { "port", &srv_port }, { "target", &srv_target }, { NULL, NULL } }; /* srv_methods[] */ static const luaL_Reg srv_metatable[] = { { "__tostring", &any__tostring }, { NULL, NULL } }; /* srv_metatable[] */ /* * OPT RR Bindings */ static int opt_rcode(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_OPT_CLASS); lua_pushinteger(L, rr->data.opt.rcode); return 1; } /* opt_rcode() */ static int opt_version(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_OPT_CLASS); lua_pushinteger(L, rr->data.opt.version); return 1; } /* opt_version() */ static int opt_maxsize(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_OPT_CLASS); lua_pushinteger(L, rr->data.opt.maxsize); return 1; } /* opt_maxsize() */ static const luaL_Reg opt_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "rcode", &opt_rcode }, { "version", &opt_version }, { "maxsize", &opt_maxsize }, { NULL, NULL } }; /* opt_methods[] */ static const luaL_Reg opt_metatable[] = { { "__tostring", &any__tostring }, { NULL, NULL } }; /* opt_metatable[] */ /* * SSHFP RR Bindings */ static int sshfp_algo(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SSHFP_CLASS); lua_pushinteger(L, rr->data.sshfp.algo); return 1; } /* sshfp_algo() */ static int sshfp_digest(lua_State *L) { struct rr *rr = luaL_checkudata(L, 1, RR_SSHFP_CLASS); int fmt = luaL_checkoption(L, 2, "x", (const char *[]){ "s", "x", 0 }); unsigned char *hash; size_t hashlen; lua_pushinteger(L, rr->data.sshfp.type); switch (rr->data.sshfp.type) { case DNS_SSHFP_SHA1: hash = rr->data.sshfp.digest.sha1; hashlen = sizeof rr->data.sshfp.digest.sha1; break; default: lua_pushnil(L); return 2; } switch (fmt) { case 1: { luaL_Buffer B; size_t i; luaL_buffinit(L, &B); for (i = 0; i < hashlen; i++) { luaL_addchar(&B, "0123456789abcdef"[0x0f & (hash[i] >> 4)]); luaL_addchar(&B, "0123456789abcdef"[0x0f & (hash[i] >> 0)]); } luaL_pushresult(&B); break; } default: lua_pushlstring(L, (char *)hash, hashlen); break; } /* switch() */ return 2; } /* sshfp_digest() */ static const luaL_Reg sshfp_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "algo", &sshfp_algo }, { "digest", &sshfp_digest }, { NULL, NULL } }; /* sshfp_methods[] */ static const luaL_Reg sshfp_metatable[] = { { "__tostring", &any__tostring }, { NULL, NULL } }; /* sshfp_metatable[] */ /* * SPF RR Bindings */ static const luaL_Reg spf_methods[] = { { "section", &any_section }, { "name", &any_name }, { "type", &any_type }, { "class", &any_class }, { "ttl", &any_ttl }, { "policy", &any_rdata }, { "data", &any_rdata }, { NULL, NULL } }; /* spf_methods[] */ static const luaL_Reg spf_metatable[] = { { "__tostring", &any_rdata }, { NULL, NULL } }; /* spf_metatable[] */ static void rr_loadall(lua_State *L) { int top = lua_gettop(L); cqs_newmetatable(L, RR_ANY_CLASS, any_methods, any_metatable, 0); cqs_newmetatable(L, RR_A_CLASS, a_methods, a_metatable, 0); cqs_newmetatable(L, RR_NS_CLASS, ns_methods, ns_metatable, 0); cqs_newmetatable(L, RR_CNAME_CLASS, ns_methods, ns_metatable, 0); cqs_newmetatable(L, RR_SOA_CLASS, soa_methods, soa_metatable, 0); cqs_newmetatable(L, RR_PTR_CLASS, ns_methods, ns_metatable, 0); cqs_newmetatable(L, RR_MX_CLASS, mx_methods, mx_metatable, 0); cqs_newmetatable(L, RR_TXT_CLASS, txt_methods, txt_metatable, 0); cqs_newmetatable(L, RR_AAAA_CLASS, aaaa_methods, aaaa_metatable, 0); cqs_newmetatable(L, RR_SRV_CLASS, srv_methods, srv_metatable, 0); cqs_newmetatable(L, RR_OPT_CLASS, opt_methods, opt_metatable, 0); cqs_newmetatable(L, RR_SSHFP_CLASS, sshfp_methods, sshfp_metatable, 0); cqs_newmetatable(L, RR_SPF_CLASS, spf_methods, spf_metatable, 0); lua_settop(L, top); } /* rr_loadall() */ static int rr_type(lua_State *L) { unsigned i; lua_settop(L, 2); lua_pushnil(L); /* default result */ if (lua_isuserdata(L, 2)) { for (i = 0; i < countof(rrinfo); i++) { if (!rrinfo[i].tname) continue; if (!luaL_testudata(L, 2, rrinfo[i].tname) && !luaL_testudata(L, 2, RR_ANY_CLASS)) continue; lua_pushstring(L, "dns record"); break; } } return 1; } /* rr_type() */ static const luaL_Reg rr_globals[] = { { NULL, NULL } }; int luaopen__cqueues_dns_record(lua_State *L) { static const struct cqs_macro classes[] = { { "IN", DNS_C_IN }, { "ANY", DNS_C_ANY }, }; static const struct cqs_macro types[] = { { "A", DNS_T_A }, { "NS", DNS_T_NS }, { "CNAME", DNS_T_CNAME }, { "SOA", DNS_T_SOA }, { "PTR", DNS_T_PTR }, { "MX", DNS_T_MX }, { "TXT", DNS_T_TXT }, { "AAAA", DNS_T_AAAA }, { "SRV", DNS_T_SRV }, { "OPT", DNS_T_OPT }, { "SSHFP", DNS_T_SSHFP }, { "SPF", DNS_T_SPF }, { "ALL", DNS_T_ALL }, }; static const struct cqs_macro sshfp[] = { { "RSA", DNS_SSHFP_RSA }, { "DSA", DNS_SSHFP_DSA }, { "SHA1", DNS_SSHFP_SHA1 }, }; rr_loadall(L); luaL_newlib(L, rr_globals); lua_createtable(L, 0, countof(classes)); cqs_setmacros(L, -1, classes, countof(classes), 1); lua_setfield(L, -2, "class"); lua_createtable(L, 0, countof(types)); cqs_setmacros(L, -1, types, countof(types), 1); lua_createtable(L, 0, 1); lua_pushcfunction(L, &rr_type); lua_setfield(L, -2, "__call"); lua_setmetatable(L, -2); lua_setfield(L, -2, "type"); lua_createtable(L, 0, countof(sshfp)); cqs_setmacros(L, -1, sshfp, countof(sshfp), 1); lua_setfield(L, -2, "sshfp"); return 1; } /* luaopen__cqueues_dns_record() */ /* * P A C K E T B I N D I N G S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static void pkt_reload(struct dns_packet *P, const void *data, size_t size) { if (P->size < size) { memcpy(P->data, data, P->size); P->end = P->size; dns_header(P)->tc = 1; } else { memcpy(P->data, data, size); P->end = size; } dns_p_study(P); memset(P->dict, 0, sizeof P->dict); dns_p_dictadd(P, 12); /* add query name to dictionary */ } /* pkt_reload() */ static int pkt_new(lua_State *L) { struct dns_packet *P; const char *data = NULL; size_t prepbufsiz, datasiz, size; if (lua_isnoneornil(L, 1) || lua_isnumber(L, 1)) { prepbufsiz = luaL_optunsigned(L, 1, DNS_P_QBUFSIZ); } else { data = luaL_checklstring(L, 1, &datasiz); prepbufsiz = luaL_optunsigned(L, 2, datasiz); } size = dns_p_calcsize(prepbufsiz); P = memset(lua_newuserdata(L, size), '\0', size); luaL_setmetatable(L, PACKET_CLASS); dns_p_init(P, size); if (data) pkt_reload(P, data, datasiz); return 1; } /* pkt_new() */ static int pkt_type(lua_State *L) { if (luaL_testudata(L, 1, PACKET_CLASS)) { lua_pushstring(L, "dns packet"); } else { lua_pushnil(L); } return 1; } /* pkt_type() */ static int pkt_interpose(lua_State *L) { return cqs_interpose(L, PACKET_CLASS); } /* pkt_interpose() */ static int pkt_qid(lua_State *L) { struct dns_packet *P = lua_touserdata(L, 1); lua_pushinteger(L, ntohs(dns_header(P)->qid)); return 1; } /* pkt_qid() */ static int pkt_setqid(lua_State *L) { struct dns_packet *P = luaL_checkudata(L, 1, PACKET_CLASS); int qid = luaL_checkint(L, 2); dns_header(P)->qid = htons(qid); lua_settop(L, 1); return 1; } /* pkt_setqid() */ static int pkt_flags(lua_State *L) { struct dns_packet *P = lua_touserdata(L, 1); struct dns_header *hdr = dns_header(P); lua_newtable(L); lua_pushboolean(L, hdr->qr); lua_setfield(L, -2, "qr"); lua_pushinteger(L, hdr->opcode); lua_setfield(L, -2, "opcode"); lua_pushboolean(L, hdr->aa); lua_setfield(L, -2, "aa"); lua_pushboolean(L, hdr->tc); lua_setfield(L, -2, "tc"); lua_pushboolean(L, hdr->rd); lua_setfield(L, -2, "rd"); lua_pushboolean(L, hdr->ra); lua_setfield(L, -2, "ra"); lua_pushinteger(L, hdr->unused); lua_setfield(L, -2, "z"); lua_pushinteger(L, hdr->rcode); lua_setfield(L, -2, "rcode"); return 1; } /* pkt_flags() */ #define pkt_isflag(a, b) (0 == strcmp((a), (b))) static _Bool pkt_tobool(lua_State *L, int index) { if (lua_isnumber(L, index)) { return lua_tointeger(L, index); } else { return lua_toboolean(L, index); } } /* pkt_tobool() */ static int pkt_setflags(lua_State *L) { struct dns_packet *P = luaL_checkudata(L, 1, PACKET_CLASS); struct dns_header *hdr = dns_header(P); if (lua_isnumber(L, 2)) { int flags = luaL_checkint(L, 2); hdr->qr = 0x01 & (flags >> 15); hdr->opcode = 0x0f & (flags >> 11); hdr->aa = 0x01 & (flags >> 10); hdr->tc = 0x01 & (flags >> 9); hdr->rd = 0x01 & (flags >> 8); hdr->ra = 0x01 & (flags >> 7); hdr->unused = 0x07 & (flags >> 4); hdr->rcode = 0x0f & (flags >> 0); } else { luaL_checktype(L, 2, LUA_TTABLE); for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) { const char *flag = luaL_checkstring(L, -2); if (pkt_isflag(flag, "qr")) { hdr->qr = pkt_tobool(L, -1); } else if (pkt_isflag(flag, "opcode")) { hdr->opcode = luaL_checkint(L, -1); } else if (pkt_isflag(flag, "aa")) { hdr->aa = pkt_tobool(L, -1); } else if (pkt_isflag(flag, "tc")) { hdr->tc = pkt_tobool(L, -1); } else if (pkt_isflag(flag, "rd")) { hdr->rd = pkt_tobool(L, -1); } else if (pkt_isflag(flag, "ra")) { hdr->ra = pkt_tobool(L, -1); } else if (pkt_isflag(flag, "z")) { hdr->unused = luaL_checkint(L, -1); } else if (pkt_isflag(flag, "rcode")) { hdr->rcode = luaL_checkint(L, -1); } } } lua_settop(L, 1); return 1; } /* pkt_setflags() */ static int pkt_push(lua_State *L) { struct dns_packet *P = lua_touserdata(L, 1); int section = luaL_checkint(L, 2); size_t namelen; const char *name = luaL_checklstring(L, 3, &namelen); int type = luaL_optint(L, 4, DNS_T_A); int class = luaL_optint(L, 5, DNS_C_IN); int error; luaL_argcheck(L, section == DNS_S_QUESTION, 2, "pushing RDATA not yet supported"); if ((error = dns_p_push(P, section, name, namelen, type, class, 0, NULL))) return lua_pushnil(L), lua_pushinteger(L, error), 2; lua_settop(L, 1); return 1; } /* pkt_push() */ static int pkt_count(lua_State *L) { struct dns_packet *P = lua_touserdata(L, 1); int flags = luaL_optint(L, 2, DNS_S_ALL); lua_pushinteger(L, dns_p_count(P, flags)); return 1; } /* pkt_count() */ static int pkt_next(lua_State *L) { struct dns_packet *P = lua_touserdata(L, lua_upvalueindex(1)); struct dns_rr_i *rr_i = lua_touserdata(L, lua_upvalueindex(2)); struct dns_rr rr; int error = 0; if (!dns_rr_grep(&rr, 1, rr_i, P, &error)) return (error)? luaL_error(L, "dns.packet:grep: %s", cqs_strerror(error)) : 0; rr_push(L, &rr, P); return 1; } /* pkt_next() */ static int pkt_grep(lua_State *L) { struct dns_packet *P = luaL_checkudata(L, 1, PACKET_CLASS); struct dns_rr_i *rr_i; lua_settop(L, 2); lua_pushvalue(L, 1); rr_i = memset(lua_newuserdata(L, sizeof *rr_i), '\0', sizeof *rr_i); rr_i = dns_rr_i_init(rr_i, P); if (!lua_isnil(L, 2)) { luaL_checktype(L, 2, LUA_TTABLE); rr_i->section = optfint(L, 2, "section", 0); rr_i->type = optfint(L, 2, "type", 0); rr_i->class = optfint(L, 2, "class", 0); lua_getfield(L, 2, "name"); if (!(rr_i->name = luaL_optstring(L, -1, NULL))) lua_pop(L, 1); } lua_pushcclosure(L, &pkt_next, lua_gettop(L) - 2); return 1; } /* pkt_grep() */ static int pkt_load(lua_State *L) { struct dns_packet *P = luaL_checkudata(L, 1, PACKET_CLASS); size_t size; const char *data = luaL_checklstring(L, 2, &size); pkt_reload(P, data, size); lua_settop(L, 1); return 1; } /* pkt_load() */ /* like Lua string.dump, which has opposite meaning from dns.c usage */ static int pkt_dump(lua_State *L) { struct dns_packet *P = luaL_checkudata(L, 1, PACKET_CLASS); lua_pushlstring(L, (char *)P->data, P->end); return 1; } /* pkt_dump() */ /* FIXME: Potential memory leak on Lua panic. */ static int pkt__tostring(lua_State *L) { struct dns_packet *P = luaL_checkudata(L, 1, PACKET_CLASS); char line[1024]; luaL_Buffer B; FILE *fp; if (!(fp = tmpfile())) return luaL_error(L, "tmpfile: %s", cqs_strerror(errno)); dns_p_dump(P, fp); luaL_buffinit(L, &B); rewind(fp); while (fgets(line, sizeof line, fp)) luaL_addstring(&B, line); fclose(fp); luaL_pushresult(&B); return 1; } /* pkt__tostring() */ static const luaL_Reg pkt_methods[] = { { "qid", &pkt_qid }, { "setqid", &pkt_setqid }, { "flags", &pkt_flags }, { "setflags", &pkt_setflags }, { "push", &pkt_push }, { "count", &pkt_count }, { "grep", &pkt_grep }, { "load", &pkt_load }, { "dump", &pkt_dump }, { NULL, NULL }, }; /* pkt_methods[] */ static const luaL_Reg pkt_metatable[] = { { "__tostring", &pkt__tostring }, { NULL, NULL } }; /* pkt_metatable[] */ static const luaL_Reg pkt_globals[] = { { "new", &pkt_new }, { "type", &pkt_type }, { "interpose", &pkt_interpose }, { NULL, NULL } }; int luaopen__cqueues_dns_packet(lua_State *L) { static const struct cqs_macro section[] = { { "QUESTION", DNS_S_QD }, { "ANSWER", DNS_S_AN }, { "AUTHORITY", DNS_S_NS }, { "ADDITIONAL", DNS_S_AR }, }; static const struct cqs_macro shortsec[] = { { "QD", DNS_S_QD }, { "AN", DNS_S_AN }, { "NS", DNS_S_NS }, { "AR", DNS_S_AR }, }; static const struct cqs_macro opcode[] = { { "QUERY", DNS_OP_QUERY }, { "IQUERY", DNS_OP_IQUERY }, { "STATUS", DNS_OP_STATUS }, { "NOTIFY", DNS_OP_NOTIFY }, { "UPDATE", DNS_OP_UPDATE }, }; static const struct cqs_macro rcode[] = { { "NOERROR", DNS_RC_NOERROR }, { "FORMERR", DNS_RC_FORMERR }, { "SERVFAIL", DNS_RC_SERVFAIL }, { "NXDOMAIN", DNS_RC_NXDOMAIN }, { "NOTIMP", DNS_RC_NOTIMP }, { "REFUSED", DNS_RC_REFUSED }, { "YXDOMAIN", DNS_RC_YXDOMAIN }, { "YXRRSET", DNS_RC_YXRRSET }, { "NXRRSET", DNS_RC_NXRRSET }, { "NOTAUTH", DNS_RC_NOTAUTH }, { "NOTZONE", DNS_RC_NOTZONE }, }; static const struct cqs_macro other[] = { { "QBUFSIZ", DNS_P_QBUFSIZ }, }; cqs_newmetatable(L, PACKET_CLASS, pkt_methods, pkt_metatable, 0); luaL_newlib(L, pkt_globals); lua_newtable(L); cqs_setmacros(L, -1, section, countof(section), 1); cqs_setmacros(L, -1, shortsec, countof(shortsec), 0); lua_setfield(L, -2, "section"); lua_newtable(L); cqs_setmacros(L, -1, opcode, countof(opcode), 1); lua_setfield(L, -2, "opcode"); lua_newtable(L); cqs_setmacros(L, -1, rcode, countof(rcode), 1); lua_setfield(L, -2, "rcode"); cqs_setmacros(L, -1, other, countof(other), 0); return 1; } /* luaopen__cqueues_dns_packet() */ /* * R E S O L V . C O N F B I N D I N G S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define RESCONF_RESOLV_CONF 0 #define RESCONF_NSSWITCH_CONF 1 static int resconf_new(lua_State *L) { struct dns_resolv_conf **resconf = lua_newuserdata(L, sizeof *resconf); int error; *resconf = 0; if (!(*resconf = dns_resconf_open(&error))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; luaL_setmetatable(L, RESCONF_CLASS); return 1; } /* resconf_new() */ static int resconf_stub(lua_State *L) { struct dns_resolv_conf **resconf = lua_newuserdata(L, sizeof *resconf); int error; *resconf = 0; if (!(*resconf = dns_resconf_local(&error))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; luaL_setmetatable(L, RESCONF_CLASS); return 1; } /* resconf_stub() */ static int resconf_root(lua_State *L) { struct dns_resolv_conf **resconf = lua_newuserdata(L, sizeof *resconf); int error; *resconf = 0; if (!(*resconf = dns_resconf_root(&error))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; luaL_setmetatable(L, RESCONF_CLASS); return 1; } /* resconf_root() */ static int resconf_interpose(lua_State *L) { return cqs_interpose(L, RESCONF_CLASS); } /* resconf_interpose() */ static struct dns_resolv_conf *resconf_check(lua_State *L, int index) { return *(struct dns_resolv_conf **)luaL_checkudata(L, index, RESCONF_CLASS); } /* resconf_check() */ static struct dns_resolv_conf *resconf_test(lua_State *L, int index) { struct dns_resolv_conf **resconf = luaL_testudata(L, index, RESCONF_CLASS); return (resconf)? *resconf : 0; } /* resconf_test() */ static int resconf_type(lua_State *L) { if (resconf_test(L, 1)) { lua_pushstring(L, "dns config"); } else { lua_pushnil(L); } return 1; } /* resconf_type() */ static int resconf_getns(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); lua_newtable(L); for (unsigned i = 0; i < countof(resconf->nameserver); i++) { union { struct sockaddr_storage *other; struct sockaddr_in *in; struct sockaddr_in6 *in6; } any; char ns[INET6_ADDRSTRLEN + 1] = ""; int port; any.other = &resconf->nameserver[i]; switch (any.other->ss_family) { case AF_INET: inet_ntop(AF_INET, &any.in->sin_addr, ns, sizeof ns); port = ntohs(any.in->sin_port); break; case AF_INET6: inet_ntop(AF_INET6, &any.in6->sin6_addr, ns, sizeof ns); port = ntohs(any.in6->sin6_port); break; default: continue; } if (port && port != 53) lua_pushfstring(L, "[%s]:%d", ns, port); else lua_pushstring(L, ns); lua_rawseti(L, -2, i + 1); } return 1; } /* resconf_getns() */ static int resconf_setns(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); luaL_checktype(L, 2, LUA_TTABLE); for (unsigned i = 0; i < countof(resconf->nameserver); i++) { const char *ns; int error; lua_rawgeti(L, 2, i + 1); if ((ns = luaL_optstring(L, -1, 0))) { if ((error = dns_resconf_pton(&resconf->nameserver[i], ns))) return luaL_error(L, "%s: %s", ns, cqs_strerror(error)); } else { memset(&resconf->nameserver[i], 0, sizeof resconf->nameserver[i]); resconf->nameserver[i].ss_family = AF_UNSPEC; } lua_pop(L, 1); } return lua_pushboolean(L, 1), 1; } /* resconf_setns() */ static int resconf_getsearch(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); lua_newtable(L); for (unsigned i = 0; i < countof(resconf->search) && *resconf->search[i]; i++) { lua_pushstring(L, resconf->search[i]); lua_rawseti(L, -2, i + 1); } return 1; } /* resconf_getsearch() */ static int resconf_setsearch(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); luaL_checktype(L, 2, LUA_TTABLE); for (unsigned i = 0; i < countof(resconf->search); i++) { const char *dn; lua_rawgeti(L, 2, i + 1); if ((dn = luaL_optstring(L, -1, 0))) { dns_strlcpy(resconf->search[i], dn, sizeof resconf->search[i]); } else { memset(resconf->search[i], 0, sizeof resconf->search[i]); } lua_pop(L, 1); } return lua_pushboolean(L, 1), 1; } /* resconf_setsearch() */ static int resconf_getlookup(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); lua_newtable(L); for (unsigned i = 0; i < countof(resconf->lookup) && resconf->lookup[i]; i++) { switch (resconf->lookup[i]) { case 'f': case 'F': lua_pushliteral(L, "file"); break; case 'b': case 'B': lua_pushliteral(L, "bind"); break; case 'c': case 'C': lua_pushliteral(L, "cache"); break; default: continue; } lua_rawseti(L, -2, i + 1); } return 1; } /* resconf_getlookup() */ static int resconf_setlookup(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); luaL_checktype(L, 2, LUA_TTABLE); memset(resconf->lookup, 0, sizeof resconf->lookup); for (unsigned i = 0; i < countof(resconf->lookup); i++) { const char *lu; lua_rawgeti(L, 2, i + 1); if ((lu = luaL_optstring(L, -1, 0))) { switch (*lu) { case 'f': case 'F': resconf->lookup[i] = 'f'; break; case 'b': case 'B': resconf->lookup[i] = 'b'; break; case 'c': case 'C': resconf->lookup[i] = 'c'; break; } } lua_pop(L, 1); } return lua_pushboolean(L, 1), 1; } /* resconf_setlookup() */ static int resconf_getopts(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); lua_newtable(L); lua_pushboolean(L, resconf->options.edns0); lua_setfield(L, -2, "edns0"); lua_pushinteger(L, resconf->options.ndots); lua_setfield(L, -2, "ndots"); lua_pushinteger(L, resconf->options.timeout); lua_setfield(L, -2, "timeout"); lua_pushinteger(L, resconf->options.attempts); lua_setfield(L, -2, "attempts"); lua_pushboolean(L, resconf->options.rotate); lua_setfield(L, -2, "rotate"); lua_pushboolean(L, resconf->options.recurse); lua_setfield(L, -2, "recurse"); lua_pushboolean(L, resconf->options.smart); lua_setfield(L, -2, "smart"); lua_pushinteger(L, resconf->options.tcp); lua_setfield(L, -2, "tcp"); return 1; } /* resconf_getopts() */ static int resconf_setopts(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); luaL_checktype(L, 2, LUA_TTABLE); resconf->options.edns0 = optfbool(L, 2, "edns0", resconf->options.edns0); resconf->options.ndots = optfint(L, 2, "ndots", resconf->options.ndots); resconf->options.timeout = optfint(L, 2, "timeout", resconf->options.timeout); resconf->options.attempts = optfint(L, 2, "attempts", resconf->options.attempts); resconf->options.rotate = optfbool(L, 2, "rotate", resconf->options.rotate); resconf->options.recurse = optfbool(L, 2, "recurse", resconf->options.recurse); resconf->options.smart = optfbool(L, 2, "smart", resconf->options.smart); resconf->options.tcp = optfint(L, 2, "tcp", resconf->options.tcp); return lua_pushboolean(L, 1), 1; } /* resconf_setopts() */ static int resconf_getiface(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); union { struct sockaddr_storage *other; struct sockaddr_in *in; struct sockaddr_in6 *in6; } any; char ipbuf[INET6_ADDRSTRLEN + 1]; const char *ip = 0; int port = 0; any.other = &resconf->iface; switch (any.other->ss_family) { case AF_INET: ip = inet_ntop(AF_INET, &any.in->sin_addr, ipbuf, sizeof ipbuf); port = ntohs(any.in->sin_port); break; case AF_INET6: ip = inet_ntop(AF_INET6, &any.in6->sin6_addr, ipbuf, sizeof ipbuf); port = ntohs(any.in6->sin6_port); break; } if (!ip) return 0; if (port && port != 53) lua_pushfstring(L, "[%s]:%d", ip, port); else lua_pushstring(L, ip); return 1; } /* resconf_getiface() */ static int resconf_setiface(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); const char *ip = luaL_checkstring(L, 2); int error; if ((error = dns_resconf_pton(&resconf->iface, ip))) return luaL_error(L, "%s: %s", ip, cqs_strerror(error)); return lua_pushboolean(L, 1), 1; } /* resconf_setiface */ static int resconf_loadfile(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); luaL_Stream *file = luaL_checkudata(L, 2, LUA_FILEHANDLE); int syntax = luaL_optint(L, 3, RESCONF_RESOLV_CONF); int error; switch (syntax) { case RESCONF_NSSWITCH_CONF: error = dns_nssconf_loadfile(resconf, file->f); break; default: error = dns_resconf_loadfile(resconf, file->f); break; } if (error) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; return lua_pushboolean(L, 1), 1; } /* resconf_loadfile() */ static int resconf_loadpath(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); const char *path = luaL_checkstring(L, 2); int syntax = luaL_optint(L, 3, RESCONF_RESOLV_CONF); int error; switch (syntax) { case RESCONF_NSSWITCH_CONF: error = dns_nssconf_loadpath(resconf, path); break; default: error = dns_resconf_loadpath(resconf, path); break; } if (error) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; return lua_pushboolean(L, 1), 1; } /* resconf_loadpath() */ static int resconf__next(lua_State *L) { struct dns_resolv_conf *resconf = *(struct dns_resolv_conf **)lua_touserdata(L, lua_upvalueindex(1)); size_t len; const char *qn = lua_tolstring(L, lua_upvalueindex(2), &len); dns_resconf_i_t *i = lua_touserdata(L, lua_upvalueindex(3)); char dn[DNS_D_MAXNAME + 1]; if (!(len = dns_resconf_search(dn, sizeof dn, qn, len, resconf, i))) return 0; lua_pushlstring(L, dn, len); return 1; } /* resconf__next() */ static int resconf_search(lua_State *L) { dns_resconf_i_t *i; resconf_check(L, 1); lua_settop(L, 2); luaL_checktype(L, 2, LUA_TSTRING); i = lua_newuserdata(L, sizeof *i); *i = 0; lua_pushcclosure(L, &resconf__next, 3); return 1; } /* resconf_search() */ /* FIXME: Potential memory leak on Lua panic. */ static int resconf__tostring(lua_State *L) { struct dns_resolv_conf *resconf = resconf_check(L, 1); char line[1024]; luaL_Buffer B; FILE *fp; if (!(fp = tmpfile())) return luaL_error(L, "tmpfile: %s", cqs_strerror(errno)); dns_resconf_dump(resconf, fp); luaL_buffinit(L, &B); rewind(fp); while (fgets(line, sizeof line, fp)) luaL_addstring(&B, line); fclose(fp); luaL_pushresult(&B); return 1; } /* resconf__tostring() */ static int resconf__gc(lua_State *L) { struct dns_resolv_conf **resconf = luaL_checkudata(L, 1, RESCONF_CLASS); dns_resconf_close(*resconf); *resconf = 0; return 0; } /* resconf__gc() */ static const luaL_Reg resconf_methods[] = { { "getns", &resconf_getns }, { "setns", &resconf_setns }, { "getsearch", &resconf_getsearch }, { "setsearch", &resconf_setsearch }, { "getlookup", &resconf_getlookup }, { "setlookup", &resconf_setlookup }, { "getopts", &resconf_getopts }, { "setopts", &resconf_setopts }, { "getiface", &resconf_getiface }, { "setiface", &resconf_setiface }, { "loadfile", &resconf_loadfile }, { "loadpath", &resconf_loadpath }, { "search", &resconf_search }, { NULL, NULL }, }; /* resconf_methods[] */ static const luaL_Reg resconf_metatable[] = { { "__tostring", &resconf__tostring }, { "__gc", &resconf__gc }, { NULL, NULL } }; /* resconf_metatable[] */ static const luaL_Reg resconf_globals[] = { { "new", &resconf_new }, { "stub", &resconf_stub }, { "root", &resconf_root }, { "interpose", &resconf_interpose }, { "type", &resconf_type }, { NULL, NULL } }; int luaopen__cqueues_dns_config(lua_State *L) { cqs_newmetatable(L, RESCONF_CLASS, resconf_methods, resconf_metatable, 0); luaL_newlib(L, resconf_globals); lua_pushinteger(L, DNS_RESCONF_TCP_ENABLE); lua_setfield(L, -2, "TCP_ENABLE"); lua_pushinteger(L, DNS_RESCONF_TCP_ONLY); lua_setfield(L, -2, "TCP_ONLY"); lua_pushinteger(L, DNS_RESCONF_TCP_DISABLE); lua_setfield(L, -2, "TCP_DISABLE"); lua_pushinteger(L, RESCONF_RESOLV_CONF); lua_setfield(L, -2, "RESOLV_CONF"); lua_pushinteger(L, RESCONF_NSSWITCH_CONF); lua_setfield(L, -2, "NSSWITCH_CONF"); return 1; } /* luaopen__cqueues_dns_config() */ /* * H O S T S B I N D I N G S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int hosts_new(lua_State *L) { struct dns_hosts **hosts = lua_newuserdata(L, sizeof *hosts); int error; *hosts = 0; if (!(*hosts = dns_hosts_open(&error))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; luaL_setmetatable(L, HOSTS_CLASS); return 1; } /* hosts_new() */ static int hosts_interpose(lua_State *L) { return cqs_interpose(L, HOSTS_CLASS); } /* hosts_interpose() */ static struct dns_hosts *hosts_check(lua_State *L, int index) { return *(struct dns_hosts **)luaL_checkudata(L, index, HOSTS_CLASS); } /* hosts_check() */ static struct dns_hosts *hosts_test(lua_State *L, int index) { struct dns_hosts **hosts = luaL_testudata(L, index, HOSTS_CLASS); return (hosts)? *hosts : 0; } /* hosts_test() */ static int hosts_type(lua_State *L) { if (hosts_test(L, 1)) { lua_pushstring(L, "dns hosts"); } else { lua_pushnil(L); } return 1; } /* hosts_type() */ static int hosts_loadfile(lua_State *L) { struct dns_hosts *hosts = hosts_check(L, 1); luaL_Stream *file = luaL_checkudata(L, 2, LUA_FILEHANDLE); int error; if ((error = dns_hosts_loadfile(hosts, file->f))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; return lua_pushboolean(L, 1), 1; } /* hosts_loadfile() */ static int hosts_loadpath(lua_State *L) { struct dns_hosts *hosts = hosts_check(L, 1); const char *path = luaL_checkstring(L, 2); int error; if ((error = dns_hosts_loadpath(hosts, path))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; return lua_pushboolean(L, 1), 1; } /* hosts_loadpath() */ static int hosts_insert(lua_State *L) { struct dns_hosts *hosts = hosts_check(L, 1); const char *ip = luaL_checkstring(L, 2); const char *dn = luaL_checkstring(L, 3); _Bool alias = (!lua_isnoneornil(L, 4))? lua_toboolean(L, 4) : 0; union { struct sockaddr_storage other; struct sockaddr_in in; struct sockaddr_in6 in6; } any; int error; if ((error = dns_resconf_pton(&any.other, ip))) goto error; switch (any.other.ss_family) { case AF_INET: if ((error = dns_hosts_insert(hosts, AF_INET, &any.in.sin_addr, dn, alias))) goto error; break; case AF_INET6: if ((error = dns_hosts_insert(hosts, AF_INET6, &any.in6.sin6_addr, dn, alias))) goto error; break; default: break; } return lua_pushboolean(L, 1), 1; error: return luaL_error(L, "%s: %s", ip, cqs_strerror(error)); } /* hosts_insert() */ /* FIXME: Potential memory leak on Lua panic. */ static int hosts__tostring(lua_State *L) { struct dns_hosts *hosts = hosts_check(L, 1); char line[1024]; luaL_Buffer B; FILE *fp; if (!(fp = tmpfile())) return luaL_error(L, "tmpfile: %s", cqs_strerror(errno)); dns_hosts_dump(hosts, fp); luaL_buffinit(L, &B); rewind(fp); while (fgets(line, sizeof line, fp)) luaL_addstring(&B, line); fclose(fp); luaL_pushresult(&B); return 1; } /* hosts__tostring() */ static int hosts__gc(lua_State *L) { struct dns_hosts **hosts = luaL_checkudata(L, 1, HOSTS_CLASS); dns_hosts_close(*hosts); *hosts = 0; return 0; } /* hosts__gc() */ static const luaL_Reg hosts_methods[] = { { "loadfile", &hosts_loadfile }, { "loadpath", &hosts_loadpath }, { "insert", &hosts_insert }, { NULL, NULL }, }; /* hosts_methods[] */ static const luaL_Reg hosts_metatable[] = { { "__tostring", &hosts__tostring }, { "__gc", &hosts__gc }, { NULL, NULL } }; /* hosts_metatable[] */ static const luaL_Reg hosts_globals[] = { { "new", &hosts_new }, { "interpose", &hosts_interpose }, { "type", &hosts_type }, { NULL, NULL } }; int luaopen__cqueues_dns_hosts(lua_State *L) { // not needed until dns_hosts_query bound //cqs_requiref(L, "_cqueues.dns.packet", &luaopen__cqueues_dns_packet, 0); cqs_newmetatable(L, HOSTS_CLASS, hosts_methods, hosts_metatable, 0); luaL_newlib(L, hosts_globals); return 1; } /* luaopen__cqueues_dns_hosts() */ /* * H I N T S B I N D I N G S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int hints_new(lua_State *L) { struct dns_resolv_conf *resconf = resconf_test(L, 1); struct dns_hints **hints; int error; hints = lua_newuserdata(L, sizeof *hints); *hints = 0; if (!(*hints = dns_hints_open(resconf, &error))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; luaL_setmetatable(L, HINTS_CLASS); return 1; } /* hints_new() */ static int hints_root(lua_State *L) { struct dns_resolv_conf *resconf = resconf_test(L, 1); struct dns_hints **hints; int error; hints = lua_newuserdata(L, sizeof *hints); *hints = 0; if (!(*hints = dns_hints_root(resconf, &error))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; luaL_setmetatable(L, HINTS_CLASS); return 1; } /* hints_root() */ static int hints_stub(lua_State *L) { struct dns_resolv_conf *resconf = resconf_test(L, 1); struct dns_hints **hints; int error; hints = lua_newuserdata(L, sizeof *hints); *hints = 0; if (!(*hints = dns_hints_local(resconf, &error))) return lua_pushboolean(L, 0), lua_pushinteger(L, error), 2; luaL_setmetatable(L, HINTS_CLASS); return 1; } /* hints_stub() */ static int hints_interpose(lua_State *L) { return cqs_interpose(L, HINTS_CLASS); } /* hints_interpose() */ static struct dns_hints *hints_check(lua_State *L, int index) { return *(struct dns_hints **)luaL_checkudata(L, index, HINTS_CLASS); } /* hints_check() */ static struct dns_hints *hints_test(lua_State *L, int index) { struct dns_hints **hints = luaL_testudata(L, index, HINTS_CLASS); return (hints)? *hints : 0; } /* hints_test() */ static int hints_type(lua_State *L) { if (hints_test(L, 1)) { lua_pushstring(L, "dns hints"); } else { lua_pushnil(L); } return 1; } /* hints_type() */ static int hints_insert(lua_State *L) { struct dns_hints *hints = hints_check(L, 1); const char *zone = luaL_checkstring(L, 2), *ns; int priority = luaL_optint(L, 4, 0); int error = 0; if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) { dns_hints_insert_resconf(hints, zone, resconf_check(L, 3), &error); } else { ns = luaL_checkstring(L, 3); struct sockaddr_storage any; if (!(error = dns_resconf_pton(&any, ns))) error = dns_hints_insert(hints, zone, (struct sockaddr *)&any, priority); } if (error) return luaL_error(L, "%s: %s", zone, cqs_strerror(error)); return lua_pushboolean(L, 1), 1; } /* hints_insert() */ static int hints_next(lua_State *L) { struct dns_hints *hints = hints_check(L, lua_upvalueindex(1)); struct dns_hints_i *i = lua_touserdata(L, lua_upvalueindex(3)); union { struct sockaddr *sa; struct sockaddr_in *in; struct sockaddr_in6 *in6; } any; socklen_t salen; char ip[INET6_ADDRSTRLEN + 1] = ""; int port; while (dns_hints_grep(&any.sa, &salen, 1, i, hints)) { switch (any.sa->sa_family) { case AF_INET: inet_ntop(AF_INET, &any.in->sin_addr, ip, sizeof ip); port = ntohs(any.in->sin_port); break; case AF_INET6: inet_ntop(AF_INET6, &any.in6->sin6_addr, ip, sizeof ip); port = ntohs(any.in6->sin6_port); break; default: continue; } if (port && port != 53) lua_pushfstring(L, "[%s]:%d", ip, port); else lua_pushstring(L, ip); return 1; } return 0; } /* hints_next() */ static int hints_grep(lua_State *L) { struct dns_hints_i *i; hints_check(L, 1); lua_settop(L, 2); i = memset(lua_newuserdata(L, sizeof *i), 0, sizeof *i); i->zone = luaL_optstring(L, 2, "."); lua_pushcclosure(L, &hints_next, 3); return 1; } /* hints_grep() */ /* FIXME: Potential memory leak on Lua panic. */ static int hints__tostring(lua_State *L) { struct dns_hints *hints = hints_check(L, 1); char line[1024]; luaL_Buffer B; FILE *fp; if (!(fp = tmpfile())) return luaL_error(L, "tmpfile: %s", cqs_strerror(errno)); dns_hints_dump(hints, fp); luaL_buffinit(L, &B); rewind(fp); while (fgets(line, sizeof line, fp)) luaL_addstring(&B, line); fclose(fp); luaL_pushresult(&B); return 1; } /* hints__tostring() */ static int hints__gc(lua_State *L) { struct dns_hints **hints = luaL_checkudata(L, 1, HINTS_CLASS); dns_hints_close(*hints); *hints = 0; return 0; } /* hints__gc() */ static const luaL_Reg hints_methods[] = { { "insert", &hints_insert }, { "grep", &hints_grep }, { NULL, NULL }, }; /* hints_methods[] */ static const luaL_Reg hints_metatable[] = { { "__tostring", &hints__tostring }, { "__gc", &hints__gc }, { NULL, NULL } }; /* hints_metatable[] */ static const luaL_Reg hints_globals[] = { { "new", &hints_new }, { "root", &hints_root }, { "stub", &hints_stub }, { "interpose", &hints_interpose }, { "type", &hints_type }, { NULL, NULL } }; int luaopen__cqueues_dns_hints(lua_State *L) { cqs_newmetatable(L, HINTS_CLASS, hints_methods, hints_metatable, 0); cqs_requiref(L, "_cqueues.dns.config", &luaopen__cqueues_dns_config, 0); // not needed until dns_hints_query bound //cqs_requiref(L, "_cqueues.dns.packet", &luaopen__cqueues_dns_packet, 0); luaL_newlib(L, hints_globals); return 1; } /* luaopen__cqueues_dns_hints() */ /* * R E S O L V E R B I N D I N G S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct resolver { struct dns_resolver *res; lua_State *mainthread; }; /* struct resolver */ static struct resolver *res_prep(lua_State *L) { struct resolver *R = lua_newuserdata(L, sizeof *R); R->res = 0; #if defined LUA_RIDX_MAINTHREAD lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); R->mainthread = lua_tothread(L, -1); lua_pop(L, 1); #else R->mainthread = 0; #endif luaL_setmetatable(L, RESOLVER_CLASS); return R; } /* res_prep() */ static int res_closefd(int *fd, void *arg) { struct resolver *R = arg; if (R->mainthread) { cqs_cancelfd(R->mainthread, *fd); cqs_closefd(fd); } return 0; } /* res_closefd() */ static int res_new(lua_State *L) { struct resolver *R = res_prep(L); struct dns_resolv_conf *resconf = resconf_test(L, 1); struct dns_hosts *hosts = hosts_test(L, 2); struct dns_hints *hints = hints_test(L, 3); int error; if (resconf) dns_resconf_acquire(resconf); if (hosts) dns_hosts_acquire(hosts); if (hints) dns_hints_acquire(hints); if (!resconf && !(resconf = dns_resconf_local(&error))) goto error; if (!hosts) { if (resconf->options.recurse) hosts = dns_hosts_open(&error); else hosts = dns_hosts_local(&error); if (!hosts) goto error; } if (!hints) { if (resconf->options.recurse) hints = dns_hints_root(resconf, &error); else hints = dns_hints_local(resconf, &error); if (!hints) goto error; } if (!(R->res = dns_res_open(resconf, hosts, hints, NULL, dns_opts(.closefd = { R, &res_closefd }), &error))) goto error; dns_resconf_close(resconf); dns_hosts_close(hosts); dns_hints_close(hints); return 1; error: dns_resconf_close(resconf); dns_hosts_close(hosts); dns_hints_close(hints); lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* res_new() */ static int res_interpose(lua_State *L) { return cqs_interpose(L, RESOLVER_CLASS); } /* res_interpose() */ static int res_type(lua_State *L) { struct resolver *R; if ((R = luaL_testudata(L, 1, RESOLVER_CLASS))) { lua_pushstring(L, (R->res)? "dns resolver" : "closed dns resolver"); } else { lua_pushnil(L); } return 1; } /* res_type() */ static inline struct dns_resolver *res_check(lua_State *L, int index) { struct resolver *R = luaL_checkudata(L, index, RESOLVER_CLASS); if (!R->res) luaL_argerror(L, index, "resolver defunct"); return R->res; } /* res_check() */ static int res_submit(lua_State *L) { struct dns_resolver *R = res_check(L, 1); const char *name = luaL_checkstring(L, 2); int type = luaL_optint(L, 3, DNS_T_A); int class = luaL_optint(L, 4, DNS_C_IN); int error; if (!(error = dns_res_submit(R, name, type, class))) { lua_pushboolean(L, 1); return 1; } else { lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } } /* res_submit() */ static int res_fetch(lua_State *L) { struct dns_resolver *R = res_check(L, 1); struct dns_packet *pkt; size_t size; int error; if ((error = dns_res_check(R)) || !(pkt = dns_res_fetch(R, &error))) { error: lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } /* FIXME: Leaks packet if lua_newuserdata throws */ size = dns_p_sizeof(pkt); error = dns_p_study(dns_p_copy(dns_p_init(lua_newuserdata(L, size), size), pkt)); free(pkt); if (error) goto error; luaL_setmetatable(L, PACKET_CLASS); return 1; } /* res_fetch() */ static int res_pollfd(lua_State *L) { struct dns_resolver *R = res_check(L, 1); lua_pushinteger(L, dns_res_pollfd(R)); return 1; } /* res_pollfd() */ static int res_events(lua_State *L) { struct dns_resolver *R = res_check(L, 1); switch (dns_res_events(R)) { case POLLIN|POLLOUT: lua_pushliteral(L, "rw"); break; case POLLIN: lua_pushliteral(L, "r"); break; case POLLOUT: lua_pushliteral(L, "w"); break; default: lua_pushnil(L); break; } return 1; } /* res_events() */ static int res_timeout(lua_State *L) { struct dns_resolver *R = res_check(L, 1); lua_pushnumber(L, dns_res_timeout(R)); return 1; } /* res_timeout() */ static int res_stat(lua_State *L) { struct dns_resolver *R = res_check(L, 1); const struct dns_stat *st = dns_res_stat(R); lua_newtable(L); lua_pushinteger(L, st->queries); lua_setfield(L, -2, "queries"); #define setboth(st, table) do { \ lua_newtable(L); \ lua_pushinteger(L, (st).count); \ lua_setfield(L, -2, "count"); \ lua_pushinteger(L, (st).bytes); \ lua_setfield(L, -2, "bytes"); \ lua_setfield(L, -2, table); \ } while (0) lua_newtable(L); setboth(st->udp.sent, "sent"); setboth(st->udp.rcvd, "rcvd"); lua_setfield(L, -2, "udp"); lua_newtable(L); setboth(st->tcp.sent, "sent"); setboth(st->tcp.rcvd, "rcvd"); lua_setfield(L, -2, "tcp"); return 1; } /* res_stat() */ static int res_close(lua_State *L) { struct resolver *R = luaL_checkudata(L, 1, RESOLVER_CLASS); if (!R->mainthread) { R->mainthread = L; dns_res_close(R->res); R->res = 0; R->mainthread = 0; } else { dns_res_close(R->res); R->res = 0; } return 0; } /* res_close() */ static int res__gc(lua_State *L) { struct resolver *R = luaL_checkudata(L, 1, RESOLVER_CLASS); R->mainthread = 0; dns_res_close(R->res); R->res = 0; return 0; } /* res__gc() */ static const luaL_Reg res_methods[] = { { "submit", &res_submit }, { "fetch", &res_fetch }, { "pollfd", &res_pollfd }, { "events", &res_events }, { "timeout", &res_timeout }, { "stat", &res_stat }, { "close", &res_close }, { NULL, NULL }, }; /* res_methods[] */ static const luaL_Reg res_metatable[] = { { "__gc", &res__gc }, { NULL, NULL } }; /* res_metatable[] */ static const luaL_Reg res_globals[] = { { "new", &res_new }, { "interpose", &res_interpose }, { "type", &res_type }, { NULL, NULL } }; int luaopen__cqueues_dns_resolver(lua_State *L) { cqs_newmetatable(L, RESOLVER_CLASS, res_methods, res_metatable, 0); cqs_requiref(L, "_cqueues.dns.config", &luaopen__cqueues_dns_config, 0); cqs_requiref(L, "_cqueues.dns.hosts", &luaopen__cqueues_dns_hosts, 0); cqs_requiref(L, "_cqueues.dns.hints", &luaopen__cqueues_dns_hints, 0); cqs_requiref(L, "_cqueues.dns.packet", &luaopen__cqueues_dns_packet, 0); luaL_newlib(L, res_globals); return 1; } /* luaopen__cqueues_dns_resolver() */ /* * G L O B A L B I N D I N G S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int dnsL_version(lua_State *L) { lua_pushinteger(L, dns_v_rel()); lua_pushinteger(L, dns_v_abi()); lua_pushinteger(L, dns_v_api()); return 3; } /* dnsL_version() */ static int dnsL_random(lua_State *L) { lua_Number modn = luaL_optnumber(L, 1, UINT_MAX + 1.0); if (modn >= (UINT_MAX + 1.0)) { lua_pushnumber(L, dns_random()); } else { unsigned n = (unsigned)modn; unsigned r, min; luaL_argcheck(L, n > 1, 1, lua_pushfstring(L, "[0, %d): interval is empty", (int)n)); min = -n % n; for (;;) { r = dns_random(); if (r >= min) break; } lua_pushinteger(L, r % n); } return 1; } /* dnsL_random() */ static const luaL_Reg dnsL_globals[] = { { "version", &dnsL_version }, { "random", &dnsL_random }, { NULL, NULL } }; int luaopen__cqueues_dns(lua_State *L) { luaL_newlib(L, dnsL_globals); return 1; } /* luaopen__cqueues_dns() */ cqueues-rel-20161214/src/dns.config.lua000066400000000000000000000031021302435770500175320ustar00rootroot00000000000000local loader = function(loader, ...) local config = require"_cqueues.dns.config" config.loadfile = function (file, syntax) local cfg = config.new() cfg:loadfile(file, syntax) return cfg end config.loadpath = function (path, syntax) local cfg = config.new() cfg:loadpath(path, syntax) return cfg end local new = config.new; config.new = function(init) local cfg = new() if init then cfg:set(init) end return cfg end local stub = config.stub; config.stub = function(init) local cfg = stub() if init then cfg:set(init) end return cfg end local root = config.root; config.root = function(init) local cfg = root() if init then cfg:set(init) end return cfg end config.interpose("set", function (self, init) if init.nameserver then self:setns(init.nameserver) end if init.search then self:setsearch(init.search) end if init.lookup then self:setlookup(init.lookup) end local opts = init.options or init.opts or { } local copy = { "edns0", "ndots", "timeout", "attempts", "rotate", "recurse", "smart", "tcp" } for i, k in ipairs(copy) do if opts[k] == nil and init[k] ~= nil then opts[k] = init[k]; end end self:setopts(opts) if init.interface then self:setiface(init.interface) end end) config.interpose("get", function (self) return { nameserver = self:getns(), search = self:getsearch(), lookup = self:getlookup(), options = self:getopts(), interface = self:getiface(), } end) config.loader = loader return config end return loader(loader, ...) cqueues-rel-20161214/src/dns.hints.lua000066400000000000000000000001771302435770500174230ustar00rootroot00000000000000local loader = function(loader, ...) local hints = require"_cqueues.dns.hints" return hints end return loader(loader, ...) cqueues-rel-20161214/src/dns.hosts.lua000066400000000000000000000007201302435770500174300ustar00rootroot00000000000000local loader = function(loader, ...) local hosts = require"_cqueues.dns.hosts" hosts.loadfile = function (file) local hosts = hosts.new() hosts:loadfile(file) return hosts end hosts.loadpath = function (path) local hosts = hosts.new() hosts:loadpath(path) return hosts end hosts.stub = function () return hosts.loadpath"/etc/hosts" end hosts.root = function () return hosts.new() end return hosts end return loader(loader, ...) cqueues-rel-20161214/src/dns.lua000066400000000000000000000012161302435770500162720ustar00rootroot00000000000000local loader = function(loader, ...) local dns = require"_cqueues.dns" local assert = require"cqueues".assert -- -- NOTE: defer loading dns.resolvers as it depends on us -- local pool = nil function dns.setpool(p) local o = pool assert(require"cqueues.dns.resolvers".type(p), "not dns resolver pool") pool = p return o end -- dns.setpool function dns.getpool() if not pool then dns.setpool(assert(require"cqueues.dns.resolvers".stub())) end return pool end -- dns.getpool function dns.query(...) return dns.getpool():query(...) end -- dns.query dns.loader = loader return dns end return loader(loader, ...) cqueues-rel-20161214/src/dns.packet.lua000066400000000000000000000033741302435770500175470ustar00rootroot00000000000000local loader = function(loader, ...) local packet = require"_cqueues.dns.packet" local record = require"_cqueues.dns.record" -- dns.record depends on dns.packet local function toconst(id, map, what, lvl) local n if id == nil then return elseif type(id) == "number" then n = map[id] and id elseif type(id) == "string" then n = map[id] or map[string.upper(id)] end if not n then error((tostring(id) .. ": unknown DNS " .. what), lvl + 1) end return n end -- toconst local _push; _push = packet.interpose("push", function (self, section, name, type, class, ttl, rdata) section = toconst(section, packet.section, "section", 2) type = toconst(type, record.type, "type", 2) class = toconst(class, record.class, "class", 2) return _push(self, section, name, type, class, ttl, rdata) end) -- packet:push -- -- TODO: Don't restrict ourselves to the C iteration interface, -- which is limited by fixed-sized fields. For example, you cannot -- specify multiple different record types because the type -- identifiers cannot be ORd together. -- local _grep; _grep = packet.interpose("grep", function (self, opts) if opts then opts.type = toconst(opts.type, record.type, "type", 2) opts.class = toconst(opts.class, record.class, "class", 2) if type(opts.section) == "string" then local n = 0 for s in string.gmatch(opts.section, "%a+") do n = n + toconst(s, packet.section, "section", 2) end opts.section = n elseif type(opts.section) == "table" then local n = 0 for i=1,#opts.section do n = n + toconst(opts.section[i], packet.section, "section", 2) end opts.section = n end end return _grep(self, opts) end) -- packet:grep return packet end return loader(loader, ...) cqueues-rel-20161214/src/dns.record.lua000066400000000000000000000006651302435770500175560ustar00rootroot00000000000000local loader = function(loader, ...) local record = require"_cqueues.dns.record" for k, v in pairs(record.class) do if type(k) == "string" then record[k] = v end end for k, v in pairs(record.type) do if type(k) == "string" then record[k] = v end end for k, v in pairs(require"cqueues.dns.packet".section) do if type(k) == "string" then record[k] = v end end return record end return loader(loader, ...) cqueues-rel-20161214/src/dns.resolver.lua000066400000000000000000000037541302435770500201430ustar00rootroot00000000000000local loader = function(loader, ...) local cqueues = require"cqueues" local resolver = require"_cqueues.dns.resolver" local config = require"cqueues.dns.config" local record = require"cqueues.dns.record" local errno = require"cqueues.errno" local EAGAIN = errno.EAGAIN local ETIMEDOUT = errno.ETIMEDOUT local monotime = cqueues.monotime local _new = resolver.new; resolver.new = function (resconf, hosts, hints) if type(resconf) == "table" then resconf = config.new(resconf) end return _new(resconf, hosts, hints) end resolver.stub = function (init) return resolver.new(config.stub(init), nil, nil) end resolver.root = function (init) return resolver.new(config.root(init), nil, nil) end local function toconst(id, map, what, lvl) local n if id == nil then return elseif type(id) == "number" then n = map[id] and id elseif type(id) == "string" then n = map[id] or map[string.upper(id)] end if not n then error((tostring(id) .. ": unknown DNS " .. what), lvl + 1) end return n end -- toconst local _submit; _submit = resolver.interpose("submit", function (self, name, type, class) type = toconst(type, record.type, "type", 2) class = toconst(class, record.class, "class", 2) return _submit(self, name, type, class) end) resolver.interpose("query", function (self, name, type, class, timeout) local deadline = timeout and (monotime() + timeout) local ok, why, answer ok, why = self:submit(name, type, class) if not ok then return nil, why end repeat answer, why = self:fetch() if not answer then if why == EAGAIN then if deadline then local curtime = monotime() if deadline <= curtime then return nil, ETIMEDOUT else cqueues.poll(self, math.min(deadline - curtime, 1)) end else cqueues.poll(self, 1) end else return nil, why end end until answer return answer end) resolver.loader = loader return resolver end return loader(loader, ...) cqueues-rel-20161214/src/dns.resolvers.lua000066400000000000000000000120471302435770500203210ustar00rootroot00000000000000local loader = function(loader, ...) local resolver = require"cqueues.dns.resolver" local config = require"cqueues.dns.config" local condition = require"cqueues.condition" local monotime = require"cqueues".monotime local random = require"cqueues.dns".random local errno = require"cqueues.errno" local ETIMEDOUT = errno.ETIMEDOUT local function todeadline(timeout) return (timeout and (monotime() + timeout)) or nil end -- todeadline local function totimeout(deadline) return (deadline and math.max(0, deadline - monotime())) or nil end -- totimeout -- -- NOTE: Keep track of an unordered collection of objects, and in -- particular a count of objects in the collection. If an object is -- garbage collected automatically decrement the count and signal -- the condition variable. -- local alive = {} function alive.new(condvar) local self = setmetatable({}, { __index = alive }) self.n = 0 self.table = setmetatable({}, { __mode = "k" }) self.condvar = condvar self.hooks = {} self.hookmt = { __gc = function (hook) self.n = self.n - 1 self.condvar:signal() if self.leakcb then pcall(self.leakcb) end end } return self end -- alive.new function alive:newhook() if not self._newhook then if _G._VERSION == "Lua 5.1" then -- Lua 5.1 does not support __gc on tables, so we need to use newproxy self._newhook = function(mt) local u = newproxy(false) debug.setmetatable(u, mt) return u end else self._newhook = function(mt) return setmetatable({}, mt) end end end return self._newhook(self.hookmt) end -- alive:newhook function alive:add(x) if not self.table[x] then local hook = self.hooks[#self.hooks] if hook then self.hooks[#self.hooks] = nil else hook = self:newhook() end self.table[x] = hook self.n = self.n + 1 end end -- alive:add function alive:delete(x) if self.table[x] then self.hooks[#self.hooks + 1] = self.table[x] self.table[x] = nil self.n = self.n - 1 self.condvar:signal() end end -- alive:delete function alive:check() local n = 0 for _ in pairs(self.table) do n = n + 1 end return assert(n == self.n, "resolver registry corrupt") end -- alive:check function alive:onleak(f) local old = self.onleak self.leakcb = f return old end -- alive:onleak local pool = {} local function getby(self, deadline) local res while true do local cache_len = #self.cache if cache_len > 1 then res = self.cache[cache_len] self.cache[cache_len] = nil if res then break else if deadline and deadline <= monotime() then return nil, ETIMEDOUT end self.condvar:wait(totimeout(deadline)) end elseif self.alive.n < self.hiwat then local why res, why = resolver.new(self.resconf, self.hosts, self.hints) if not res then return nil, why end break end end self.alive:add(res) return res end -- getby function pool:get(timeout) return getby(self, todeadline(timeout)) end -- pool:get function pool:put(res) self.alive:delete(res) local cache_len = #self.cache if cache_len < self.lowat and res:stat().queries < self.querymax then if not self.lifo and cache_len > 0 then local i = random(cache_len+1) + 1 self.cache[cache_len+1] = self.cache[i] self.cache[i] = res else self.cache[cache_len+1] = res end else res:close() end end -- pool:put function pool:signal() self.condvar:signal() end -- pool:signal function pool:query(name, type, class, timeout) local deadline = todeadline(timeout or self.timeout) local res, why = getby(self, deadline) if not res then return nil, why end local r, y = res:query(name, type, class, totimeout(deadline)) self:put(res) if not r then return nil, y end return r end -- pool:query function pool:check() return self.alive:check() end -- pool:check function pool:onleak(f) return self.alive:onleak(f) end -- pool:onleak local resolvers = {} resolvers.lowat = 1 resolvers.hiwat = 32 resolvers.querymax = 2048 resolvers.onleak = nil resolvers.lifo = false function resolvers.new(resconf, hosts, hints) local self = {} self.resconf = (type(resconf) == "table" and config.new(resconf)) or resconf self.hosts = hosts self.hints = hints self.condvar = condition.new() self.lowat = resolvers.lowat self.hiwat = resolvers.hiwat self.timeout = resolvers.timeout self.querymax = resolvers.querymax self.onleak = resolvers.onleak self.lifo = resolvers.lifo self.cache = {} self.alive = alive.new(self.condvar) return setmetatable(self, { __index = pool }) end -- resolvers.new function resolvers.stub(cfg) return resolvers.new(config.stub(cfg)) end -- resolvers.stub function resolvers.root(cfg) return resolvers.new(config.root(cfg)) end -- resolvers.root function resolvers.type(o) local mt = getmetatable(o) if mt and mt.__index == pool then return "dns resolver pool" end end -- resolvers.type resolvers.loader = loader return resolvers end return loader(loader, ...) cqueues-rel-20161214/src/errno.c.m4000066400000000000000000000100001302435770500166020ustar00rootroot00000000000000/* ========================================================================== * errno.c.m4 - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012, 2015 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include /* memcpy(3) strcmp(3) strerror_r(3) strnlen(3) */ #include #include #include #include "lib/dns.h" #include "lib/socket.h" #include "cqueues.h" #ifndef STRERROR_R_CHAR_P #define STRERROR_R_CHAR_P ((GLIBC_PREREQ(0,0) || UCLIBC_PREREQ(0,0,0)) && (_GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600))) #endif cqs_error_t cqs_strerror_r(cqs_error_t error, char *dst, size_t lim) { const char *src; if (error >= DNS_EBASE && error < DNS_ELAST) { src = dns_strerror(error); } else if (error >= SO_EBASE && error < SO_ELAST) { src = so_strerror(error); } else { #if STRERROR_R_CHAR_P if (!(src = strerror_r(error, dst, lim))) return EINVAL; #else /* glibc between 2.3.4 and 2.13 returns -1 on error */ if (-1 == (error = strerror_r(error, dst, lim))) return errno; return error; #endif } if (src != dst && lim > 0) { size_t n = strnlen(src, lim - 1); memcpy(dst, src, n); dst[n] = '\0'; } return 0; } /* cqs_strerror_r() */ const char *(cqs_strerror)(int error, void *dst, size_t lim) { char *p, *pe, *unknown; char e10[((sizeof error * CHAR_BIT) / 3) + 1], *ep; int n; if (!lim) return dst; if (0 == cqs_strerror_r(error, dst, lim) && *(char *)dst) return dst; p = dst; pe = p + lim; unknown = "Unknown error: "; while (*unknown && p < pe) *p++ = *unknown++; if (error < 0 && p < pe) *p++ = '-'; /* translate integer to string in LSB order */ for (ep = e10, n = error; n; ep++, n /= 10) *ep = "0123456789"[abs(n % 10)]; if (ep == e10) *ep++ = '0'; /* copy string, flipping from LSB to MSB */ while (ep > e10 && p < pe) *p++ = *--ep; p[-1] = '\0'; return dst; } /* cqs_strerror() */ static const struct { const char *name; int value; } errlist[] = { changequote(<<<,>>>)dnl ifdef(<<>>,<<>>,<<>>)(<<< ../mk/errno.ls | awk '{ print "#ifdef "$1"\n\t{ \""$1"\", "$1" },\n#endif" }' >>>)dnl }; static int le_strerror(lua_State *L) { lua_pushstring(L, cqs_strerror(luaL_checkint(L, 1))); return 1; } /* le_strerror() */ static const luaL_Reg le_globals[] = { { "strerror", &le_strerror }, { NULL, NULL } }; int luaopen__cqueues_errno(lua_State *L) { unsigned i; luaL_newlib(L, le_globals); for (i = 0; i < sizeof errlist / sizeof *errlist; i++) { lua_pushstring(L, errlist[i].name); lua_pushinteger(L, errlist[i].value); lua_settable(L, -3); #if EAGAIN == EWOULDBLOCK if (!strcmp(errlist[i].name, "EWOULDBLOCK")) continue; #endif lua_pushinteger(L, errlist[i].value); lua_pushstring(L, errlist[i].name); lua_settable(L, -3); } return 1; } /* luaopen__cqueues_errno() */ cqueues-rel-20161214/src/errno.lua000066400000000000000000000002251302435770500166320ustar00rootroot00000000000000local loader = function(loader, ...) local errno = require("_cqueues.errno") errno.loader = loader return errno end return loader(loader, ...) cqueues-rel-20161214/src/lib/000077500000000000000000000000001302435770500155515ustar00rootroot00000000000000cqueues-rel-20161214/src/lib/GNUmakefile000066400000000000000000000025561302435770500176330ustar00rootroot00000000000000# non-recursive prologue sp := $(sp).x dirstack_$(sp) := $(d) d := $(abspath $(lastword $(MAKEFILE_LIST))/..) ifeq ($(origin GUARD_$(d)), undefined) GUARD_$(d) := 1 # # E N V I R O N M E N T C O N F I G U R A T I O N # include $(d)/../../GNUmakefile # # C O M P I L I A T I O N F L A G S # OS_$(d) := $(shell $(d)/../../mk/vendor.os) CPPFLAGS_$(d) = $(ALL_CPPFLAGS) -DSOCKET_DEBUG -DHAVE_CONFIG_H CFLAGS_$(d) = $(ALL_CFLAGS) ifeq ($(findstring DNS_RANDOM,$(CPPFLAGS_$(d))),) ifeq ($(OS_$(d)), $(filter $(OS_$(d)), OpenBSD NetBSD FreeBSD Darwin)) CPPFLAGS_$(d) += -DDNS_RANDOM=arc4random else CPPFLAGS_$(d) += -DDNS_RANDOM=RAND_bytes endif endif # # C O M P I L I A T I O N R U L E S # $(d)/config.h: $(abspath $(d)/../..)/config.h $(CP) $< $@ $(d)/%.o: $(d)/%.c $(d)/%.h $(d)/config.h $(CC) $(CFLAGS_$(@D)) $(CPPFLAGS_$(@D)) -c -o $@ $< $(d)/libnonlua.a: $(d)/socket.o $(d)/dns.o $(d)/notify.o $(AR) cr $@ $^ $(RANLIB) $@ # # L O C A L R U L E S # ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" libnonlua.a: $(d)/libnonlua.a endif # local guard .PHONY: $(d)/clean $(d)/clean~ clean clean~ $(d)/clean: rm -fr $(@D)/config.h $(@D)/*.[oa] $(@D)/*.dSYM $(d)/clean~: $(d)/clean rm -fr $(@D)/*~ clean: $(d)/clean clean~: $(d)/clean~ endif # include guard # non-recursive epilogue d := $(dirstack_$(sp)) sp := $(basename $(sp)) cqueues-rel-20161214/src/lib/Makefile000066400000000000000000000001161302435770500172070ustar00rootroot00000000000000.POSIX: all: +gmake -f GNUmakefile all .DEFAULT: +gmake -f GNUmakefile $< cqueues-rel-20161214/src/lib/dns.c000066400000000000000000006713301302435770500165130ustar00rootroot00000000000000/* ========================================================================== * dns.c - Recursive, Reentrant DNS Resolver. * -------------------------------------------------------------------------- * Copyright (c) 2008, 2009, 2010, 2012-2016 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #if !defined(__FreeBSD__) && !defined(__sun) #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #undef _BSD_SOURCE #define _BSD_SOURCE #undef _DARWIN_C_SOURCE #define _DARWIN_C_SOURCE #undef _NETBSD_SOURCE #define _NETBSD_SOURCE #endif #include /* INT_MAX */ #include /* offsetof() */ #ifdef _WIN32 #define uint32_t unsigned int #else #include /* uint32_t */ #endif #include /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */ #include /* FILE fopen(3) fclose(3) getc(3) rewind(3) */ #include /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */ #include /* strcasecmp(3) strncasecmp(3) */ #include /* isspace(3) isdigit(3) */ #include /* time_t time(2) difftime(3) */ #include /* SIGPIPE sigemptyset(3) sigaddset(3) sigpending(2) sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) */ #include /* errno EINVAL ENOENT */ #undef NDEBUG #include /* assert(3) */ #if _WIN32 #ifndef FD_SETSIZE #define FD_SETSIZE 256 #endif #include #include #else #include /* FD_SETSIZE socklen_t */ #include /* FD_ZERO FD_SET fd_set select(2) */ #include /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */ #if defined(AF_UNIX) #include /* struct sockaddr_un */ #endif #include /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */ #include /* _POSIX_THREADS gethostname(3) close(2) */ #include /* POLLIN POLLOUT */ #include /* struct sockaddr_in struct sockaddr_in6 */ #include /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */ #include /* struct addrinfo */ #endif #include "dns.h" /* * C O M P I L E R V E R S I O N & F E A T U R E D E T E C T I O N * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p)) #define DNS_GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && DNS_GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= DNS_GNUC_2VER((M), (m), (p))) #define DNS_MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p)) #define DNS_MSC_PREREQ(M, m, p) (_MSC_VER_FULL > 0 && _MSC_VER_FULL >= DNS_MSC_2VER((M), (m), (p))) #define DNS_SUNPRO_PREREQ(M, m, p) (__SUNPRO_C > 0 && __SUNPRO_C >= 0x ## M ## m ## p) #if defined __has_builtin #define dns_has_builtin(x) __has_builtin(x) #else #define dns_has_builtin(x) 0 #endif #if defined __has_extension #define dns_has_extension(x) __has_extension(x) #else #define dns_has_extension(x) 0 #endif #ifndef HAVE___ASSUME #define HAVE___ASSUME DNS_MSC_PREREQ(8,0,0) #endif #ifndef HAVE___BUILTIN_TYPES_COMPATIBLE_P #define HAVE___BUILTIN_TYPES_COMPATIBLE_P (DNS_GNUC_PREREQ(3,1,1) || __clang__) #endif #ifndef HAVE___BUILTIN_UNREACHABLE #define HAVE___BUILTIN_UNREACHABLE (DNS_GNUC_PREREQ(4,5,0) || dns_has_builtin(__builtin_unreachable)) #endif #ifndef HAVE_PRAGMA_MESSAGE #define HAVE_PRAGMA_MESSAGE (DNS_GNUC_PREREQ(4,4,0) || __clang__ || _MSC_VER) #endif /* * C O M P I L E R A N N O T A T I O N S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if __GNUC__ #define DNS_NOTUSED __attribute__((unused)) #define DNS_NORETURN __attribute__((noreturn)) #else #define DNS_NOTUSED #define DNS_NORETURN #endif #if __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wmissing-field-initializers" #elif DNS_GNUC_PREREQ(4,6,0) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif /* * S T A N D A R D M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if HAVE___BUILTIN_TYPES_COMPATIBLE_P #define dns_same_type(a, b, def) __builtin_types_compatible_p(__typeof__ (a), __typeof__ (b)) #else #define dns_same_type(a, b, def) (def) #endif #define dns_isarray(a) (!dns_same_type((a), (&(a)[0]), 0)) /* NB: "_" field silences Sun Studio "zero-sized struct/union" error diagnostic */ #define dns_inline_assert(cond) ((void)(sizeof (struct { int:-!(cond); int _; }))) #if HAVE___ASSUME #define dns_assume(cond) __assume(cond) #elif HAVE___BUILTIN_UNREACHABLE #define dns_assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) #else #define dns_assume(cond) do { (void)(cond); } while (0) #endif #ifndef lengthof #define lengthof(a) (dns_inline_assert(dns_isarray(a)), (sizeof (a) / sizeof (a)[0])) #endif #ifndef endof #define endof(a) (dns_inline_assert(dns_isarray(a)), &(a)[lengthof((a))]) #endif /* * M I S C E L L A N E O U S C O M P A T * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if _WIN32 || _WIN64 #define PRIuZ "Iu" #else #define PRIuZ "zu" #endif #ifndef DNS_THREAD_SAFE #if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0 #define DNS_THREAD_SAFE 1 #else #define DNS_THREAD_SAFE 0 #endif #endif #ifndef HAVE__STATIC_ASSERT #define HAVE__STATIC_ASSERT \ (dns_has_extension(c_static_assert) || DNS_GNUC_PREREQ(4,6,0) || \ __C11FEATURES__ || __STDC_VERSION__ >= 201112L) #endif #ifndef HAVE_STATIC_ASSERT #if DNS_GNUC_PREREQ(0,0,0) && !DNS_GNUC_PREREQ(4,6,0) #define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */ #else #define HAVE_STATIC_ASSERT (defined static_assert) #endif #endif #if HAVE_STATIC_ASSERT #define dns_static_assert(cond, msg) static_assert(cond, msg) #elif HAVE__STATIC_ASSERT #define dns_static_assert(cond, msg) _Static_assert(cond, msg) #else #define dns_static_assert(cond, msg) extern char DNS_PP_XPASTE(dns_assert_, __LINE__)[sizeof (int[1 - 2*!(cond)])] #endif /* * D E B U G M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int *dns_debug_p(void) { static int debug; return &debug; } /* dns_debug_p() */ #if DNS_DEBUG #undef DNS_DEBUG #define DNS_DEBUG dns_debug #define DNS_SAY_(fmt, ...) \ do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0) #define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n") #define DNS_HAI DNS_SAY("HAI") #define DNS_SHOW_(P, fmt, ...) do { \ if (DNS_DEBUG > 1) { \ fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \ fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \ dns_p_dump((P), stderr); \ fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \ } \ } while (0) #define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "") #else /* !DNS_DEBUG */ #undef DNS_DEBUG #define DNS_DEBUG 0 #define DNS_SAY(...) #define DNS_HAI #define DNS_SHOW(...) #endif /* DNS_DEBUG */ #define DNS_CARP(...) DNS_SAY(__VA_ARGS__) /* * V E R S I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ const char *dns_vendor(void) { return DNS_VENDOR; } /* dns_vendor() */ int dns_v_rel(void) { return DNS_V_REL; } /* dns_v_rel() */ int dns_v_abi(void) { return DNS_V_ABI; } /* dns_v_abi() */ int dns_v_api(void) { return DNS_V_API; } /* dns_v_api() */ /* * E R R O R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if _WIN32 #define DNS_EINTR WSAEINTR #define DNS_EINPROGRESS WSAEINPROGRESS #define DNS_EISCONN WSAEISCONN #define DNS_EWOULDBLOCK WSAEWOULDBLOCK #define DNS_EALREADY WSAEALREADY #define DNS_EAGAIN EAGAIN #define DNS_ETIMEDOUT WSAETIMEDOUT #define dns_syerr() ((int)GetLastError()) #define dns_soerr() ((int)WSAGetLastError()) #else #define DNS_EINTR EINTR #define DNS_EINPROGRESS EINPROGRESS #define DNS_EISCONN EISCONN #define DNS_EWOULDBLOCK EWOULDBLOCK #define DNS_EALREADY EALREADY #define DNS_EAGAIN EAGAIN #define DNS_ETIMEDOUT ETIMEDOUT #define dns_syerr() errno #define dns_soerr() errno #endif const char *dns_strerror(int error) { switch (error) { case DNS_ENOBUFS: return "DNS packet buffer too small"; case DNS_EILLEGAL: return "Illegal DNS RR name or data"; case DNS_EORDER: return "Attempt to push RR out of section order"; case DNS_ESECTION: return "Invalid section specified"; case DNS_EUNKNOWN: return "Unknown DNS error"; case DNS_EADDRESS: return "Invalid textual address form"; case DNS_ENOQUERY: return "Bad execution state (missing query packet)"; case DNS_ENOANSWER: return "Bad execution state (missing answer packet)"; case DNS_EFETCHED: return "Answer already fetched"; case DNS_ESERVICE: return "The service passed was not recognized for the specified socket type"; case DNS_ENONAME: return "The name does not resolve for the supplied parameters"; case DNS_EFAIL: return "A non-recoverable error occurred when attempting to resolve the name"; default: return strerror(error); } /* switch() */ } /* dns_strerror() */ /* * A T O M I C R O U T I N E S * * Use GCC's __atomic built-ins if possible. Unlike the __sync built-ins, we * can use the preprocessor to detect API and, more importantly, ISA * support. We want to avoid linking headaches where the API depends on an * external library if the ISA (e.g. i386) doesn't support lockless * operation. * * TODO: Support C11's atomic API. Although that may require some finesse * with how we define some public types, such as dns_atomic_t and struct * dns_resolv_conf. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef HAVE___ATOMIC_FETCH_ADD #define HAVE___ATOMIC_FETCH_ADD (defined __ATOMIC_RELAXED) #endif #ifndef HAVE___ATOMIC_FETCH_SUB #define HAVE___ATOMIC_FETCH_SUB HAVE___ATOMIC_FETCH_ADD #endif #ifndef DNS_ATOMIC_FETCH_ADD #if HAVE___ATOMIC_FETCH_ADD && __GCC_ATOMIC_LONG_LOCK_FREE == 2 #define DNS_ATOMIC_FETCH_ADD(i) __atomic_fetch_add((i), 1, __ATOMIC_RELAXED) #else #pragma message("no atomic_fetch_add available") #define DNS_ATOMIC_FETCH_ADD(i) ((*(i))++) #endif #endif #ifndef DNS_ATOMIC_FETCH_SUB #if HAVE___ATOMIC_FETCH_SUB && __GCC_ATOMIC_LONG_LOCK_FREE == 2 #define DNS_ATOMIC_FETCH_SUB(i) __atomic_fetch_sub((i), 1, __ATOMIC_RELAXED) #else #pragma message("no atomic_fetch_sub available") #define DNS_ATOMIC_FETCH_SUB(i) ((*(i))--) #endif #endif static inline unsigned dns_atomic_fetch_add(dns_atomic_t *i) { return DNS_ATOMIC_FETCH_ADD(i); } /* dns_atomic_fetch_add() */ static inline unsigned dns_atomic_fetch_sub(dns_atomic_t *i) { return DNS_ATOMIC_FETCH_SUB(i); } /* dns_atomic_fetch_sub() */ /* * C R Y P T O R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * P R N G */ #ifndef DNS_RANDOM #if defined(HAVE_ARC4RANDOM) \ || defined(__OpenBSD__) \ || defined(__FreeBSD__) \ || defined(__NetBSD__) \ || defined(__APPLE__) #define DNS_RANDOM arc4random #elif __linux #define DNS_RANDOM random #else #define DNS_RANDOM rand #endif #endif #define DNS_RANDOM_arc4random 1 #define DNS_RANDOM_random 2 #define DNS_RANDOM_rand 3 #define DNS_RANDOM_RAND_bytes 4 #define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM)) #if DNS_RANDOM_OPENSSL #include #endif static unsigned dns_random_(void) { #if DNS_RANDOM_OPENSSL unsigned r; _Bool ok; ok = (1 == RAND_bytes((unsigned char *)&r, sizeof r)); assert(ok && "1 == RAND_bytes()"); return r; #else return DNS_RANDOM(); #endif } /* dns_random_() */ dns_random_f **dns_random_p(void) { static dns_random_f *random_f = &dns_random_; return &random_f; } /* dns_random_p() */ /* * P E R M U T A T I O N G E N E R A T O R */ #define DNS_K_TEA_KEY_SIZE 16 #define DNS_K_TEA_BLOCK_SIZE 8 #define DNS_K_TEA_CYCLES 32 #define DNS_K_TEA_MAGIC 0x9E3779B9U struct dns_k_tea { uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; unsigned cycles; }; /* struct dns_k_tea */ static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) { memcpy(tea->key, key, sizeof tea->key); tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES; } /* dns_k_tea_init() */ static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) { uint32_t y, z, sum, n; y = v[0]; z = v[1]; sum = 0; for (n = 0; n < tea->cycles; n++) { sum += DNS_K_TEA_MAGIC; y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]); z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]); } w[0] = y; w[1] = z; return /* void */; } /* dns_k_tea_encrypt() */ /* * Permutation generator, based on a Luby-Rackoff Feistel construction. * * Specifically, this is a generic balanced Feistel block cipher using TEA * (another block cipher) as the pseudo-random function, F. At best it's as * strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or * perhaps Bernstein's Salsa20 core; I am naively trying to keep things * simple. * * The generator can create a permutation of any set of numbers, as long as * the size of the set is an even power of 2. This limitation arises either * out of an inherent property of balanced Feistel constructions, or by my * own ignorance. I'll tackle an unbalanced construction after I wrap my * head around Schneier and Kelsey's paper. * * CAVEAT EMPTOR. IANAC. */ #define DNS_K_PERMUTOR_ROUNDS 8 struct dns_k_permutor { unsigned stepi, length, limit; unsigned shift, mask, rounds; struct dns_k_tea tea; }; /* struct dns_k_permutor */ static inline unsigned dns_k_permutor_powof(unsigned n) { unsigned m, i = 0; for (m = 1; m < n; m <<= 1, i++) ;; return i; } /* dns_k_permutor_powof() */ static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) { uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)]; unsigned width, i; p->stepi = 0; p->length = (high - low) + 1; p->limit = high; width = dns_k_permutor_powof(p->length); width += width % 2; p->shift = width / 2; p->mask = (1U << p->shift) - 1; p->rounds = DNS_K_PERMUTOR_ROUNDS; for (i = 0; i < lengthof(key); i++) key[i] = dns_random(); dns_k_tea_init(&p->tea, key, 0); return /* void */; } /* dns_k_permutor_init() */ static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) { uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)]; memset(in, '\0', sizeof in); in[0] = k; in[1] = x; dns_k_tea_encrypt(&p->tea, in, out); return p->mask & out[0]; } /* dns_k_permutor_F() */ static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) { unsigned l[2], r[2]; unsigned i; i = 0; l[i] = p->mask & (n >> p->shift); r[i] = p->mask & (n >> 0); do { l[(i + 1) % 2] = r[i % 2]; r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]); i++; } while (i < p->rounds - 1); return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); } /* dns_k_permutor_E() */ DNS_NOTUSED static unsigned dns_k_permutor_D(struct dns_k_permutor *p, unsigned n) { unsigned l[2], r[2]; unsigned i; i = p->rounds - 1; l[i % 2] = p->mask & (n >> p->shift); r[i % 2] = p->mask & (n >> 0); do { i--; r[i % 2] = l[(i + 1) % 2]; l[i % 2] = r[(i + 1) % 2] ^ dns_k_permutor_F(p, i, l[(i + 1) % 2]); } while (i > 0); return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0); } /* dns_k_permutor_D() */ static unsigned dns_k_permutor_step(struct dns_k_permutor *p) { unsigned n; do { n = dns_k_permutor_E(p, p->stepi++); } while (n >= p->length); return n + (p->limit + 1 - p->length); } /* dns_k_permutor_step() */ /* * Simple permutation box. Useful for shuffling rrsets from an iterator. * Uses AES s-box to provide good diffusion. * * Seems to pass muster under runs test. * * $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done * $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }' * library(lawstat) * runs.test(scan(file="/tmp/out")) * EOF */ static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) { static const unsigned char sbox[256] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; unsigned char a, b; unsigned i; a = 0xff & (n >> 0); b = 0xff & (n >> 8); for (i = 0; i < 4; i++) { a ^= 0xff & s; a = sbox[a] ^ b; b = sbox[b] ^ a; s >>= 8; } return ((0xff00 & (a << 8)) | (0x00ff & (b << 0))); } /* dns_k_shuffle16() */ /* * S T A T E M A C H I N E R O U T I N E S * * Application code should define DNS_SM_RESTORE and DNS_SM_SAVE, and the * local variable pc. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_SM_ENTER \ do { \ static const int pc0 = __LINE__; \ DNS_SM_RESTORE; \ switch (pc0 + pc) { \ case __LINE__: (void)0 #define DNS_SM_SAVE_AND_DO(do_statement) \ do { \ pc = __LINE__ - pc0; \ DNS_SM_SAVE; \ do_statement; \ case __LINE__: (void)0; \ } while (0) #define DNS_SM_YIELD(rv) \ DNS_SM_SAVE_AND_DO(return (rv)) #define DNS_SM_EXIT \ do { goto leave; } while (0) #define DNS_SM_LEAVE \ leave: (void)0; \ DNS_SM_SAVE_AND_DO(break); \ } \ } while (0) /* * U T I L I T Y R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_MAXINTERVAL 300 struct dns_clock { time_t sample, elapsed; }; /* struct dns_clock */ static void dns_begin(struct dns_clock *clk) { clk->sample = time(0); clk->elapsed = 0; } /* dns_begin() */ static time_t dns_elapsed(struct dns_clock *clk) { time_t curtime; if ((time_t)-1 == time(&curtime)) return clk->elapsed; if (curtime > clk->sample) clk->elapsed += (time_t)DNS_PP_MIN(difftime(curtime, clk->sample), DNS_MAXINTERVAL); clk->sample = curtime; return clk->elapsed; } /* dns_elapsed() */ DNS_NOTUSED static size_t dns_strnlen(const char *src, size_t m) { size_t n = 0; while (*src++ && n < m) ++n; return n; } /* dns_strnlen() */ DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, size_t max) { size_t len = dns_strnlen(src, max), n; if (lim > 0) { n = DNS_PP_MIN(lim - 1, len); memcpy(dst, src, n); dst[n] = '\0'; } return len; } /* dns_strnlcpy() */ #define DNS_HAVE_SOCKADDR_UN (defined AF_UNIX && !defined _WIN32) static size_t dns_af_len(int af) { static const size_t table[AF_MAX] = { [AF_INET6] = sizeof (struct sockaddr_in6), [AF_INET] = sizeof (struct sockaddr_in), #if DNS_HAVE_SOCKADDR_UN [AF_UNIX] = sizeof (struct sockaddr_un), #endif }; return table[af]; } /* dns_af_len() */ #define dns_sa_family(sa) (((struct sockaddr *)(sa))->sa_family) #define dns_sa_len(sa) dns_af_len(dns_sa_family(sa)) #define DNS_SA_NOPORT &dns_sa_noport static unsigned short dns_sa_noport; static unsigned short *dns_sa_port(int af, void *sa) { switch (af) { case AF_INET6: return &((struct sockaddr_in6 *)sa)->sin6_port; case AF_INET: return &((struct sockaddr_in *)sa)->sin_port; default: return DNS_SA_NOPORT; } } /* dns_sa_port() */ static void *dns_sa_addr(int af, const void *sa, socklen_t *size) { switch (af) { case AF_INET6: { struct in6_addr *in6 = &((struct sockaddr_in6 *)sa)->sin6_addr; if (size) *size = sizeof *in6; return in6; } case AF_INET: { struct in_addr *in = &((struct sockaddr_in *)sa)->sin_addr; if (size) *size = sizeof *in; return in; } default: if (size) *size = 0; return 0; } } /* dns_sa_addr() */ #if DNS_HAVE_SOCKADDR_UN #define DNS_SUNPATHMAX (sizeof ((struct sockaddr_un *)0)->sun_path) #endif DNS_NOTUSED static void *dns_sa_path(void *sa, socklen_t *size) { switch (dns_sa_family(sa)) { #if DNS_HAVE_SOCKADDR_UN case AF_UNIX: { char *path = ((struct sockaddr_un *)sa)->sun_path; if (size) *size = dns_strnlen(path, DNS_SUNPATHMAX); return path; } #endif default: if (size) *size = 0; return NULL; } } /* dns_sa_path() */ static int dns_sa_cmp(void *a, void *b) { int cmp, af; if ((cmp = dns_sa_family(a) - dns_sa_family(b))) return cmp; switch ((af = dns_sa_family(a))) { case AF_INET: { struct in_addr *a4, *b4; if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) return cmp; a4 = dns_sa_addr(af, a, NULL); b4 = dns_sa_addr(af, b, NULL); if (ntohl(a4->s_addr) < ntohl(b4->s_addr)) return -1; if (ntohl(a4->s_addr) > ntohl(b4->s_addr)) return 1; return 0; } case AF_INET6: { struct in6_addr *a6, *b6; size_t i; if ((cmp = htons(*dns_sa_port(af, a)) - htons(*dns_sa_port(af, b)))) return cmp; a6 = dns_sa_addr(af, a, NULL); b6 = dns_sa_addr(af, b, NULL); /* XXX: do we need to use in6_clearscope()? */ for (i = 0; i < sizeof a6->s6_addr; i++) { if ((cmp = a6->s6_addr[i] - b6->s6_addr[i])) return cmp; } return 0; } #if DNS_HAVE_SOCKADDR_UN case AF_UNIX: { char a_path[DNS_SUNPATHMAX + 1], b_path[sizeof a_path]; dns_strnlcpy(a_path, sizeof a_path, dns_sa_path(a, NULL), DNS_SUNPATHMAX); dns_strnlcpy(b_path, sizeof b_path, dns_sa_path(b, NULL), DNS_SUNPATHMAX); return strcmp(a_path, b_path); } #endif default: return -1; } } /* dns_sa_cmp() */ #if _WIN32 static int dns_inet_pton(int af, const void *src, void *dst) { union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; u.sin.sin_family = af; if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u })) return -1; switch (af) { case AF_INET6: *(struct in6_addr *)dst = u.sin6.sin6_addr; return 1; case AF_INET: *(struct in_addr *)dst = u.sin.sin_addr; return 1; default: return 0; } } /* dns_inet_pton() */ static const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) { union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u; /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */ memset(&u, 0, sizeof u); u.sin.sin_family = af; switch (af) { case AF_INET6: u.sin6.sin6_addr = *(struct in6_addr *)src; break; case AF_INET: u.sin.sin_addr = *(struct in_addr *)src; break; default: return 0; } if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim)) return 0; return dst; } /* dns_inet_ntop() */ #else #define dns_inet_pton(...) inet_pton(__VA_ARGS__) #define dns_inet_ntop(...) inet_ntop(__VA_ARGS__) #endif static dns_error_t dns_pton(int af, const void *src, void *dst) { switch (dns_inet_pton(af, src, dst)) { case 1: return 0; case -1: return dns_soerr(); default: return DNS_EADDRESS; } } /* dns_pton() */ static dns_error_t dns_ntop(int af, const void *src, void *dst, unsigned long lim) { return (dns_inet_ntop(af, src, dst, lim))? 0 : dns_soerr(); } /* dns_ntop() */ size_t dns_strlcpy(char *dst, const char *src, size_t lim) { char *d = dst; char *e = &dst[lim]; const char *s = src; if (d < e) { do { if ('\0' == (*d++ = *s++)) return s - src - 1; } while (d < e); d[-1] = '\0'; } while (*s++ != '\0') ;; return s - src - 1; } /* dns_strlcpy() */ size_t dns_strlcat(char *dst, const char *src, size_t lim) { char *d = memchr(dst, '\0', lim); char *e = &dst[lim]; const char *s = src; const char *p; if (d && d < e) { do { if ('\0' == (*d++ = *s++)) return d - dst - 1; } while (d < e); d[-1] = '\0'; } p = s; while (*s++ != '\0') ;; return lim + (s - p - 1); } /* dns_strlcat() */ #if _WIN32 static char *dns_strsep(char **sp, const char *delim) { char *p; if (!(p = *sp)) return 0; *sp += strcspn(p, delim); if (**sp != '\0') { **sp = '\0'; ++*sp; } else *sp = NULL; return p; } /* dns_strsep() */ #else #define dns_strsep(...) strsep(__VA_ARGS__) #endif #if _WIN32 #define strcasecmp(...) _stricmp(__VA_ARGS__) #define strncasecmp(...) _strnicmp(__VA_ARGS__) #endif static inline _Bool dns_isalpha(unsigned char c) { return isalpha(c); } /* dns_isalpha() */ static inline _Bool dns_isdigit(unsigned char c) { return isdigit(c); } /* dns_isdigit() */ static inline _Bool dns_isalnum(unsigned char c) { return isalnum(c); } /* dns_isalnum() */ static inline _Bool dns_isspace(unsigned char c) { return isspace(c); } /* dns_isspace() */ static int dns_poll(int fd, short events, int timeout) { fd_set rset, wset; if (!events) return 0; assert(fd >= 0 && (unsigned)fd < FD_SETSIZE); FD_ZERO(&rset); FD_ZERO(&wset); if (events & DNS_POLLIN) FD_SET(fd, &rset); if (events & DNS_POLLOUT) FD_SET(fd, &wset); select(fd + 1, &rset, &wset, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : NULL); return 0; } /* dns_poll() */ #if !_WIN32 DNS_NOTUSED static int dns_sigmask(int how, const sigset_t *set, sigset_t *oset) { #if DNS_THREAD_SAFE return pthread_sigmask(how, set, oset); #else return (0 == sigprocmask(how, set, oset))? 0 : errno; #endif } /* dns_sigmask() */ #endif static long dns_send(int fd, const void *src, size_t lim, int flags) { #if _WIN32 || !defined SIGPIPE || defined SO_NOSIGPIPE return send(fd, src, lim, flags); #elif defined MSG_NOSIGNAL return send(fd, src, lim, flags|MSG_NOSIGNAL); #elif _POSIX_REALTIME_SIGNALS > 0 /* require sigtimedwait */ /* * SIGPIPE handling similar to the approach described in * http://krokisplace.blogspot.com/2010/02/suppressing-sigpipe-in-library.html */ sigset_t pending, blocked, piped; long count; int saved, error; sigemptyset(&pending); sigpending(&pending); if (!sigismember(&pending, SIGPIPE)) { sigemptyset(&piped); sigaddset(&piped, SIGPIPE); sigemptyset(&blocked); if ((error = dns_sigmask(SIG_BLOCK, &piped, &blocked))) goto error; } count = send(fd, src, lim, flags); if (!sigismember(&pending, SIGPIPE)) { saved = errno; if (count == -1 && errno == EPIPE) { while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR) ;; } if ((error = dns_sigmask(SIG_SETMASK, &blocked, NULL))) goto error; errno = saved; } return count; error: errno = error; return -1; #else #error "unable to suppress SIGPIPE" return send(fd, src, lim, flags); #endif } /* dns_send() */ #define DNS_FOPEN_STDFLAGS "rwabt+" static dns_error_t dns_fopen_addflag(char *dst, const char *src, size_t lim, int fc) { char *p = dst, *pe = dst + lim; /* copy standard flags */ while (*src && strchr(DNS_FOPEN_STDFLAGS, *src)) { if (!(p < pe)) return ENOMEM; *p++ = *src++; } /* append flag to standard flags */ if (!(p < pe)) return ENOMEM; *p++ = fc; /* copy remaining mode string, including '\0' */ do { if (!(p < pe)) return ENOMEM; } while ((*p++ = *src++)); return 0; } /* dns_fopen_addflag() */ static FILE *dns_fopen(const char *path, const char *mode, dns_error_t *_error) { FILE *fp; char mode_cloexec[32]; int error; assert(path && mode && *mode); if (!*path) { error = EINVAL; goto error; } #if _WIN32 || _WIN64 if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'N'))) goto error; if (!(fp = fopen(path, mode_cloexec))) goto syerr; #else if ((error = dns_fopen_addflag(mode_cloexec, mode, sizeof mode_cloexec, 'e'))) goto error; if (!(fp = fopen(path, mode_cloexec))) { if (errno != EINVAL) goto syerr; if (!(fp = fopen(path, mode))) goto syerr; } #endif return fp; syerr: error = dns_syerr(); error: *_error = error; return NULL; } /* dns_fopen() */ /* * F I X E D - S I Z E D B U F F E R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_B_INIT(src, n) { \ (unsigned char *)(src), \ (unsigned char *)(src), \ (unsigned char *)(src) + (n), \ } #define DNS_B_FROM(src, n) DNS_B_INIT((src), (n)) #define DNS_B_INTO(src, n) DNS_B_INIT((src), (n)) struct dns_buf { const unsigned char *base; unsigned char *p; const unsigned char *pe; dns_error_t error; size_t overflow; }; /* struct dns_buf */ static inline size_t dns_b_tell(struct dns_buf *b) { return b->p - b->base; } static inline dns_error_t dns_b_setoverflow(struct dns_buf *b, size_t n, dns_error_t error) { b->overflow += n; return b->error = error; } DNS_NOTUSED static struct dns_buf * dns_b_into(struct dns_buf *b, void *src, size_t n) { *b = (struct dns_buf)DNS_B_INTO(src, n); return b; } static dns_error_t dns_b_putc(struct dns_buf *b, unsigned char uc) { if (!(b->p < b->pe)) return dns_b_setoverflow(b, 1, DNS_ENOBUFS); *b->p++ = uc; return 0; } static dns_error_t dns_b_pputc(struct dns_buf *b, unsigned char uc, size_t p) { size_t pe = b->pe - b->base; if (pe <= p) return dns_b_setoverflow(b, p - pe + 1, DNS_ENOBUFS); *((unsigned char *)b->base + p) = uc; return 0; } static inline dns_error_t dns_b_put16(struct dns_buf *b, uint16_t u) { return dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); } static inline dns_error_t dns_b_pput16(struct dns_buf *b, uint16_t u, size_t p) { if (dns_b_pputc(b, u >> 8, p) || dns_b_pputc(b, u >> 0, p + 1)) return b->error; return 0; } DNS_NOTUSED static inline dns_error_t dns_b_put32(struct dns_buf *b, uint32_t u) { return dns_b_putc(b, u >> 24), dns_b_putc(b, u >> 16), dns_b_putc(b, u >> 8), dns_b_putc(b, u >> 0); } static dns_error_t dns_b_put(struct dns_buf *b, const void *src, size_t len) { size_t n = DNS_PP_MIN((size_t)(b->pe - b->p), len); memcpy(b->p, src, n); b->p += n; if (n < len) return dns_b_setoverflow(b, len - n, DNS_ENOBUFS); return 0; } static dns_error_t dns_b_puts(struct dns_buf *b, const void *src) { return dns_b_put(b, src, strlen(src)); } DNS_NOTUSED static inline dns_error_t dns_b_fmtju(struct dns_buf *b, const uintmax_t u, const unsigned width) { size_t digits, padding, overflow; uintmax_t r; unsigned char *tp, *te, tc; digits = 0; r = u; do { digits++; r /= 10; } while (r); padding = width - DNS_PP_MIN(digits, width); overflow = (digits + padding) - DNS_PP_MIN((size_t)(b->pe - b->p), (digits + padding)); while (padding--) { dns_b_putc(b, '0'); } digits = 0; tp = b->p; r = u; do { if (overflow < ++digits) dns_b_putc(b, '0' + (r % 10)); r /= 10; } while (r); te = b->p; while (tp < te) { tc = *--te; *te = *tp; *tp++ = tc; } return b->error; } static void dns_b_popc(struct dns_buf *b) { if (b->overflow && !--b->overflow) b->error = 0; if (b->p > b->base) b->p--; } static inline const char * dns_b_tolstring(struct dns_buf *b, size_t *n) { if (b->p < b->pe) { *b->p = '\0'; *n = b->p - b->base; return (const char *)b->base; } else if (b->p > b->base) { if (b->p[-1] != '\0') { dns_b_setoverflow(b, 1, DNS_ENOBUFS); b->p[-1] = '\0'; } *n = &b->p[-1] - b->base; return (const char *)b->base; } else { *n = 0; return ""; } } static inline const char * dns_b_tostring(struct dns_buf *b) { size_t n; return dns_b_tolstring(b, &n); } static inline size_t dns_b_strlen(struct dns_buf *b) { size_t n; dns_b_tolstring(b, &n); return n; } static inline size_t dns_b_strllen(struct dns_buf *b) { size_t n = dns_b_strlen(b); return n + b->overflow; } DNS_NOTUSED static const struct dns_buf * dns_b_from(const struct dns_buf *b, const void *src, size_t n) { *(struct dns_buf *)b = (struct dns_buf)DNS_B_FROM(src, n); return b; } static inline int dns_b_getc(const struct dns_buf *_b, const int eof) { struct dns_buf *b = (struct dns_buf *)_b; if (!(b->p < b->pe)) return dns_b_setoverflow(b, 1, DNS_EILLEGAL), eof; return *b->p++; } static inline intmax_t dns_b_get16(const struct dns_buf *b, const intmax_t eof) { intmax_t n; n = (dns_b_getc(b, 0) << 8); n |= (dns_b_getc(b, 0) << 0); return (!b->overflow)? n : eof; } DNS_NOTUSED static inline intmax_t dns_b_get32(const struct dns_buf *b, const intmax_t eof) { intmax_t n; n = (dns_b_get16(b, 0) << 16); n |= (dns_b_get16(b, 0) << 0); return (!b->overflow)? n : eof; } static inline dns_error_t dns_b_move(struct dns_buf *dst, const struct dns_buf *_src, size_t n) { struct dns_buf *src = (struct dns_buf *)_src; size_t src_n = DNS_PP_MIN((size_t)(src->pe - src->p), n); size_t src_r = n - src_n; dns_b_put(dst, src->p, src_n); src->p += src_n; if (src_r) return dns_b_setoverflow(src, src_r, DNS_EILLEGAL); return dst->error; } /* * P A C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ unsigned dns_p_count(struct dns_packet *P, enum dns_section section) { unsigned count; switch (section) { case DNS_S_QD: return ntohs(dns_header(P)->qdcount); case DNS_S_AN: return ntohs(dns_header(P)->ancount); case DNS_S_NS: return ntohs(dns_header(P)->nscount); case DNS_S_AR: return ntohs(dns_header(P)->arcount); default: count = 0; if (section & DNS_S_QD) count += ntohs(dns_header(P)->qdcount); if (section & DNS_S_AN) count += ntohs(dns_header(P)->ancount); if (section & DNS_S_NS) count += ntohs(dns_header(P)->nscount); if (section & DNS_S_AR) count += ntohs(dns_header(P)->arcount); return count; } } /* dns_p_count() */ struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) { if (!P) return 0; assert(size >= offsetof(struct dns_packet, data) + 12); memset(P, 0, sizeof *P); P->size = size - offsetof(struct dns_packet, data); P->end = 12; memset(P->data, '\0', 12); return P; } /* dns_p_init() */ static struct dns_packet *dns_p_reset(struct dns_packet *P) { return dns_p_init(P, offsetof(struct dns_packet, data) + P->size); } /* dns_p_reset() */ static unsigned short dns_p_qend(struct dns_packet *P) { unsigned short qend = 12; unsigned i, count = dns_p_count(P, DNS_S_QD); for (i = 0; i < count && qend < P->end; i++) { if (P->end == (qend = dns_d_skip(qend, P))) goto invalid; if (P->end - qend < 4) goto invalid; qend += 4; } return DNS_PP_MIN(qend, P->end); invalid: return P->end; } /* dns_p_qend() */ struct dns_packet *dns_p_make(size_t len, int *error) { struct dns_packet *P; size_t size = dns_p_calcsize(len); if (!(P = dns_p_init(malloc(size), size))) *error = dns_syerr(); return P; } /* dns_p_make() */ static void dns_p_free(struct dns_packet *P) { free(P); } /* dns_p_free() */ /* convience routine to free any existing packet before storing new packet */ static struct dns_packet *dns_p_setptr(struct dns_packet **dst, struct dns_packet *src) { dns_p_free(*dst); *dst = src; return src; } /* dns_p_setptr() */ static struct dns_packet *dns_p_movptr(struct dns_packet **dst, struct dns_packet **src) { dns_p_setptr(dst, *src); *src = NULL; return *dst; } /* dns_p_movptr() */ int dns_p_grow(struct dns_packet **P) { struct dns_packet *tmp; size_t size; int error; if (!*P) { if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error))) return error; return 0; } size = dns_p_sizeof(*P); size |= size >> 1; size |= size >> 2; size |= size >> 4; size |= size >> 8; size++; if (size > 65536) return DNS_ENOBUFS; if (!(tmp = realloc(*P, dns_p_calcsize(size)))) return dns_syerr(); tmp->size = size; *P = tmp; return 0; } /* dns_p_grow() */ struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) { if (!P) return 0; P->end = DNS_PP_MIN(P->size, P0->end); memcpy(P->data, P0->data, P->end); return P; } /* dns_p_copy() */ struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) { size_t bufsiz = DNS_PP_MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0)); struct dns_packet *M; enum dns_section section; struct dns_rr rr, mr; int error, copy; if (!A && B) { A = B; Amask = Bmask; B = 0; } merge: if (!(M = dns_p_make(bufsiz, &error))) goto error; for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) { if (A && (section & Amask)) { dns_rr_foreach(&rr, A, .section = section) { if ((error = dns_rr_copy(M, &rr, A))) goto error; } } if (B && (section & Bmask)) { dns_rr_foreach(&rr, B, .section = section) { copy = 1; dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) { if (!(copy = dns_rr_cmp(&rr, B, &mr, M))) break; } if (copy && (error = dns_rr_copy(M, &rr, B))) goto error; } } } return M; error: dns_p_setptr(&M, NULL); if (error == DNS_ENOBUFS && bufsiz < 65535) { bufsiz = DNS_PP_MIN(65535, bufsiz * 2); goto merge; } *error_ = error; return 0; } /* dns_p_merge() */ static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t); void dns_p_dictadd(struct dns_packet *P, unsigned short dn) { unsigned short lp, lptr, i; lp = dn; while (lp < P->end) { if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) { lptr = ((0x3f & P->data[lp + 0]) << 8) | ((0xff & P->data[lp + 1]) << 0); for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { if (P->dict[i] == lptr) { P->dict[i] = dn; return; } } } lp = dns_l_skip(lp, P->data, P->end); } for (i = 0; i < lengthof(P->dict); i++) { if (!P->dict[i]) { P->dict[i] = dn; break; } } } /* dns_p_dictadd() */ int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) { size_t end = P->end; int error; if ((error = dns_d_push(P, dn, dnlen))) goto error; if (P->size - P->end < 4) goto nobufs; P->data[P->end++] = 0xff & (type >> 8); P->data[P->end++] = 0xff & (type >> 0); P->data[P->end++] = 0xff & (class >> 8); P->data[P->end++] = 0xff & (class >> 0); if (section == DNS_S_QD) goto update; if (P->size - P->end < 6) goto nobufs; if (type != DNS_T_OPT) ttl = DNS_PP_MIN(ttl, 0x7fffffffU); P->data[P->end++] = ttl >> 24; P->data[P->end++] = ttl >> 16; P->data[P->end++] = ttl >> 8; P->data[P->end++] = ttl >> 0; if ((error = dns_any_push(P, (union dns_any *)any, type))) goto error; update: switch (section) { case DNS_S_QD: if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR)) goto order; if (!P->memo.qd.base && (error = dns_p_study(P))) goto error; dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1); P->memo.qd.end = P->end; P->memo.an.base = P->end; P->memo.an.end = P->end; P->memo.ns.base = P->end; P->memo.ns.end = P->end; P->memo.ar.base = P->end; P->memo.ar.end = P->end; break; case DNS_S_AN: if (dns_p_count(P, DNS_S_NS|DNS_S_AR)) goto order; if (!P->memo.an.base && (error = dns_p_study(P))) goto error; dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1); P->memo.an.end = P->end; P->memo.ns.base = P->end; P->memo.ns.end = P->end; P->memo.ar.base = P->end; P->memo.ar.end = P->end; break; case DNS_S_NS: if (dns_p_count(P, DNS_S_AR)) goto order; if (!P->memo.ns.base && (error = dns_p_study(P))) goto error; dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1); P->memo.ns.end = P->end; P->memo.ar.base = P->end; P->memo.ar.end = P->end; break; case DNS_S_AR: if (!P->memo.ar.base && (error = dns_p_study(P))) goto error; dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1); P->memo.ar.end = P->end; if (type == DNS_T_OPT && !P->memo.opt.p) { P->memo.opt.p = end; P->memo.opt.maxudp = class; P->memo.opt.ttl = ttl; } break; default: error = DNS_ESECTION; goto error; } /* switch() */ return 0; nobufs: error = DNS_ENOBUFS; goto error; order: error = DNS_EORDER; goto error; error: P->end = end; return error; } /* dns_p_push() */ static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) { enum dns_section section; struct dns_rr rr; int error; union dns_any any; char pretty[sizeof any * 2]; size_t len; fputs(";; [HEADER]\n", fp); fprintf(fp, ";; qid : %d\n", ntohs(dns_header(P)->qid)); fprintf(fp, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); fprintf(fp, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); fprintf(fp, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); fprintf(fp, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); fprintf(fp, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); fprintf(fp, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); fprintf(fp, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); section = 0; while (dns_rr_grep(&rr, 1, I, P, &error)) { if (section != rr.section) fprintf(fp, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) fprintf(fp, "%s\n", pretty); section = rr.section; } } /* dns_p_dump3() */ void dns_p_dump(struct dns_packet *P, FILE *fp) { dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp); } /* dns_p_dump() */ static void dns_s_unstudy(struct dns_s_memo *m) { m->base = 0; m->end = 0; } static void dns_m_unstudy(struct dns_p_memo *m) { dns_s_unstudy(&m->qd); dns_s_unstudy(&m->an); dns_s_unstudy(&m->ns); dns_s_unstudy(&m->ar); m->opt.p = 0; m->opt.maxudp = 0; m->opt.ttl = 0; } /* dns_m_unstudy() */ static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned short base, struct dns_packet *P) { unsigned short count, rp; count = dns_p_count(P, section); for (rp = base; count && rp < P->end; count--) rp = dns_rr_skip(rp, P); m->base = base; m->end = rp; return 0; } /* dns_s_study() */ static int dns_m_study(struct dns_p_memo *m, struct dns_packet *P) { struct dns_rr rr; int error; if ((error = dns_s_study(&m->qd, DNS_S_QD, 12, P))) goto error; if ((error = dns_s_study(&m->an, DNS_S_AN, m->qd.end, P))) goto error; if ((error = dns_s_study(&m->ns, DNS_S_NS, m->an.end, P))) goto error; if ((error = dns_s_study(&m->ar, DNS_S_AR, m->ns.end, P))) goto error; m->opt.p = 0; m->opt.maxudp = 0; m->opt.ttl = 0; dns_rr_foreach(&rr, P, .type = DNS_T_OPT, .section = DNS_S_AR) { m->opt.p = rr.dn.p; m->opt.maxudp = rr.class; m->opt.ttl = rr.ttl; break; } return 0; error: dns_m_unstudy(m); return error; } /* dns_m_study() */ int dns_p_study(struct dns_packet *P) { return dns_m_study(&P->memo, P); } /* dns_p_study() */ enum dns_rcode dns_p_rcode(struct dns_packet *P) { return 0xfff & ((P->memo.opt.ttl >> 20) | dns_header(P)->rcode); } /* dns_p_rcode() */ /* * Q U E R Y P A C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_Q_RD 0x1 /* recursion desired */ #define DNS_Q_EDNS0 0x2 /* include OPT RR */ static dns_error_t dns_q_make2(struct dns_packet **_Q, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass, int qflags) { struct dns_packet *Q = NULL; int error; if (dns_p_movptr(&Q, _Q)) { dns_p_reset(Q); } else if (!(Q = dns_p_make(DNS_P_QBUFSIZ, &error))) { goto error; } if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, qtype, qclass, 0, 0))) goto error; dns_header(Q)->rd = !!(qflags & DNS_Q_RD); if (qflags & DNS_Q_EDNS0) { struct dns_opt opt = DNS_OPT_INIT(&opt); opt.version = 0; /* RFC 6891 version */ opt.maxudp = 4096; if ((error = dns_p_push(Q, DNS_S_AR, ".", 1, DNS_T_OPT, dns_opt_class(&opt), dns_opt_ttl(&opt), &opt))) goto error; } *_Q = Q; return 0; error: dns_p_free(Q); return error; } static dns_error_t dns_q_make(struct dns_packet **Q, const char *qname, enum dns_type qtype, enum dns_class qclass, int qflags) { return dns_q_make2(Q, qname, strlen(qname), qtype, qclass, qflags); } static dns_error_t dns_q_remake(struct dns_packet **Q, int qflags) { char qname[DNS_D_MAXNAME + 1]; size_t qlen; struct dns_rr rr; int error; assert(Q && *Q); if ((error = dns_rr_parse(&rr, 12, *Q))) return error; if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, *Q, &error))) return error; if (qlen >= sizeof qname) return DNS_EILLEGAL; return dns_q_make2(Q, qname, qlen, rr.type, rr.class, qflags); } /* * D O M A I N N A M E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DNS_D_MAXPTRS #define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */ #endif static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) { unsigned short len; unsigned nptrs = 0; retry: if (src >= end) goto invalid; switch (0x03 & (data[src] >> 6)) { case 0x00: len = (0x3f & (data[src++])); if (end - src < len) goto invalid; if (lim > 0) { memcpy(dst, &data[src], DNS_PP_MIN(lim, len)); dst[DNS_PP_MIN(lim - 1, len)] = '\0'; } *nxt = src + len; return len; case 0x01: goto invalid; case 0x02: goto invalid; case 0x03: if (++nptrs > DNS_D_MAXPTRS) goto invalid; if (end - src < 2) goto invalid; src = ((0x3f & data[src + 0]) << 8) | ((0xff & data[src + 1]) << 0); goto retry; } /* switch() */ /* NOT REACHED */ invalid: *nxt = end; return 0; } /* dns_l_expand() */ static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) { unsigned short len; if (src >= end) goto invalid; switch (0x03 & (data[src] >> 6)) { case 0x00: len = (0x3f & (data[src++])); if (end - src < len) goto invalid; return (len)? src + len : end; case 0x01: goto invalid; case 0x02: goto invalid; case 0x03: return end; } /* switch() */ /* NOT REACHED */ invalid: return end; } /* dns_l_skip() */ static _Bool dns_d_isanchored(const void *_src, size_t len) { const unsigned char *src = _src; return len > 0 && src[len - 1] == '.'; } /* dns_d_isanchored() */ static size_t dns_d_ndots(const void *_src, size_t len) { const unsigned char *p = _src, *pe = p + len; size_t ndots = 0; while ((p = memchr(p, '.', pe - p))) { ndots++; p++; } return ndots; } /* dns_d_ndots() */ static size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) { unsigned char *dst = dst_; const unsigned char *src = src_; size_t dp = 0, sp = 0; int lc; /* trim any leading dot(s) */ while (sp < len && src[sp] == '.') sp++; for (lc = 0; sp < len; lc = src[sp++]) { /* trim extra dot(s) */ if (src[sp] == '.' && lc == '.') continue; if (dp < lim) dst[dp] = src[sp]; dp++; } if ((flags & DNS_D_ANCHOR) && lc != '.') { if (dp < lim) dst[dp] = '.'; dp++; } if (lim > 0) dst[DNS_PP_MIN(dp, lim - 1)] = '\0'; return dp; } /* dns_d_trim() */ char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) { if (flags & DNS_D_TRIM) { dns_d_trim(dst, lim, src, len, flags); } if (flags & DNS_D_ANCHOR) { dns_d_anchor(dst, lim, src, len); } else { memmove(dst, src, DNS_PP_MIN(lim, len)); if (lim > 0) ((char *)dst)[DNS_PP_MIN(len, lim - 1)] = '\0'; } return dst; } /* dns_d_init() */ size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) { if (len == 0) return 0; memmove(dst, src, DNS_PP_MIN(lim, len)); if (((const char *)src)[len - 1] != '.') { if (len < lim) ((char *)dst)[len] = '.'; len++; } if (lim > 0) ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; return len; } /* dns_d_anchor() */ size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) { const char *dot; /* XXX: Skip any leading dot. Handles cleaving root ".". */ if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1))) return 0; len -= dot - (const char *)src; /* XXX: Unless root, skip the label's trailing dot. */ if (len > 1) { src = ++dot; len--; } else src = dot; memmove(dst, src, DNS_PP_MIN(lim, len)); if (lim > 0) ((char *)dst)[DNS_PP_MIN(lim - 1, len)] = '\0'; return len; } /* dns_d_cleave() */ size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error) { struct { unsigned char *b; size_t p, x; } dst, src; unsigned char ch = '.'; dst.b = dst_; dst.p = 0; dst.x = 1; src.b = (unsigned char *)src_; src.p = 0; src.x = 0; while (src.x < len) { ch = src.b[src.x]; if (ch == '.') { if (dst.p < lim) dst.b[dst.p] = (0x3f & (src.x - src.p)); dst.p = dst.x++; src.p = ++src.x; } else { if (dst.x < lim) dst.b[dst.x] = ch; dst.x++; src.x++; } } /* while() */ if (src.x > src.p) { if (dst.p < lim) dst.b[dst.p] = (0x3f & (src.x - src.p)); dst.p = dst.x; } if (dst.p > 1) { if (dst.p < lim) dst.b[dst.p] = 0x00; dst.p++; } #if 1 if (dst.p < lim) { struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b; unsigned i; a.p = 0; while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) { for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) { b.p = P->dict[i]; while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) { a.y = a.x; b.y = b.x; while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) { a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim); b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end); } if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) { dst.b[a.p++] = 0xc0 | (0x3f & (b.p >> 8)); dst.b[a.p++] = (0xff & (b.p >> 0)); /* silence static analyzers */ dns_assume(a.p > 0); return a.p; } b.p = b.x; } /* while() */ } /* for() */ a.p = a.x; } /* while() */ } /* if () */ #endif if (!dst.p) *error = DNS_EILLEGAL; return dst.p; } /* dns_d_comp() */ unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) { unsigned short len; while (src < P->end) { switch (0x03 & (P->data[src] >> 6)) { case 0x00: /* FOLLOWS */ len = (0x3f & P->data[src++]); if (0 == len) { /* success ==> */ return src; } else if (P->end - src > len) { src += len; break; } else goto invalid; /* NOT REACHED */ case 0x01: /* RESERVED */ goto invalid; case 0x02: /* RESERVED */ goto invalid; case 0x03: /* POINTER */ if (P->end - src < 2) goto invalid; src += 2; /* success ==> */ return src; } /* switch() */ } /* while() */ invalid: return P->end; } /* dns_d_skip() */ #include size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) { size_t dstp = 0; unsigned nptrs = 0; unsigned char len; while (src < P->end) { switch ((0x03 & (P->data[src] >> 6))) { case 0x00: /* FOLLOWS */ len = (0x3f & P->data[src]); if (0 == len) { if (dstp == 0) { if (dstp < lim) ((unsigned char *)dst)[dstp] = '.'; dstp++; } /* NUL terminate */ if (lim > 0) ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; /* success ==> */ return dstp; } src++; if (P->end - src < len) goto toolong; if (dstp < lim) memcpy(&((unsigned char *)dst)[dstp], &P->data[src], DNS_PP_MIN(len, lim - dstp)); src += len; dstp += len; if (dstp < lim) ((unsigned char *)dst)[dstp] = '.'; dstp++; nptrs = 0; continue; case 0x01: /* RESERVED */ goto reserved; case 0x02: /* RESERVED */ goto reserved; case 0x03: /* POINTER */ if (++nptrs > DNS_D_MAXPTRS) goto toolong; if (P->end - src < 2) goto toolong; src = ((0x3f & P->data[src + 0]) << 8) | ((0xff & P->data[src + 1]) << 0); continue; } /* switch() */ } /* while() */ toolong: *error = DNS_EILLEGAL; if (lim > 0) ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; return 0; reserved: *error = DNS_EILLEGAL; if (lim > 0) ((unsigned char *)dst)[DNS_PP_MIN(dstp, lim - 1)] = '\0'; return 0; } /* dns_d_expand() */ int dns_d_push(struct dns_packet *P, const void *dn, size_t len) { size_t lim = P->size - P->end; unsigned dp = P->end; int error = DNS_EILLEGAL; /* silence compiler */ len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error); if (len == 0) return error; if (len > lim) return DNS_ENOBUFS; P->end += len; dns_p_dictadd(P, dp); return 0; } /* dns_d_push() */ size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) { char host[DNS_D_MAXNAME + 1]; struct dns_rr_i i; struct dns_rr rr; unsigned depth; int error; if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len)) { error = ENAMETOOLONG; goto error; } for (depth = 0; depth < 7; depth++) { dns_rr_i_init(memset(&i, 0, sizeof i), P); i.section = DNS_S_ALL & ~DNS_S_QD; i.name = host; i.type = DNS_T_CNAME; if (!dns_rr_grep(&rr, 1, &i, P, &error)) break; if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P))) goto error; } return dns_strlcpy(dst, host, lim); error: *error_ = error; return 0; } /* dns_d_cname() */ /* * R E S O U R C E R E C O R D R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) { unsigned char dn[DNS_D_MAXNAME + 1]; union dns_any any; size_t len; int error; if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error))) return error; else if (len >= sizeof dn) return DNS_EILLEGAL; if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q))) return error; return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any); } /* dns_rr_copy() */ int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) { unsigned short p = src; if (src >= P->end) goto invalid; rr->dn.p = p; rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p; if (P->end - p < 4) goto invalid; rr->type = ((0xff & P->data[p + 0]) << 8) | ((0xff & P->data[p + 1]) << 0); rr->class = ((0xff & P->data[p + 2]) << 8) | ((0xff & P->data[p + 3]) << 0); p += 4; if (src < dns_p_qend(P)) { rr->section = DNS_S_QUESTION; rr->ttl = 0; rr->rd.p = 0; rr->rd.len = 0; return 0; } if (P->end - p < 4) goto invalid; rr->ttl = ((0xff & P->data[p + 0]) << 24) | ((0xff & P->data[p + 1]) << 16) | ((0xff & P->data[p + 2]) << 8) | ((0xff & P->data[p + 3]) << 0); if (rr->type != DNS_T_OPT) rr->ttl = DNS_PP_MIN(rr->ttl, 0x7fffffffU); p += 4; if (P->end - p < 2) goto invalid; rr->rd.len = ((0xff & P->data[p + 0]) << 8) | ((0xff & P->data[p + 1]) << 0); rr->rd.p = p + 2; p += 2; if (P->end - p < rr->rd.len) goto invalid; return 0; invalid: return DNS_EILLEGAL; } /* dns_rr_parse() */ static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) { unsigned short rp, rdlen; rp = dns_d_skip(src, P); if (P->end - rp < 4) return P->end - src; rp += 4; /* TYPE, CLASS */ if (rp <= dns_p_qend(P)) return rp - src; if (P->end - rp < 6) return P->end - src; rp += 6; /* TTL, RDLEN */ rdlen = ((0xff & P->data[rp - 2]) << 8) | ((0xff & P->data[rp - 1]) << 0); if (P->end - rp < rdlen) return P->end - src; rp += rdlen; return rp - src; } /* dns_rr_len() */ unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) { return src + dns_rr_len(src, P); } /* dns_rr_skip() */ static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) { enum dns_section section; unsigned count, index; unsigned short rp; if (src >= P->memo.qd.base && src < P->memo.qd.end) return DNS_S_QD; if (src >= P->memo.an.base && src < P->memo.an.end) return DNS_S_AN; if (src >= P->memo.ns.base && src < P->memo.ns.end) return DNS_S_NS; if (src >= P->memo.ar.base && src < P->memo.ar.end) return DNS_S_AR; /* NOTE: Possibly bad memoization. Try it the hard-way. */ for (rp = 12, index = 0; rp < src && rp < P->end; index++) rp = dns_rr_skip(rp, P); section = DNS_S_QD; count = dns_p_count(P, section); while (index >= count && section <= DNS_S_AR) { section <<= 1; count += dns_p_count(P, section); } return DNS_S_ALL & section; } /* dns_rr_section() */ static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) { struct dns_rr rr; int error; if ((error = dns_rr_parse(&rr, src, P))) return 0; return rr.type; } /* dns_rr_type() */ int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) { char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1]; union dns_any any0, any1; int cmp, error; size_t len; if ((cmp = r0->type - r1->type)) return cmp; if ((cmp = r0->class - r1->class)) return cmp; /* * FIXME: Do label-by-label comparison to handle illegally long names? */ if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error)) || len >= sizeof host0) return -1; if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error)) || len >= sizeof host1) return 1; if ((cmp = strcasecmp(host0, host1))) return cmp; if (DNS_S_QD & (r0->section | r1->section)) { if (r0->section == r1->section) return 0; return (r0->section == DNS_S_QD)? -1 : 1; } if ((error = dns_any_parse(&any0, r0, P0))) return -1; if ((error = dns_any_parse(&any1, r1, P1))) return 1; return dns_any_cmp(&any0, r0->type, &any1, r1->type); } /* dns_rr_cmp() */ static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) { struct dns_rr rr1; dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) { if (0 == dns_rr_cmp(rr0, P0, &rr1, P1)) return 1; } return 0; } /* dns_rr_exists() */ static unsigned short dns_rr_offset(struct dns_rr *rr) { return rr->dn.p; } /* dns_rr_offset() */ static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) { if (i->section && !(rr->section & i->section)) return 0; if (i->type && rr->type != i->type && i->type != DNS_T_ALL) return 0; if (i->class && rr->class != i->class && i->class != DNS_C_ANY) return 0; if (i->name) { char dn[DNS_D_MAXNAME + 1]; size_t len; int error; if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error)) || len >= sizeof dn) return 0; if (0 != strcasecmp(dn, i->name)) return 0; } if (i->data && i->type && rr->section > DNS_S_QD) { union dns_any rd; int error; if ((error = dns_any_parse(&rd, rr, P))) return 0; if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type)) return 0; } return 1; } /* dns_rr_i_match() */ static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) { unsigned short rp; struct dns_rr r0, rr; int error; if ((i->section & DNS_S_QD) && P->memo.qd.base) rp = P->memo.qd.base; else if ((i->section & DNS_S_AN) && P->memo.an.base) rp = P->memo.an.base; else if ((i->section & DNS_S_NS) && P->memo.ns.base) rp = P->memo.ns.base; else if ((i->section & DNS_S_AR) && P->memo.ar.base) rp = P->memo.ar.base; else rp = 12; for (; rp < P->end; rp = dns_rr_skip(rp, P)) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; r0 = rr; goto lower; } return P->end; lower: if (i->sort == &dns_rr_i_packet) return dns_rr_offset(&r0); while ((rp = dns_rr_skip(rp, P)) < P->end) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; if (i->sort(&rr, &r0, i, P) < 0) r0 = rr; } return dns_rr_offset(&r0); } /* dns_rr_i_start() */ static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) { struct dns_rr r0, r1, rr; int error; if ((error = dns_rr_parse(&r0, rp, P))) return P->end; r0.section = dns_rr_section(rp, P); rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12; for (; rp < P->end; rp = dns_rr_skip(rp, P)) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; if (i->sort(&rr, &r0, i, P) <= 0) continue; r1 = rr; goto lower; } return P->end; lower: if (i->sort == &dns_rr_i_packet) return dns_rr_offset(&r1); while ((rp = dns_rr_skip(rp, P)) < P->end) { if ((error = dns_rr_parse(&rr, rp, P))) continue; rr.section = dns_rr_section(rp, P); if (!dns_rr_i_match(&rr, i, P)) continue; if (i->sort(&rr, &r0, i, P) <= 0) continue; if (i->sort(&rr, &r1, i, P) >= 0) continue; r1 = rr; } return dns_rr_offset(&r1); } /* dns_rr_i_skip() */ int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { (void)i; (void)P; return (int)a->dn.p - (int)b->dn.p; } /* dns_rr_i_packet() */ int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { int cmp; (void)i; if ((cmp = a->section - b->section)) return cmp; if (a->type != b->type) return (int)a->dn.p - (int)b->dn.p; return dns_rr_cmp(a, P, b, P); } /* dns_rr_i_order() */ int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { int cmp; (void)i; (void)P; while (!i->state.regs[0]) i->state.regs[0] = dns_random(); if ((cmp = a->section - b->section)) return cmp; return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]); } /* dns_rr_i_shuffle() */ struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P) { static const struct dns_rr_i i_initializer; (void)P; i->state = i_initializer.state; i->saved = i->state; return i; } /* dns_rr_i_init() */ unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) { unsigned count = 0; int error; switch (i->state.exec) { case 0: if (!i->sort) i->sort = &dns_rr_i_packet; i->state.next = dns_rr_i_start(i, P); i->state.exec++; /* FALL THROUGH */ case 1: while (count < lim && i->state.next < P->end) { if ((error = dns_rr_parse(rr, i->state.next, P))) goto error; rr->section = dns_rr_section(i->state.next, P); rr++; count++; i->state.count++; i->state.next = dns_rr_i_skip(i->state.next, i, P); } /* while() */ break; } /* switch() */ return count; error: *error_ = error; return count; } /* dns_rr_grep() */ size_t dns_rr_print(void *_dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *_error) { struct dns_buf dst = DNS_B_INTO(_dst, lim); union dns_any any; size_t n; int error; if (rr->section == DNS_S_QD) dns_b_putc(&dst, ';'); if (!(n = dns_d_expand(any.ns.host, sizeof any.ns.host, rr->dn.p, P, &error))) goto error; dns_b_put(&dst, any.ns.host, DNS_PP_MIN(n, sizeof any.ns.host - 1)); if (rr->section != DNS_S_QD) { dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, rr->ttl, 0); } dns_b_putc(&dst, ' '); dns_b_puts(&dst, dns_strclass(rr->class)); dns_b_putc(&dst, ' '); dns_b_puts(&dst, dns_strtype(rr->type)); if (rr->section == DNS_S_QD) goto epilog; dns_b_putc(&dst, ' '); if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P))) goto error; n = dns_any_print(dst.p, dst.pe - dst.p, &any, rr->type); dst.p += DNS_PP_MIN(n, (size_t)(dst.pe - dst.p)); epilog: return dns_b_strllen(&dst); error: *_error = error; return 0; } /* dns_rr_print() */ int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) { unsigned long addr; if (rr->rd.len != 4) return DNS_EILLEGAL; addr = ((0xffU & P->data[rr->rd.p + 0]) << 24) | ((0xffU & P->data[rr->rd.p + 1]) << 16) | ((0xffU & P->data[rr->rd.p + 2]) << 8) | ((0xffU & P->data[rr->rd.p + 3]) << 0); a->addr.s_addr = htonl(addr); return 0; } /* dns_a_parse() */ int dns_a_push(struct dns_packet *P, struct dns_a *a) { unsigned long addr; if (P->size - P->end < 6) return DNS_ENOBUFS; P->data[P->end++] = 0x00; P->data[P->end++] = 0x04; addr = ntohl(a->addr.s_addr); P->data[P->end++] = 0xffU & (addr >> 24); P->data[P->end++] = 0xffU & (addr >> 16); P->data[P->end++] = 0xffU & (addr >> 8); P->data[P->end++] = 0xffU & (addr >> 0); return 0; } /* dns_a_push() */ size_t dns_a_arpa(void *_dst, size_t lim, const struct dns_a *a) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned long octets = ntohl(a->addr.s_addr); unsigned i; for (i = 0; i < 4; i++) { dns_b_fmtju(&dst, 0xff & octets, 0); dns_b_putc(&dst, '.'); octets >>= 8; } dns_b_puts(&dst, "in-addr.arpa."); return dns_b_strllen(&dst); } /* dns_a_arpa() */ int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) { if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr)) return -1; if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr)) return 1; return 0; } /* dns_a_cmp() */ size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) { char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0"; dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr); return dns_strlcpy(dst, addr, lim); } /* dns_a_print() */ int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) { if (rr->rd.len != sizeof aaaa->addr.s6_addr) return DNS_EILLEGAL; memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr); return 0; } /* dns_aaaa_parse() */ int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) { if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr) return DNS_ENOBUFS; P->data[P->end++] = 0x00; P->data[P->end++] = 0x10; memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr); P->end += sizeof aaaa->addr.s6_addr; return 0; } /* dns_aaaa_push() */ int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) { unsigned i; int cmp; for (i = 0; i < lengthof(a->addr.s6_addr); i++) { if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i]))) return cmp; } return 0; } /* dns_aaaa_cmp() */ size_t dns_aaaa_arpa(void *_dst, size_t lim, const struct dns_aaaa *aaaa) { static const unsigned char hex[16] = "0123456789abcdef"; struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned nyble; int i, j; for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) { nyble = aaaa->addr.s6_addr[i]; for (j = 0; j < 2; j++) { dns_b_putc(&dst, hex[0x0f & nyble]); dns_b_putc(&dst, '.'); nyble >>= 4; } } dns_b_puts(&dst, "ip6.arpa."); return dns_b_strllen(&dst); } /* dns_aaaa_arpa() */ size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) { char addr[INET6_ADDRSTRLEN + 1] = "::"; dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr); return dns_strlcpy(dst, addr, lim); } /* dns_aaaa_print() */ int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) { size_t len; int error; if (rr->rd.len < 3) return DNS_EILLEGAL; mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8)) | (0x00ff & (P->data[rr->rd.p + 1] << 0)); if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error))) return error; else if (len >= sizeof mx->host) return DNS_EILLEGAL; return 0; } /* dns_mx_parse() */ int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) { size_t end, len; int error; if (P->size - P->end < 5) return DNS_ENOBUFS; end = P->end; P->end += 2; P->data[P->end++] = 0xff & (mx->preference >> 8); P->data[P->end++] = 0xff & (mx->preference >> 0); if ((error = dns_d_push(P, mx->host, strlen(mx->host)))) goto error; len = P->end - end - 2; P->data[end + 0] = 0xff & (len >> 8); P->data[end + 1] = 0xff & (len >> 0); return 0; error: P->end = end; return error; } /* dns_mx_push() */ int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) { int cmp; if ((cmp = a->preference - b->preference)) return cmp; return strcasecmp(a->host, b->host); } /* dns_mx_cmp() */ size_t dns_mx_print(void *_dst, size_t lim, struct dns_mx *mx) { struct dns_buf dst = DNS_B_INTO(_dst, lim); dns_b_fmtju(&dst, mx->preference, 0); dns_b_putc(&dst, ' '); dns_b_puts(&dst, mx->host); return dns_b_strllen(&dst); } /* dns_mx_print() */ size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) { return dns_strlcpy(dst, mx->host, lim); } /* dns_mx_cname() */ int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) { size_t len; int error; if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error))) return error; else if (len >= sizeof ns->host) return DNS_EILLEGAL; return 0; } /* dns_ns_parse() */ int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) { size_t end, len; int error; if (P->size - P->end < 3) return DNS_ENOBUFS; end = P->end; P->end += 2; if ((error = dns_d_push(P, ns->host, strlen(ns->host)))) goto error; len = P->end - end - 2; P->data[end + 0] = 0xff & (len >> 8); P->data[end + 1] = 0xff & (len >> 0); return 0; error: P->end = end; return error; } /* dns_ns_push() */ int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) { return strcasecmp(a->host, b->host); } /* dns_ns_cmp() */ size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) { return dns_strlcpy(dst, ns->host, lim); } /* dns_ns_print() */ size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) { return dns_strlcpy(dst, ns->host, lim); } /* dns_ns_cname() */ int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) { return dns_ns_parse((struct dns_ns *)cname, rr, P); } /* dns_cname_parse() */ int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) { return dns_ns_push(P, (struct dns_ns *)cname); } /* dns_cname_push() */ int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) { return strcasecmp(a->host, b->host); } /* dns_cname_cmp() */ size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) { return dns_ns_print(dst, lim, (struct dns_ns *)cname); } /* dns_cname_print() */ size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) { return dns_strlcpy(dst, cname->host, lim); } /* dns_cname_cname() */ int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) { struct { void *dst; size_t lim; } dn[] = { { soa->mname, sizeof soa->mname }, { soa->rname, sizeof soa->rname } }; unsigned *ts[] = { &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum }; unsigned short rp; unsigned i, j, n; int error; /* MNAME / RNAME */ if ((rp = rr->rd.p) >= P->end) return DNS_EILLEGAL; for (i = 0; i < lengthof(dn); i++) { if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error))) return error; else if (n >= dn[i].lim) return DNS_EILLEGAL; if ((rp = dns_d_skip(rp, P)) >= P->end) return DNS_EILLEGAL; } /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ for (i = 0; i < lengthof(ts); i++) { for (j = 0; j < 4; j++, rp++) { if (rp >= P->end) return DNS_EILLEGAL; *ts[i] <<= 8; *ts[i] |= (0xff & P->data[rp]); } } return 0; } /* dns_soa_parse() */ int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) { void *dn[] = { soa->mname, soa->rname }; unsigned ts[] = { (0xffffffff & soa->serial), (0x7fffffff & soa->refresh), (0x7fffffff & soa->retry), (0x7fffffff & soa->expire), (0xffffffff & soa->minimum) }; unsigned i, j; size_t end, len; int error; end = P->end; if ((P->end += 2) >= P->size) goto toolong; /* MNAME / RNAME */ for (i = 0; i < lengthof(dn); i++) { if ((error = dns_d_push(P, dn[i], strlen(dn[i])))) goto error; } /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */ for (i = 0; i < lengthof(ts); i++) { if ((P->end += 4) >= P->size) goto toolong; for (j = 1; j <= 4; j++) { P->data[P->end - j] = (0xff & ts[i]); ts[i] >>= 8; } } len = P->end - end - 2; P->data[end + 0] = (0xff & (len >> 8)); P->data[end + 1] = (0xff & (len >> 0)); return 0; toolong: error = DNS_ENOBUFS; /* FALL THROUGH */ error: P->end = end; return error; } /* dns_soa_push() */ int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) { int cmp; if ((cmp = strcasecmp(a->mname, b->mname))) return cmp; if ((cmp = strcasecmp(a->rname, b->rname))) return cmp; if (a->serial > b->serial) return -1; else if (a->serial < b->serial) return 1; if (a->refresh > b->refresh) return -1; else if (a->refresh < b->refresh) return 1; if (a->retry > b->retry) return -1; else if (a->retry < b->retry) return 1; if (a->expire > b->expire) return -1; else if (a->expire < b->expire) return 1; if (a->minimum > b->minimum) return -1; else if (a->minimum < b->minimum) return 1; return 0; } /* dns_soa_cmp() */ size_t dns_soa_print(void *_dst, size_t lim, struct dns_soa *soa) { struct dns_buf dst = DNS_B_INTO(_dst, lim); dns_b_puts(&dst, soa->mname); dns_b_putc(&dst, ' '); dns_b_puts(&dst, soa->rname); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->serial, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->refresh, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->retry, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->expire, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, soa->minimum, 0); return dns_b_strllen(&dst); } /* dns_soa_print() */ int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) { unsigned short rp; unsigned i; size_t n; int error; memset(srv, '\0', sizeof *srv); rp = rr->rd.p; if (rr->rd.len < 7) return DNS_EILLEGAL; for (i = 0; i < 2; i++, rp++) { srv->priority <<= 8; srv->priority |= (0xff & P->data[rp]); } for (i = 0; i < 2; i++, rp++) { srv->weight <<= 8; srv->weight |= (0xff & P->data[rp]); } for (i = 0; i < 2; i++, rp++) { srv->port <<= 8; srv->port |= (0xff & P->data[rp]); } if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error))) return error; else if (n >= sizeof srv->target) return DNS_EILLEGAL; return 0; } /* dns_srv_parse() */ int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) { size_t end, len; int error; end = P->end; if (P->size - P->end < 2) goto toolong; P->end += 2; if (P->size - P->end < 6) goto toolong; P->data[P->end++] = 0xff & (srv->priority >> 8); P->data[P->end++] = 0xff & (srv->priority >> 0); P->data[P->end++] = 0xff & (srv->weight >> 8); P->data[P->end++] = 0xff & (srv->weight >> 0); P->data[P->end++] = 0xff & (srv->port >> 8); P->data[P->end++] = 0xff & (srv->port >> 0); if (0 == (len = dns_d_comp(&P->data[P->end], P->size - P->end, srv->target, strlen(srv->target), P, &error))) goto error; else if (P->size - P->end < len) goto toolong; P->end += len; if (P->end > 65535) goto toolong; len = P->end - end - 2; P->data[end + 0] = 0xff & (len >> 8); P->data[end + 1] = 0xff & (len >> 0); return 0; toolong: error = DNS_ENOBUFS; /* FALL THROUGH */ error: P->end = end; return error; } /* dns_srv_push() */ int dns_srv_cmp(const struct dns_srv *a, const struct dns_srv *b) { int cmp; if ((cmp = a->priority - b->priority)) return cmp; /* * FIXME: We need some sort of random seed to implement the dynamic * weighting required by RFC 2782. */ if ((cmp = a->weight - b->weight)) return cmp; if ((cmp = a->port - b->port)) return cmp; return strcasecmp(a->target, b->target); } /* dns_srv_cmp() */ size_t dns_srv_print(void *_dst, size_t lim, struct dns_srv *srv) { struct dns_buf dst = DNS_B_INTO(_dst, lim); dns_b_fmtju(&dst, srv->priority, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, srv->weight, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, srv->port, 0); dns_b_putc(&dst, ' '); dns_b_puts(&dst, srv->target); return dns_b_strllen(&dst); } /* dns_srv_print() */ size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) { return dns_strlcpy(dst, srv->target, lim); } /* dns_srv_cname() */ unsigned int dns_opt_ttl(const struct dns_opt *opt) { unsigned int ttl = 0; ttl |= (0xffU & opt->rcode) << 24; ttl |= (0xffU & opt->version) << 16; ttl |= (0xffffU & opt->flags) << 0; return ttl; } /* dns_opt_ttl() */ unsigned short dns_opt_class(const struct dns_opt *opt) { return opt->maxudp; } /* dns_opt_class() */ struct dns_opt *dns_opt_init(struct dns_opt *opt, size_t size) { assert(size >= offsetof(struct dns_opt, data)); opt->size = size - offsetof(struct dns_opt, data); opt->len = 0; opt->rcode = 0; opt->version = 0; opt->maxudp = 0; return opt; } /* dns_opt_init() */ static union dns_any *dns_opt_initany(union dns_any *any, size_t size) { return dns_opt_init(&any->opt, size), any; } /* dns_opt_initany() */ int dns_opt_parse(struct dns_opt *opt, struct dns_rr *rr, struct dns_packet *P) { const struct dns_buf src = DNS_B_FROM(&P->data[rr->rd.p], rr->rd.len); struct dns_buf dst = DNS_B_INTO(opt->data, opt->size); int error; opt->rcode = 0xfff & ((rr->ttl >> 20) | dns_header(P)->rcode); opt->version = 0xff & (rr->ttl >> 16); opt->flags = 0xffff & rr->ttl; opt->maxudp = 0xffff & rr->class; while (src.p < src.pe) { int code, len; if (-1 == (code = dns_b_get16(&src, -1))) return src.error; if (-1 == (len = dns_b_get16(&src, -1))) return src.error; switch (code) { default: dns_b_put16(&dst, code); dns_b_put16(&dst, len); if ((error = dns_b_move(&dst, &src, len))) return error; break; } } return 0; } /* dns_opt_parse() */ int dns_opt_push(struct dns_packet *P, struct dns_opt *opt) { const struct dns_buf src = DNS_B_FROM(opt->data, opt->len); struct dns_buf dst = DNS_B_INTO(&P->data[P->end], (P->size - P->end)); int error; /* rdata length (see below) */ if ((error = dns_b_put16(&dst, 0))) goto error; /* ... push known options here */ /* push opaque option data */ if ((error = dns_b_move(&dst, &src, (size_t)(src.pe - src.p)))) goto error; /* rdata length */ if ((error = dns_b_pput16(&dst, dns_b_tell(&dst) - 2, 0))) goto error; #if !DNS_DEBUG_OPT_FORMERR P->end += dns_b_tell(&dst); #endif return 0; error: return error; } /* dns_opt_push() */ int dns_opt_cmp(const struct dns_opt *a, const struct dns_opt *b) { (void)a; (void)b; return -1; } /* dns_opt_cmp() */ size_t dns_opt_print(void *_dst, size_t lim, struct dns_opt *opt) { struct dns_buf dst = DNS_B_INTO(_dst, lim); size_t p; dns_b_putc(&dst, '"'); for (p = 0; p < opt->len; p++) { dns_b_putc(&dst, '\\'); dns_b_fmtju(&dst, opt->data[p], 3); } dns_b_putc(&dst, '"'); return dns_b_strllen(&dst); } /* dns_opt_print() */ int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) { return dns_ns_parse((struct dns_ns *)ptr, rr, P); } /* dns_ptr_parse() */ int dns_ptr_push(struct dns_packet *P, struct dns_ptr *ptr) { return dns_ns_push(P, (struct dns_ns *)ptr); } /* dns_ptr_push() */ size_t dns_ptr_qname(void *dst, size_t lim, int af, void *addr) { switch (af) { case AF_INET6: return dns_aaaa_arpa(dst, lim, addr); case AF_INET: return dns_a_arpa(dst, lim, addr); default: { struct dns_a a; a.addr.s_addr = INADDR_NONE; return dns_a_arpa(dst, lim, &a); } } } /* dns_ptr_qname() */ int dns_ptr_cmp(const struct dns_ptr *a, const struct dns_ptr *b) { return strcasecmp(a->host, b->host); } /* dns_ptr_cmp() */ size_t dns_ptr_print(void *dst, size_t lim, struct dns_ptr *ptr) { return dns_ns_print(dst, lim, (struct dns_ns *)ptr); } /* dns_ptr_print() */ size_t dns_ptr_cname(void *dst, size_t lim, struct dns_ptr *ptr) { return dns_strlcpy(dst, ptr->host, lim); } /* dns_ptr_cname() */ int dns_sshfp_parse(struct dns_sshfp *fp, struct dns_rr *rr, struct dns_packet *P) { unsigned p = rr->rd.p, pe = rr->rd.p + rr->rd.len; if (pe - p < 2) return DNS_EILLEGAL; fp->algo = P->data[p++]; fp->type = P->data[p++]; switch (fp->type) { case DNS_SSHFP_SHA1: if (pe - p < sizeof fp->digest.sha1) return DNS_EILLEGAL; memcpy(fp->digest.sha1, &P->data[p], sizeof fp->digest.sha1); break; default: break; } /* switch() */ return 0; } /* dns_sshfp_parse() */ int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) { unsigned p = P->end, pe = P->size, n; if (pe - p < 4) return DNS_ENOBUFS; p += 2; P->data[p++] = 0xff & fp->algo; P->data[p++] = 0xff & fp->type; switch (fp->type) { case DNS_SSHFP_SHA1: if (pe - p < sizeof fp->digest.sha1) return DNS_ENOBUFS; memcpy(&P->data[p], fp->digest.sha1, sizeof fp->digest.sha1); p += sizeof fp->digest.sha1; break; default: return DNS_EILLEGAL; } /* switch() */ n = p - P->end - 2; P->data[P->end++] = 0xff & (n >> 8); P->data[P->end++] = 0xff & (n >> 0); P->end = p; return 0; } /* dns_sshfp_push() */ int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) { int cmp; if ((cmp = a->algo - b->algo) || (cmp = a->type - b->type)) return cmp; switch (a->type) { case DNS_SSHFP_SHA1: return memcmp(a->digest.sha1, b->digest.sha1, sizeof a->digest.sha1); default: return 0; } /* switch() */ /* NOT REACHED */ } /* dns_sshfp_cmp() */ size_t dns_sshfp_print(void *_dst, size_t lim, struct dns_sshfp *fp) { static const unsigned char hex[16] = "0123456789abcdef"; struct dns_buf dst = DNS_B_INTO(_dst, lim); size_t i; dns_b_fmtju(&dst, fp->algo, 0); dns_b_putc(&dst, ' '); dns_b_fmtju(&dst, fp->type, 0); dns_b_putc(&dst, ' '); switch (fp->type) { case DNS_SSHFP_SHA1: for (i = 0; i < sizeof fp->digest.sha1; i++) { dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 4)]); dns_b_putc(&dst, hex[0x0f & (fp->digest.sha1[i] >> 0)]); } break; default: dns_b_putc(&dst, '0'); break; } /* switch() */ return dns_b_strllen(&dst); } /* dns_sshfp_print() */ struct dns_txt *dns_txt_init(struct dns_txt *txt, size_t size) { assert(size > offsetof(struct dns_txt, data)); txt->size = size - offsetof(struct dns_txt, data); txt->len = 0; return txt; } /* dns_txt_init() */ static union dns_any *dns_txt_initany(union dns_any *any, size_t size) { /* NB: union dns_any is already initialized as struct dns_txt */ (void)size; return any; } /* dns_txt_initany() */ int dns_txt_parse(struct dns_txt *txt, struct dns_rr *rr, struct dns_packet *P) { struct { unsigned char *b; size_t p, end; } dst, src; unsigned n; dst.b = txt->data; dst.p = 0; dst.end = txt->size; src.b = P->data; src.p = rr->rd.p; src.end = src.p + rr->rd.len; while (src.p < src.end) { n = 0xff & P->data[src.p++]; if (src.end - src.p < n || dst.end - dst.p < n) return DNS_EILLEGAL; memcpy(&dst.b[dst.p], &src.b[src.p], n); dst.p += n; src.p += n; } txt->len = dst.p; return 0; } /* dns_txt_parse() */ int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) { struct { unsigned char *b; size_t p, end; } dst, src; unsigned n; dst.b = P->data; dst.p = P->end; dst.end = P->size; src.b = txt->data; src.p = 0; src.end = txt->len; if (dst.end - dst.p < 2) return DNS_ENOBUFS; n = txt->len + ((txt->len + 254) / 255); dst.b[dst.p++] = 0xff & (n >> 8); dst.b[dst.p++] = 0xff & (n >> 0); while (src.p < src.end) { n = DNS_PP_MIN(255, src.end - src.p); if (dst.p >= dst.end) return DNS_ENOBUFS; dst.b[dst.p++] = n; if (dst.end - dst.p < n) return DNS_ENOBUFS; memcpy(&dst.b[dst.p], &src.b[src.p], n); dst.p += n; src.p += n; } P->end = dst.p; return 0; } /* dns_txt_push() */ int dns_txt_cmp(const struct dns_txt *a, const struct dns_txt *b) { (void)a; (void)b; return -1; } /* dns_txt_cmp() */ size_t dns_txt_print(void *_dst, size_t lim, struct dns_txt *txt) { struct dns_buf src = DNS_B_FROM(txt->data, txt->len); struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; if (src.p < src.pe) { do { dns_b_putc(&dst, '"'); for (i = 0; i < 256 && src.p < src.pe; i++, src.p++) { if (*src.p < 32 || *src.p > 126 || *src.p == '"' || *src.p == '\\') { dns_b_putc(&dst, '\\'); dns_b_fmtju(&dst, *src.p, 3); } else { dns_b_putc(&dst, *src.p); } } dns_b_putc(&dst, '"'); dns_b_putc(&dst, ' '); } while (src.p < src.pe); dns_b_popc(&dst); } else { dns_b_putc(&dst, '"'); dns_b_putc(&dst, '"'); } return dns_b_strllen(&dst); } /* dns_txt_print() */ static const struct dns_rrtype { enum dns_type type; const char *name; union dns_any *(*init)(union dns_any *, size_t); int (*parse)(); int (*push)(); int (*cmp)(); size_t (*print)(); size_t (*cname)(); } dns_rrtypes[] = { { DNS_T_A, "A", 0, &dns_a_parse, &dns_a_push, &dns_a_cmp, &dns_a_print, 0, }, { DNS_T_AAAA, "AAAA", 0, &dns_aaaa_parse, &dns_aaaa_push, &dns_aaaa_cmp, &dns_aaaa_print, 0, }, { DNS_T_MX, "MX", 0, &dns_mx_parse, &dns_mx_push, &dns_mx_cmp, &dns_mx_print, &dns_mx_cname, }, { DNS_T_NS, "NS", 0, &dns_ns_parse, &dns_ns_push, &dns_ns_cmp, &dns_ns_print, &dns_ns_cname, }, { DNS_T_CNAME, "CNAME", 0, &dns_cname_parse, &dns_cname_push, &dns_cname_cmp, &dns_cname_print, &dns_cname_cname, }, { DNS_T_SOA, "SOA", 0, &dns_soa_parse, &dns_soa_push, &dns_soa_cmp, &dns_soa_print, 0, }, { DNS_T_SRV, "SRV", 0, &dns_srv_parse, &dns_srv_push, &dns_srv_cmp, &dns_srv_print, &dns_srv_cname, }, { DNS_T_OPT, "OPT", &dns_opt_initany, &dns_opt_parse, &dns_opt_push, &dns_opt_cmp, &dns_opt_print, 0, }, { DNS_T_PTR, "PTR", 0, &dns_ptr_parse, &dns_ptr_push, &dns_ptr_cmp, &dns_ptr_print, &dns_ptr_cname, }, { DNS_T_TXT, "TXT", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, { DNS_T_SPF, "SPF", &dns_txt_initany, &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0, }, { DNS_T_SSHFP, "SSHFP", 0, &dns_sshfp_parse, &dns_sshfp_push, &dns_sshfp_cmp, &dns_sshfp_print, 0, }, { DNS_T_AXFR, "AXFR", 0, 0, 0, 0, 0, 0, }, }; /* dns_rrtypes[] */ static const struct dns_rrtype *dns_rrtype(enum dns_type type) { const struct dns_rrtype *t; for (t = dns_rrtypes; t < endof(dns_rrtypes); t++) { if (t->type == type && t->parse) { return t; } } return NULL; } /* dns_rrtype() */ union dns_any *dns_any_init(union dns_any *any, size_t size) { dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); return (union dns_any *)dns_txt_init(&any->rdata, size); } /* dns_any_init() */ static size_t dns_any_sizeof(union dns_any *any) { dns_static_assert(dns_same_type(any->txt, any->rdata, 1), "unexpected rdata type"); return offsetof(struct dns_txt, data) + any->rdata.size; } /* dns_any_sizeof() */ static union dns_any *dns_any_reinit(union dns_any *any, const struct dns_rrtype *t) { return (t->init)? t->init(any, dns_any_sizeof(any)) : any; } /* dns_any_reinit() */ int dns_any_parse(union dns_any *any, struct dns_rr *rr, struct dns_packet *P) { const struct dns_rrtype *t; if ((t = dns_rrtype(rr->type))) return t->parse(dns_any_reinit(any, t), rr, P); if (rr->rd.len > any->rdata.size) return DNS_EILLEGAL; memcpy(any->rdata.data, &P->data[rr->rd.p], rr->rd.len); any->rdata.len = rr->rd.len; return 0; } /* dns_any_parse() */ int dns_any_push(struct dns_packet *P, union dns_any *any, enum dns_type type) { const struct dns_rrtype *t; if ((t = dns_rrtype(type))) return t->push(P, any); if (P->size - P->end < any->rdata.len + 2) return DNS_ENOBUFS; P->data[P->end++] = 0xff & (any->rdata.len >> 8); P->data[P->end++] = 0xff & (any->rdata.len >> 0); memcpy(&P->data[P->end], any->rdata.data, any->rdata.len); P->end += any->rdata.len; return 0; } /* dns_any_push() */ int dns_any_cmp(const union dns_any *a, enum dns_type x, const union dns_any *b, enum dns_type y) { const struct dns_rrtype *t; int cmp; if ((cmp = x - y)) return cmp; if ((t = dns_rrtype(x))) return t->cmp(a, b); return -1; } /* dns_any_cmp() */ size_t dns_any_print(void *_dst, size_t lim, union dns_any *any, enum dns_type type) { const struct dns_rrtype *t; struct dns_buf src, dst; if ((t = dns_rrtype(type))) return t->print(_dst, lim, any); dns_b_from(&src, any->rdata.data, any->rdata.len); dns_b_into(&dst, _dst, lim); dns_b_putc(&dst, '"'); while (src.p < src.pe) { dns_b_putc(&dst, '\\'); dns_b_fmtju(&dst, *src.p++, 3); } dns_b_putc(&dst, '"'); return dns_b_strllen(&dst); } /* dns_any_print() */ size_t dns_any_cname(void *dst, size_t lim, union dns_any *any, enum dns_type type) { const struct dns_rrtype *t; if ((t = dns_rrtype(type)) && t->cname) return t->cname(dst, lim, any); return 0; } /* dns_any_cname() */ /* * H O S T S R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hosts { struct dns_hosts_entry { char host[DNS_D_MAXNAME + 1]; char arpa[73 + 1]; int af; union { struct in_addr a4; struct in6_addr a6; } addr; _Bool alias; struct dns_hosts_entry *next; } *head, **tail; dns_atomic_t refcount; }; /* struct dns_hosts */ struct dns_hosts *dns_hosts_open(int *error) { static const struct dns_hosts hosts_initializer = { .refcount = 1 }; struct dns_hosts *hosts; if (!(hosts = malloc(sizeof *hosts))) goto syerr; *hosts = hosts_initializer; hosts->tail = &hosts->head; return hosts; syerr: *error = dns_syerr(); free(hosts); return 0; } /* dns_hosts_open() */ void dns_hosts_close(struct dns_hosts *hosts) { struct dns_hosts_entry *ent, *xnt; if (!hosts || 1 != dns_hosts_release(hosts)) return; for (ent = hosts->head; ent; ent = xnt) { xnt = ent->next; free(ent); } free(hosts); return; } /* dns_hosts_close() */ dns_refcount_t dns_hosts_acquire(struct dns_hosts *hosts) { return dns_atomic_fetch_add(&hosts->refcount); } /* dns_hosts_acquire() */ dns_refcount_t dns_hosts_release(struct dns_hosts *hosts) { return dns_atomic_fetch_sub(&hosts->refcount); } /* dns_hosts_release() */ struct dns_hosts *dns_hosts_mortal(struct dns_hosts *hosts) { if (hosts) dns_hosts_release(hosts); return hosts; } /* dns_hosts_mortal() */ struct dns_hosts *dns_hosts_local(int *error_) { struct dns_hosts *hosts; int error; if (!(hosts = dns_hosts_open(&error))) goto error; if ((error = dns_hosts_loadpath(hosts, "/etc/hosts"))) goto error; return hosts; error: *error_ = error; dns_hosts_close(hosts); return 0; } /* dns_hosts_local() */ #define dns_hosts_issep(ch) (dns_isspace(ch)) #define dns_hosts_iscom(ch) ((ch) == '#' || (ch) == ';') int dns_hosts_loadfile(struct dns_hosts *hosts, FILE *fp) { struct dns_hosts_entry ent; char word[DNS_PP_MAX(INET6_ADDRSTRLEN, DNS_D_MAXNAME) + 1]; unsigned wp, wc, skip; int ch, error; rewind(fp); do { memset(&ent, '\0', sizeof ent); wc = 0; skip = 0; do { memset(word, '\0', sizeof word); wp = 0; while (EOF != (ch = fgetc(fp)) && ch != '\n') { skip |= !!dns_hosts_iscom(ch); if (skip) continue; if (dns_hosts_issep(ch)) break; if (wp < sizeof word - 1) word[wp] = ch; wp++; } if (!wp) continue; wc++; switch (wc) { case 0: break; case 1: ent.af = (strchr(word, ':'))? AF_INET6 : AF_INET; skip = (1 != dns_inet_pton(ent.af, word, &ent.addr)); break; default: if (!wp) break; dns_d_anchor(ent.host, sizeof ent.host, word, wp); if ((error = dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, (wc > 2)))) return error; break; } /* switch() */ } while (ch != EOF && ch != '\n'); } while (ch != EOF); return 0; } /* dns_hosts_loadfile() */ int dns_hosts_loadpath(struct dns_hosts *hosts, const char *path) { FILE *fp; int error; if (!(fp = dns_fopen(path, "rt", &error))) return error; error = dns_hosts_loadfile(hosts, fp); fclose(fp); return error; } /* dns_hosts_loadpath() */ int dns_hosts_dump(struct dns_hosts *hosts, FILE *fp) { struct dns_hosts_entry *ent, *xnt; char addr[INET6_ADDRSTRLEN + 1]; unsigned i; for (ent = hosts->head; ent; ent = xnt) { xnt = ent->next; dns_inet_ntop(ent->af, &ent->addr, addr, sizeof addr); fputs(addr, fp); for (i = strlen(addr); i < INET_ADDRSTRLEN; i++) fputc(' ', fp); fputc(' ', fp); fputs(ent->host, fp); fputc('\n', fp); } return 0; } /* dns_hosts_dump() */ int dns_hosts_insert(struct dns_hosts *hosts, int af, const void *addr, const void *host, _Bool alias) { struct dns_hosts_entry *ent; int error; if (!(ent = malloc(sizeof *ent))) goto syerr; dns_d_anchor(ent->host, sizeof ent->host, host, strlen(host)); switch ((ent->af = af)) { case AF_INET6: memcpy(&ent->addr.a6, addr, sizeof ent->addr.a6); dns_aaaa_arpa(ent->arpa, sizeof ent->arpa, addr); break; case AF_INET: memcpy(&ent->addr.a4, addr, sizeof ent->addr.a4); dns_a_arpa(ent->arpa, sizeof ent->arpa, addr); break; default: error = EINVAL; goto error; } /* switch() */ ent->alias = alias; ent->next = 0; *hosts->tail = ent; hosts->tail = &ent->next; return 0; syerr: error = dns_syerr(); error: free(ent); return error; } /* dns_hosts_insert() */ struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) { struct dns_packet *P = dns_p_new(512); struct dns_packet *A = 0; struct dns_rr rr; struct dns_hosts_entry *ent; int error, af; char qname[DNS_D_MAXNAME + 1]; size_t qlen; if ((error = dns_rr_parse(&rr, 12, Q))) goto error; if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, Q, &error))) goto error; else if (qlen >= sizeof qname) goto toolong; if ((error = dns_p_push(P, DNS_S_QD, qname, qlen, rr.type, rr.class, 0, 0))) goto error; switch (rr.type) { case DNS_T_PTR: for (ent = hosts->head; ent; ent = ent->next) { if (ent->alias || 0 != strcasecmp(qname, ent->arpa)) continue; if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, ent->host))) goto error; } break; case DNS_T_AAAA: af = AF_INET6; goto loop; case DNS_T_A: af = AF_INET; loop: for (ent = hosts->head; ent; ent = ent->next) { if (ent->af != af || 0 != strcasecmp(qname, ent->host)) continue; if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, &ent->addr))) goto error; } break; default: break; } /* switch() */ if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) goto error; return A; toolong: error = DNS_EILLEGAL; error: *error_ = error; dns_p_free(A); return 0; } /* dns_hosts_query() */ /* * R E S O L V . C O N F R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_resolv_conf *dns_resconf_open(int *error) { static const struct dns_resolv_conf resconf_initializer = { .lookup = "bf", .family = { AF_INET, AF_INET6 }, .options = { .ndots = 1, .timeout = 5, .attempts = 2, .tcp = DNS_RESCONF_TCP_ENABLE, }, .iface = { .ss_family = AF_INET }, }; struct dns_resolv_conf *resconf; struct sockaddr_in *sin; if (!(resconf = malloc(sizeof *resconf))) goto syerr; *resconf = resconf_initializer; sin = (struct sockaddr_in *)&resconf->nameserver[0]; sin->sin_family = AF_INET; sin->sin_addr.s_addr = INADDR_ANY; sin->sin_port = htons(53); #if defined(SA_LEN) sin->sin_len = sizeof *sin; #endif if (0 != gethostname(resconf->search[0], sizeof resconf->search[0])) goto syerr; dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0])); /* * XXX: If gethostname() returned a string without any label * separator, then search[0][0] should be NUL. */ dns_resconf_acquire(resconf); return resconf; syerr: *error = dns_syerr(); free(resconf); return 0; } /* dns_resconf_open() */ void dns_resconf_close(struct dns_resolv_conf *resconf) { if (!resconf || 1 != dns_resconf_release(resconf)) return /* void */; free(resconf); } /* dns_resconf_close() */ dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *resconf) { return dns_atomic_fetch_add(&resconf->_.refcount); } /* dns_resconf_acquire() */ dns_refcount_t dns_resconf_release(struct dns_resolv_conf *resconf) { return dns_atomic_fetch_sub(&resconf->_.refcount); } /* dns_resconf_release() */ struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *resconf) { if (resconf) dns_resconf_release(resconf); return resconf; } /* dns_resconf_mortal() */ struct dns_resolv_conf *dns_resconf_local(int *error_) { struct dns_resolv_conf *resconf; int error; if (!(resconf = dns_resconf_open(&error))) goto error; if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf"))) { /* * NOTE: Both the glibc and BIND9 resolvers ignore a missing * /etc/resolv.conf, defaulting to a nameserver of * 127.0.0.1. See also dns_hints_insert_resconf, and the * default initialization of nameserver[0] in * dns_resconf_open. */ if (error != ENOENT) goto error; } if ((error = dns_nssconf_loadpath(resconf, "/etc/nsswitch.conf"))) { if (error != ENOENT) goto error; } return resconf; error: *error_ = error; dns_resconf_close(resconf); return 0; } /* dns_resconf_local() */ struct dns_resolv_conf *dns_resconf_root(int *error) { struct dns_resolv_conf *resconf; if ((resconf = dns_resconf_local(error))) resconf->options.recurse = 1; return resconf; } /* dns_resconf_root() */ static time_t dns_resconf_timeout(const struct dns_resolv_conf *resconf) { return (time_t)DNS_PP_MIN(INT_MAX, resconf->options.timeout); } /* dns_resconf_timeout() */ enum dns_resconf_keyword { DNS_RESCONF_NAMESERVER, DNS_RESCONF_DOMAIN, DNS_RESCONF_SEARCH, DNS_RESCONF_LOOKUP, DNS_RESCONF_FILE, DNS_RESCONF_BIND, DNS_RESCONF_CACHE, DNS_RESCONF_FAMILY, DNS_RESCONF_INET4, DNS_RESCONF_INET6, DNS_RESCONF_OPTIONS, DNS_RESCONF_EDNS0, DNS_RESCONF_NDOTS, DNS_RESCONF_TIMEOUT, DNS_RESCONF_ATTEMPTS, DNS_RESCONF_ROTATE, DNS_RESCONF_RECURSE, DNS_RESCONF_SMART, DNS_RESCONF_TCP, DNS_RESCONF_TCPx, DNS_RESCONF_INTERFACE, DNS_RESCONF_ZERO, DNS_RESCONF_ONE, DNS_RESCONF_ENABLE, DNS_RESCONF_ONLY, DNS_RESCONF_DISABLE, }; /* enum dns_resconf_keyword */ static enum dns_resconf_keyword dns_resconf_keyword(const char *word) { static const char *words[] = { [DNS_RESCONF_NAMESERVER] = "nameserver", [DNS_RESCONF_DOMAIN] = "domain", [DNS_RESCONF_SEARCH] = "search", [DNS_RESCONF_LOOKUP] = "lookup", [DNS_RESCONF_FILE] = "file", [DNS_RESCONF_BIND] = "bind", [DNS_RESCONF_CACHE] = "cache", [DNS_RESCONF_FAMILY] = "family", [DNS_RESCONF_INET4] = "inet4", [DNS_RESCONF_INET6] = "inet6", [DNS_RESCONF_OPTIONS] = "options", [DNS_RESCONF_EDNS0] = "edns0", [DNS_RESCONF_ROTATE] = "rotate", [DNS_RESCONF_RECURSE] = "recurse", [DNS_RESCONF_SMART] = "smart", [DNS_RESCONF_TCP] = "tcp", [DNS_RESCONF_INTERFACE] = "interface", [DNS_RESCONF_ZERO] = "0", [DNS_RESCONF_ONE] = "1", [DNS_RESCONF_ENABLE] = "enable", [DNS_RESCONF_ONLY] = "only", [DNS_RESCONF_DISABLE] = "disable", }; unsigned i; for (i = 0; i < lengthof(words); i++) { if (words[i] && 0 == strcasecmp(words[i], word)) return i; } if (0 == strncasecmp(word, "ndots:", sizeof "ndots:" - 1)) return DNS_RESCONF_NDOTS; if (0 == strncasecmp(word, "timeout:", sizeof "timeout:" - 1)) return DNS_RESCONF_TIMEOUT; if (0 == strncasecmp(word, "attempts:", sizeof "attempts:" - 1)) return DNS_RESCONF_ATTEMPTS; if (0 == strncasecmp(word, "tcp:", sizeof "tcp:" - 1)) return DNS_RESCONF_TCPx; return -1; } /* dns_resconf_keyword() */ /** OpenBSD-style "[1.2.3.4]:53" nameserver syntax */ int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) { struct { char buf[128], *p; } addr = { "", addr.buf }; unsigned short port = 0; int ch, af = AF_INET, error; while ((ch = *src++)) { switch (ch) { case ' ': /* FALL THROUGH */ case '\t': break; case '[': break; case ']': while ((ch = *src++)) { if (dns_isdigit(ch)) { port *= 10; port += ch - '0'; } } goto inet; case ':': af = AF_INET6; /* FALL THROUGH */ default: if (addr.p < endof(addr.buf) - 1) *addr.p++ = ch; break; } /* switch() */ } /* while() */ inet: if ((error = dns_pton(af, addr.buf, dns_sa_addr(af, ss, NULL)))) return error; port = (!port)? 53 : port; *dns_sa_port(af, ss) = htons(port); dns_sa_family(ss) = af; return 0; } /* dns_resconf_pton() */ #define dns_resconf_issep(ch) (dns_isspace(ch) || (ch) == ',') #define dns_resconf_iscom(ch) ((ch) == '#' || (ch) == ';') int dns_resconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { unsigned sa_count = 0; char words[6][DNS_D_MAXNAME + 1]; unsigned wp, wc, i, j, n; int ch, error; rewind(fp); do { memset(words, '\0', sizeof words); wp = 0; wc = 0; while (EOF != (ch = getc(fp)) && ch != '\n') { if (dns_resconf_issep(ch)) { if (wp > 0) { wp = 0; if (++wc >= lengthof(words)) goto skip; } } else if (dns_resconf_iscom(ch)) { skip: do { ch = getc(fp); } while (ch != EOF && ch != '\n'); break; } else if (wp < sizeof words[wc] - 1) { words[wc][wp++] = ch; } else { wp = 0; /* drop word */ goto skip; } } if (wp > 0) wc++; if (wc < 2) continue; switch (dns_resconf_keyword(words[0])) { case DNS_RESCONF_NAMESERVER: if (sa_count >= lengthof(resconf->nameserver)) continue; if ((error = dns_resconf_pton(&resconf->nameserver[sa_count], words[1]))) continue; sa_count++; break; case DNS_RESCONF_DOMAIN: case DNS_RESCONF_SEARCH: memset(resconf->search, '\0', sizeof resconf->search); for (i = 1, j = 0; i < wc && j < lengthof(resconf->search); i++, j++) dns_d_anchor(resconf->search[j], sizeof resconf->search[j], words[i], strlen(words[i])); break; case DNS_RESCONF_LOOKUP: for (i = 1, j = 0; i < wc && j < lengthof(resconf->lookup); i++) { switch (dns_resconf_keyword(words[i])) { case DNS_RESCONF_FILE: resconf->lookup[j++] = 'f'; break; case DNS_RESCONF_BIND: resconf->lookup[j++] = 'b'; break; case DNS_RESCONF_CACHE: resconf->lookup[j++] = 'c'; break; default: break; } /* switch() */ } /* for() */ break; case DNS_RESCONF_FAMILY: for (i = 1, j = 0; i < wc && j < lengthof(resconf->family); i++) { switch (dns_resconf_keyword(words[i])) { case DNS_RESCONF_INET4: resconf->family[j++] = AF_INET; break; case DNS_RESCONF_INET6: resconf->family[j++] = AF_INET6; break; default: break; } } break; case DNS_RESCONF_OPTIONS: for (i = 1; i < wc; i++) { switch (dns_resconf_keyword(words[i])) { case DNS_RESCONF_EDNS0: resconf->options.edns0 = 1; break; case DNS_RESCONF_NDOTS: for (j = sizeof "ndots:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { n *= 10; n += words[i][j] - '0'; } /* for() */ resconf->options.ndots = n; break; case DNS_RESCONF_TIMEOUT: for (j = sizeof "timeout:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { n *= 10; n += words[i][j] - '0'; } /* for() */ resconf->options.timeout = n; break; case DNS_RESCONF_ATTEMPTS: for (j = sizeof "attempts:" - 1, n = 0; dns_isdigit(words[i][j]); j++) { n *= 10; n += words[i][j] - '0'; } /* for() */ resconf->options.attempts = n; break; case DNS_RESCONF_ROTATE: resconf->options.rotate = 1; break; case DNS_RESCONF_RECURSE: resconf->options.recurse = 1; break; case DNS_RESCONF_SMART: resconf->options.smart = 1; break; case DNS_RESCONF_TCP: resconf->options.tcp = DNS_RESCONF_TCP_ONLY; break; case DNS_RESCONF_TCPx: switch (dns_resconf_keyword(&words[i][sizeof "tcp:" - 1])) { case DNS_RESCONF_ENABLE: resconf->options.tcp = DNS_RESCONF_TCP_ENABLE; break; case DNS_RESCONF_ONE: case DNS_RESCONF_ONLY: resconf->options.tcp = DNS_RESCONF_TCP_ONLY; break; case DNS_RESCONF_ZERO: case DNS_RESCONF_DISABLE: resconf->options.tcp = DNS_RESCONF_TCP_DISABLE; break; default: break; } /* switch() */ break; default: break; } /* switch() */ } /* for() */ break; case DNS_RESCONF_INTERFACE: for (i = 0, n = 0; dns_isdigit(words[2][i]); i++) { n *= 10; n += words[2][i] - '0'; } dns_resconf_setiface(resconf, words[1], n); break; default: break; } /* switch() */ } while (ch != EOF); return 0; } /* dns_resconf_loadfile() */ int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { FILE *fp; int error; if (!(fp = dns_fopen(path, "rt", &error))) return error; error = dns_resconf_loadfile(resconf, fp); fclose(fp); return error; } /* dns_resconf_loadpath() */ struct dns_anyconf { char *token[16]; unsigned count; char buffer[1024], *tp, *cp; }; /* struct dns_anyconf */ static void dns_anyconf_reset(struct dns_anyconf *cf) { cf->count = 0; cf->tp = cf->cp = cf->buffer; } /* dns_anyconf_reset() */ static int dns_anyconf_push(struct dns_anyconf *cf) { if (!(cf->cp < endof(cf->buffer) && cf->count < lengthof(cf->token))) return ENOMEM; *cf->cp++ = '\0'; cf->token[cf->count++] = cf->tp; cf->tp = cf->cp; return 0; } /* dns_anyconf_push() */ static void dns_anyconf_pop(struct dns_anyconf *cf) { if (cf->count > 0) { --cf->count; cf->tp = cf->cp = cf->token[cf->count]; cf->token[cf->count] = 0; } } /* dns_anyconf_pop() */ static int dns_anyconf_addc(struct dns_anyconf *cf, int ch) { if (!(cf->cp < endof(cf->buffer))) return ENOMEM; *cf->cp++ = ch; return 0; } /* dns_anyconf_addc() */ static _Bool dns_anyconf_match(const char *pat, int mc) { _Bool match; int pc; if (*pat == '^') { match = 0; ++pat; } else { match = 1; } while ((pc = *(const unsigned char *)pat++)) { switch (pc) { case '%': if (!(pc = *(const unsigned char *)pat++)) return !match; switch (pc) { case 'a': if (dns_isalpha(mc)) return match; break; case 'd': if (dns_isdigit(mc)) return match; break; case 'w': if (dns_isalnum(mc)) return match; break; case 's': if (dns_isspace(mc)) return match; break; default: if (mc == pc) return match; break; } /* switch() */ break; default: if (mc == pc) return match; break; } /* switch() */ } /* while() */ return !match; } /* dns_anyconf_match() */ static int dns_anyconf_peek(FILE *fp) { int ch; ch = getc(fp); ungetc(ch, fp); return ch; } /* dns_anyconf_peek() */ static size_t dns_anyconf_skip(const char *pat, FILE *fp) { size_t count = 0; int ch; while (EOF != (ch = getc(fp))) { if (dns_anyconf_match(pat, ch)) { count++; continue; } ungetc(ch, fp); break; } return count; } /* dns_anyconf_skip() */ static size_t dns_anyconf_scan(struct dns_anyconf *cf, const char *pat, FILE *fp, int *error) { size_t len; int ch; while (EOF != (ch = getc(fp))) { if (dns_anyconf_match(pat, ch)) { if ((*error = dns_anyconf_addc(cf, ch))) return 0; continue; } else { ungetc(ch, fp); break; } } if ((len = cf->cp - cf->tp)) { if ((*error = dns_anyconf_push(cf))) return 0; return len; } else { *error = 0; return 0; } } /* dns_anyconf_scan() */ DNS_NOTUSED static void dns_anyconf_dump(struct dns_anyconf *cf, FILE *fp) { unsigned i; fprintf(fp, "tokens:"); for (i = 0; i < cf->count; i++) { fprintf(fp, " %s", cf->token[i]); } fputc('\n', fp); } /* dns_anyconf_dump() */ enum dns_nssconf_keyword { DNS_NSSCONF_INVALID = 0, DNS_NSSCONF_HOSTS = 1, DNS_NSSCONF_SUCCESS, DNS_NSSCONF_NOTFOUND, DNS_NSSCONF_UNAVAIL, DNS_NSSCONF_TRYAGAIN, DNS_NSSCONF_CONTINUE, DNS_NSSCONF_RETURN, DNS_NSSCONF_FILES, DNS_NSSCONF_DNS, DNS_NSSCONF_MDNS, DNS_NSSCONF_LAST, }; /* enum dns_nssconf_keyword */ static enum dns_nssconf_keyword dns_nssconf_keyword(const char *word) { static const char *list[] = { [DNS_NSSCONF_HOSTS] = "hosts", [DNS_NSSCONF_SUCCESS] = "success", [DNS_NSSCONF_NOTFOUND] = "notfound", [DNS_NSSCONF_UNAVAIL] = "unavail", [DNS_NSSCONF_TRYAGAIN] = "tryagain", [DNS_NSSCONF_CONTINUE] = "continue", [DNS_NSSCONF_RETURN] = "return", [DNS_NSSCONF_FILES] = "files", [DNS_NSSCONF_DNS] = "dns", [DNS_NSSCONF_MDNS] = "mdns", }; unsigned i; for (i = 1; i < lengthof(list); i++) { if (list[i] && 0 == strcasecmp(list[i], word)) return i; } return DNS_NSSCONF_INVALID; } /* dns_nssconf_keyword() */ static enum dns_nssconf_keyword dns_nssconf_c2k(int ch) { static const char map[] = { ['S'] = DNS_NSSCONF_SUCCESS, ['N'] = DNS_NSSCONF_NOTFOUND, ['U'] = DNS_NSSCONF_UNAVAIL, ['T'] = DNS_NSSCONF_TRYAGAIN, ['C'] = DNS_NSSCONF_CONTINUE, ['R'] = DNS_NSSCONF_RETURN, ['f'] = DNS_NSSCONF_FILES, ['F'] = DNS_NSSCONF_FILES, ['d'] = DNS_NSSCONF_DNS, ['D'] = DNS_NSSCONF_DNS, ['b'] = DNS_NSSCONF_DNS, ['B'] = DNS_NSSCONF_DNS, ['m'] = DNS_NSSCONF_MDNS, ['M'] = DNS_NSSCONF_MDNS, }; return (ch >= 0 && ch < (int)lengthof(map))? map[ch] : DNS_NSSCONF_INVALID; } /* dns_nssconf_c2k() */ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET static int dns_nssconf_k2c(int k) { static const char map[DNS_NSSCONF_LAST] = { [DNS_NSSCONF_SUCCESS] = 'S', [DNS_NSSCONF_NOTFOUND] = 'N', [DNS_NSSCONF_UNAVAIL] = 'U', [DNS_NSSCONF_TRYAGAIN] = 'T', [DNS_NSSCONF_CONTINUE] = 'C', [DNS_NSSCONF_RETURN] = 'R', [DNS_NSSCONF_FILES] = 'f', [DNS_NSSCONF_DNS] = 'b', [DNS_NSSCONF_MDNS] = 'm', }; return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : '?') : '?'; } /* dns_nssconf_k2c() */ static const char *dns_nssconf_k2s(int k) { static const char *const map[DNS_NSSCONF_LAST] = { [DNS_NSSCONF_SUCCESS] = "SUCCESS", [DNS_NSSCONF_NOTFOUND] = "NOTFOUND", [DNS_NSSCONF_UNAVAIL] = "UNAVAIL", [DNS_NSSCONF_TRYAGAIN] = "TRYAGAIN", [DNS_NSSCONF_CONTINUE] = "continue", [DNS_NSSCONF_RETURN] = "return", [DNS_NSSCONF_FILES] = "files", [DNS_NSSCONF_DNS] = "dns", [DNS_NSSCONF_MDNS] = "mdns", }; return (k >= 0 && k < (int)lengthof(map))? (map[k]? map[k] : "") : ""; } /* dns_nssconf_k2s() */ DNS_PRAGMA_POP int dns_nssconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) { enum dns_nssconf_keyword source, status, action; char lookup[sizeof resconf->lookup] = "", *lp; struct dns_anyconf cf; size_t i; int error; while (!feof(fp) && !ferror(fp)) { dns_anyconf_reset(&cf); dns_anyconf_skip("%s", fp); if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) goto nextent; if (DNS_NSSCONF_HOSTS != dns_nssconf_keyword(cf.token[0])) goto nextent; dns_anyconf_pop(&cf); if (!dns_anyconf_skip(": \t", fp)) goto nextent; *(lp = lookup) = '\0'; while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { dns_anyconf_skip(" \t", fp); if ('[' == dns_anyconf_peek(fp)) { dns_anyconf_skip("[! \t", fp); while (dns_anyconf_scan(&cf, "%w_", fp, &error)) { dns_anyconf_skip("= \t", fp); if (!dns_anyconf_scan(&cf, "%w_", fp, &error)) { dns_anyconf_pop(&cf); /* discard status */ dns_anyconf_skip("^#;]\n", fp); /* skip to end of criteria */ break; } dns_anyconf_skip(" \t", fp); } dns_anyconf_skip("] \t", fp); } if ((size_t)(endof(lookup) - lp) < cf.count + 1) /* +1 for '\0' */ goto nextsrc; source = dns_nssconf_keyword(cf.token[0]); switch (source) { case DNS_NSSCONF_DNS: case DNS_NSSCONF_MDNS: case DNS_NSSCONF_FILES: *lp++ = dns_nssconf_k2c(source); break; default: goto nextsrc; } for (i = 1; i + 1 < cf.count; i += 2) { status = dns_nssconf_keyword(cf.token[i]); action = dns_nssconf_keyword(cf.token[i + 1]); switch (status) { case DNS_NSSCONF_SUCCESS: case DNS_NSSCONF_NOTFOUND: case DNS_NSSCONF_UNAVAIL: case DNS_NSSCONF_TRYAGAIN: *lp++ = dns_nssconf_k2c(status); break; default: continue; } switch (action) { case DNS_NSSCONF_CONTINUE: case DNS_NSSCONF_RETURN: break; default: action = (status == DNS_NSSCONF_SUCCESS) ? DNS_NSSCONF_RETURN : DNS_NSSCONF_CONTINUE; break; } *lp++ = dns_nssconf_k2c(action); } nextsrc: *lp = '\0'; dns_anyconf_reset(&cf); } nextent: dns_anyconf_skip("^\n", fp); } if (*lookup) strncpy(resconf->lookup, lookup, sizeof resconf->lookup); return 0; } /* dns_nssconf_loadfile() */ int dns_nssconf_loadpath(struct dns_resolv_conf *resconf, const char *path) { FILE *fp; int error; if (!(fp = dns_fopen(path, "rt", &error))) return error; error = dns_nssconf_loadfile(resconf, fp); fclose(fp); return error; } /* dns_nssconf_loadpath() */ struct dns_nssconf_source { enum dns_nssconf_keyword source, success, notfound, unavail, tryagain; }; /* struct dns_nssconf_source */ typedef unsigned dns_nssconf_i; static inline int dns_nssconf_peek(const struct dns_resolv_conf *resconf, dns_nssconf_i state) { return (state < lengthof(resconf->lookup) && resconf->lookup[state])? resconf->lookup[state] : 0; } /* dns_nssconf_peek() */ static _Bool dns_nssconf_next(struct dns_nssconf_source *src, const struct dns_resolv_conf *resconf, dns_nssconf_i *state) { int source, status, action; src->source = DNS_NSSCONF_INVALID; src->success = DNS_NSSCONF_RETURN; src->notfound = DNS_NSSCONF_CONTINUE; src->unavail = DNS_NSSCONF_CONTINUE; src->tryagain = DNS_NSSCONF_CONTINUE; while ((source = dns_nssconf_peek(resconf, *state))) { source = dns_nssconf_c2k(source); ++*state; switch (source) { case DNS_NSSCONF_FILES: case DNS_NSSCONF_DNS: case DNS_NSSCONF_MDNS: src->source = source; break; default: continue; } while ((status = dns_nssconf_peek(resconf, *state)) && (action = dns_nssconf_peek(resconf, *state + 1))) { status = dns_nssconf_c2k(status); action = dns_nssconf_c2k(action); switch (action) { case DNS_NSSCONF_RETURN: case DNS_NSSCONF_CONTINUE: break; default: goto done; } switch (status) { case DNS_NSSCONF_SUCCESS: src->success = action; break; case DNS_NSSCONF_NOTFOUND: src->notfound = action; break; case DNS_NSSCONF_UNAVAIL: src->unavail = action; break; case DNS_NSSCONF_TRYAGAIN: src->tryagain = action; break; default: goto done; } *state += 2; } break; } done: return src->source != DNS_NSSCONF_INVALID; } /* dns_nssconf_next() */ static int dns_nssconf_dump_status(int status, int action, unsigned *count, FILE *fp) { switch (status) { case DNS_NSSCONF_SUCCESS: if (action == DNS_NSSCONF_RETURN) return 0; break; default: if (action == DNS_NSSCONF_CONTINUE) return 0; break; } fputc(' ', fp); if (!*count) fputc('[', fp); fprintf(fp, "%s=%s", dns_nssconf_k2s(status), dns_nssconf_k2s(action)); ++*count; return 0; } /* dns_nssconf_dump_status() */ int dns_nssconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { struct dns_nssconf_source src; dns_nssconf_i i = 0; fputs("hosts:", fp); while (dns_nssconf_next(&src, resconf, &i)) { unsigned n = 0; fprintf(fp, " %s", dns_nssconf_k2s(src.source)); dns_nssconf_dump_status(DNS_NSSCONF_SUCCESS, src.success, &n, fp); dns_nssconf_dump_status(DNS_NSSCONF_NOTFOUND, src.notfound, &n, fp); dns_nssconf_dump_status(DNS_NSSCONF_UNAVAIL, src.unavail, &n, fp); dns_nssconf_dump_status(DNS_NSSCONF_TRYAGAIN, src.tryagain, &n, fp); if (n) fputc(']', fp); } fputc('\n', fp); return 0; } /* dns_nssconf_dump() */ int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) { int af = (strchr(addr, ':'))? AF_INET6 : AF_INET; int error; if ((error = dns_pton(af, addr, dns_sa_addr(af, &resconf->iface, NULL)))) return error; *dns_sa_port(af, &resconf->iface) = htons(port); resconf->iface.ss_family = af; return 0; } /* dns_resconf_setiface() */ #define DNS_SM_RESTORE \ do { \ pc = 0xff & (*state >> 0); \ srchi = 0xff & (*state >> 8); \ ndots = 0xff & (*state >> 16); \ } while (0) #define DNS_SM_SAVE \ do { \ *state = ((0xff & pc) << 0) \ | ((0xff & srchi) << 8) \ | ((0xff & ndots) << 16); \ } while (0) size_t dns_resconf_search(void *dst, size_t lim, const void *qname, size_t qlen, struct dns_resolv_conf *resconf, dns_resconf_i_t *state) { unsigned pc, srchi, ndots, len; DNS_SM_ENTER; /* if FQDN then return as-is and finish */ if (dns_d_isanchored(qname, qlen)) { len = dns_d_anchor(dst, lim, qname, qlen); DNS_SM_YIELD(len); DNS_SM_EXIT; } ndots = dns_d_ndots(qname, qlen); if (ndots >= resconf->options.ndots) { len = dns_d_anchor(dst, lim, qname, qlen); DNS_SM_YIELD(len); } while (srchi < lengthof(resconf->search) && resconf->search[srchi][0]) { struct dns_buf buf = DNS_B_INTO(dst, lim); const char *dn = resconf->search[srchi++]; dns_b_put(&buf, qname, qlen); dns_b_putc(&buf, '.'); dns_b_puts(&buf, dn); if (!dns_d_isanchored(dn, strlen(dn))) dns_b_putc(&buf, '.'); len = dns_b_strllen(&buf); DNS_SM_YIELD(len); } if (ndots < resconf->options.ndots) { len = dns_d_anchor(dst, lim, qname, qlen); DNS_SM_YIELD(len); } DNS_SM_LEAVE; return dns_strlcpy(dst, "", lim); } /* dns_resconf_search() */ #undef DNS_SM_SAVE #undef DNS_SM_RESTORE int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) { unsigned i; int af; for (i = 0; i < lengthof(resconf->nameserver) && (af = resconf->nameserver[i].ss_family) != AF_UNSPEC; i++) { char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; unsigned short port; dns_inet_ntop(af, dns_sa_addr(af, &resconf->nameserver[i], NULL), addr, sizeof addr); port = ntohs(*dns_sa_port(af, &resconf->nameserver[i])); if (port == 53) fprintf(fp, "nameserver %s\n", addr); else fprintf(fp, "nameserver [%s]:%hu\n", addr, port); } fprintf(fp, "search"); for (i = 0; i < lengthof(resconf->search) && resconf->search[i][0]; i++) fprintf(fp, " %s", resconf->search[i]); fputc('\n', fp); fputs("; ", fp); dns_nssconf_dump(resconf, fp); fprintf(fp, "lookup"); for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) { switch (resconf->lookup[i]) { case 'b': fprintf(fp, " bind"); break; case 'f': fprintf(fp, " file"); break; case 'c': fprintf(fp, " cache"); break; } } fputc('\n', fp); fprintf(fp, "options ndots:%u timeout:%u attempts:%u", resconf->options.ndots, resconf->options.timeout, resconf->options.attempts); if (resconf->options.edns0) fprintf(fp, " edns0"); if (resconf->options.rotate) fprintf(fp, " rotate"); if (resconf->options.recurse) fprintf(fp, " recurse"); if (resconf->options.smart) fprintf(fp, " smart"); switch (resconf->options.tcp) { case DNS_RESCONF_TCP_ENABLE: break; case DNS_RESCONF_TCP_ONLY: fprintf(fp, " tcp"); break; case DNS_RESCONF_TCP_DISABLE: fprintf(fp, " tcp:disable"); break; } fputc('\n', fp); if ((af = resconf->iface.ss_family) != AF_UNSPEC) { char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]"; dns_inet_ntop(af, dns_sa_addr(af, &resconf->iface, NULL), addr, sizeof addr); fprintf(fp, "interface %s %hu\n", addr, ntohs(*dns_sa_port(af, &resconf->iface))); } return 0; } /* dns_resconf_dump() */ /* * H I N T S E R V E R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hints_soa { unsigned char zone[DNS_D_MAXNAME + 1]; struct { struct sockaddr_storage ss; unsigned priority; } addrs[16]; unsigned count; struct dns_hints_soa *next; }; /* struct dns_hints_soa */ struct dns_hints { dns_atomic_t refcount; struct dns_hints_soa *head; }; /* struct dns_hints */ struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf, int *error) { static const struct dns_hints H_initializer; struct dns_hints *H; (void)resconf; if (!(H = malloc(sizeof *H))) goto syerr; *H = H_initializer; dns_hints_acquire(H); return H; syerr: *error = dns_syerr(); free(H); return 0; } /* dns_hints_open() */ void dns_hints_close(struct dns_hints *H) { struct dns_hints_soa *soa, *nxt; if (!H || 1 != dns_hints_release(H)) return /* void */; for (soa = H->head; soa; soa = nxt) { nxt = soa->next; free(soa); } free(H); return /* void */; } /* dns_hints_close() */ dns_refcount_t dns_hints_acquire(struct dns_hints *H) { return dns_atomic_fetch_add(&H->refcount); } /* dns_hints_acquire() */ dns_refcount_t dns_hints_release(struct dns_hints *H) { return dns_atomic_fetch_sub(&H->refcount); } /* dns_hints_release() */ struct dns_hints *dns_hints_mortal(struct dns_hints *hints) { if (hints) dns_hints_release(hints); return hints; } /* dns_hints_mortal() */ struct dns_hints *dns_hints_local(struct dns_resolv_conf *resconf, int *error_) { struct dns_hints *hints = 0; int error; if (resconf) dns_resconf_acquire(resconf); else if (!(resconf = dns_resconf_local(&error))) goto error; if (!(hints = dns_hints_open(resconf, &error))) goto error; error = 0; if (0 == dns_hints_insert_resconf(hints, ".", resconf, &error) && error) goto error; dns_resconf_close(resconf); return hints; error: *error_ = error; dns_resconf_close(resconf); dns_hints_close(hints); return 0; } /* dns_hints_local() */ struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) { static const struct { int af; char addr[INET6_ADDRSTRLEN]; } root_hints[] = { { AF_INET, "198.41.0.4" }, /* A.ROOT-SERVERS.NET. */ { AF_INET6, "2001:503:ba3e::2:30" }, /* A.ROOT-SERVERS.NET. */ { AF_INET, "192.228.79.201" }, /* B.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:84::b" }, /* B.ROOT-SERVERS.NET. */ { AF_INET, "192.33.4.12" }, /* C.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:2::c" }, /* C.ROOT-SERVERS.NET. */ { AF_INET, "199.7.91.13" }, /* D.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:2d::d" }, /* D.ROOT-SERVERS.NET. */ { AF_INET, "192.203.230.10" }, /* E.ROOT-SERVERS.NET. */ { AF_INET, "192.5.5.241" }, /* F.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:2f::f" }, /* F.ROOT-SERVERS.NET. */ { AF_INET, "192.112.36.4" }, /* G.ROOT-SERVERS.NET. */ { AF_INET, "128.63.2.53" }, /* H.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:1::803f:235" }, /* H.ROOT-SERVERS.NET. */ { AF_INET, "192.36.148.17" }, /* I.ROOT-SERVERS.NET. */ { AF_INET6, "2001:7FE::53" }, /* I.ROOT-SERVERS.NET. */ { AF_INET, "192.58.128.30" }, /* J.ROOT-SERVERS.NET. */ { AF_INET6, "2001:503:c27::2:30" }, /* J.ROOT-SERVERS.NET. */ { AF_INET, "193.0.14.129" }, /* K.ROOT-SERVERS.NET. */ { AF_INET6, "2001:7FD::1" }, /* K.ROOT-SERVERS.NET. */ { AF_INET, "199.7.83.42" }, /* L.ROOT-SERVERS.NET. */ { AF_INET6, "2001:500:3::42" }, /* L.ROOT-SERVERS.NET. */ { AF_INET, "202.12.27.33" }, /* M.ROOT-SERVERS.NET. */ { AF_INET6, "2001:DC3::35" }, /* M.ROOT-SERVERS.NET. */ }; struct dns_hints *hints = 0; struct sockaddr_storage ss; unsigned i; int error, af; if (!(hints = dns_hints_open(resconf, &error))) goto error; for (i = 0; i < lengthof(root_hints); i++) { af = root_hints[i].af; if ((error = dns_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss, NULL)))) goto error; *dns_sa_port(af, &ss) = htons(53); ss.ss_family = af; if ((error = dns_hints_insert(hints, ".", (struct sockaddr *)&ss, 1))) goto error; } return hints; error: *error_ = error; dns_hints_close(hints); return 0; } /* dns_hints_root() */ static struct dns_hints_soa *dns_hints_fetch(struct dns_hints *H, const char *zone) { struct dns_hints_soa *soa; for (soa = H->head; soa; soa = soa->next) { if (0 == strcasecmp(zone, (char *)soa->zone)) return soa; } return 0; } /* dns_hints_fetch() */ int dns_hints_insert(struct dns_hints *H, const char *zone, const struct sockaddr *sa, unsigned priority) { static const struct dns_hints_soa soa_initializer; struct dns_hints_soa *soa; unsigned i; if (!(soa = dns_hints_fetch(H, zone))) { if (!(soa = malloc(sizeof *soa))) return dns_syerr(); *soa = soa_initializer; dns_strlcpy((char *)soa->zone, zone, sizeof soa->zone); soa->next = H->head; H->head = soa; } i = soa->count % lengthof(soa->addrs); memcpy(&soa->addrs[i].ss, sa, dns_sa_len(sa)); soa->addrs[i].priority = DNS_PP_MAX(1, priority); if (soa->count < lengthof(soa->addrs)) soa->count++; return 0; } /* dns_hints_insert() */ static _Bool dns_hints_isinaddr_any(const void *sa) { struct in_addr *addr; if (dns_sa_family(sa) != AF_INET) return 0; addr = dns_sa_addr(AF_INET, sa, NULL); return addr->s_addr == htonl(INADDR_ANY); } unsigned dns_hints_insert_resconf(struct dns_hints *H, const char *zone, const struct dns_resolv_conf *resconf, int *error_) { unsigned i, n, p; int error; for (i = 0, n = 0, p = 1; i < lengthof(resconf->nameserver) && resconf->nameserver[i].ss_family != AF_UNSPEC; i++, n++) { union { struct sockaddr_in sin; } tmp; struct sockaddr *ns; /* * dns_resconf_open initializes nameserver[0] to INADDR_ANY. * * Traditionally the semantics of 0.0.0.0 meant the default * interface, which evolved to mean the loopback interface. * See comment block preceding resolv/res_init.c:res_init in * glibc 2.23. As of 2.23, glibc no longer translates * 0.0.0.0 despite the code comment, but it does default to * 127.0.0.1 when no nameservers are present. * * BIND9 as of 9.10.3 still translates 0.0.0.0 to 127.0.0.1. * See lib/lwres/lwconfig.c:lwres_create_addr and the * convert_zero flag. 127.0.0.1 is also the default when no * nameservers are present. */ if (dns_hints_isinaddr_any(&resconf->nameserver[i])) { memcpy(&tmp.sin, &resconf->nameserver[i], sizeof tmp.sin); tmp.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); ns = (struct sockaddr *)&tmp.sin; } else { ns = (struct sockaddr *)&resconf->nameserver[i]; } if ((error = dns_hints_insert(H, zone, ns, p))) goto error; p += !resconf->options.rotate; } return n; error: *error_ = error; return n; } /* dns_hints_insert_resconf() */ static int dns_hints_i_cmp(unsigned a, unsigned b, struct dns_hints_i *i, struct dns_hints_soa *soa) { int cmp; if ((cmp = soa->addrs[a].priority - soa->addrs[b].priority)) return cmp; return dns_k_shuffle16(a, i->state.seed) - dns_k_shuffle16(b, i->state.seed); } /* dns_hints_i_cmp() */ static unsigned dns_hints_i_start(struct dns_hints_i *i, struct dns_hints_soa *soa) { unsigned p0, p; p0 = 0; for (p = 1; p < soa->count; p++) { if (dns_hints_i_cmp(p, p0, i, soa) < 0) p0 = p; } return p0; } /* dns_hints_i_start() */ static unsigned dns_hints_i_skip(unsigned p0, struct dns_hints_i *i, struct dns_hints_soa *soa) { unsigned pZ, p; for (pZ = 0; pZ < soa->count; pZ++) { if (dns_hints_i_cmp(pZ, p0, i, soa) > 0) goto cont; } return soa->count; cont: for (p = pZ + 1; p < soa->count; p++) { if (dns_hints_i_cmp(p, p0, i, soa) <= 0) continue; if (dns_hints_i_cmp(p, pZ, i, soa) >= 0) continue; pZ = p; } return pZ; } /* dns_hints_i_skip() */ static struct dns_hints_i *dns_hints_i_init(struct dns_hints_i *i, struct dns_hints *hints) { static const struct dns_hints_i i_initializer; struct dns_hints_soa *soa; i->state = i_initializer.state; do { i->state.seed = dns_random(); } while (0 == i->state.seed); if ((soa = dns_hints_fetch(hints, i->zone))) { i->state.next = dns_hints_i_start(i, soa); } return i; } /* dns_hints_i_init() */ unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, struct dns_hints_i *i, struct dns_hints *H) { struct dns_hints_soa *soa; unsigned n; if (!(soa = dns_hints_fetch(H, i->zone))) return 0; n = 0; while (i->state.next < soa->count && n < lim) { *sa = (struct sockaddr *)&soa->addrs[i->state.next].ss; *sa_len = dns_sa_len(*sa); sa++; sa_len++; n++; i->state.next = dns_hints_i_skip(i->state.next, i, soa); } return n; } /* dns_hints_grep() */ struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) { struct dns_packet *A, *P; struct dns_rr rr; char zone[DNS_D_MAXNAME + 1]; size_t zlen; struct dns_hints_i i; struct sockaddr *sa; socklen_t slen; int error; if (!dns_rr_grep(&rr, 1, dns_rr_i_new(Q, .section = DNS_S_QUESTION), Q, &error)) goto error; if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error))) goto error; else if (zlen >= sizeof zone) goto toolong; P = dns_p_new(512); dns_header(P)->qr = 1; if ((error = dns_rr_copy(P, &rr, Q))) goto error; if ((error = dns_p_push(P, DNS_S_AUTHORITY, ".", strlen("."), DNS_T_NS, DNS_C_IN, 0, "hints.local."))) goto error; do { i.zone = zone; dns_hints_i_init(&i, hints); while (dns_hints_grep(&sa, &slen, 1, &i, hints)) { int af = sa->sa_family; int rtype = (af == AF_INET6)? DNS_T_AAAA : DNS_T_A; if ((error = dns_p_push(P, DNS_S_ADDITIONAL, "hints.local.", strlen("hints.local."), rtype, DNS_C_IN, 0, dns_sa_addr(af, sa, NULL)))) goto error; } } while ((zlen = dns_d_cleave(zone, sizeof zone, zone, zlen))); if (!(A = dns_p_copy(dns_p_make(P->end, &error), P))) goto error; return A; toolong: error = DNS_EILLEGAL; error: *error_ = error; return 0; } /* dns_hints_query() */ /** ugly hack to support specifying ports other than 53 in resolv.conf. */ static unsigned short dns_hints_port(struct dns_hints *hints, int af, void *addr) { struct dns_hints_soa *soa; void *addrsoa; socklen_t addrlen; unsigned short port; unsigned i; for (soa = hints->head; soa; soa = soa->next) { for (i = 0; i < soa->count; i++) { if (af != soa->addrs[i].ss.ss_family) continue; if (!(addrsoa = dns_sa_addr(af, &soa->addrs[i].ss, &addrlen))) continue; if (memcmp(addr, addrsoa, addrlen)) continue; port = *dns_sa_port(af, &soa->addrs[i].ss); return (port)? port : htons(53); } } return htons(53); } /* dns_hints_port() */ int dns_hints_dump(struct dns_hints *hints, FILE *fp) { struct dns_hints_soa *soa; char addr[INET6_ADDRSTRLEN]; unsigned i; int af, error; for (soa = hints->head; soa; soa = soa->next) { fprintf(fp, "ZONE \"%s\"\n", soa->zone); for (i = 0; i < soa->count; i++) { af = soa->addrs[i].ss.ss_family; if ((error = dns_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss, NULL), addr, sizeof addr))) return error; fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss))); } } return 0; } /* dns_hints_dump() */ /* * C A C H E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static dns_refcount_t dns_cache_acquire(struct dns_cache *cache) { return dns_atomic_fetch_add(&cache->_.refcount); } /* dns_cache_acquire() */ static dns_refcount_t dns_cache_release(struct dns_cache *cache) { return dns_atomic_fetch_sub(&cache->_.refcount); } /* dns_cache_release() */ static struct dns_packet *dns_cache_query(struct dns_packet *query, struct dns_cache *cache, int *error) { (void)query; (void)cache; (void)error; return NULL; } /* dns_cache_query() */ static int dns_cache_submit(struct dns_packet *query, struct dns_cache *cache) { (void)query; (void)cache; return 0; } /* dns_cache_submit() */ static int dns_cache_check(struct dns_cache *cache) { (void)cache; return 0; } /* dns_cache_check() */ static struct dns_packet *dns_cache_fetch(struct dns_cache *cache, int *error) { (void)cache; (void)error; return NULL; } /* dns_cache_fetch() */ static int dns_cache_pollfd(struct dns_cache *cache) { (void)cache; return -1; } /* dns_cache_pollfd() */ static short dns_cache_events(struct dns_cache *cache) { (void)cache; return 0; } /* dns_cache_events() */ static void dns_cache_clear(struct dns_cache *cache) { (void)cache; return; } /* dns_cache_clear() */ struct dns_cache *dns_cache_init(struct dns_cache *cache) { static const struct dns_cache c_init = { .acquire = &dns_cache_acquire, .release = &dns_cache_release, .query = &dns_cache_query, .submit = &dns_cache_submit, .check = &dns_cache_check, .fetch = &dns_cache_fetch, .pollfd = &dns_cache_pollfd, .events = &dns_cache_events, .clear = &dns_cache_clear, ._ = { .refcount = 1, }, }; *cache = c_init; return cache; } /* dns_cache_init() */ void dns_cache_close(struct dns_cache *cache) { if (cache) cache->release(cache); } /* dns_cache_close() */ /* * S O C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static void dns_socketclose(int *fd, const struct dns_options *opts) { if (opts && opts->closefd.cb) opts->closefd.cb(fd, opts->closefd.arg); if (*fd != -1) { #if _WIN32 closesocket(*fd); #else close(*fd); #endif *fd = -1; } } /* dns_socketclose() */ #ifndef HAVE_IOCTLSOCKET #define HAVE_IOCTLSOCKET (_WIN32 || _WIN64) #endif #ifndef HAVE_SOCK_CLOEXEC #define HAVE_SOCK_CLOEXEC (defined SOCK_CLOEXEC) #endif #ifndef HAVE_SOCK_NONBLOCK #define HAVE_SOCK_NONBLOCK (defined SOCK_NONBLOCK) #endif #define DNS_SO_MAXTRY 7 static int dns_socket(struct sockaddr *local, int type, int *error_) { int fd = -1, flags, error; #if defined FIONBIO unsigned long opt; #endif flags = 0; #if HAVE_SOCK_CLOEXEC flags |= SOCK_CLOEXEC; #endif #if HAVE_SOCK_NONBLOCK flags |= SOCK_NONBLOCK; #endif if (-1 == (fd = socket(local->sa_family, type|flags, 0))) goto soerr; #if defined F_SETFD && !HAVE_SOCK_CLOEXEC if (-1 == fcntl(fd, F_SETFD, 1)) goto syerr; #endif #if defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK if (-1 == (flags = fcntl(fd, F_GETFL))) goto syerr; if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) goto syerr; #elif defined FIONBIO && HAVE_IOCTLSOCKET opt = 1; if (0 != ioctlsocket(fd, FIONBIO, &opt)) goto soerr; #endif #if defined SO_NOSIGPIPE if (type != SOCK_DGRAM) { if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof (int))) goto soerr; } #endif if (local->sa_family != AF_INET && local->sa_family != AF_INET6) return fd; if (type != SOCK_DGRAM) return fd; /* * FreeBSD, Linux, OpenBSD, OS X, and Solaris use random ports by * default. Though the ephemeral range is quite small on OS X * (49152-65535 on 10.10) and Linux (32768-60999 on 4.4.0, Ubuntu * Xenial). See also RFC 6056. * * TODO: Optionally rely on the kernel to select a random port. */ if (*dns_sa_port(local->sa_family, local) == 0) { struct sockaddr_storage tmp; unsigned i, port; memcpy(&tmp, local, dns_sa_len(local)); for (i = 0; i < DNS_SO_MAXTRY; i++) { port = 1025 + (dns_random() % 64510); *dns_sa_port(tmp.ss_family, &tmp) = htons(port); if (0 == bind(fd, (struct sockaddr *)&tmp, dns_sa_len(&tmp))) return fd; } /* NB: continue to next bind statement */ } if (0 == bind(fd, local, dns_sa_len(local))) return fd; /* FALL THROUGH */ soerr: error = dns_soerr(); goto error; #if (defined F_SETFD && !HAVE_SOCK_CLOEXEC) || (defined O_NONBLOCK && !HAVE_SOCK_NONBLOCK) syerr: error = dns_syerr(); goto error; #endif error: *error_ = error; dns_socketclose(&fd, NULL); return -1; } /* dns_socket() */ enum { DNS_SO_UDP_INIT = 1, DNS_SO_UDP_CONN, DNS_SO_UDP_SEND, DNS_SO_UDP_RECV, DNS_SO_UDP_DONE, DNS_SO_TCP_INIT, DNS_SO_TCP_CONN, DNS_SO_TCP_SEND, DNS_SO_TCP_RECV, DNS_SO_TCP_DONE, }; struct dns_socket { struct dns_options opts; int udp; int tcp; int *old; unsigned onum, olim; int type; struct sockaddr_storage local, remote; struct dns_k_permutor qids; struct dns_stat stat; /* * NOTE: dns_so_reset() zeroes everything from here down. */ int state; unsigned short qid; char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; enum dns_class qclass; struct dns_packet *query; size_t qout; struct dns_clock elapsed; struct dns_packet *answer; size_t alen, apos; }; /* struct dns_socket */ /* * NOTE: Actual closure delayed so that kqueue(2) and epoll(2) callers have * a chance to recognize a state change after installing a persistent event * and where sequential descriptors with the same integer value returned * from _pollfd() would be ambiguous. See dns_so_closefds(). */ static int dns_so_closefd(struct dns_socket *so, int *fd) { int error; if (*fd == -1) return 0; if (so->opts.closefd.cb) { if ((error = so->opts.closefd.cb(fd, so->opts.closefd.arg))) { return error; } else if (*fd == -1) return 0; } if (!(so->onum < so->olim)) { unsigned olim = DNS_PP_MAX(4, so->olim * 2); void *old; if (!(old = realloc(so->old, sizeof so->old[0] * olim))) return dns_syerr(); so->old = old; so->olim = olim; } so->old[so->onum++] = *fd; *fd = -1; return 0; } /* dns_so_closefd() */ #define DNS_SO_CLOSE_UDP 0x01 #define DNS_SO_CLOSE_TCP 0x02 #define DNS_SO_CLOSE_OLD 0x04 #define DNS_SO_CLOSE_ALL (DNS_SO_CLOSE_UDP|DNS_SO_CLOSE_TCP|DNS_SO_CLOSE_OLD) static void dns_so_closefds(struct dns_socket *so, int which) { if (DNS_SO_CLOSE_UDP & which) dns_socketclose(&so->udp, &so->opts); if (DNS_SO_CLOSE_TCP & which) dns_socketclose(&so->tcp, &so->opts); if (DNS_SO_CLOSE_OLD & which) { unsigned i; for (i = 0; i < so->onum; i++) dns_socketclose(&so->old[i], &so->opts); so->onum = 0; free(so->old); so->old = 0; so->olim = 0; } } /* dns_so_closefds() */ static void dns_so_destroy(struct dns_socket *); static struct dns_socket *dns_so_init(struct dns_socket *so, const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { static const struct dns_socket so_initializer = { .opts = DNS_OPTS_INITIALIZER, .udp = -1, .tcp = -1, }; *so = so_initializer; so->type = type; if (opts) so->opts = *opts; if (local) memcpy(&so->local, local, dns_sa_len(local)); if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, error))) goto error; dns_k_permutor_init(&so->qids, 1, 65535); return so; error: dns_so_destroy(so); return 0; } /* dns_so_init() */ struct dns_socket *dns_so_open(const struct sockaddr *local, int type, const struct dns_options *opts, int *error) { struct dns_socket *so; if (!(so = malloc(sizeof *so))) goto syerr; if (!dns_so_init(so, local, type, opts, error)) goto error; return so; syerr: *error = dns_syerr(); error: dns_so_close(so); return 0; } /* dns_so_open() */ static void dns_so_destroy(struct dns_socket *so) { dns_so_reset(so); dns_so_closefds(so, DNS_SO_CLOSE_ALL); } /* dns_so_destroy() */ void dns_so_close(struct dns_socket *so) { if (!so) return; dns_so_destroy(so); free(so); } /* dns_so_close() */ void dns_so_reset(struct dns_socket *so) { dns_p_setptr(&so->answer, NULL); memset(&so->state, '\0', sizeof *so - offsetof(struct dns_socket, state)); } /* dns_so_reset() */ unsigned short dns_so_mkqid(struct dns_socket *so) { return dns_k_permutor_step(&so->qids); } /* dns_so_mkqid() */ #define DNS_SO_MINBUF 768 static int dns_so_newanswer(struct dns_socket *so, size_t len) { size_t size = offsetof(struct dns_packet, data) + DNS_PP_MAX(len, DNS_SO_MINBUF); void *p; if (!(p = realloc(so->answer, size))) return dns_syerr(); so->answer = dns_p_init(p, size); return 0; } /* dns_so_newanswer() */ int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host) { struct dns_rr rr; int error = DNS_EUNKNOWN; dns_so_reset(so); if ((error = dns_rr_parse(&rr, 12, Q))) goto error; if (!(so->qlen = dns_d_expand(so->qname, sizeof so->qname, rr.dn.p, Q, &error))) goto error; /* * NOTE: Don't bail if expansion is too long; caller may be * intentionally sending long names. However, we won't be able to * verify it on return. */ so->qtype = rr.type; so->qclass = rr.class; if ((error = dns_so_newanswer(so, (Q->memo.opt.maxudp)? Q->memo.opt.maxudp : DNS_SO_MINBUF))) goto syerr; memcpy(&so->remote, host, dns_sa_len(host)); so->query = Q; so->qout = 0; dns_begin(&so->elapsed); if (dns_header(so->query)->qid == 0) dns_header(so->query)->qid = dns_so_mkqid(so); so->qid = dns_header(so->query)->qid; so->state = (so->type == SOCK_STREAM)? DNS_SO_TCP_INIT : DNS_SO_UDP_INIT; so->stat.queries++; return 0; syerr: error = dns_syerr(); error: dns_so_reset(so); return error; } /* dns_so_submit() */ static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) { char qname[DNS_D_MAXNAME + 1]; size_t qlen; struct dns_rr rr; int error = -1; if (so->qid != dns_header(so->answer)->qid) goto reject; if (!dns_p_count(so->answer, DNS_S_QD)) goto reject; if (0 != dns_rr_parse(&rr, 12, so->answer)) goto reject; if (rr.type != so->qtype || rr.class != so->qclass) goto reject; if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, P, &error))) goto error; else if (qlen >= sizeof qname || qlen != so->qlen) goto reject; if (0 != strcasecmp(so->qname, qname)) goto reject; return 0; reject: error = DNS_EUNKNOWN; error: DNS_SHOW(P, "rejecting packet (%s)", dns_strerror(error)); return error; } /* dns_so_verify() */ static _Bool dns_so_tcp_keep(struct dns_socket *so) { struct sockaddr_storage remote; if (so->tcp == -1) return 0; if (0 != getpeername(so->tcp, (struct sockaddr *)&remote, &(socklen_t){ sizeof remote })) return 0; return 0 == dns_sa_cmp(&remote, &so->remote); } /* dns_so_tcp_keep() */ #if defined __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warray-bounds" #endif static int dns_so_tcp_send(struct dns_socket *so) { unsigned char *qsrc; size_t qend; long n; so->query->data[-2] = 0xff & (so->query->end >> 8); so->query->data[-1] = 0xff & (so->query->end >> 0); qsrc = &so->query->data[-2] + so->qout; qend = so->query->end + 2; while (so->qout < qend) { if (0 > (n = dns_send(so->tcp, (void *)&qsrc[so->qout], qend - so->qout, 0))) return dns_soerr(); so->qout += n; so->stat.tcp.sent.bytes += n; } so->stat.tcp.sent.count++; return 0; } /* dns_so_tcp_send() */ static int dns_so_tcp_recv(struct dns_socket *so) { unsigned char *asrc; size_t aend, alen; int error; long n; aend = so->alen + 2; while (so->apos < aend) { asrc = &so->answer->data[-2]; if (0 > (n = recv(so->tcp, (void *)&asrc[so->apos], aend - so->apos, 0))) return dns_soerr(); else if (n == 0) return DNS_EUNKNOWN; /* FIXME */ so->apos += n; so->stat.tcp.rcvd.bytes += n; if (so->alen == 0 && so->apos >= 2) { alen = ((0xff & so->answer->data[-2]) << 8) | ((0xff & so->answer->data[-1]) << 0); if ((error = dns_so_newanswer(so, alen))) return error; so->alen = alen; aend = alen + 2; } } so->answer->end = so->alen; so->stat.tcp.rcvd.count++; return 0; } /* dns_so_tcp_recv() */ #if __clang__ #pragma clang diagnostic pop #endif int dns_so_check(struct dns_socket *so) { int error; long n; retry: switch (so->state) { case DNS_SO_UDP_INIT: so->state++; case DNS_SO_UDP_CONN: if (0 != connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) goto soerr; so->state++; case DNS_SO_UDP_SEND: if (0 > (n = send(so->udp, (void *)so->query->data, so->query->end, 0))) goto soerr; so->stat.udp.sent.bytes += n; so->stat.udp.sent.count++; so->state++; case DNS_SO_UDP_RECV: if (0 > (n = recv(so->udp, (void *)so->answer->data, so->answer->size, 0))) goto soerr; so->stat.udp.rcvd.bytes += n; so->stat.udp.rcvd.count++; if ((so->answer->end = n) < 12) goto trash; if ((error = dns_so_verify(so, so->answer))) goto trash; so->state++; case DNS_SO_UDP_DONE: if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM) return 0; so->state++; case DNS_SO_TCP_INIT: if (dns_so_tcp_keep(so)) { so->state = DNS_SO_TCP_SEND; goto retry; } if ((error = dns_so_closefd(so, &so->tcp))) goto error; if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error))) goto error; so->state++; case DNS_SO_TCP_CONN: if (0 != connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) { if (dns_soerr() != DNS_EISCONN) goto soerr; } so->state++; case DNS_SO_TCP_SEND: if ((error = dns_so_tcp_send(so))) goto error; so->state++; case DNS_SO_TCP_RECV: if ((error = dns_so_tcp_recv(so))) goto error; so->state++; case DNS_SO_TCP_DONE: /* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */ if (so->type != SOCK_STREAM) { if ((error = dns_so_closefd(so, &so->tcp))) goto error; } if (so->answer->end < 12) return DNS_EILLEGAL; if ((error = dns_so_verify(so, so->answer))) goto error; return 0; default: error = DNS_EUNKNOWN; goto error; } /* switch() */ trash: DNS_CARP("discarding packet"); goto retry; soerr: error = dns_soerr(); goto error; error: switch (error) { case DNS_EINTR: goto retry; case DNS_EINPROGRESS: /* FALL THROUGH */ case DNS_EALREADY: /* FALL THROUGH */ #if DNS_EWOULDBLOCK != DNS_EAGAIN case DNS_EWOULDBLOCK: /* FALL THROUGH */ #endif error = DNS_EAGAIN; break; } /* switch() */ return error; } /* dns_so_check() */ struct dns_packet *dns_so_fetch(struct dns_socket *so, int *error) { struct dns_packet *answer; switch (so->state) { case DNS_SO_UDP_DONE: case DNS_SO_TCP_DONE: answer = so->answer; so->answer = 0; return answer; default: *error = DNS_EUNKNOWN; return 0; } } /* dns_so_fetch() */ struct dns_packet *dns_so_query(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host, int *error_) { struct dns_packet *A; int error; if (!so->state) { if ((error = dns_so_submit(so, Q, host))) goto error; } if ((error = dns_so_check(so))) goto error; if (!(A = dns_so_fetch(so, &error))) goto error; dns_so_reset(so); return A; error: *error_ = error; return 0; } /* dns_so_query() */ time_t dns_so_elapsed(struct dns_socket *so) { return dns_elapsed(&so->elapsed); } /* dns_so_elapsed() */ void dns_so_clear(struct dns_socket *so) { dns_so_closefds(so, DNS_SO_CLOSE_OLD); } /* dns_so_clear() */ static int dns_so_events2(struct dns_socket *so, enum dns_events type) { int events = 0; switch (so->state) { case DNS_SO_UDP_CONN: case DNS_SO_UDP_SEND: events |= DNS_POLLOUT; break; case DNS_SO_UDP_RECV: events |= DNS_POLLIN; break; case DNS_SO_TCP_CONN: case DNS_SO_TCP_SEND: events |= DNS_POLLOUT; break; case DNS_SO_TCP_RECV: events |= DNS_POLLIN; break; } /* switch() */ switch (type) { case DNS_LIBEVENT: return DNS_POLL2EV(events); default: return events; } /* switch() */ } /* dns_so_events2() */ int dns_so_events(struct dns_socket *so) { return dns_so_events2(so, so->opts.events); } /* dns_so_events() */ int dns_so_pollfd(struct dns_socket *so) { switch (so->state) { case DNS_SO_UDP_CONN: case DNS_SO_UDP_SEND: case DNS_SO_UDP_RECV: return so->udp; case DNS_SO_TCP_CONN: case DNS_SO_TCP_SEND: case DNS_SO_TCP_RECV: return so->tcp; } /* switch() */ return -1; } /* dns_so_pollfd() */ int dns_so_poll(struct dns_socket *so, int timeout) { return dns_poll(dns_so_pollfd(so), dns_so_events2(so, DNS_SYSPOLL), timeout); } /* dns_so_poll() */ const struct dns_stat *dns_so_stat(struct dns_socket *so) { return &so->stat; } /* dns_so_stat() */ /* * R E S O L V E R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ enum dns_res_state { DNS_R_INIT, DNS_R_GLUE, DNS_R_SWITCH, /* (B)IND, (F)ILE, (C)ACHE */ DNS_R_FILE, /* Lookup in local hosts database */ DNS_R_CACHE, /* Lookup in application cache */ DNS_R_SUBMIT, DNS_R_CHECK, DNS_R_FETCH, DNS_R_BIND, /* Lookup in the network */ DNS_R_SEARCH, DNS_R_HINTS, DNS_R_ITERATE, DNS_R_FOREACH_NS, DNS_R_RESOLV0_NS, /* Prologue: Setup next frame and recurse */ DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */ DNS_R_FOREACH_A, DNS_R_QUERY_A, DNS_R_CNAME0_A, DNS_R_CNAME1_A, DNS_R_FINISH, DNS_R_SMART0_A, DNS_R_SMART1_A, DNS_R_DONE, DNS_R_SERVFAIL, }; /* enum dns_res_state */ #define DNS_R_MAXDEPTH 8 #define DNS_R_ENDFRAME (DNS_R_MAXDEPTH - 1) struct dns_resolver { struct dns_socket so; struct dns_resolv_conf *resconf; struct dns_hosts *hosts; struct dns_hints *hints; struct dns_cache *cache; dns_atomic_t refcount; /* Reset zeroes everything below here. */ char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; enum dns_class qclass; struct dns_clock elapsed; dns_resconf_i_t search; struct dns_rr_i smart; struct dns_packet *nodata; /* answer if nothing better */ unsigned sp; struct dns_res_frame { enum dns_res_state state; int error; int which; /* (B)IND, (F)ILE; index into resconf->lookup */ int qflags; unsigned attempts; struct dns_packet *query, *answer, *hints; struct dns_rr_i hints_i, hints_j; struct dns_rr hints_ns, ans_cname; } stack[DNS_R_MAXDEPTH]; }; /* struct dns_resolver */ static int dns_res_tcp2type(int tcp) { switch (tcp) { case DNS_RESCONF_TCP_ONLY: return SOCK_STREAM; case DNS_RESCONF_TCP_DISABLE: return SOCK_DGRAM; default: return 0; } } /* dns_res_tcp2type() */ struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *_error) { static const struct dns_resolver R_initializer = { .refcount = 1, }; struct dns_resolver *R = 0; int type, error; /* * Grab ref count early because the caller may have passed us a mortal * reference, and we want to do the right thing if we return early * from an error. */ if (resconf) dns_resconf_acquire(resconf); if (hosts) dns_hosts_acquire(hosts); if (hints) dns_hints_acquire(hints); if (cache) dns_cache_acquire(cache); /* * Don't try to load it ourselves because a NULL object might be an * error from, say, dns_resconf_root(), and loading * dns_resconf_local() by default would create undesirable surpises. */ if (!resconf || !hosts || !hints) { if (!*_error) *_error = EINVAL; goto _error; } if (!(R = malloc(sizeof *R))) goto syerr; *R = R_initializer; type = dns_res_tcp2type(resconf->options.tcp); if (!dns_so_init(&R->so, (struct sockaddr *)&resconf->iface, type, opts, &error)) goto error; R->resconf = resconf; R->hosts = hosts; R->hints = hints; R->cache = cache; return R; syerr: error = dns_syerr(); error: *_error = error; _error: dns_res_close(R); dns_resconf_close(resconf); dns_hosts_close(hosts); dns_hints_close(hints); dns_cache_close(cache); return 0; } /* dns_res_open() */ struct dns_resolver *dns_res_stub(const struct dns_options *opts, int *error) { struct dns_resolv_conf *resconf = 0; struct dns_hosts *hosts = 0; struct dns_hints *hints = 0; struct dns_resolver *res = 0; if (!(resconf = dns_resconf_local(error))) goto epilog; if (!(hosts = dns_hosts_local(error))) goto epilog; if (!(hints = dns_hints_local(resconf, error))) goto epilog; if (!(res = dns_res_open(resconf, hosts, hints, NULL, opts, error))) goto epilog; epilog: dns_resconf_close(resconf); dns_hosts_close(hosts); dns_hints_close(hints); return res; } /* dns_res_stub() */ static void dns_res_frame_destroy(struct dns_resolver *R, struct dns_res_frame *frame) { (void)R; dns_p_setptr(&frame->query, NULL); dns_p_setptr(&frame->answer, NULL); dns_p_setptr(&frame->hints, NULL); } /* dns_res_frame_destroy() */ static void dns_res_frame_init(struct dns_resolver *R, struct dns_res_frame *frame) { memset(frame, '\0', sizeof *frame); /* * NB: Can be invoked from dns_res_open, before R->resconf has been * initialized. */ if (R->resconf) { if (!R->resconf->options.recurse) frame->qflags |= DNS_Q_RD; if (R->resconf->options.edns0) frame->qflags |= DNS_Q_EDNS0; } } /* dns_res_frame_init() */ static void dns_res_frame_reset(struct dns_resolver *R, struct dns_res_frame *frame) { dns_res_frame_destroy(R, frame); dns_res_frame_init(R, frame); } /* dns_res_frame_reset() */ static dns_error_t dns_res_frame_prepare(struct dns_resolver *R, struct dns_res_frame *F, const char *qname, enum dns_type qtype, enum dns_class qclass) { struct dns_packet *P = NULL; if (!(F < endof(R->stack))) return DNS_EUNKNOWN; dns_p_movptr(&P, &F->query); dns_res_frame_reset(R, F); dns_p_movptr(&F->query, &P); return dns_q_make(&F->query, qname, qtype, qclass, F->qflags); } /* dns_res_frame_prepare() */ void dns_res_reset(struct dns_resolver *R) { unsigned i; dns_so_reset(&R->so); dns_p_setptr(&R->nodata, NULL); for (i = 0; i < lengthof(R->stack); i++) dns_res_frame_destroy(R, &R->stack[i]); memset(&R->qname, '\0', sizeof *R - offsetof(struct dns_resolver, qname)); for (i = 0; i < lengthof(R->stack); i++) dns_res_frame_init(R, &R->stack[i]); } /* dns_res_reset() */ void dns_res_close(struct dns_resolver *R) { if (!R || 1 < dns_res_release(R)) return; dns_res_reset(R); dns_so_destroy(&R->so); dns_hints_close(R->hints); dns_hosts_close(R->hosts); dns_resconf_close(R->resconf); dns_cache_close(R->cache); free(R); } /* dns_res_close() */ dns_refcount_t dns_res_acquire(struct dns_resolver *R) { return dns_atomic_fetch_add(&R->refcount); } /* dns_res_acquire() */ dns_refcount_t dns_res_release(struct dns_resolver *R) { return dns_atomic_fetch_sub(&R->refcount); } /* dns_res_release() */ struct dns_resolver *dns_res_mortal(struct dns_resolver *res) { if (res) dns_res_release(res); return res; } /* dns_res_mortal() */ static struct dns_packet *dns_res_merge(struct dns_packet *P0, struct dns_packet *P1, int *error_) { size_t bufsiz = P0->end + P1->end; struct dns_packet *P[3] = { P0, P1, 0 }; struct dns_rr rr[3]; int error, copy, i; enum dns_section section; retry: if (!(P[2] = dns_p_make(bufsiz, &error))) goto error; dns_rr_foreach(&rr[0], P[0], .section = DNS_S_QD) { if ((error = dns_rr_copy(P[2], &rr[0], P[0]))) goto error; } for (section = DNS_S_AN; (DNS_S_ALL & section); section <<= 1) { for (i = 0; i < 2; i++) { dns_rr_foreach(&rr[i], P[i], .section = section) { copy = 1; dns_rr_foreach(&rr[2], P[2], .type = rr[i].type, .section = (DNS_S_ALL & ~DNS_S_QD)) { if (0 == dns_rr_cmp(&rr[i], P[i], &rr[2], P[2])) { copy = 0; break; } } if (copy && (error = dns_rr_copy(P[2], &rr[i], P[i]))) { if (error == DNS_ENOBUFS && bufsiz < 65535) { dns_p_setptr(&P[2], NULL); bufsiz = DNS_PP_MAX(65535, bufsiz * 2); goto retry; } goto error; } } /* foreach(rr) */ } /* foreach(packet) */ } /* foreach(section) */ return P[2]; error: *error_ = error; dns_p_free(P[2]); return 0; } /* dns_res_merge() */ static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) { struct dns_packet *P = dns_p_new(512); char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; struct dns_rr rr; unsigned sp; int error; if (!(qlen = dns_d_expand(qname, sizeof qname, 12, Q, &error)) || qlen >= sizeof qname) return 0; if (!(qtype = dns_rr_type(12, Q))) return 0; if ((error = dns_p_push(P, DNS_S_QD, qname, strlen(qname), qtype, DNS_C_IN, 0, 0))) return 0; for (sp = 0; sp <= R->sp; sp++) { if (!R->stack[sp].answer) continue; dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = qtype, .section = (DNS_S_ALL & ~DNS_S_QD)) { rr.section = DNS_S_AN; if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) return 0; } } if (dns_p_count(P, DNS_S_AN) > 0) goto copy; /* Otherwise, look for a CNAME */ for (sp = 0; sp <= R->sp; sp++) { if (!R->stack[sp].answer) continue; dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = DNS_T_CNAME, .section = (DNS_S_ALL & ~DNS_S_QD)) { rr.section = DNS_S_AN; if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer))) return 0; } } if (!dns_p_count(P, DNS_S_AN)) return 0; copy: return dns_p_copy(dns_p_make(P->end, &error), P); } /* dns_res_glue() */ /* * Sort NS records by three criteria: * * 1) Whether glue is present. * 2) Whether glue record is original or of recursive lookup. * 3) Randomly shuffle records which share the above criteria. * * NOTE: Assumes only NS records passed, AND ASSUMES no new NS records will * be added during an iteration. * * FIXME: Only groks A glue, not AAAA glue. */ static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) { _Bool glued[2] = { 0 }; struct dns_rr x = { 0 }, y = { 0 }; struct dns_ns ns; int cmp, error; if (!(error = dns_ns_parse(&ns, a, P))) glued[0] = !!dns_rr_grep(&x, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); if (!(error = dns_ns_parse(&ns, b, P))) glued[1] = !!dns_rr_grep(&y, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error); if ((cmp = glued[1] - glued[0])) { return cmp; } else if ((cmp = (dns_rr_offset(&y) < i->args[0]) - (dns_rr_offset(&x) < i->args[0]))) { return cmp; } else { return dns_rr_i_shuffle(a, b, i, P); } } /* dns_res_nameserv_cmp() */ #define dgoto(sp, i) \ do { R->stack[(sp)].state = (i); goto exec; } while (0) static int dns_res_exec(struct dns_resolver *R) { struct dns_res_frame *F; struct dns_packet *P; union { char host[DNS_D_MAXNAME + 1]; char name[DNS_D_MAXNAME + 1]; struct dns_ns ns; struct dns_cname cname; } u; size_t len; struct dns_rr rr; int error; exec: F = &R->stack[R->sp]; switch (F->state) { case DNS_R_INIT: F->state++; case DNS_R_GLUE: if (R->sp == 0) dgoto(R->sp, DNS_R_SWITCH); if (!F->query) goto noquery; if (!(F->answer = dns_res_glue(R, F->query))) dgoto(R->sp, DNS_R_SWITCH); if (!(len = dns_d_expand(u.name, sizeof u.name, 12, F->query, &error))) goto error; else if (len >= sizeof u.name) goto toolong; dns_rr_foreach(&rr, F->answer, .name = u.name, .type = dns_rr_type(12, F->query), .section = DNS_S_AN) { dgoto(R->sp, DNS_R_FINISH); } dns_rr_foreach(&rr, F->answer, .name = u.name, .type = DNS_T_CNAME, .section = DNS_S_AN) { F->ans_cname = rr; dgoto(R->sp, DNS_R_CNAME0_A); } F->state++; case DNS_R_SWITCH: while (F->which < (int)sizeof R->resconf->lookup && R->resconf->lookup[F->which]) { switch (R->resconf->lookup[F->which++]) { case 'b': case 'B': dgoto(R->sp, DNS_R_BIND); case 'f': case 'F': dgoto(R->sp, DNS_R_FILE); case 'c': case 'C': if (R->cache) dgoto(R->sp, DNS_R_CACHE); break; default: break; } } /* * FIXME: Examine more closely whether our logic is correct * and DNS_R_SERVFAIL is the correct default response. * * Case 1: We got here because we never got an answer on the * wire. All queries timed-out and we reached maximum * attempts count. See DNS_R_FOREACH_NS. In that case * DNS_R_SERVFAIL is the correct state, unless we want to * return DNS_ETIMEDOUT. * * Case 2: We were a stub resolver and got an unsatisfactory * answer (empty ANSWER section) which caused us to jump * back to DNS_R_SEARCH and ultimately to DNS_R_SWITCH. We * return the answer returned from the wire, which we * stashed in R->nodata. * * Case 3: We reached maximum attempts count as in case #1, * but never got an authoritative response which caused us * to short-circuit. See end of DNS_R_QUERY_A case. We * should probably prepare R->nodata as in case #2. */ if (R->sp == 0 && R->nodata) { /* XXX: can we just return nodata regardless? */ dns_p_movptr(&F->answer, &R->nodata); dgoto(R->sp, DNS_R_FINISH); } dgoto(R->sp, DNS_R_SERVFAIL); case DNS_R_FILE: if (R->sp > 0) { if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) goto error; if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); } else { R->search = 0; while ((len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) { if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) goto error; if (!dns_p_setptr(&F->answer, dns_hosts_query(R->hosts, F->query, &error))) goto error; if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); } } dgoto(R->sp, DNS_R_SWITCH); case DNS_R_CACHE: error = 0; if (!F->query && (error = dns_q_make(&F->query, R->qname, R->qtype, R->qclass, F->qflags))) goto error; if (dns_p_setptr(&F->answer, R->cache->query(F->query, R->cache, &error))) { if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); dgoto(R->sp, DNS_R_SWITCH); } else if (error) goto error; F->state++; case DNS_R_SUBMIT: if ((error = R->cache->submit(F->query, R->cache))) goto error; F->state++; case DNS_R_CHECK: if ((error = R->cache->check(R->cache))) goto error; F->state++; case DNS_R_FETCH: error = 0; if (dns_p_setptr(&F->answer, R->cache->fetch(R->cache, &error))) { if (dns_p_count(F->answer, DNS_S_AN) > 0) dgoto(R->sp, DNS_R_FINISH); dns_p_setptr(&F->answer, NULL); dgoto(R->sp, DNS_R_SWITCH); } else if (error) goto error; dgoto(R->sp, DNS_R_SWITCH); case DNS_R_BIND: if (R->sp > 0) { if (!F->query) goto noquery; dgoto(R->sp, DNS_R_HINTS); } R->search = 0; F->state++; case DNS_R_SEARCH: /* * XXX: We probably should only apply the domain search * algorithm if R->sp == 0. */ if (!(len = dns_resconf_search(u.name, sizeof u.name, R->qname, R->qlen, R->resconf, &R->search))) dgoto(R->sp, DNS_R_SWITCH); if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags))) goto error; F->state++; case DNS_R_HINTS: if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error))) goto error; F->state++; case DNS_R_ITERATE: dns_rr_i_init(&F->hints_i, F->hints); F->hints_i.section = DNS_S_AUTHORITY; F->hints_i.type = DNS_T_NS; F->hints_i.sort = &dns_res_nameserv_cmp; F->hints_i.args[0] = F->hints->end; F->state++; case DNS_R_FOREACH_NS: dns_rr_i_save(&F->hints_i); /* Load our next nameserver host. */ if (!dns_rr_grep(&F->hints_ns, 1, &F->hints_i, F->hints, &error)) { if (++F->attempts < R->resconf->options.attempts) dgoto(R->sp, DNS_R_ITERATE); dgoto(R->sp, DNS_R_SWITCH); } dns_rr_i_init(&F->hints_j, F->hints); /* Assume there are glue records */ dgoto(R->sp, DNS_R_FOREACH_A); case DNS_R_RESOLV0_NS: /* Have we reached our max depth? */ if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_FOREACH_NS); if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) goto error; if ((error = dns_res_frame_prepare(R, &F[1], u.ns.host, DNS_T_A, DNS_C_IN))) goto error; F->state++; dgoto(++R->sp, DNS_R_INIT); case DNS_R_RESOLV1_NS: if (!(len = dns_d_expand(u.host, sizeof u.host, 12, F[1].query, &error))) goto error; else if (len >= sizeof u.host) goto toolong; dns_rr_foreach(&rr, F[1].answer, .name = u.host, .type = DNS_T_A, .section = (DNS_S_ALL & ~DNS_S_QD)) { rr.section = DNS_S_AR; if ((error = dns_rr_copy(F->hints, &rr, F[1].answer))) goto error; dns_rr_i_rewind(&F->hints_i); /* Now there's glue. */ } dgoto(R->sp, DNS_R_FOREACH_NS); case DNS_R_FOREACH_A: { struct dns_a a; struct sockaddr_in sin; /* * NOTE: Iterator initialized in DNS_R_FOREACH_NS because * this state is re-entrant, but we need to reset * .name to a valid pointer each time. */ if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints))) goto error; F->hints_j.name = u.ns.host; F->hints_j.type = DNS_T_A; F->hints_j.section = DNS_S_ALL & ~DNS_S_QD; if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) { if (!dns_rr_i_count(&F->hints_j)) dgoto(R->sp, DNS_R_RESOLV0_NS); dgoto(R->sp, DNS_R_FOREACH_NS); } if ((error = dns_a_parse(&a, &rr, F->hints))) goto error; sin.sin_family = AF_INET; sin.sin_addr = a.addr; if (R->sp == 0) sin.sin_port = dns_hints_port(R->hints, AF_INET, &sin.sin_addr); else sin.sin_port = htons(53); if (DNS_DEBUG) { char addr[INET_ADDRSTRLEN + 1]; dns_a_print(addr, sizeof addr, &a); dns_header(F->query)->qid = dns_so_mkqid(&R->so); DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp); } if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin))) goto error; F->state++; } case DNS_R_QUERY_A: if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf)) dgoto(R->sp, DNS_R_FOREACH_A); if ((error = dns_so_check(&R->so))) goto error; if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error))) goto error; if (DNS_DEBUG) { DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp); } if (dns_p_rcode(F->answer) == DNS_RC_FORMERR || dns_p_rcode(F->answer) == DNS_RC_NOTIMP || dns_p_rcode(F->answer) == DNS_RC_BADVERS) { /* Temporarily disable EDNS0 and try again. */ if (F->qflags & DNS_Q_EDNS0) { F->qflags &= ~DNS_Q_EDNS0; if ((error = dns_q_remake(&F->query, F->qflags))) goto error; dgoto(R->sp, DNS_R_FOREACH_A); } } if ((error = dns_rr_parse(&rr, 12, F->query))) goto error; if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error))) goto error; else if (len >= sizeof u.name) goto toolong; dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) { dgoto(R->sp, DNS_R_FINISH); /* Found */ } dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) { F->ans_cname = rr; dgoto(R->sp, DNS_R_CNAME0_A); } /* * XXX: The condition here should probably check whether * R->sp == 0, because DNS_R_SEARCH runs regardless of * options.recurse. See DNS_R_BIND. */ if (!R->resconf->options.recurse) { /* Make first answer our tentative answer */ if (!R->nodata) dns_p_movptr(&R->nodata, &F->answer); dgoto(R->sp, DNS_R_SEARCH); } dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) { dns_p_movptr(&F->hints, &F->answer); dgoto(R->sp, DNS_R_ITERATE); } /* XXX: Should this go further up? */ if (dns_header(F->answer)->aa) dgoto(R->sp, DNS_R_FINISH); /* XXX: Should we copy F->answer to R->nodata? */ dgoto(R->sp, DNS_R_FOREACH_A); case DNS_R_CNAME0_A: if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_FINISH); if ((error = dns_cname_parse(&u.cname, &F->ans_cname, F->answer))) goto error; if ((error = dns_res_frame_prepare(R, &F[1], u.cname.host, dns_rr_type(12, F->query), DNS_C_IN))) goto error; F->state++; dgoto(++R->sp, DNS_R_INIT); case DNS_R_CNAME1_A: if (!(P = dns_res_merge(F->answer, F[1].answer, &error))) goto error; dns_p_setptr(&F->answer, P); dgoto(R->sp, DNS_R_FINISH); case DNS_R_FINISH: if (!F->answer) goto noanswer; if (!R->resconf->options.smart || R->sp > 0) dgoto(R->sp, DNS_R_DONE); R->smart.section = DNS_S_AN; R->smart.type = R->qtype; dns_rr_i_init(&R->smart, F->answer); F->state++; case DNS_R_SMART0_A: if (&F[1] >= endof(R->stack)) dgoto(R->sp, DNS_R_DONE); while (dns_rr_grep(&rr, 1, &R->smart, F->answer, &error)) { union { struct dns_ns ns; struct dns_mx mx; struct dns_srv srv; } rd; const char *qname; enum dns_type qtype; enum dns_class qclass; switch (rr.type) { case DNS_T_NS: if ((error = dns_ns_parse(&rd.ns, &rr, F->answer))) goto error; qname = rd.ns.host; qtype = DNS_T_A; qclass = DNS_C_IN; break; case DNS_T_MX: if ((error = dns_mx_parse(&rd.mx, &rr, F->answer))) goto error; qname = rd.mx.host; qtype = DNS_T_A; qclass = DNS_C_IN; break; case DNS_T_SRV: if ((error = dns_srv_parse(&rd.srv, &rr, F->answer))) goto error; qname = rd.srv.target; qtype = DNS_T_A; qclass = DNS_C_IN; break; default: continue; } /* switch() */ if ((error = dns_res_frame_prepare(R, &F[1], qname, qtype, qclass))) goto error; F->state++; dgoto(++R->sp, DNS_R_INIT); } /* while() */ /* * NOTE: SMTP specification says to fallback to A record. * * XXX: Should we add a mock MX answer? */ if (R->qtype == DNS_T_MX && R->smart.state.count == 0) { if ((error = dns_res_frame_prepare(R, &F[1], R->qname, DNS_T_A, DNS_C_IN))) goto error; R->smart.state.count++; F->state++; dgoto(++R->sp, DNS_R_INIT); } dgoto(R->sp, DNS_R_DONE); case DNS_R_SMART1_A: if (!F[1].answer) goto noanswer; /* * FIXME: For CNAME chains (which are typically illegal in * this context), we should rewrite the record host name * to the original smart qname. All the user cares about * is locating that A/AAAA record. */ dns_rr_foreach(&rr, F[1].answer, .section = DNS_S_AN, .type = DNS_T_A) { rr.section = DNS_S_AR; if (dns_rr_exists(&rr, F[1].answer, F->answer)) continue; while ((error = dns_rr_copy(F->answer, &rr, F[1].answer))) { if (error != DNS_ENOBUFS) goto error; if ((error = dns_p_grow(&F->answer))) goto error; } } dgoto(R->sp, DNS_R_SMART0_A); case DNS_R_DONE: if (!F->answer) goto noanswer; if (R->sp > 0) dgoto(--R->sp, F[-1].state); break; case DNS_R_SERVFAIL: if (!dns_p_setptr(&F->answer, dns_p_make(DNS_P_QBUFSIZ, &error))) goto error; dns_header(F->answer)->qr = 1; dns_header(F->answer)->rcode = DNS_RC_SERVFAIL; if ((error = dns_p_push(F->answer, DNS_S_QD, R->qname, strlen(R->qname), R->qtype, R->qclass, 0, 0))) goto error; dgoto(R->sp, DNS_R_DONE); default: error = EINVAL; goto error; } /* switch () */ return 0; noquery: error = DNS_ENOQUERY; goto error; noanswer: error = DNS_ENOANSWER; goto error; toolong: error = DNS_EILLEGAL; /* FALL THROUGH */ error: return error; } /* dns_res_exec() */ #undef goto void dns_res_clear(struct dns_resolver *R) { switch (R->stack[R->sp].state) { case DNS_R_CHECK: R->cache->clear(R->cache); break; default: dns_so_clear(&R->so); break; } } /* dns_res_clear() */ static int dns_res_events2(struct dns_resolver *R, enum dns_events type) { int events; switch (R->stack[R->sp].state) { case DNS_R_CHECK: events = R->cache->events(R->cache); return (type == DNS_LIBEVENT)? DNS_POLL2EV(events) : events; default: return dns_so_events2(&R->so, type); } } /* dns_res_events2() */ int dns_res_events(struct dns_resolver *R) { return dns_res_events2(R, R->so.opts.events); } /* dns_res_events() */ int dns_res_pollfd(struct dns_resolver *R) { switch (R->stack[R->sp].state) { case DNS_R_CHECK: return R->cache->pollfd(R->cache); default: return dns_so_pollfd(&R->so); } } /* dns_res_pollfd() */ time_t dns_res_timeout(struct dns_resolver *R) { time_t elapsed; switch (R->stack[R->sp].state) { #if 0 case DNS_R_QUERY_AAAA: #endif case DNS_R_QUERY_A: elapsed = dns_so_elapsed(&R->so); if (elapsed <= dns_resconf_timeout(R->resconf)) return R->resconf->options.timeout - elapsed; break; default: break; } /* switch() */ /* * NOTE: We're not in a pollable state, or the user code hasn't * called dns_res_check properly. The calling code is probably * broken. Put them into a slow-burn pattern. */ return 1; } /* dns_res_timeout() */ time_t dns_res_elapsed(struct dns_resolver *R) { return dns_elapsed(&R->elapsed); } /* dns_res_elapsed() */ int dns_res_poll(struct dns_resolver *R, int timeout) { return dns_poll(dns_res_pollfd(R), dns_res_events2(R, DNS_SYSPOLL), timeout); } /* dns_res_poll() */ int dns_res_submit2(struct dns_resolver *R, const char *qname, size_t qlen, enum dns_type qtype, enum dns_class qclass) { dns_res_reset(R); /* Don't anchor; that can conflict with searchlist generation. */ dns_d_init(R->qname, sizeof R->qname, qname, (R->qlen = qlen), 0); R->qtype = qtype; R->qclass = qclass; dns_begin(&R->elapsed); return 0; } /* dns_res_submit2() */ int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass) { return dns_res_submit2(R, qname, strlen(qname), qtype, qclass); } /* dns_res_submit() */ int dns_res_check(struct dns_resolver *R) { int error; if (R->stack[0].state != DNS_R_DONE) { if ((error = dns_res_exec(R))) return error; } return 0; } /* dns_res_check() */ struct dns_packet *dns_res_fetch(struct dns_resolver *R, int *error) { struct dns_packet *P = NULL; if (R->stack[0].state != DNS_R_DONE) return *error = DNS_EUNKNOWN, (void *)0; if (!dns_p_movptr(&P, &R->stack[0].answer)) return *error = DNS_EFETCHED, (void *)0; return P; } /* dns_res_fetch() */ static struct dns_packet *dns_res_fetch_and_study(struct dns_resolver *R, int *_error) { struct dns_packet *P = NULL; int error; if (!(P = dns_res_fetch(R, &error))) goto error; if ((error = dns_p_study(P))) goto error; return P; error: *_error = error; dns_p_free(P); return NULL; } /* dns_res_fetch_and_study() */ struct dns_packet *dns_res_query(struct dns_resolver *res, const char *qname, enum dns_type qtype, enum dns_class qclass, int timeout, int *error_) { int error; if ((error = dns_res_submit(res, qname, qtype, qclass))) goto error; while ((error = dns_res_check(res))) { if (dns_res_elapsed(res) > timeout) error = DNS_ETIMEDOUT; if (error != DNS_EAGAIN) goto error; if ((error = dns_res_poll(res, 1))) goto error; } return dns_res_fetch(res, error_); error: *error_ = error; return 0; } /* dns_res_query() */ const struct dns_stat *dns_res_stat(struct dns_resolver *res) { return dns_so_stat(&res->so); } /* dns_res_stat() */ void dns_res_sethints(struct dns_resolver *res, struct dns_hints *hints) { dns_hints_acquire(hints); /* acquire first in case same hints object */ dns_hints_close(res->hints); res->hints = hints; } /* dns_res_sethints() */ /* * A D D R I N F O R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_addrinfo { struct addrinfo hints; struct dns_resolver *res; char qname[DNS_D_MAXNAME + 1]; enum dns_type qtype; unsigned short qport, port; struct { unsigned long todo; int state; int atype; enum dns_type qtype; } af; struct dns_packet *answer; struct dns_packet *glue; struct dns_rr_i i, g; struct dns_rr rr; char cname[DNS_D_MAXNAME + 1]; char i_cname[DNS_D_MAXNAME + 1], g_cname[DNS_D_MAXNAME + 1]; int g_depth; int state; int found; struct dns_stat st; }; /* struct dns_addrinfo */ #define DNS_AI_AFMAX 32 #define DNS_AI_AF2INDEX(af) (1UL << ((af) - 1)) static inline unsigned long dns_ai_af2index(int af) { dns_static_assert(dns_same_type(unsigned long, DNS_AI_AF2INDEX(1), 1), "internal type mismatch"); dns_static_assert(dns_same_type(unsigned long, ((struct dns_addrinfo *)0)->af.todo, 1), "internal type mismatch"); return (af > 0 && af <= DNS_AI_AFMAX)? DNS_AI_AF2INDEX(af) : 0; } static int dns_ai_setaf(struct dns_addrinfo *ai, int af, int qtype) { ai->af.atype = af; ai->af.qtype = qtype; ai->af.todo &= ~dns_ai_af2index(af); return af; } /* dns_ai_setaf() */ #define DNS_SM_RESTORE \ do { pc = 0xff & (ai->af.state >> 0); i = 0xff & (ai->af.state >> 8); } while (0) #define DNS_SM_SAVE \ do { ai->af.state = ((0xff & pc) << 0) | ((0xff & i) << 8); } while (0) static int dns_ai_nextaf(struct dns_addrinfo *ai) { int i, pc; dns_static_assert(AF_UNSPEC == 0, "AF_UNSPEC constant not 0"); dns_static_assert(AF_INET <= DNS_AI_AFMAX, "AF_INET constant too large"); dns_static_assert(AF_INET6 <= DNS_AI_AFMAX, "AF_INET6 constant too large"); DNS_SM_ENTER; if (ai->res) { /* * NB: On OpenBSD, at least, the types of entries resolved * is the intersection of the /etc/resolv.conf families and * the families permitted by the .ai_type hint. So if * /etc/resolv.conf has "family inet4" and .ai_type * is AF_INET6, then the address ::1 will return 0 entries * even if AI_NUMERICHOST is specified in .ai_flags. */ while (i < (int)lengthof(ai->res->resconf->family)) { int af = ai->res->resconf->family[i++]; if (af == AF_UNSPEC) { DNS_SM_EXIT; } else if (af < 0 || af > DNS_AI_AFMAX) { continue; } else if (!(DNS_AI_AF2INDEX(af) & ai->af.todo)) { continue; } else if (af == AF_INET) { DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); } else if (af == AF_INET6) { DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); } } } else { /* * NB: If we get here than AI_NUMERICFLAGS should be set and * order shouldn't matter. */ if (DNS_AI_AF2INDEX(AF_INET) & ai->af.todo) DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET, DNS_T_A)); if (DNS_AI_AF2INDEX(AF_INET6) & ai->af.todo) DNS_SM_YIELD(dns_ai_setaf(ai, AF_INET6, DNS_T_AAAA)); } DNS_SM_LEAVE; return dns_ai_setaf(ai, AF_UNSPEC, 0); } /* dns_ai_nextaf() */ #undef DNS_SM_RESTORE #undef DNS_SM_SAVE static enum dns_type dns_ai_qtype(struct dns_addrinfo *ai) { return (ai->qtype)? ai->qtype : ai->af.qtype; } /* dns_ai_qtype() */ static dns_error_t dns_ai_parseport(unsigned short *port, const char *serv, const struct addrinfo *hints) { const char *cp = serv; unsigned long n = 0; while (*cp >= '0' && *cp <= '9' && n < 65536) { n *= 10; n += *cp++ - '0'; } if (*cp == '\0') { if (cp == serv || n >= 65536) return DNS_ESERVICE; *port = n; return 0; } if (hints->ai_flags & AI_NUMERICSERV) return DNS_ESERVICE; /* TODO: try getaddrinfo(NULL, serv, { .ai_flags = AI_NUMERICSERV }) */ return DNS_ESERVICE; } /* dns_ai_parseport() */ struct dns_addrinfo *dns_ai_open(const char *host, const char *serv, enum dns_type qtype, const struct addrinfo *hints, struct dns_resolver *res, int *_error) { static const struct dns_addrinfo ai_initializer; struct dns_addrinfo *ai; int error; if (res) { dns_res_acquire(res); } else if (!(hints->ai_flags & AI_NUMERICHOST)) { /* * NOTE: it's assumed that *_error is set from a previous * API function call, such as dns_res_stub(). Should change * this semantic, but it's applied elsewhere, too. */ if (!*_error) *_error = EINVAL; return NULL; } if (!(ai = malloc(sizeof *ai))) goto syerr; *ai = ai_initializer; ai->hints = *hints; ai->res = res; res = NULL; if (sizeof ai->qname <= dns_strlcpy(ai->qname, host, sizeof ai->qname)) { error = ENAMETOOLONG; goto error; } ai->qtype = qtype; ai->qport = 0; if (serv && (error = dns_ai_parseport(&ai->qport, serv, hints))) goto error; ai->port = ai->qport; /* * FIXME: If an explicit A or AAAA record type conflicts with * .ai_family or with resconf.family (i.e. AAAA specified but * AF_INET6 not in interection of .ai_family and resconf.family), * then what? */ switch (ai->qtype) { case DNS_T_A: ai->af.todo = DNS_AI_AF2INDEX(AF_INET); break; case DNS_T_AAAA: ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); break; default: /* 0, MX, SRV, etc */ switch (ai->hints.ai_family) { case AF_UNSPEC: ai->af.todo = DNS_AI_AF2INDEX(AF_INET) | DNS_AI_AF2INDEX(AF_INET6); break; case AF_INET: ai->af.todo = DNS_AI_AF2INDEX(AF_INET); break; case AF_INET6: ai->af.todo = DNS_AI_AF2INDEX(AF_INET6); break; default: break; } } return ai; syerr: error = dns_syerr(); error: *_error = error; dns_ai_close(ai); dns_res_close(res); return NULL; } /* dns_ai_open() */ void dns_ai_close(struct dns_addrinfo *ai) { if (!ai) return; dns_res_close(ai->res); if (ai->answer != ai->glue) dns_p_free(ai->glue); dns_p_free(ai->answer); free(ai); } /* dns_ai_close() */ static int dns_ai_setent(struct addrinfo **ent, union dns_any *any, enum dns_type type, struct dns_addrinfo *ai) { struct sockaddr *saddr; struct sockaddr_in sin; struct sockaddr_in6 sin6; const char *cname; size_t clen; switch (type) { case DNS_T_A: saddr = memset(&sin, '\0', sizeof sin); sin.sin_family = AF_INET; sin.sin_port = htons(ai->port); memcpy(&sin.sin_addr, any, sizeof sin.sin_addr); break; case DNS_T_AAAA: saddr = memset(&sin6, '\0', sizeof sin6); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(ai->port); memcpy(&sin6.sin6_addr, any, sizeof sin6.sin6_addr); break; default: return EINVAL; } /* switch() */ if (ai->hints.ai_flags & AI_CANONNAME) { cname = (*ai->cname)? ai->cname : ai->qname; clen = strlen(cname); } else { cname = NULL; clen = 0; } if (!(*ent = malloc(sizeof **ent + dns_sa_len(saddr) + ((ai->hints.ai_flags & AI_CANONNAME)? clen + 1 : 0)))) return dns_syerr(); memset(*ent, '\0', sizeof **ent); (*ent)->ai_family = saddr->sa_family; (*ent)->ai_socktype = ai->hints.ai_socktype; (*ent)->ai_protocol = ai->hints.ai_protocol; (*ent)->ai_addr = memcpy((unsigned char *)*ent + sizeof **ent, saddr, dns_sa_len(saddr)); (*ent)->ai_addrlen = dns_sa_len(saddr); if (ai->hints.ai_flags & AI_CANONNAME) (*ent)->ai_canonname = memcpy((unsigned char *)*ent + sizeof **ent + dns_sa_len(saddr), cname, clen + 1); ai->found++; return 0; } /* dns_ai_setent() */ enum dns_ai_state { DNS_AI_S_INIT, DNS_AI_S_NEXTAF, DNS_AI_S_NUMERIC, DNS_AI_S_SUBMIT, DNS_AI_S_CHECK, DNS_AI_S_FETCH, DNS_AI_S_FOREACH_I, DNS_AI_S_INIT_G, DNS_AI_S_ITERATE_G, DNS_AI_S_FOREACH_G, DNS_AI_S_SUBMIT_G, DNS_AI_S_CHECK_G, DNS_AI_S_FETCH_G, DNS_AI_S_DONE, }; /* enum dns_ai_state */ #define dns_ai_goto(which) do { ai->state = (which); goto exec; } while (0) int dns_ai_nextent(struct addrinfo **ent, struct dns_addrinfo *ai) { struct dns_packet *ans, *glue; struct dns_rr rr; char qname[DNS_D_MAXNAME + 1]; union dns_any any; size_t qlen, clen; int error; *ent = 0; exec: switch (ai->state) { case DNS_AI_S_INIT: ai->state++; case DNS_AI_S_NEXTAF: if (!dns_ai_nextaf(ai)) dns_ai_goto(DNS_AI_S_DONE); ai->state++; case DNS_AI_S_NUMERIC: if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) { if (ai->af.atype == AF_INET) { ai->state = DNS_AI_S_NEXTAF; return dns_ai_setent(ent, &any, DNS_T_A, ai); } else { dns_ai_goto(DNS_AI_S_NEXTAF); } } if (1 == dns_inet_pton(AF_INET6, ai->qname, &any.aaaa)) { if (ai->af.atype == AF_INET6) { ai->state = DNS_AI_S_NEXTAF; return dns_ai_setent(ent, &any, DNS_T_AAAA, ai); } else { dns_ai_goto(DNS_AI_S_NEXTAF); } } if (ai->hints.ai_flags & AI_NUMERICHOST) dns_ai_goto(DNS_AI_S_NEXTAF); ai->state++; case DNS_AI_S_SUBMIT: assert(ai->res); if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN))) return error; ai->state++; case DNS_AI_S_CHECK: if ((error = dns_res_check(ai->res))) return error; ai->state++; case DNS_AI_S_FETCH: if (!(ans = dns_res_fetch_and_study(ai->res, &error))) return error; if (ai->glue != ai->answer) dns_p_free(ai->glue); ai->glue = dns_p_movptr(&ai->answer, &ans); /* Search generator may have changed the qname. */ if (!(qlen = dns_d_expand(qname, sizeof qname, 12, ai->answer, &error))) return error; else if (qlen >= sizeof qname) return DNS_EILLEGAL; if (!dns_d_cname(ai->cname, sizeof ai->cname, qname, qlen, ai->answer, &error)) return error; dns_strlcpy(ai->i_cname, ai->cname, sizeof ai->i_cname); dns_rr_i_init(&ai->i, ai->answer); ai->i.section = DNS_S_AN; ai->i.name = ai->i_cname; ai->i.type = dns_ai_qtype(ai); ai->i.sort = &dns_rr_i_order; ai->state++; case DNS_AI_S_FOREACH_I: if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error)) dns_ai_goto(DNS_AI_S_NEXTAF); if ((error = dns_any_parse(&any, &rr, ai->answer))) return error; ai->port = ai->qport; switch (rr.type) { case DNS_T_A: case DNS_T_AAAA: return dns_ai_setent(ent, &any, rr.type, ai); default: if (!(clen = dns_any_cname(ai->cname, sizeof ai->cname, &any, rr.type))) dns_ai_goto(DNS_AI_S_FOREACH_I); /* * Find the "real" canonical name. Some authorities * publish aliases where an RFC defines a canonical * name. We trust that the resolver followed any * CNAME chains on it's own, regardless of whether * the "smart" option is enabled. */ if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, clen, ai->answer, &error)) return error; if (rr.type == DNS_T_SRV) ai->port = any.srv.port; break; } /* switch() */ ai->state++; case DNS_AI_S_INIT_G: ai->g_depth = 0; ai->state++; case DNS_AI_S_ITERATE_G: dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname); dns_rr_i_init(&ai->g, ai->glue); ai->g.section = DNS_S_ALL & ~DNS_S_QD; ai->g.name = ai->g_cname; ai->g.type = ai->af.qtype; ai->state++; case DNS_AI_S_FOREACH_G: if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) { if (dns_rr_i_count(&ai->g) > 0) dns_ai_goto(DNS_AI_S_FOREACH_I); else dns_ai_goto(DNS_AI_S_SUBMIT_G); } if ((error = dns_any_parse(&any, &rr, ai->glue))) return error; return dns_ai_setent(ent, &any, rr.type, ai); case DNS_AI_S_SUBMIT_G: /* skip if already queried */ if (dns_rr_grep(&rr, 1, dns_rr_i_new(ai->glue, .section = DNS_S_QD, .name = ai->g.name, .type = ai->g.type), ai->glue, &error)) dns_ai_goto(DNS_AI_S_FOREACH_I); /* skip if we recursed (CNAME chains should have been handled in the resolver) */ if (++ai->g_depth > 1) dns_ai_goto(DNS_AI_S_FOREACH_I); if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN))) return error; ai->state++; case DNS_AI_S_CHECK_G: if ((error = dns_res_check(ai->res))) return error; ai->state++; case DNS_AI_S_FETCH_G: if (!(ans = dns_res_fetch_and_study(ai->res, &error))) return error; glue = dns_p_merge(ai->glue, DNS_S_ALL, ans, DNS_S_ALL, &error); dns_p_setptr(&ans, NULL); if (!glue) return error; if (ai->glue != ai->answer) dns_p_free(ai->glue); ai->glue = glue; if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->g.name, strlen(ai->g.name), ai->glue, &error)) dns_ai_goto(DNS_AI_S_FOREACH_I); dns_ai_goto(DNS_AI_S_ITERATE_G); case DNS_AI_S_DONE: if (ai->found) { return ENOENT; /* TODO: Just return 0 */ } else if (ai->answer) { switch (dns_p_rcode(ai->answer)) { case DNS_RC_NOERROR: /* FALL THROUGH */ case DNS_RC_NXDOMAIN: return DNS_ENONAME; default: return DNS_EFAIL; } } else { return DNS_EFAIL; } default: return EINVAL; } /* switch() */ } /* dns_ai_nextent() */ time_t dns_ai_elapsed(struct dns_addrinfo *ai) { return (ai->res)? dns_res_elapsed(ai->res) : 0; } /* dns_ai_elapsed() */ void dns_ai_clear(struct dns_addrinfo *ai) { if (ai->res) dns_res_clear(ai->res); } /* dns_ai_clear() */ int dns_ai_events(struct dns_addrinfo *ai) { return (ai->res)? dns_res_events(ai->res) : 0; } /* dns_ai_events() */ int dns_ai_pollfd(struct dns_addrinfo *ai) { return (ai->res)? dns_res_pollfd(ai->res) : -1; } /* dns_ai_pollfd() */ time_t dns_ai_timeout(struct dns_addrinfo *ai) { return (ai->res)? dns_res_timeout(ai->res) : 0; } /* dns_ai_timeout() */ int dns_ai_poll(struct dns_addrinfo *ai, int timeout) { return (ai->res)? dns_res_poll(ai->res, timeout) : 0; } /* dns_ai_poll() */ size_t dns_ai_print(void *_dst, size_t lim, struct addrinfo *ent, struct dns_addrinfo *ai) { struct dns_buf dst = DNS_B_INTO(_dst, lim); char addr[DNS_PP_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; dns_b_puts(&dst, "[ "); dns_b_puts(&dst, ai->qname); dns_b_puts(&dst, " IN "); if (ai->qtype) { dns_b_puts(&dst, dns_strtype(ai->qtype)); } else if (ent->ai_family == AF_INET) { dns_b_puts(&dst, dns_strtype(DNS_T_A)); } else if (ent->ai_family == AF_INET6) { dns_b_puts(&dst, dns_strtype(DNS_T_AAAA)); } else { dns_b_puts(&dst, "0"); } dns_b_puts(&dst, " ]\n"); dns_b_puts(&dst, ".ai_family = "); switch (ent->ai_family) { case AF_INET: dns_b_puts(&dst, "AF_INET"); break; case AF_INET6: dns_b_puts(&dst, "AF_INET6"); break; default: dns_b_fmtju(&dst, ent->ai_family, 0); break; } dns_b_putc(&dst, '\n'); dns_b_puts(&dst, ".ai_socktype = "); switch (ent->ai_socktype) { case SOCK_STREAM: dns_b_puts(&dst, "SOCK_STREAM"); break; case SOCK_DGRAM: dns_b_puts(&dst, "SOCK_DGRAM"); break; default: dns_b_fmtju(&dst, ent->ai_socktype, 0); break; } dns_b_putc(&dst, '\n'); dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr, NULL), addr, sizeof addr); dns_b_puts(&dst, ".ai_addr = ["); dns_b_puts(&dst, addr); dns_b_puts(&dst, "]:"); dns_b_fmtju(&dst, ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)), 0); dns_b_putc(&dst, '\n'); dns_b_puts(&dst, ".ai_canonname = "); dns_b_puts(&dst, (ent->ai_canonname)? ent->ai_canonname : "[NULL]"); dns_b_putc(&dst, '\n'); return dns_b_strllen(&dst); } /* dns_ai_print() */ const struct dns_stat *dns_ai_stat(struct dns_addrinfo *ai) { return (ai->res)? dns_res_stat(ai->res) : &ai->st; } /* dns_ai_stat() */ /* * M I S C E L L A N E O U S R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static const struct { char name[16]; enum dns_section type; } dns_sections[] = { { "QUESTION", DNS_S_QUESTION }, { "QD", DNS_S_QUESTION }, { "ANSWER", DNS_S_ANSWER }, { "AN", DNS_S_ANSWER }, { "AUTHORITY", DNS_S_AUTHORITY }, { "NS", DNS_S_AUTHORITY }, { "ADDITIONAL", DNS_S_ADDITIONAL }, { "AR", DNS_S_ADDITIONAL }, }; const char *(dns_strsection)(enum dns_section section, void *_dst, size_t lim) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; for (i = 0; i < lengthof(dns_sections); i++) { if (dns_sections[i].type & section) { dns_b_puts(&dst, dns_sections[i].name); section &= ~dns_sections[i].type; if (section) dns_b_putc(&dst, '|'); } } if (section || dst.p == dst.base) dns_b_fmtju(&dst, (0xffff & section), 0); return dns_b_tostring(&dst); } /* dns_strsection() */ enum dns_section dns_isection(const char *src) { enum dns_section section = 0; char sbuf[128]; char *name, *next; unsigned i; dns_strlcpy(sbuf, src, sizeof sbuf); next = sbuf; while ((name = dns_strsep(&next, "|+, \t"))) { for (i = 0; i < lengthof(dns_sections); i++) { if (!strcasecmp(dns_sections[i].name, name)) { section |= dns_sections[i].type; break; } } } return section; } /* dns_isection() */ static const struct { char name[8]; enum dns_class type; } dns_classes[] = { { "IN", DNS_C_IN }, }; const char *(dns_strclass)(enum dns_class type, void *_dst, size_t lim) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; for (i = 0; i < lengthof(dns_classes); i++) { if (dns_classes[i].type == type) { dns_b_puts(&dst, dns_classes[i].name); break; } } if (dst.p == dst.base) dns_b_fmtju(&dst, (0xffff & type), 0); return dns_b_tostring(&dst); } /* dns_strclass() */ enum dns_class dns_iclass(const char *name) { unsigned i, class; for (i = 0; i < lengthof(dns_classes); i++) { if (!strcasecmp(dns_classes[i].name, name)) return dns_classes[i].type; } class = 0; while (dns_isdigit(*name)) { class *= 10; class += *name++ - '0'; } return DNS_PP_MIN(class, 0xffff); } /* dns_iclass() */ const char *(dns_strtype)(enum dns_type type, void *_dst, size_t lim) { struct dns_buf dst = DNS_B_INTO(_dst, lim); unsigned i; for (i = 0; i < lengthof(dns_rrtypes); i++) { if (dns_rrtypes[i].type == type) { dns_b_puts(&dst, dns_rrtypes[i].name); break; } } if (dst.p == dst.base) dns_b_fmtju(&dst, (0xffff & type), 0); return dns_b_tostring(&dst); } /* dns_strtype() */ enum dns_type dns_itype(const char *name) { unsigned i, type; for (i = 0; i < lengthof(dns_rrtypes); i++) { if (!strcasecmp(dns_rrtypes[i].name, name)) return dns_rrtypes[i].type; } type = 0; while (dns_isdigit(*name)) { type *= 10; type += *name++ - '0'; } return DNS_PP_MIN(type, 0xffff); } /* dns_itype() */ static char dns_opcodes[16][16] = { [DNS_OP_QUERY] = "QUERY", [DNS_OP_IQUERY] = "IQUERY", [DNS_OP_STATUS] = "STATUS", [DNS_OP_NOTIFY] = "NOTIFY", [DNS_OP_UPDATE] = "UPDATE", }; static const char *dns__strcode(int code, volatile char *dst, size_t lim) { char _tmp[48] = ""; struct dns_buf tmp; size_t p; assert(lim > 0); dns_b_fmtju(dns_b_into(&tmp, _tmp, DNS_PP_MIN(sizeof _tmp, lim - 1)), code, 0); /* copy downwards so first byte is copied last (see below) */ p = (size_t)(tmp.p - tmp.base); dst[p] = '\0'; while (p--) dst[p] = _tmp[p]; return (const char *)dst; } const char *dns_stropcode(enum dns_opcode opcode) { opcode = (unsigned)opcode % lengthof(dns_opcodes); if ('\0' == dns_opcodes[opcode][0]) return dns__strcode(opcode, dns_opcodes[opcode], sizeof dns_opcodes[opcode]); return dns_opcodes[opcode]; } /* dns_stropcode() */ enum dns_opcode dns_iopcode(const char *name) { unsigned opcode; for (opcode = 0; opcode < lengthof(dns_opcodes); opcode++) { if (!strcasecmp(name, dns_opcodes[opcode])) return opcode; } opcode = 0; while (dns_isdigit(*name)) { opcode *= 10; opcode += *name++ - '0'; } return DNS_PP_MIN(opcode, 0x0f); } /* dns_iopcode() */ static char dns_rcodes[32][16] = { [DNS_RC_NOERROR] = "NOERROR", [DNS_RC_FORMERR] = "FORMERR", [DNS_RC_SERVFAIL] = "SERVFAIL", [DNS_RC_NXDOMAIN] = "NXDOMAIN", [DNS_RC_NOTIMP] = "NOTIMP", [DNS_RC_REFUSED] = "REFUSED", [DNS_RC_YXDOMAIN] = "YXDOMAIN", [DNS_RC_YXRRSET] = "YXRRSET", [DNS_RC_NXRRSET] = "NXRRSET", [DNS_RC_NOTAUTH] = "NOTAUTH", [DNS_RC_NOTZONE] = "NOTZONE", /* EDNS(0) extended RCODEs ... */ [DNS_RC_BADVERS] = "BADVERS", }; const char *dns_strrcode(enum dns_rcode rcode) { rcode = (unsigned)rcode % lengthof(dns_rcodes); if ('\0' == dns_rcodes[rcode][0]) return dns__strcode(rcode, dns_rcodes[rcode], sizeof dns_rcodes[rcode]); return dns_rcodes[rcode]; } /* dns_strrcode() */ enum dns_rcode dns_ircode(const char *name) { unsigned rcode; for (rcode = 0; rcode < lengthof(dns_rcodes); rcode++) { if (!strcasecmp(name, dns_rcodes[rcode])) return rcode; } rcode = 0; while (dns_isdigit(*name)) { rcode *= 10; rcode += *name++ - '0'; } return DNS_PP_MIN(rcode, 0xfff); } /* dns_ircode() */ /* * C O M M A N D - L I N E / R E G R E S S I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if DNS_MAIN #include #include #include #include #if _WIN32 #include #endif #if !_WIN32 #include #endif struct { struct { const char *path[8]; unsigned count; } resconf, nssconf, hosts, cache; const char *qname; enum dns_type qtype; int (*sort)(); int verbose; } MAIN = { .sort = &dns_rr_i_packet, }; static void hexdump(const unsigned char *src, size_t len, FILE *fp) { static const unsigned char hex[] = "0123456789abcdef"; static const unsigned char tmpl[] = " | |\n"; unsigned char ln[sizeof tmpl]; const unsigned char *sp, *se; unsigned char *h, *g; unsigned i, n; sp = src; se = sp + len; while (sp < se) { memcpy(ln, tmpl, sizeof ln); h = &ln[2]; g = &ln[53]; for (n = 0; n < 2; n++) { for (i = 0; i < 8 && se - sp > 0; i++, sp++) { h[0] = hex[0x0f & (*sp >> 4)]; h[1] = hex[0x0f & (*sp >> 0)]; h += 3; *g++ = (isgraph(*sp))? *sp : '.'; } h++; } fputs((char *)ln, fp); } return /* void */; } /* hexdump() */ DNS_NORETURN static void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); #if _WIN32 vfprintf(stderr, fmt, ap); exit(EXIT_FAILURE); #else verrx(EXIT_FAILURE, fmt, ap); #endif } /* panic() */ #define panic_(fn, ln, fmt, ...) \ panic(fmt "%0s", (fn), (ln), __VA_ARGS__) #define panic(...) \ panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "") static void *grow(unsigned char *p, size_t size) { void *tmp; if (!(tmp = realloc(p, size))) panic("realloc(%"PRIuZ"): %s", size, dns_strerror(errno)); return tmp; } /* grow() */ static size_t add(size_t a, size_t b) { if (~a < b) panic("%"PRIuZ" + %"PRIuZ": integer overflow", a, b); return a + b; } /* add() */ static size_t append(unsigned char **dst, size_t osize, const void *src, size_t len) { size_t size = add(osize, len); *dst = grow(*dst, size); memcpy(*dst + osize, src, len); return size; } /* append() */ static size_t slurp(unsigned char **dst, size_t osize, FILE *fp, const char *path) { size_t size = osize; unsigned char buf[1024]; size_t count; while ((count = fread(buf, 1, sizeof buf, fp))) size = append(dst, size, buf, count); if (ferror(fp)) panic("%s: %s", path, dns_strerror(errno)); return size; } /* slurp() */ static struct dns_resolv_conf *resconf(void) { static struct dns_resolv_conf *resconf; const char *path; unsigned i; int error; if (resconf) return resconf; if (!(resconf = dns_resconf_open(&error))) panic("dns_resconf_open: %s", dns_strerror(error)); if (!MAIN.resconf.count) MAIN.resconf.path[MAIN.resconf.count++] = "/etc/resolv.conf"; for (i = 0; i < MAIN.resconf.count; i++) { path = MAIN.resconf.path[i]; if (0 == strcmp(path, "-")) error = dns_resconf_loadfile(resconf, stdin); else error = dns_resconf_loadpath(resconf, path); if (error) panic("%s: %s", path, dns_strerror(error)); } for (i = 0; i < MAIN.nssconf.count; i++) { path = MAIN.nssconf.path[i]; if (0 == strcmp(path, "-")) error = dns_nssconf_loadfile(resconf, stdin); else error = dns_nssconf_loadpath(resconf, path); if (error) panic("%s: %s", path, dns_strerror(error)); } if (!MAIN.nssconf.count) { path = "/etc/nsswitch.conf"; if (!(error = dns_nssconf_loadpath(resconf, path))) MAIN.nssconf.path[MAIN.nssconf.count++] = path; else if (error != ENOENT) panic("%s: %s", path, dns_strerror(error)); } return resconf; } /* resconf() */ static struct dns_hosts *hosts(void) { static struct dns_hosts *hosts; const char *path; unsigned i; int error; if (hosts) return hosts; if (!MAIN.hosts.count) { MAIN.hosts.path[MAIN.hosts.count++] = "/etc/hosts"; /* Explicitly test dns_hosts_local() */ if (!(hosts = dns_hosts_local(&error))) panic("%s: %s", "/etc/hosts", dns_strerror(error)); return hosts; } if (!(hosts = dns_hosts_open(&error))) panic("dns_hosts_open: %s", dns_strerror(error)); for (i = 0; i < MAIN.hosts.count; i++) { path = MAIN.hosts.path[i]; if (0 == strcmp(path, "-")) error = dns_hosts_loadfile(hosts, stdin); else error = dns_hosts_loadpath(hosts, path); if (error) panic("%s: %s", path, dns_strerror(error)); } return hosts; } /* hosts() */ #if DNS_CACHE #include "cache.h" static struct dns_cache *cache(void) { static struct cache *cache; const char *path; unsigned i; int error; if (cache) return cache_resi(cache); if (!MAIN.cache.count) return NULL; if (!(cache = cache_open(&error))) panic("%s: %s", MAIN.cache.path[0], dns_strerror(error)); for (i = 0; i < MAIN.cache.count; i++) { path = MAIN.cache.path[i]; if (!strcmp(path, "-")) { if ((error = cache_loadfile(cache, stdin, NULL, 0))) panic("%s: %s", path, dns_strerror(error)); } else if ((error = cache_loadpath(cache, path, NULL, 0))) panic("%s: %s", path, dns_strerror(error)); } return cache_resi(cache); } /* cache() */ #else static struct dns_cache *cache(void) { return NULL; } #endif static void print_packet(struct dns_packet *P, FILE *fp) { dns_p_dump3(P, dns_rr_i_new(P, .sort = MAIN.sort), fp); if (MAIN.verbose > 2) hexdump(P->data, P->end, fp); } /* print_packet() */ static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { struct dns_packet *P = dns_p_new(512); struct dns_packet *Q = dns_p_new(512); enum dns_section section; struct dns_rr rr; int error; union dns_any any; char pretty[sizeof any * 2]; size_t len; P->end = fread(P->data, 1, P->size, stdin); fputs(";; [HEADER]\n", stdout); fprintf(stdout, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr); fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode); fprintf(stdout, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa); fprintf(stdout, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc); fprintf(stdout, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd); fprintf(stdout, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra); fprintf(stdout, ";; rcode : %s(%d)\n", dns_strrcode(dns_p_rcode(P)), dns_p_rcode(P)); section = 0; dns_rr_foreach(&rr, P, .sort = MAIN.sort) { if (section != rr.section) fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section)); if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error))) fprintf(stdout, "%s\n", pretty); dns_rr_copy(Q, &rr, P); section = rr.section; } fputs("; ; ; ; ; ; ; ;\n\n", stdout); section = 0; #if 0 dns_rr_foreach(&rr, Q, .name = "ns8.yahoo.com.") { #else struct dns_rr rrset[32]; struct dns_rr_i *rri = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort); unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error); for (unsigned i = 0; i < rrcount; i++) { rr = rrset[i]; #endif if (section != rr.section) fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(Q, rr.section)); if ((len = dns_rr_print(pretty, sizeof pretty, &rr, Q, &error))) fprintf(stdout, "%s\n", pretty); section = rr.section; } if (MAIN.verbose > 1) { fprintf(stderr, "orig:%"PRIuZ"\n", P->end); hexdump(P->data, P->end, stdout); fprintf(stderr, "copy:%"PRIuZ"\n", Q->end); hexdump(Q->data, Q->end, stdout); } return 0; } /* parse_packet() */ static int parse_domain(int argc, char *argv[]) { char *dn; dn = (argc > 1)? argv[1] : "f.l.google.com"; printf("[%s]\n", dn); dn = dns_d_new(dn); do { puts(dn); } while (dns_d_cleave(dn, strlen(dn) + 1, dn, strlen(dn))); return 0; } /* parse_domain() */ static int trim_domain(int argc, char **argv) { for (argc--, argv++; argc > 0; argc--, argv++) { char name[DNS_D_MAXNAME + 1]; dns_d_trim(name, sizeof name, *argv, strlen(*argv), DNS_D_ANCHOR); puts(name); } return 0; } /* trim_domain() */ static int expand_domain(int argc, char *argv[]) { unsigned short rp = 0; unsigned char *src = NULL; unsigned char *dst; struct dns_packet *pkt; size_t lim = 0, len; int error; if (argc > 1) rp = atoi(argv[1]); len = slurp(&src, 0, stdin, "-"); if (!(pkt = dns_p_make(len, &error))) panic("malloc(%"PRIuZ"): %s", len, dns_strerror(error)); memcpy(pkt->data, src, len); pkt->end = len; lim = 1; dst = grow(NULL, lim); while (lim <= (len = dns_d_expand(dst, lim, rp, pkt, &error))) { lim = add(len, 1); dst = grow(dst, lim); } if (!len) panic("expand: %s", dns_strerror(error)); fwrite(dst, 1, len, stdout); fflush(stdout); free(src); free(dst); free(pkt); return 0; } /* expand_domain() */ static int show_resconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { unsigned i; resconf(); /* load it */ fputs("; SOURCES\n", stdout); for (i = 0; i < MAIN.resconf.count; i++) fprintf(stdout, "; %s\n", MAIN.resconf.path[i]); for (i = 0; i < MAIN.nssconf.count; i++) fprintf(stdout, "; %s\n", MAIN.nssconf.path[i]); fputs(";\n", stdout); dns_resconf_dump(resconf(), stdout); return 0; } /* show_resconf() */ static int show_nssconf(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { unsigned i; resconf(); fputs("# SOURCES\n", stdout); for (i = 0; i < MAIN.resconf.count; i++) fprintf(stdout, "# %s\n", MAIN.resconf.path[i]); for (i = 0; i < MAIN.nssconf.count; i++) fprintf(stdout, "# %s\n", MAIN.nssconf.path[i]); fputs("#\n", stdout); dns_nssconf_dump(resconf(), stdout); return 0; } /* show_nssconf() */ static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { unsigned i; hosts(); fputs("# SOURCES\n", stdout); for (i = 0; i < MAIN.hosts.count; i++) fprintf(stdout, "# %s\n", MAIN.hosts.path[i]); fputs("#\n", stdout); dns_hosts_dump(hosts(), stdout); return 0; } /* show_hosts() */ static int query_hosts(int argc, char *argv[]) { struct dns_packet *Q = dns_p_new(512); struct dns_packet *A; char qname[DNS_D_MAXNAME + 1]; size_t qlen; int error; if (!MAIN.qname) MAIN.qname = (argc > 1)? argv[1] : "localhost"; if (!MAIN.qtype) MAIN.qtype = DNS_T_A; hosts(); if (MAIN.qtype == DNS_T_PTR && !strstr(MAIN.qname, "arpa")) { union { struct in_addr a; struct in6_addr a6; } addr; int af = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET; if ((error = dns_pton(af, MAIN.qname, &addr))) panic("%s: %s", MAIN.qname, dns_strerror(error)); qlen = dns_ptr_qname(qname, sizeof qname, af, &addr); } else qlen = dns_strlcpy(qname, MAIN.qname, sizeof qname); if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, MAIN.qtype, DNS_C_IN, 0, 0))) panic("%s: %s", qname, dns_strerror(error)); if (!(A = dns_hosts_query(hosts(), Q, &error))) panic("%s: %s", qname, dns_strerror(error)); print_packet(A, stdout); free(A); return 0; } /* query_hosts() */ static int search_list(int argc, char *argv[]) { const char *qname = (argc > 1)? argv[1] : "f.l.google.com"; unsigned long i = 0; char name[DNS_D_MAXNAME + 1]; printf("[%s]\n", qname); while (dns_resconf_search(name, sizeof name, qname, strlen(qname), resconf(), &i)) puts(name); return 0; } /* search_list() */ static int permute_set(int argc, char *argv[]) { unsigned lo, hi, i; struct dns_k_permutor p; hi = (--argc > 0)? atoi(argv[argc]) : 8; lo = (--argc > 0)? atoi(argv[argc]) : 0; fprintf(stderr, "[%u .. %u]\n", lo, hi); dns_k_permutor_init(&p, lo, hi); for (i = lo; i <= hi; i++) fprintf(stdout, "%u\n", dns_k_permutor_step(&p)); // printf("%u -> %u -> %u\n", i, dns_k_permutor_E(&p, i), dns_k_permutor_D(&p, dns_k_permutor_E(&p, i))); return 0; } /* permute_set() */ static int shuffle_16(int argc, char *argv[]) { unsigned n, r; if (--argc > 0) { n = 0xffff & atoi(argv[argc]); r = (--argc > 0)? (unsigned)atoi(argv[argc]) : dns_random(); fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); } else { r = dns_random(); for (n = 0; n < 65536; n++) fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r)); } return 0; } /* shuffle_16() */ static int dump_random(int argc, char *argv[]) { unsigned char b[32]; unsigned i, j, n, r; n = (argc > 1)? atoi(argv[1]) : 32; while (n) { i = 0; do { r = dns_random(); for (j = 0; j < sizeof r && i < n && i < sizeof b; i++, j++) { b[i] = 0xff & r; r >>= 8; } } while (i < n && i < sizeof b); hexdump(b, i, stdout); n -= i; } return 0; } /* dump_random() */ static int send_query(int argc, char *argv[]) { struct dns_packet *A, *Q = dns_p_new(512); char host[INET6_ADDRSTRLEN + 1]; struct sockaddr_storage ss; struct dns_socket *so; int error, type; if (argc > 1) { ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET; if ((error = dns_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss, NULL)))) panic("%s: %s", argv[1], dns_strerror(error)); *dns_sa_port(ss.ss_family, &ss) = htons(53); } else memcpy(&ss, &resconf()->nameserver[0], dns_sa_len(&resconf()->nameserver[0])); if (!dns_inet_ntop(ss.ss_family, dns_sa_addr(ss.ss_family, &ss, NULL), host, sizeof host)) panic("bad host address, or none provided"); if (!MAIN.qname) MAIN.qname = "ipv6.google.com"; if (!MAIN.qtype) MAIN.qtype = DNS_T_AAAA; if ((error = dns_p_push(Q, DNS_S_QD, MAIN.qname, strlen(MAIN.qname), MAIN.qtype, DNS_C_IN, 0, 0))) panic("dns_p_push: %s", dns_strerror(error)); dns_header(Q)->rd = 1; if (strstr(argv[0], "udp")) type = SOCK_DGRAM; else if (strstr(argv[0], "tcp")) type = SOCK_STREAM; else type = dns_res_tcp2type(resconf()->options.tcp); fprintf(stderr, "querying %s for %s IN %s\n", host, MAIN.qname, dns_strtype(MAIN.qtype)); if (!(so = dns_so_open((struct sockaddr *)&resconf()->iface, type, dns_opts(), &error))) panic("dns_so_open: %s", dns_strerror(error)); while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) { if (error != DNS_EAGAIN) panic("dns_so_query: %s (%d)", dns_strerror(error), error); if (dns_so_elapsed(so) > 10) panic("query timed-out"); dns_so_poll(so, 1); } print_packet(A, stdout); dns_so_close(so); return 0; } /* send_query() */ static int print_arpa(int argc, char *argv[]) { const char *ip = (argc > 1)? argv[1] : "::1"; int af = (strchr(ip, ':'))? AF_INET6 : AF_INET; union { struct in_addr a4; struct in6_addr a6; } addr; char host[DNS_D_MAXNAME + 1]; if (1 != dns_inet_pton(af, ip, &addr) || 0 == dns_ptr_qname(host, sizeof host, af, &addr)) panic("%s: invalid address", ip); fprintf(stdout, "%s\n", host); return 0; } /* print_arpa() */ static int show_hints(int argc, char *argv[]) { struct dns_hints *(*load)(struct dns_resolv_conf *, int *); const char *which, *how, *who; struct dns_hints *hints; int error; which = (argc > 1)? argv[1] : "local"; how = (argc > 2)? argv[2] : "plain"; who = (argc > 3)? argv[3] : "google.com"; load = (0 == strcmp(which, "local")) ? &dns_hints_local : &dns_hints_root; if (!(hints = load(resconf(), &error))) panic("%s: %s", argv[0], dns_strerror(error)); if (0 == strcmp(how, "plain")) { dns_hints_dump(hints, stdout); } else { struct dns_packet *query, *answer; query = dns_p_new(512); if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0))) panic("%s: %s", who, dns_strerror(error)); if (!(answer = dns_hints_query(hints, query, &error))) panic("%s: %s", who, dns_strerror(error)); print_packet(answer, stdout); free(answer); } dns_hints_close(hints); return 0; } /* show_hints() */ static int resolve_query(int argc DNS_NOTUSED, char *argv[]) { _Bool recurse = !!strstr(argv[0], "recurse"); struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; struct dns_resolver *R; struct dns_packet *ans; const struct dns_stat *st; int error; if (!MAIN.qname) MAIN.qname = "www.google.com"; if (!MAIN.qtype) MAIN.qtype = DNS_T_A; resconf()->options.recurse = recurse; if (!(R = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) panic("%s: %s", MAIN.qname, dns_strerror(error)); if ((error = dns_res_submit(R, MAIN.qname, MAIN.qtype, DNS_C_IN))) panic("%s: %s", MAIN.qname, dns_strerror(error)); while ((error = dns_res_check(R))) { if (error != DNS_EAGAIN) panic("dns_res_check: %s (%d)", dns_strerror(error), error); if (dns_res_elapsed(R) > 30) panic("query timed-out"); dns_res_poll(R, 1); } ans = dns_res_fetch(R, &error); print_packet(ans, stdout); free(ans); st = dns_res_stat(R); putchar('\n'); printf(";; queries: %"PRIuZ"\n", st->queries); printf(";; udp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.sent.count, st->udp.sent.bytes); printf(";; udp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes); printf(";; tcp sent: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.sent.count, st->tcp.sent.bytes); printf(";; tcp rcvd: %"PRIuZ" in %"PRIuZ" bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes); dns_res_close(R); return 0; } /* resolve_query() */ static int resolve_addrinfo(int argc DNS_NOTUSED, char *argv[]) { _Bool recurse = !!strstr(argv[0], "recurse"); struct dns_hints *(*hints)() = (recurse)? &dns_hints_root : &dns_hints_local; struct dns_resolver *res = NULL; struct dns_addrinfo *ai = NULL; struct addrinfo ai_hints = { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_CANONNAME }; struct addrinfo *ent; char pretty[512]; int error; if (!MAIN.qname) MAIN.qname = "www.google.com"; /* NB: MAIN.qtype of 0 means obey hints.ai_family */ resconf()->options.recurse = recurse; if (!(res = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error))) panic("%s: %s", MAIN.qname, dns_strerror(error)); if (!(ai = dns_ai_open(MAIN.qname, "80", MAIN.qtype, &ai_hints, res, &error))) panic("%s: %s", MAIN.qname, dns_strerror(error)); do { switch (error = dns_ai_nextent(&ent, ai)) { case 0: dns_ai_print(pretty, sizeof pretty, ent, ai); fputs(pretty, stdout); free(ent); break; case ENOENT: break; case DNS_EAGAIN: if (dns_ai_elapsed(ai) > 30) panic("query timed-out"); dns_ai_poll(ai, 1); break; default: panic("dns_ai_nextent: %s (%d)", dns_strerror(error), error); } } while (error != ENOENT); dns_res_close(res); dns_ai_close(ai); return 0; } /* resolve_addrinfo() */ static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { union { struct sockaddr sa; struct sockaddr_in sin; } port; int fd; memset(&port, 0, sizeof port); port.sin.sin_family = AF_INET; port.sin.sin_port = htons(5354); port.sin.sin_addr.s_addr = inet_addr("127.0.0.1"); if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0))) panic("socket: %s", strerror(errno)); if (0 != bind(fd, &port.sa, sizeof port.sa)) panic("127.0.0.1:5353: %s", dns_strerror(errno)); for (;;) { struct dns_packet *pkt = dns_p_new(512); struct sockaddr_storage ss; socklen_t slen = sizeof ss; ssize_t count; #if defined(MSG_WAITALL) /* MinGW issue */ int rflags = MSG_WAITALL; #else int rflags = 0; #endif count = recvfrom(fd, (char *)pkt->data, pkt->size, rflags, (struct sockaddr *)&ss, &slen); if (!count || count < 0) panic("recvfrom: %s", strerror(errno)); pkt->end = count; dns_p_dump(pkt, stdout); (void)sendto(fd, (char *)pkt->data, pkt->end, 0, (struct sockaddr *)&ss, slen); } return 0; } /* echo_port() */ static int isection(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_isection(name); name = dns_strsection(type); printf("%s (%d)\n", name, type); return 0; } /* isection() */ static int iclass(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_iclass(name); name = dns_strclass(type); printf("%s (%d)\n", name, type); return 0; } /* iclass() */ static int itype(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_itype(name); name = dns_strtype(type); printf("%s (%d)\n", name, type); return 0; } /* itype() */ static int iopcode(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_iopcode(name); name = dns_stropcode(type); printf("%s (%d)\n", name, type); return 0; } /* iopcode() */ static int ircode(int argc, char *argv[]) { const char *name = (argc > 1)? argv[1] : ""; int type; type = dns_ircode(name); name = dns_strrcode(type); printf("%s (%d)\n", name, type); return 0; } /* ircode() */ #define SIZE1(x) { DNS_PP_STRINGIFY(x), sizeof (x) } #define SIZE2(x, ...) SIZE1(x), SIZE1(__VA_ARGS__) #define SIZE3(x, ...) SIZE1(x), SIZE2(__VA_ARGS__) #define SIZE4(x, ...) SIZE1(x), SIZE3(__VA_ARGS__) #define SIZE(...) DNS_PP_CALL(DNS_PP_XPASTE(SIZE, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) static int sizes(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { static const struct { const char *name; size_t size; } type[] = { SIZE(struct dns_header, struct dns_packet, struct dns_rr, struct dns_rr_i), SIZE(struct dns_a, struct dns_aaaa, struct dns_mx, struct dns_ns), SIZE(struct dns_cname, struct dns_soa, struct dns_ptr, struct dns_srv), SIZE(struct dns_sshfp, struct dns_txt, union dns_any), SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i), SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo), SIZE(struct dns_cache), SIZE(size_t), SIZE(void *), SIZE(long) }; unsigned i, max; for (i = 0, max = 0; i < lengthof(type); i++) max = DNS_PP_MAX(max, strlen(type[i].name)); for (i = 0; i < lengthof(type); i++) printf("%*s : %"PRIuZ"\n", max, type[i].name, type[i].size); return 0; } /* sizes() */ static const struct { const char *cmd; int (*run)(); const char *help; } cmds[] = { { "parse-packet", &parse_packet, "parse binary packet from stdin" }, { "parse-domain", &parse_domain, "anchor and iteratively cleave domain" }, { "trim-domain", &trim_domain, "trim and anchor domain name" }, { "expand-domain", &expand_domain, "expand domain at offset NN in packet from stdin" }, { "show-resconf", &show_resconf, "show resolv.conf data" }, { "show-hosts", &show_hosts, "show hosts data" }, { "show-nssconf", &show_nssconf, "show nsswitch.conf data" }, { "query-hosts", &query_hosts, "query A, AAAA or PTR in hosts data" }, { "search-list", &search_list, "generate query search list from domain" }, { "permute-set", &permute_set, "generate random permutation -> (0 .. N or N .. M)" }, { "shuffle-16", &shuffle_16, "simple 16-bit permutation" }, { "dump-random", &dump_random, "generate random bytes" }, { "send-query", &send_query, "send query to host" }, { "send-query-udp", &send_query, "send udp query to host" }, { "send-query-tcp", &send_query, "send tcp query to host" }, { "print-arpa", &print_arpa, "print arpa. zone name of address" }, { "show-hints", &show_hints, "print hints: show-hints [local|root] [plain|packet]" }, { "resolve-stub", &resolve_query, "resolve as stub resolver" }, { "resolve-recurse", &resolve_query, "resolve as recursive resolver" }, { "addrinfo-stub", &resolve_addrinfo, "resolve through getaddrinfo clone" }, { "addrinfo-recurse", &resolve_addrinfo, "resolve through getaddrinfo clone" }, /* { "resolve-nameinfo", &resolve_query, "resolve as recursive resolver" }, */ { "echo", &echo_port, "server echo mode, for nmap fuzzing" }, { "isection", &isection, "parse section string" }, { "iclass", &iclass, "parse class string" }, { "itype", &itype, "parse type string" }, { "iopcode", &iopcode, "parse opcode string" }, { "ircode", &ircode, "parse rcode string" }, { "sizes", &sizes, "print data structure sizes" }, }; static void print_usage(const char *progname, FILE *fp) { static const char *usage = " [OPTIONS] COMMAND [ARGS]\n" " -c PATH Path to resolv.conf\n" " -n PATH Path to nsswitch.conf\n" " -l PATH Path to local hosts\n" " -z PATH Path to zone cache\n" " -q QNAME Query name\n" " -t QTYPE Query type\n" " -s HOW Sort records\n" " -v Be more verbose (-vv show packets; -vvv hexdump packets)\n" " -V Print version info\n" " -h Print this usage message\n" "\n"; unsigned i, n, m; fputs(progname, fp); fputs(usage, fp); for (i = 0, m = 0; i < lengthof(cmds); i++) { if (strlen(cmds[i].cmd) > m) m = strlen(cmds[i].cmd); } for (i = 0; i < lengthof(cmds); i++) { fprintf(fp, " %s ", cmds[i].cmd); for (n = strlen(cmds[i].cmd); n < m; n++) putc(' ', fp); fputs(cmds[i].help, fp); putc('\n', fp); } fputs("\nReport bugs to William Ahern \n", fp); } /* print_usage() */ static void print_version(const char *progname, FILE *fp) { fprintf(fp, "%s (dns.c) %.8X\n", progname, dns_v_rel()); fprintf(fp, "vendor %s\n", dns_vendor()); fprintf(fp, "release %.8X\n", dns_v_rel()); fprintf(fp, "abi %.8X\n", dns_v_abi()); fprintf(fp, "api %.8X\n", dns_v_api()); } /* print_version() */ int main(int argc, char **argv) { extern int optind; extern char *optarg; const char *progname = argv[0]; unsigned i; int ch; while (-1 != (ch = getopt(argc, argv, "q:t:c:n:l:z:s:vVh"))) { switch (ch) { case 'c': assert(MAIN.resconf.count < lengthof(MAIN.resconf.path)); MAIN.resconf.path[MAIN.resconf.count++] = optarg; break; case 'n': assert(MAIN.nssconf.count < lengthof(MAIN.nssconf.path)); MAIN.nssconf.path[MAIN.nssconf.count++] = optarg; break; case 'l': assert(MAIN.hosts.count < lengthof(MAIN.hosts.path)); MAIN.hosts.path[MAIN.hosts.count++] = optarg; break; case 'z': assert(MAIN.cache.count < lengthof(MAIN.cache.path)); MAIN.cache.path[MAIN.cache.count++] = optarg; break; case 'q': MAIN.qname = optarg; break; case 't': for (i = 0; i < lengthof(dns_rrtypes); i++) { if (0 == strcasecmp(dns_rrtypes[i].name, optarg)) { MAIN.qtype = dns_rrtypes[i].type; break; } } if (MAIN.qtype) break; for (i = 0; dns_isdigit(optarg[i]); i++) { MAIN.qtype *= 10; MAIN.qtype += optarg[i] - '0'; } if (!MAIN.qtype) panic("%s: invalid query type", optarg); break; case 's': if (0 == strcasecmp(optarg, "packet")) MAIN.sort = &dns_rr_i_packet; else if (0 == strcasecmp(optarg, "shuffle")) MAIN.sort = &dns_rr_i_shuffle; else if (0 == strcasecmp(optarg, "order")) MAIN.sort = &dns_rr_i_order; else panic("%s: invalid sort method", optarg); break; case 'v': dns_debug = ++MAIN.verbose; break; case 'V': print_version(progname, stdout); return 0; case 'h': print_usage(progname, stdout); return 0; default: print_usage(progname, stderr); return EXIT_FAILURE; } /* switch() */ } /* while() */ argc -= optind; argv += optind; for (i = 0; i < lengthof(cmds) && argv[0]; i++) { if (0 == strcmp(cmds[i].cmd, argv[0])) return cmds[i].run(argc, argv); } print_usage(progname, stderr); return EXIT_FAILURE; } /* main() */ #endif /* DNS_MAIN */ /* * pop file-scoped compiler annotations */ #if __clang__ #pragma clang diagnostic pop #elif DNS_GNUC_PREREQ(4,6,0) #pragma GCC diagnostic pop #endif cqueues-rel-20161214/src/lib/dns.h000066400000000000000000001032561302435770500165150ustar00rootroot00000000000000/* ========================================================================== * dns.h - Recursive, Reentrant DNS Resolver. * -------------------------------------------------------------------------- * Copyright (c) 2009, 2010, 2012-2015 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef DNS_H #define DNS_H #include /* size_t offsetof() */ #include /* FILE */ #include /* strlen(3) */ #include /* time_t */ #if _WIN32 #include #include #else #include /* BYTE_ORDER BIG_ENDIAN _BIG_ENDIAN */ #include /* socklen_t */ #include /* struct socket */ #include /* POLLIN POLLOUT */ #include /* struct in_addr struct in6_addr */ #include /* struct addrinfo */ #endif /* * V I S I B I L I T Y * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef DNS_PUBLIC #define DNS_PUBLIC #endif /* * V E R S I O N * * Vendor: Entity for which versions numbers are relevant. (If forking * change DNS_VENDOR to avoid confusion.) * * Three versions: * * REL Official "release"--bug fixes, new features, etc. * ABI Changes to existing object sizes or parameter types. * API Changes that might effect application source. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_VENDOR "william@25thandClement.com" #define DNS_V_REL 0x20161214 #define DNS_V_ABI 0x20160608 #define DNS_V_API 0x20160608 DNS_PUBLIC const char *dns_vendor(void); DNS_PUBLIC int dns_v_rel(void); DNS_PUBLIC int dns_v_abi(void); DNS_PUBLIC int dns_v_api(void); /* * E R R O R S * * Errors and exceptions are always returned through an int. This should * hopefully make integration easier in the majority of circumstances, and * also cut down on useless compiler warnings. * * System and library errors are returned together. POSIX guarantees that * all system errors are positive integers. Library errors are always * negative integers in the range DNS_EBASE to DNS_ELAST, with the high bits * set to the three magic ASCII characters "dns". * * dns_strerror() returns static English string descriptions of all known * errors, and punts the remainder to strerror(3). * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_EBASE -(('d' << 24) | ('n' << 16) | ('s' << 8) | 64) #define dns_error_t int /* for documentation only */ enum dns_errno { DNS_ENOBUFS = DNS_EBASE, DNS_EILLEGAL, DNS_EORDER, DNS_ESECTION, DNS_EUNKNOWN, DNS_EADDRESS, DNS_ENOQUERY, DNS_ENOANSWER, DNS_EFETCHED, DNS_ESERVICE, /* EAI_SERVICE */ DNS_ENONAME, /* EAI_NONAME */ DNS_EFAIL, /* EAI_FAIL */ DNS_ELAST, }; /* dns_errno */ DNS_PUBLIC const char *dns_strerror(dns_error_t); DNS_PUBLIC int *dns_debug_p(void); #define dns_debug (*dns_debug_p()) /* was extern int dns_debug before 20160523 API */ /* * C O M P I L E R A N N O T A T I O N S * * GCC with -Wextra, and clang by default, complain about overrides in * initializer lists. Overriding previous member initializers is well * defined behavior in C. dns.c relies on this behavior to define default, * overrideable member values when instantiating configuration objects. * * dns_quietinit() guards a compound literal expression with pragmas to * silence these shrill warnings. This alleviates the burden of requiring * third-party projects to adjust their compiler flags. * * NOTE: If you take the address of the compound literal, take the address * of the transformed expression, otherwise the compound literal lifetime is * tied to the scope of the GCC statement expression. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if defined __clang__ #define DNS_PRAGMA_PUSH _Pragma("clang diagnostic push") #define DNS_PRAGMA_QUIET _Pragma("clang diagnostic ignored \"-Winitializer-overrides\"") #define DNS_PRAGMA_POP _Pragma("clang diagnostic pop") #define dns_quietinit(...) \ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__ DNS_PRAGMA_POP #elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 #define DNS_PRAGMA_PUSH _Pragma("GCC diagnostic push") #define DNS_PRAGMA_QUIET _Pragma("GCC diagnostic ignored \"-Woverride-init\"") #define DNS_PRAGMA_POP _Pragma("GCC diagnostic pop") /* GCC parses the _Pragma operator less elegantly than clang. */ #define dns_quietinit(...) \ __extension__ ({ DNS_PRAGMA_PUSH DNS_PRAGMA_QUIET __VA_ARGS__; DNS_PRAGMA_POP }) #else #define DNS_PRAGMA_PUSH #define DNS_PRAGMA_QUIET #define DNS_PRAGMA_POP #define dns_quietinit(...) __VA_ARGS__ #endif #if defined __GNUC__ #define DNS_PRAGMA_EXTENSION __extension__ #else #define DNS_PRAGMA_EXTENSION #endif /* * E V E N T S I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if defined(POLLIN) #define DNS_POLLIN POLLIN #else #define DNS_POLLIN 1 #endif #if defined(POLLOUT) #define DNS_POLLOUT POLLOUT #else #define DNS_POLLOUT 2 #endif /* * See Application Interface below for configuring libevent bitmasks instead * of poll(2) bitmasks. */ #define DNS_EVREAD 2 #define DNS_EVWRITE 4 #define DNS_POLL2EV(set) \ (((set) & DNS_POLLIN)? DNS_EVREAD : 0) | (((set) & DNS_POLLOUT)? DNS_EVWRITE : 0) #define DNS_EV2POLL(set) \ (((set) & DNS_EVREAD)? DNS_POLLIN : 0) | (((set) & DNS_EVWRITE)? DNS_POLLOUT : 0) /* * E N U M E R A T I O N I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ enum dns_section { DNS_S_QD = 0x01, #define DNS_S_QUESTION DNS_S_QD DNS_S_AN = 0x02, #define DNS_S_ANSWER DNS_S_AN DNS_S_NS = 0x04, #define DNS_S_AUTHORITY DNS_S_NS DNS_S_AR = 0x08, #define DNS_S_ADDITIONAL DNS_S_AR DNS_S_ALL = 0x0f }; /* enum dns_section */ enum dns_class { DNS_C_IN = 1, DNS_C_ANY = 255 }; /* enum dns_class */ enum dns_type { DNS_T_A = 1, DNS_T_NS = 2, DNS_T_CNAME = 5, DNS_T_SOA = 6, DNS_T_PTR = 12, DNS_T_MX = 15, DNS_T_TXT = 16, DNS_T_AAAA = 28, DNS_T_SRV = 33, DNS_T_OPT = 41, DNS_T_SSHFP = 44, DNS_T_SPF = 99, DNS_T_AXFR = 252, DNS_T_ALL = 255 }; /* enum dns_type */ enum dns_opcode { DNS_OP_QUERY = 0, DNS_OP_IQUERY = 1, DNS_OP_STATUS = 2, DNS_OP_NOTIFY = 4, DNS_OP_UPDATE = 5, }; /* dns_opcode */ enum dns_rcode { DNS_RC_NOERROR = 0, DNS_RC_FORMERR = 1, DNS_RC_SERVFAIL = 2, DNS_RC_NXDOMAIN = 3, DNS_RC_NOTIMP = 4, DNS_RC_REFUSED = 5, DNS_RC_YXDOMAIN = 6, DNS_RC_YXRRSET = 7, DNS_RC_NXRRSET = 8, DNS_RC_NOTAUTH = 9, DNS_RC_NOTZONE = 10, /* EDNS(0) extended RCODEs */ DNS_RC_BADVERS = 16, }; /* dns_rcode */ /* * NOTE: These string functions need a small buffer in case the literal * integer value needs to be printed and returned. UNLESS this buffer is * SPECIFIED, the returned string has ONLY BLOCK SCOPE. */ #define DNS_STRMAXLEN 47 /* "QUESTION|ANSWER|AUTHORITY|ADDITIONAL" */ DNS_PUBLIC const char *dns_strsection(enum dns_section, void *, size_t); #define dns_strsection3(a, b, c) \ dns_strsection((a), (b), (c)) #define dns_strsection1(a) dns_strsection((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) #define dns_strsection(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strsection, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC enum dns_section dns_isection(const char *); DNS_PUBLIC const char *dns_strclass(enum dns_class, void *, size_t); #define dns_strclass3(a, b, c) dns_strclass((a), (b), (c)) #define dns_strclass1(a) dns_strclass((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) #define dns_strclass(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strclass, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC enum dns_class dns_iclass(const char *); DNS_PUBLIC const char *dns_strtype(enum dns_type, void *, size_t); #define dns_strtype3(a, b, c) dns_strtype((a), (b), (c)) #define dns_strtype1(a) dns_strtype((a), (char [DNS_STRMAXLEN + 1]){ 0 }, DNS_STRMAXLEN + 1) #define dns_strtype(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_strtype, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC enum dns_type dns_itype(const char *); DNS_PUBLIC const char *dns_stropcode(enum dns_opcode); DNS_PUBLIC enum dns_opcode dns_iopcode(const char *); DNS_PUBLIC const char *dns_strrcode(enum dns_rcode); DNS_PUBLIC enum dns_rcode dns_ircode(const char *); /* * A T O M I C I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ typedef unsigned long dns_atomic_t; typedef unsigned long dns_refcount_t; /* must be same value type as dns_atomic_t */ /* * C R Y P T O I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ typedef unsigned dns_random_f(void); DNS_PUBLIC dns_random_f **dns_random_p(void); #define dns_random (*dns_random_p()) /* was extern unsigned (*dns_random)(void) before 20160523 API */ /* * P A C K E T I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_header { unsigned qid:16; #if (defined BYTE_ORDER && BYTE_ORDER == BIG_ENDIAN) || (defined __sun && defined _BIG_ENDIAN) unsigned qr:1; unsigned opcode:4; unsigned aa:1; unsigned tc:1; unsigned rd:1; unsigned ra:1; unsigned unused:3; unsigned rcode:4; #else unsigned rd:1; unsigned tc:1; unsigned aa:1; unsigned opcode:4; unsigned qr:1; unsigned rcode:4; unsigned unused:3; unsigned ra:1; #endif unsigned qdcount:16; unsigned ancount:16; unsigned nscount:16; unsigned arcount:16; }; /* struct dns_header */ #define dns_header(p) (&(p)->header) #ifndef DNS_P_QBUFSIZ #define DNS_P_QBUFSIZ dns_p_calcsize(256 + 4) #endif #ifndef DNS_P_DICTSIZE #define DNS_P_DICTSIZE 16 #endif struct dns_packet { unsigned short dict[DNS_P_DICTSIZE]; struct dns_p_memo { struct dns_s_memo { unsigned short base, end; } qd, an, ns, ar; struct { unsigned short p; unsigned short maxudp; unsigned ttl; } opt; } memo; struct { struct dns_packet *cqe_next, *cqe_prev; } cqe; size_t size, end; int:16; /* tcp padding */ DNS_PRAGMA_EXTENSION union { struct dns_header header; unsigned char data[1]; }; }; /* struct dns_packet */ #define dns_p_calcsize(n) (offsetof(struct dns_packet, data) + DNS_PP_MAX(12, (n))) #define dns_p_sizeof(P) dns_p_calcsize((P)->end) /** takes size of maximum desired payload */ #define dns_p_new(n) (dns_p_init((struct dns_packet *)&(union { unsigned char b[dns_p_calcsize((n))]; struct dns_packet p; }){ { 0 } }, dns_p_calcsize((n)))) /** takes size of entire packet structure as allocated */ DNS_PUBLIC struct dns_packet *dns_p_init(struct dns_packet *, size_t); /** takes size of maximum desired payload */ DNS_PUBLIC struct dns_packet *dns_p_make(size_t, int *); DNS_PUBLIC int dns_p_grow(struct dns_packet **); DNS_PUBLIC struct dns_packet *dns_p_copy(struct dns_packet *, const struct dns_packet *); #define dns_p_opcode(P) (dns_header(P)->opcode) DNS_PUBLIC enum dns_rcode dns_p_rcode(struct dns_packet *); DNS_PUBLIC unsigned dns_p_count(struct dns_packet *, enum dns_section); DNS_PUBLIC int dns_p_push(struct dns_packet *, enum dns_section, const void *, size_t, enum dns_type, enum dns_class, unsigned, const void *); DNS_PUBLIC void dns_p_dictadd(struct dns_packet *, unsigned short); DNS_PUBLIC struct dns_packet *dns_p_merge(struct dns_packet *, enum dns_section, struct dns_packet *, enum dns_section, int *); DNS_PUBLIC void dns_p_dump(struct dns_packet *, FILE *); DNS_PUBLIC int dns_p_study(struct dns_packet *); /* * D O M A I N N A M E I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_D_MAXLABEL 63 /* + 1 '\0' */ #define DNS_D_MAXNAME 255 /* + 1 '\0' */ #define DNS_D_ANCHOR 1 /* anchor domain w/ root "." */ #define DNS_D_CLEAVE 2 /* cleave sub-domain */ #define DNS_D_TRIM 4 /* remove superfluous dots */ #define dns_d_new3(a, b, f) dns_d_init(&(char[DNS_D_MAXNAME + 1]){ 0 }, DNS_D_MAXNAME + 1, (a), (b), (f)) #define dns_d_new2(a, f) dns_d_new3((a), strlen((a)), (f)) #define dns_d_new1(a) dns_d_new3((a), strlen((a)), DNS_D_ANCHOR) #define dns_d_new(...) DNS_PP_CALL(DNS_PP_XPASTE(dns_d_new, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__) DNS_PUBLIC char *dns_d_init(void *, size_t, const void *, size_t, int); DNS_PUBLIC size_t dns_d_anchor(void *, size_t, const void *, size_t); DNS_PUBLIC size_t dns_d_cleave(void *, size_t, const void *, size_t); DNS_PUBLIC size_t dns_d_comp(void *, size_t, const void *, size_t, struct dns_packet *, int *); DNS_PUBLIC size_t dns_d_expand(void *, size_t, unsigned short, struct dns_packet *, int *); DNS_PUBLIC unsigned short dns_d_skip(unsigned short, struct dns_packet *); DNS_PUBLIC int dns_d_push(struct dns_packet *, const void *, size_t); DNS_PUBLIC size_t dns_d_cname(void *, size_t, const void *, size_t, struct dns_packet *, int *error); /* * R E S O U R C E R E C O R D I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_rr { enum dns_section section; struct { unsigned short p; unsigned short len; } dn; enum dns_type type; enum dns_class class; unsigned ttl; struct { unsigned short p; unsigned short len; } rd; }; /* struct dns_rr */ DNS_PUBLIC int dns_rr_copy(struct dns_packet *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_rr_parse(struct dns_rr *, unsigned short, struct dns_packet *); DNS_PUBLIC unsigned short dns_rr_skip(unsigned short, struct dns_packet *); DNS_PUBLIC int dns_rr_cmp(struct dns_rr *, struct dns_packet *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC size_t dns_rr_print(void *, size_t, struct dns_rr *, struct dns_packet *, int *); #define dns_rr_i_new(P, ...) \ dns_rr_i_init(&dns_quietinit((struct dns_rr_i){ 0, __VA_ARGS__ }), (P)) struct dns_rr_i { enum dns_section section; const void *name; enum dns_type type; enum dns_class class; const void *data; int follow; int (*sort)(); unsigned args[2]; struct { unsigned short next; unsigned short count; unsigned exec; unsigned regs[2]; } state, saved; }; /* struct dns_rr_i */ DNS_PUBLIC int dns_rr_i_packet(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); DNS_PUBLIC int dns_rr_i_order(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); DNS_PUBLIC int dns_rr_i_shuffle(struct dns_rr *, struct dns_rr *, struct dns_rr_i *, struct dns_packet *); DNS_PUBLIC struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *, struct dns_packet *); #define dns_rr_i_save(i) ((i)->saved = (i)->state) #define dns_rr_i_rewind(i) ((i)->state = (i)->saved) #define dns_rr_i_count(i) ((i)->state.count) DNS_PUBLIC unsigned dns_rr_grep(struct dns_rr *, unsigned, struct dns_rr_i *, struct dns_packet *, int *); #define dns_rr_foreach_(rr, P, ...) \ for (struct dns_rr_i DNS_PP_XPASTE(i, __LINE__) = *dns_rr_i_new((P), __VA_ARGS__); dns_rr_grep((rr), 1, &DNS_PP_XPASTE(i, __LINE__), (P), &(int){ 0 }); ) #define dns_rr_foreach(...) dns_rr_foreach_(__VA_ARGS__) /* * A R E S O U R C E R E C O R D */ struct dns_a { struct in_addr addr; }; /* struct dns_a */ DNS_PUBLIC int dns_a_parse(struct dns_a *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_a_push(struct dns_packet *, struct dns_a *); DNS_PUBLIC int dns_a_cmp(const struct dns_a *, const struct dns_a *); DNS_PUBLIC size_t dns_a_print(void *, size_t, struct dns_a *); DNS_PUBLIC size_t dns_a_arpa(void *, size_t, const struct dns_a *); /* * AAAA R E S O U R C E R E C O R D */ struct dns_aaaa { struct in6_addr addr; }; /* struct dns_aaaa */ DNS_PUBLIC int dns_aaaa_parse(struct dns_aaaa *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_aaaa_push(struct dns_packet *, struct dns_aaaa *); DNS_PUBLIC int dns_aaaa_cmp(const struct dns_aaaa *, const struct dns_aaaa *); DNS_PUBLIC size_t dns_aaaa_print(void *, size_t, struct dns_aaaa *); DNS_PUBLIC size_t dns_aaaa_arpa(void *, size_t, const struct dns_aaaa *); /* * MX R E S O U R C E R E C O R D */ struct dns_mx { unsigned short preference; char host[DNS_D_MAXNAME + 1]; }; /* struct dns_mx */ DNS_PUBLIC int dns_mx_parse(struct dns_mx *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_mx_push(struct dns_packet *, struct dns_mx *); DNS_PUBLIC int dns_mx_cmp(const struct dns_mx *, const struct dns_mx *); DNS_PUBLIC size_t dns_mx_print(void *, size_t, struct dns_mx *); DNS_PUBLIC size_t dns_mx_cname(void *, size_t, struct dns_mx *); /* * NS R E S O U R C E R E C O R D */ struct dns_ns { char host[DNS_D_MAXNAME + 1]; }; /* struct dns_ns */ DNS_PUBLIC int dns_ns_parse(struct dns_ns *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_ns_push(struct dns_packet *, struct dns_ns *); DNS_PUBLIC int dns_ns_cmp(const struct dns_ns *, const struct dns_ns *); DNS_PUBLIC size_t dns_ns_print(void *, size_t, struct dns_ns *); DNS_PUBLIC size_t dns_ns_cname(void *, size_t, struct dns_ns *); /* * CNAME R E S O U R C E R E C O R D */ struct dns_cname { char host[DNS_D_MAXNAME + 1]; }; /* struct dns_cname */ DNS_PUBLIC int dns_cname_parse(struct dns_cname *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_cname_push(struct dns_packet *, struct dns_cname *); DNS_PUBLIC int dns_cname_cmp(const struct dns_cname *, const struct dns_cname *); DNS_PUBLIC size_t dns_cname_print(void *, size_t, struct dns_cname *); DNS_PUBLIC size_t dns_cname_cname(void *, size_t, struct dns_cname *); /* * SOA R E S O U R C E R E C O R D */ struct dns_soa { char mname[DNS_D_MAXNAME + 1]; char rname[DNS_D_MAXNAME + 1]; unsigned serial, refresh, retry, expire, minimum; }; /* struct dns_soa */ DNS_PUBLIC int dns_soa_parse(struct dns_soa *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_soa_push(struct dns_packet *, struct dns_soa *); DNS_PUBLIC int dns_soa_cmp(const struct dns_soa *, const struct dns_soa *); DNS_PUBLIC size_t dns_soa_print(void *, size_t, struct dns_soa *); /* * PTR R E S O U R C E R E C O R D */ struct dns_ptr { char host[DNS_D_MAXNAME + 1]; }; /* struct dns_ptr */ DNS_PUBLIC int dns_ptr_parse(struct dns_ptr *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_ptr_push(struct dns_packet *, struct dns_ptr *); DNS_PUBLIC int dns_ptr_cmp(const struct dns_ptr *, const struct dns_ptr *); DNS_PUBLIC size_t dns_ptr_print(void *, size_t, struct dns_ptr *); DNS_PUBLIC size_t dns_ptr_cname(void *, size_t, struct dns_ptr *); DNS_PUBLIC size_t dns_ptr_qname(void *, size_t, int, void *); /* * SRV R E S O U R C E R E C O R D */ struct dns_srv { unsigned short priority; unsigned short weight; unsigned short port; char target[DNS_D_MAXNAME + 1]; }; /* struct dns_srv */ DNS_PUBLIC int dns_srv_parse(struct dns_srv *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_srv_push(struct dns_packet *, struct dns_srv *); DNS_PUBLIC int dns_srv_cmp(const struct dns_srv *, const struct dns_srv *); DNS_PUBLIC size_t dns_srv_print(void *, size_t, struct dns_srv *); DNS_PUBLIC size_t dns_srv_cname(void *, size_t, struct dns_srv *); /* * OPT R E S O U R C E R E C O R D */ #ifndef DNS_OPT_MINDATA #define DNS_OPT_MINDATA 256 #endif #define DNS_OPT_DNSSEC 0x8000 struct dns_opt { enum dns_rcode rcode; unsigned char version; unsigned short flags; union { unsigned short maxsize; /* deprecated as confusing */ unsigned short maxudp; /* maximum UDP payload size */ }; size_t size, len; unsigned char data[DNS_OPT_MINDATA]; }; /* struct dns_opt */ #define DNS_OPT_INIT(opt) { .size = sizeof (*opt) - offsetof(struct dns_opt, data) } DNS_PUBLIC struct dns_opt *dns_opt_init(struct dns_opt *, size_t); DNS_PUBLIC int dns_opt_parse(struct dns_opt *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_opt_push(struct dns_packet *, struct dns_opt *); DNS_PUBLIC int dns_opt_cmp(const struct dns_opt *, const struct dns_opt *); DNS_PUBLIC size_t dns_opt_print(void *, size_t, struct dns_opt *); DNS_PUBLIC unsigned int dns_opt_ttl(const struct dns_opt *); DNS_PUBLIC unsigned short dns_opt_class(const struct dns_opt *); DNS_PUBLIC dns_error_t dns_opt_data_push(struct dns_opt *, unsigned char, unsigned short, const void *); /* * SSHFP R E S O U R C E R E C O R D */ struct dns_sshfp { enum dns_sshfp_key { DNS_SSHFP_RSA = 1, DNS_SSHFP_DSA = 2, } algo; enum dns_sshfp_digest { DNS_SSHFP_SHA1 = 1, } type; union { unsigned char sha1[20]; } digest; }; /* struct dns_sshfp */ DNS_PUBLIC int dns_sshfp_parse(struct dns_sshfp *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_sshfp_push(struct dns_packet *, struct dns_sshfp *); DNS_PUBLIC int dns_sshfp_cmp(const struct dns_sshfp *, const struct dns_sshfp *); DNS_PUBLIC size_t dns_sshfp_print(void *, size_t, struct dns_sshfp *); /* * TXT R E S O U R C E R E C O R D */ #ifndef DNS_TXT_MINDATA #define DNS_TXT_MINDATA 1024 #endif struct dns_txt { size_t size, len; unsigned char data[DNS_TXT_MINDATA]; }; /* struct dns_txt */ DNS_PUBLIC struct dns_txt *dns_txt_init(struct dns_txt *, size_t); DNS_PUBLIC int dns_txt_parse(struct dns_txt *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_txt_push(struct dns_packet *, struct dns_txt *); DNS_PUBLIC int dns_txt_cmp(const struct dns_txt *, const struct dns_txt *); DNS_PUBLIC size_t dns_txt_print(void *, size_t, struct dns_txt *); /* * ANY R E S O U R C E R E C O R D */ union dns_any { struct dns_a a; struct dns_aaaa aaaa; struct dns_mx mx; struct dns_ns ns; struct dns_cname cname; struct dns_soa soa; struct dns_ptr ptr; struct dns_srv srv; struct dns_opt opt; struct dns_sshfp sshfp; struct dns_txt txt, spf, rdata; }; /* union dns_any */ #define DNS_ANY_INIT(any) { .rdata = { .size = sizeof *(any) - offsetof(struct dns_txt, data) } } DNS_PUBLIC union dns_any *dns_any_init(union dns_any *, size_t); DNS_PUBLIC int dns_any_parse(union dns_any *, struct dns_rr *, struct dns_packet *); DNS_PUBLIC int dns_any_push(struct dns_packet *, union dns_any *, enum dns_type); DNS_PUBLIC int dns_any_cmp(const union dns_any *, enum dns_type, const union dns_any *, enum dns_type); DNS_PUBLIC size_t dns_any_print(void *, size_t, union dns_any *, enum dns_type); DNS_PUBLIC size_t dns_any_cname(void *, size_t, union dns_any *, enum dns_type); /* * H O S T S I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hosts; DNS_PUBLIC struct dns_hosts *dns_hosts_open(int *); DNS_PUBLIC void dns_hosts_close(struct dns_hosts *); DNS_PUBLIC dns_refcount_t dns_hosts_acquire(struct dns_hosts *); DNS_PUBLIC dns_refcount_t dns_hosts_release(struct dns_hosts *); DNS_PUBLIC struct dns_hosts *dns_hosts_mortal(struct dns_hosts *); DNS_PUBLIC struct dns_hosts *dns_hosts_local(int *); DNS_PUBLIC int dns_hosts_loadfile(struct dns_hosts *, FILE *); DNS_PUBLIC int dns_hosts_loadpath(struct dns_hosts *, const char *); DNS_PUBLIC int dns_hosts_dump(struct dns_hosts *, FILE *); DNS_PUBLIC int dns_hosts_insert(struct dns_hosts *, int, const void *, const void *, _Bool); DNS_PUBLIC struct dns_packet *dns_hosts_query(struct dns_hosts *, struct dns_packet *, int *); /* * R E S O L V . C O N F I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_resolv_conf { struct sockaddr_storage nameserver[3]; char search[4][DNS_D_MAXNAME + 1]; /* (f)ile, (b)ind, (c)ache */ char lookup[4 * (1 + (4 * 2))]; /* getaddrinfo family by preference order ("inet4", "inet6") */ int family[3]; struct { _Bool edns0; unsigned ndots; unsigned timeout; unsigned attempts; _Bool rotate; _Bool recurse; _Bool smart; enum { DNS_RESCONF_TCP_ENABLE, DNS_RESCONF_TCP_ONLY, DNS_RESCONF_TCP_DISABLE, } tcp; } options; struct sockaddr_storage iface; struct { /* PRIVATE */ dns_atomic_t refcount; } _; }; /* struct dns_resolv_conf */ DNS_PUBLIC struct dns_resolv_conf *dns_resconf_open(int *); DNS_PUBLIC void dns_resconf_close(struct dns_resolv_conf *); DNS_PUBLIC dns_refcount_t dns_resconf_acquire(struct dns_resolv_conf *); DNS_PUBLIC dns_refcount_t dns_resconf_release(struct dns_resolv_conf *); DNS_PUBLIC struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *); DNS_PUBLIC struct dns_resolv_conf *dns_resconf_local(int *); DNS_PUBLIC struct dns_resolv_conf *dns_resconf_root(int *); DNS_PUBLIC int dns_resconf_pton(struct sockaddr_storage *, const char *); DNS_PUBLIC int dns_resconf_loadfile(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_resconf_loadpath(struct dns_resolv_conf *, const char *); DNS_PUBLIC int dns_nssconf_loadfile(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_nssconf_loadpath(struct dns_resolv_conf *, const char *); DNS_PUBLIC int dns_resconf_dump(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_nssconf_dump(struct dns_resolv_conf *, FILE *); DNS_PUBLIC int dns_resconf_setiface(struct dns_resolv_conf *, const char *, unsigned short); typedef unsigned long dns_resconf_i_t; DNS_PUBLIC size_t dns_resconf_search(void *, size_t, const void *, size_t, struct dns_resolv_conf *, dns_resconf_i_t *); /* * H I N T S E R V E R I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_hints; DNS_PUBLIC struct dns_hints *dns_hints_open(struct dns_resolv_conf *, int *); DNS_PUBLIC void dns_hints_close(struct dns_hints *); DNS_PUBLIC dns_refcount_t dns_hints_acquire(struct dns_hints *); DNS_PUBLIC dns_refcount_t dns_hints_release(struct dns_hints *); DNS_PUBLIC struct dns_hints *dns_hints_mortal(struct dns_hints *); DNS_PUBLIC int dns_hints_insert(struct dns_hints *, const char *, const struct sockaddr *, unsigned); DNS_PUBLIC unsigned dns_hints_insert_resconf(struct dns_hints *, const char *, const struct dns_resolv_conf *, int *); DNS_PUBLIC struct dns_hints *dns_hints_local(struct dns_resolv_conf *, int *); DNS_PUBLIC struct dns_hints *dns_hints_root(struct dns_resolv_conf *, int *); DNS_PUBLIC struct dns_packet *dns_hints_query(struct dns_hints *, struct dns_packet *, int *); DNS_PUBLIC int dns_hints_dump(struct dns_hints *, FILE *); struct dns_hints_i { const char *zone; struct { unsigned next; unsigned seed; } state; }; /* struct dns_hints_i */ #define dns_hints_i_new(...) (&(struct dns_hints_i){ __VA_ARGS__ }) DNS_PUBLIC unsigned dns_hints_grep(struct sockaddr **, socklen_t *, unsigned, struct dns_hints_i *, struct dns_hints *); /* * C A C H E I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_cache { void *state; dns_refcount_t (*acquire)(struct dns_cache *); dns_refcount_t (*release)(struct dns_cache *); struct dns_packet *(*query)(struct dns_packet *, struct dns_cache *, int *); int (*submit)(struct dns_packet *, struct dns_cache *); int (*check)(struct dns_cache *); struct dns_packet *(*fetch)(struct dns_cache *, int *); int (*pollfd)(struct dns_cache *); short (*events)(struct dns_cache *); void (*clear)(struct dns_cache *); union { long i; void *p; } arg[3]; struct { /* PRIVATE */ dns_atomic_t refcount; } _; }; /* struct dns_cache */ DNS_PUBLIC struct dns_cache *dns_cache_init(struct dns_cache *); DNS_PUBLIC void dns_cache_close(struct dns_cache *); /* * A P P L I C A T I O N I N T E R F A C E * * Options to change the behavior of the API. Applies across all the * different components. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_OPTS_INITIALIZER_ { 0, 0 }, 0 #define DNS_OPTS_INITIALIZER { DNS_OPTS_INITIALIZER_ } #define DNS_OPTS_INIT(...) { DNS_OPTS_INITIALIZER_, __VA_ARGS__ } #define dns_opts(...) (&dns_quietinit((struct dns_options)DNS_OPTS_INIT(__VA_ARGS__))) struct dns_options { /* * If the callback closes *fd, it must set it to -1. Otherwise, the * descriptor is queued and lazily closed at object destruction or * by an explicit call to _clear(). This allows safe use of * kqueue(2), epoll(2), et al -style persistent events. */ struct { void *arg; int (*cb)(int *fd, void *arg); } closefd; /* bitmask for _events() routines */ enum dns_events { DNS_SYSPOLL, DNS_LIBEVENT, } events; }; /* struct dns_options */ /* * S T A T S I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_stat { size_t queries; struct { struct { size_t count, bytes; } sent, rcvd; } udp, tcp; }; /* struct dns_stat */ /* * S O C K E T I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_socket; DNS_PUBLIC struct dns_socket *dns_so_open(const struct sockaddr *, int, const struct dns_options *, int *error); DNS_PUBLIC void dns_so_close(struct dns_socket *); DNS_PUBLIC void dns_so_reset(struct dns_socket *); DNS_PUBLIC unsigned short dns_so_mkqid(struct dns_socket *so); DNS_PUBLIC struct dns_packet *dns_so_query(struct dns_socket *, struct dns_packet *, struct sockaddr *, int *); DNS_PUBLIC int dns_so_submit(struct dns_socket *, struct dns_packet *, struct sockaddr *); DNS_PUBLIC int dns_so_check(struct dns_socket *); DNS_PUBLIC struct dns_packet *dns_so_fetch(struct dns_socket *, int *); DNS_PUBLIC time_t dns_so_elapsed(struct dns_socket *); DNS_PUBLIC void dns_so_clear(struct dns_socket *); DNS_PUBLIC int dns_so_events(struct dns_socket *); DNS_PUBLIC int dns_so_pollfd(struct dns_socket *); DNS_PUBLIC int dns_so_poll(struct dns_socket *, int); DNS_PUBLIC const struct dns_stat *dns_so_stat(struct dns_socket *); /* * R E S O L V E R I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_resolver; DNS_PUBLIC struct dns_resolver *dns_res_open(struct dns_resolv_conf *, struct dns_hosts *hosts, struct dns_hints *, struct dns_cache *, const struct dns_options *, int *); DNS_PUBLIC struct dns_resolver *dns_res_stub(const struct dns_options *, int *); DNS_PUBLIC void dns_res_reset(struct dns_resolver *); DNS_PUBLIC void dns_res_close(struct dns_resolver *); DNS_PUBLIC dns_refcount_t dns_res_acquire(struct dns_resolver *); DNS_PUBLIC dns_refcount_t dns_res_release(struct dns_resolver *); DNS_PUBLIC struct dns_resolver *dns_res_mortal(struct dns_resolver *); DNS_PUBLIC int dns_res_submit(struct dns_resolver *, const char *, enum dns_type, enum dns_class); DNS_PUBLIC int dns_res_submit2(struct dns_resolver *, const char *, size_t, enum dns_type, enum dns_class); DNS_PUBLIC int dns_res_check(struct dns_resolver *); DNS_PUBLIC struct dns_packet *dns_res_fetch(struct dns_resolver *, int *); DNS_PUBLIC time_t dns_res_elapsed(struct dns_resolver *); DNS_PUBLIC void dns_res_clear(struct dns_resolver *); DNS_PUBLIC int dns_res_events(struct dns_resolver *); DNS_PUBLIC int dns_res_pollfd(struct dns_resolver *); DNS_PUBLIC time_t dns_res_timeout(struct dns_resolver *); DNS_PUBLIC int dns_res_poll(struct dns_resolver *, int); DNS_PUBLIC struct dns_packet *dns_res_query(struct dns_resolver *, const char *, enum dns_type, enum dns_class, int, int *); DNS_PUBLIC const struct dns_stat *dns_res_stat(struct dns_resolver *); DNS_PUBLIC void dns_res_sethints(struct dns_resolver *, struct dns_hints *); /* * A D D R I N F O I N T E R F A C E * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct dns_addrinfo; DNS_PUBLIC struct dns_addrinfo *dns_ai_open(const char *, const char *, enum dns_type, const struct addrinfo *, struct dns_resolver *, int *); DNS_PUBLIC void dns_ai_close(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_nextent(struct addrinfo **, struct dns_addrinfo *); DNS_PUBLIC size_t dns_ai_print(void *, size_t, struct addrinfo *, struct dns_addrinfo *); DNS_PUBLIC time_t dns_ai_elapsed(struct dns_addrinfo *); DNS_PUBLIC void dns_ai_clear(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_events(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_pollfd(struct dns_addrinfo *); DNS_PUBLIC time_t dns_ai_timeout(struct dns_addrinfo *); DNS_PUBLIC int dns_ai_poll(struct dns_addrinfo *, int); DNS_PUBLIC const struct dns_stat *dns_ai_stat(struct dns_addrinfo *); /* * U T I L I T Y I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ DNS_PUBLIC size_t dns_strlcpy(char *, const char *, size_t); DNS_PUBLIC size_t dns_strlcat(char *, const char *, size_t); /* * M A C R O M A G I C S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define DNS_PP_MIN(a, b) (((a) < (b))? (a) : (b)) #define DNS_PP_MAX(a, b) (((a) > (b))? (a) : (b)) #define DNS_PP_NARG_(a, b, c, d, e, f, g, h, i, j, k, N,...) N #define DNS_PP_NARG(...) DNS_PP_NARG_(__VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define DNS_PP_CALL(F, ...) F(__VA_ARGS__) #define DNS_PP_PASTE(x, y) x##y #define DNS_PP_XPASTE(x, y) DNS_PP_PASTE(x, y) #define DNS_PP_STRINGIFY_(s) #s #define DNS_PP_STRINGIFY(s) DNS_PP_STRINGIFY_(s) #define DNS_PP_D1 0 #define DNS_PP_D2 1 #define DNS_PP_D3 2 #define DNS_PP_D4 3 #define DNS_PP_D5 4 #define DNS_PP_D6 5 #define DNS_PP_D7 6 #define DNS_PP_D8 7 #define DNS_PP_D9 8 #define DNS_PP_D10 9 #define DNS_PP_D11 10 #define DNS_PP_DEC(N) DNS_PP_XPASTE(DNS_PP_D, N) #endif /* DNS_H */ cqueues-rel-20161214/src/lib/fifo.h000066400000000000000000000431671302435770500166600ustar00rootroot00000000000000/* ========================================================================== * fifo.h - Simple byte FIFO with simple bit packing. * -------------------------------------------------------------------------- * Copyright (c) 2008-2013 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef FIFO_H #define FIFO_H #include /* va_list va_start() va_end() */ #include /* size_t */ #include /* SIZE_MAX */ #include /* EOF FILE fputc(3) vsnprintf(3) */ #include /* realloc(3) free(3) */ #include /* memcpy(3) memmove(3) strlen(3) memchr(3) */ #include /* isgraph(3) */ #include /* ENOMEM EOVERFLOW errno */ #include /* assert(3) */ #include /* struct iovec */ /* * V E R S I O N I N T E R F A C E S * * Vendor: Entity for which versions numbers are relevant. (If forking * change FIFO_VENDOR to avoid confusion.) * * Three versions: * * REL Official "release"--bug fixes, new features, etc. * ABI Changes to existing object sizes or parameter types. * API Changes that might effect application source. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define FIFO_VENDOR "william@25thandClement.com" #define FIFO_V_REL 0x20140424 /* 0x20130330 */ #define FIFO_V_ABI 0x20111113 /* 0x20100815 */ #define FIFO_V_API 0x20130325 /* 0x20111113 */ static inline const char *fifo_vendor(void) { return FIFO_VENDOR; } static inline int fifo_v_rel(void) { return FIFO_V_REL; } static inline int fifo_v_abi(void) { return FIFO_V_ABI; } static inline int fifo_v_api(void) { return FIFO_V_API; } /* * M I S C E L L A N E O U S M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define FIFO_MIN(a, b) (((a) < (b))? (a) : (b)) #define FIFO_NARG_(_15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N #define FIFO_NARG(...) FIFO_NARG_(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define FIFO_PASTE(a, b) a ## b #define FIFO_XPASTE(a, b) FIFO_PASTE(a, b) #if __GNUC__ #define FIFO_NOTUSED __attribute__((unused)) #define FIFO_FORMAT(type, x, y) __attribute__((format (type, x, y))) #else #define FIFO_NOTUSED #define FIFO_FORMAT(...) #endif /* * O B J E C T I N I T I A L I Z A T I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define FIFO_INITIALIZER { { 0 } } #define fifo_new(size) fifo_init(&(struct fifo)FIFO_INITIALIZER, ((size))? (unsigned char [(size) + !(size)]){ 0 } : (void *)0, (size)) struct fifo { /* static buffer */ struct iovec sbuf; unsigned char *base; size_t size, head, count; /* bit accumulators */ struct { unsigned char byte, count; } rbits, wbits; }; /* struct fifo */ #define FIFO_STATIC (1 << 0) #define FIFO_DYNAMIC (1 << 1) static inline int fifo_type(struct fifo *fifo) { return (1 << !fifo->sbuf.iov_base); } /* fifo_type() */ /* * Initialize fifo object as static or dynamic buffer. * * - Dynamic: Only pass the fifo object to initialize. DO NOT forget to * call fifo_reset() to release dynamic resources. * * - Static: Pass two additional arguments, pointer and size. Can safely * call fifo_reset(), but not required. */ static struct fifo *fifo_init(struct fifo *fifo, void *buf, size_t size) { fifo->sbuf = (struct iovec){ buf, size }; fifo->base = fifo->sbuf.iov_base; fifo->size = fifo->sbuf.iov_len; fifo->head = 0; fifo->count = 0; fifo->rbits.byte = 0; fifo->rbits.count = 0; fifo->wbits.byte = 0; fifo->wbits.count = 0; return fifo; } /* fifo_init() */ #define fifo_init3(...) (fifo_init)(__VA_ARGS__) #define fifo_init2(fifo, buf) fifo_init3((fifo), (buf), __builtin_object_size((buf), 3)) #define fifo_init1(fifo) fifo_init3((fifo), (void *)0, 0U) #define fifo_init(...) FIFO_XPASTE(fifo_init, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) static inline size_t fifo_update(struct fifo *, size_t); /* forward declaration */ static inline struct fifo *fifo_from(struct fifo *fifo, void *src, size_t size) { fifo_init(fifo, src, size); fifo_update(fifo, size); return fifo; } /* fifo_from() */ #define fifo_from3(...) (fifo_from)(__VA_ARGS__) #define fifo_from2(src, size) fifo_from3(&(struct fifo)FIFO_INITIALIZER, (src), (size)) #define fifo_from1(src) fifo_from3(&(struct fifo)FIFO_INITIALIZER, (src), __builtin_object_size((src), 3)) #define fifo_from(...) FIFO_XPASTE(fifo_from, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) #define fifo_into2(buf, size) fifo_init(&(struct fifo)FIFO_INITIALIZER, (buf), (size)) #define fifo_into1(buf) fifo_init(&(struct fifo)FIFO_INITIALIZER, (buf), __builtin_object_size((buf), 3)) #define fifo_into(...) FIFO_XPASTE(fifo_into, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) FIFO_NOTUSED static struct fifo *fifo_reset(struct fifo *fifo) { if (fifo->base != fifo->sbuf.iov_base) free(fifo->base); return fifo_init(fifo, fifo->sbuf.iov_base, fifo->sbuf.iov_len); } /* fifo_reset() */ FIFO_NOTUSED static struct fifo *fifo_purge(struct fifo *fifo) { fifo->head = 0; fifo->count = 0; fifo->rbits.byte = 0; fifo->rbits.count = 0; fifo->wbits.byte = 0; fifo->wbits.count = 0; return fifo; } /* fifo_purge() */ /* * M E M O R Y M A N A G E M E N T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef FIFO_TMPBUFSIZ #define FIFO_TMPBUFSIZ 2048 #endif static void fifo_realign(struct fifo *fifo) { if (fifo->size - fifo->head >= fifo->count) { memmove(fifo->base, &fifo->base[fifo->head], fifo->count); fifo->head = 0; } else { unsigned char tmp[FIFO_TMPBUFSIZ]; unsigned n, m; while (fifo->head != 0) { n = FIFO_MIN(fifo->head, sizeof tmp); m = fifo->size - n; memcpy(tmp, fifo->base, n); memmove(fifo->base, &fifo->base[n], m); memcpy(&fifo->base[m], tmp, n); fifo->head -= n; } } } /* fifo_realign() */ static inline size_t fifo_power2(size_t i) { #if defined SIZE_MAX i--; i |= i >> 1; i |= i >> 2; i |= i >> 4; i |= i >> 8; i |= i >> 16; #if SIZE_MAX != 0xffffffffu i |= i >> 32; #endif return ++i; #else #error No SIZE_MAX defined #endif } /* fifo_power2() */ static inline size_t fifo_roundup(size_t i) { if (i > ~(((size_t)-1) >> 1u)) return (size_t)-1; else return fifo_power2(i); } /* fifo_roundup() */ static int fifo_realloc(struct fifo *fifo, size_t size) { void *tmp; if (fifo->size >= size) return 0; if (fifo_type(fifo) == FIFO_STATIC) return ENOMEM; fifo_realign(fifo); size = fifo_roundup(size); if (!(tmp = realloc(fifo->base, size))) return errno; fifo->base = tmp; fifo->size = size; return 0; } /* fifo_realloc() */ static inline int fifo_grow(struct fifo *fifo, size_t size) { if (fifo->size - fifo->count >= size) return 0; if (~fifo->count < size) return EOVERFLOW; return fifo_realloc(fifo, fifo->count + size); } /* fifo_grow() */ /* * V E C T O R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static inline size_t fifo_rlen(struct fifo *fifo) { return fifo->count; } /* fifo_rlen() */ static inline size_t fifo_wlen(struct fifo *fifo) { return fifo->size - fifo->count; } /* fifo_wlen() */ static size_t fifo_rvec(struct fifo *fifo, struct iovec *iov, _Bool realign) { if (fifo->head + fifo->count > fifo->size && realign) fifo_realign(fifo); iov->iov_base = &fifo->base[fifo->head]; iov->iov_len = FIFO_MIN(fifo->size - fifo->head, fifo->count); return iov->iov_len; } /* fifo_rvec() */ #define fifo_rvec3(fifo, iov, realign, ...) (fifo_rvec)((fifo), (iov), (realign)) #define fifo_rvec(...) fifo_rvec3(__VA_ARGS__, 0, (char[-1]{ 0 })) static size_t fifo_wvec(struct fifo *fifo, struct iovec *iov, _Bool realign) { size_t tail, count; if (fifo->head + fifo->count < fifo->size && realign) fifo_realign(fifo); tail = (fifo->size)? ((fifo->head + fifo->count) % fifo->size) : 0; count = fifo_wlen(fifo); iov->iov_base = &fifo->base[tail]; iov->iov_len = FIFO_MIN(fifo->size - tail, count); return iov->iov_len; } /* fifo_wvec() */ #define fifo_wvec3(fifo, iov, realign, ...) (fifo_wvec)((fifo), (iov), (realign)) #define fifo_wvec(...) fifo_wvec3(__VA_ARGS__, 0, (char[-1]{ 0 })) FIFO_NOTUSED static size_t fifo_slice(struct fifo *fifo, struct iovec *iov, size_t p, size_t count) { size_t pe; if (p > fifo->count) { iov->iov_base = 0; iov->iov_len = 0; return 0; } count = FIFO_MIN(count, fifo->count - p); pe = p + count; if (fifo->head + p < fifo->size && fifo->head + pe > fifo->size) fifo_realign(fifo); iov->iov_base = &fifo->base[((fifo->head + p) % fifo->size)]; iov->iov_len = count; return count; } /* fifo_slice() */ static size_t fifo_tvec(struct fifo *fifo, struct iovec *iov, int ch) { unsigned char *p; if (fifo_rvec(fifo, iov)) { if ((p = memchr(iov->iov_base, ch, iov->iov_len))) return iov->iov_len = (p - (unsigned char *)iov->iov_base) + 1; if (fifo->count > iov->iov_len) { iov->iov_len = fifo->count - iov->iov_len; iov->iov_base = fifo->base; if ((p = memchr(iov->iov_base, ch, iov->iov_len))) { iov->iov_len = (p - fifo->base) + (fifo->size - fifo->head) + 1; iov->iov_base = fifo->base; fifo_realign(fifo); return iov->iov_len; } } iov->iov_len = 0; } return 0; } /* fifo_tvec() */ static inline size_t fifo_lvec(struct fifo *fifo, struct iovec *iov) { return fifo_tvec(fifo, iov, '\n'); } /* fifo_lvec() */ static int fifo_wbuf(struct fifo *fifo, struct iovec *iov, size_t size) { int error; if ((error = fifo_grow(fifo, size))) return error; /* try to avoid realigning buffer */ if (fifo_wvec(fifo, iov, 0) < size) fifo_wvec(fifo, iov, 1); return 0; } /* fifo_wbuf() */ /* * R E A D / W R I T E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef FIFO_AUTOALIGN #define FIFO_AUTOALIGN 1 #endif static inline size_t fifo_discard(struct fifo *fifo, size_t count) { count = FIFO_MIN(count, fifo->count); fifo->head = (fifo->head + count) % fifo->size; fifo->count -= count; #if FIFO_AUTOALIGN if (!fifo->count) fifo->head = 0; #endif return count; } /* fifo_discard() */ static inline size_t fifo_update(struct fifo *fifo, size_t count) { count = FIFO_MIN(count, fifo->size - fifo->count); fifo->count += count; return count; } /* fifo_update() */ static inline size_t fifo_rewind(struct fifo *fifo, size_t count) { count = FIFO_MIN(count, fifo->size - fifo->count); fifo->head = (fifo->head + (fifo->size - count)) % fifo->size; fifo->count += count; return count; } /* fifo_rewind() */ FIFO_NOTUSED static int fifo_write(struct fifo *fifo, const void *src, size_t len) { const unsigned char *p = src, *pe = p + len; struct iovec iov; size_t n; int error = 0; do { while (fifo_wvec(fifo, &iov) && p < pe) { n = FIFO_MIN(iov.iov_len, (size_t)(pe - p)); memcpy(iov.iov_base, p, n); p += n; fifo_update(fifo, n); } } while (p < pe && !(error = fifo_grow(fifo, pe - p))); return error; } /* fifo_write() */ FIFO_NOTUSED static size_t fifo_read(struct fifo *fifo, void *dst, size_t lim) { unsigned char *p = dst, *pe = p + lim; struct iovec iov; size_t n; while (p < pe && fifo_rvec(fifo, &iov)) { n = FIFO_MIN(iov.iov_len, (size_t)(pe - p)); memcpy(p, iov.iov_base, n); p += n; fifo_discard(fifo, n); } return p - (unsigned char *)dst; } /* fifo_read() */ static int fifo_putc(struct fifo *fifo, int c) { int error; if (fifo->count >= fifo->size && (error = fifo_grow(fifo, 1))) return error; fifo->base[(fifo->head + fifo->count) % fifo->size] = (0xff & c); fifo_update(fifo, 1); return 0; } /* fifo_putc() */ static inline int fifo_puts(struct fifo *fifo, const void *src) { return fifo_write(fifo, src, strlen(src)); } /* fifo_puts() */ FIFO_NOTUSED FIFO_FORMAT(printf, 2, 3) static int fifo_printf(struct fifo *fifo, const char *fmt, ...) { va_list ap; struct iovec iov; int count = 0, error; do { if ((error = fifo_wbuf(fifo, &iov, (size_t)count + 1))) return error; va_start(ap, fmt); count = vsnprintf(iov.iov_base, iov.iov_len, fmt, ap); va_end(ap); if (count < 0) return (errno)? errno : ENOMEM; } while ((size_t)count >= iov.iov_len); fifo_update(fifo, count); return 0; } /* fifo_printf() */ static inline int fifo_ungetc(struct fifo *fifo, int c) { int error; if ((error = fifo_grow(fifo, 1))) return error; assert(1 == fifo_rewind(fifo, 1)); fifo->base[fifo->head] = c; return 0; } /* fifo_ungetc() */ static inline int fifo_getc(struct fifo *fifo) { int c; if (!fifo->count) return EOF; c = fifo->base[fifo->head]; fifo_discard(fifo, 1); return c; } /* fifo_getc() */ static inline int fifo_peek(struct fifo *fifo, size_t p) { if (p >= fifo->count) return EOF; return fifo->base[(fifo->head + p) % fifo->size]; } /* fifo_peek() */ static inline int fifo_scan(struct fifo *fifo, size_t *p) { return fifo_peek(fifo, (*p)++); } /* fifo_scan() */ #define FIFO_NOINFO 1 FIFO_NOTUSED static void fifo_dump(struct fifo *fifo, FILE *fp, int flags) { static const unsigned char hex[] = "0123456789abcdef"; size_t p, n; int ch; if (!(FIFO_NOINFO & flags)) fprintf(fp, "%zu of %zu bytes in FIFO; %u unread bits (0x%.2x); %u unflushed bits (0x%.2x)\n", fifo->count, fifo->size, fifo->rbits.count, (((1 << fifo->rbits.count) - 1) & fifo->rbits.byte), fifo->wbits.count, (((1 << fifo->wbits.count) - 1) & fifo->wbits.byte)); for (p = 0; p < fifo->count; p += 16) { fprintf(fp, " %.4x ", (unsigned)p); for (n = 0; n < 8 && EOF != (ch = fifo_peek(fifo, p)); n++, p++) { fputc(hex[0x0f & (ch >> 4)], fp); fputc(hex[0x0f & (ch >> 0)], fp); fputc(' ', fp); } fputc(' ', fp); for (; n < 16 && EOF != (ch = fifo_peek(fifo, p)); n++, p++) { fputc(hex[0x0f & (ch >> 4)], fp); fputc(hex[0x0f & (ch >> 0)], fp); fputc(' ', fp); } p -= n; for (; n < 16; n++) { fputc(' ', fp); fputc(' ', fp); fputc(' ', fp); } fputc(' ', fp); fputc('|', fp); for (n = 0; n < 16 && EOF != (ch = fifo_peek(fifo, p)); n++, p++) { fputc((isgraph(ch)? ch : '.'), fp); } p -= n; for (; n < 16; n++) fputc(' ', fp); fputc('|', fp); fputc('\n', fp); } } /* fifo_dump() */ #define fifo_dump3(...) fifo_dump(__VA_ARGS__) #define fifo_dump2(...) fifo_dump3(__VA_ARGS__, 0) #define fifo_dump1(...) fifo_dump3(__VA_ARGS__, stderr, 0) #define fifo_dump(...) FIFO_XPASTE(fifo_dump, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) /* * B I T P A C K I N G R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static inline size_t fifo_rbits(struct fifo *fifo) { return fifo->rbits.count + (8 * fifo_rlen(fifo)); } /* fifo_rbits() */ static inline size_t fifo_wbits(struct fifo *fifo) { return (8 - fifo->wbits.count) + (8 * fifo_wlen(fifo)); } /* fifo_wbits() */ FIFO_NOTUSED static unsigned long long fifo_unpack(struct fifo *fifo, unsigned count) { unsigned long long bits = 0; unsigned mask, nbits; if (fifo_rbits(fifo) < count) return 0; while (count) { if (!fifo->rbits.count) { fifo->rbits.byte = fifo_getc(fifo); fifo->rbits.count = 8; } nbits = FIFO_MIN(count, fifo->rbits.count); mask = (1 << nbits) - 1; bits <<= nbits; bits |= mask & (fifo->rbits.byte >> (fifo->rbits.count - nbits)); fifo->rbits.count -= nbits; count -= nbits; } return bits; } /* fifo_unpack() */ FIFO_NOTUSED static int fifo_pack(struct fifo *fifo, unsigned long long bits, unsigned count) { unsigned mask, nbits; int error; if (fifo_wbits(fifo) < count && (error = fifo_grow(fifo, 8))) return error; while (count) { nbits = FIFO_MIN(count, 8U - fifo->wbits.count); mask = (1U << nbits) - 1; fifo->wbits.byte <<= nbits; fifo->wbits.byte |= mask & (bits >> (count - nbits)); fifo->wbits.count += nbits; count -= nbits; if (fifo->wbits.count >= 8) { fifo_putc(fifo, fifo->wbits.byte); fifo->wbits.byte = 0; fifo->wbits.count = 0; } } return 0; } /* fifo_pack() */ #endif /* FIFO_H */ #if FIFO_MAIN #include int main(void) { struct fifo *fifo = fifo_new(0); struct iovec iov; int ch; setlocale(LC_ALL, "C"); while (EOF != (ch = getchar())) fifo_putc(fifo, ch); puts("[stdin]"); fifo_dump(fifo, stdout); puts("[shifted 4 bits]"); fifo_pack(fifo, fifo_unpack(fifo, 4), 4); fifo_dump(fifo, stdout); puts("[shifted 1/2 count]"); for (size_t i = 0; i < fifo->count / 2; i++) fifo_putc(fifo, fifo_getc(fifo)); fifo_dump(fifo, stdout); puts("[slice of 17 bytes at 17]"); fifo_slice(fifo, &iov, 17, 17); fifo_dump(fifo_from(iov.iov_base, iov.iov_len), stdout); return 0; } /* main() */ #endif /* FIFO_MAIN */ cqueues-rel-20161214/src/lib/kpoll.c000066400000000000000000000244761302435770500170530ustar00rootroot00000000000000/* ========================================================================== * kpoll.c - Brew of Linux epoll, BSD kqueue, and Solaris Ports. * -------------------------------------------------------------------------- * Copyright (c) 2012 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include /* NULL offsetof */ #include /* memset(3) */ #include /* struct timespec */ #include /* errno */ #include /* POLLIN POLLOUT */ #include /* [FEATURES] read(2) write(2) */ #ifndef __GLIBC_PREREQ #define __GLIBC_PREREQ(m, n) 0 #endif #ifndef HAVE_KQUEUE #define HAVE_KQUEUE (__FreeBSD__ || __NetBSD__ || __OpenBSD__ || __APPLE__) #endif #ifndef HAVE_EPOLL #define HAVE_EPOLL (__linux__) #endif #ifndef HAVE_PORTS #define HAVE_PORTS (__sun) #endif #ifndef HAVE_EPOLL_CREATE1 #define HAVE_EPOLL_CREATE1 (HAVE_EPOLL && __GLIBC_PREREQ(2, 9)) #endif #ifndef HAVE_PIPE2 #define HAVE_PIPE2 __GLIBC_PREREQ(2, 9) #endif #if HAVE_EPOLL #include /* struct epoll_event epoll_create(2) epoll_ctl(2) epoll_wait(2) */ #elif HAVE_PORTS #include /* PORT_SOURCE_FD port_associate(2) port_disassociate(2) port_getn(2) port_alert(2) */ #else #include /* EVFILT_READ EVFILT_WRITE EV_SET EV_ADD EV_DELETE struct kevent kqueue(2) kevent(2) */ #endif #include /* LIST_ENTRY LIST_HEAD LIST_REMOVE LIST_INSERT_HEAD */ #include /* F_GETFL F_SETFL F_GETFD F_SETFD FD_CLOEXEC O_NONBLOCK O_CLOEXEC fcntl(2) */ #define KPOLL_MAXWAIT 32 #define countof(a) (sizeof (a) / sizeof *(a)) #if __GNUC__ >= 3 #define unlikely(expr) __builtin_expect((expr), 0) #else #define unlikely(expr) (expr) #endif static int setnonblock(int fd) { int flags; if (-1 == (flags = fcntl(fd, F_GETFL))) return errno; if (!(flags & O_NONBLOCK)) { if (-1 == fcntl(fd, F_SETFL, (flags | O_NONBLOCK))) return errno; } return 0; } /* setnonblock() */ static int setcloexec(int fd) { int flags; if (-1 == (flags = fcntl(fd, F_GETFD))) return errno; if (!(flags & FD_CLOEXEC)) { if (-1 == fcntl(fd, F_SETFD, (flags | FD_CLOEXEC))) return errno; } return 0; } /* setcloexec() */ static void closefd(int *fd) { if (*fd >= 0) { while (0 != close(*fd) && errno == EINTR) ;; *fd = -1; } } /* closefd() */ #if HAVE_EPOLL typedef struct epoll_event event_t; #elif HAVE_PORTS typedef port_event_t event_t; #else /* NetBSD uses intptr_t while others use void * for .udata */ #define EV_SETx(ev, a, b, c, d, e, f) EV_SET((ev), (a), (b), (c), (d), (e), ((__typeof__((ev)->udata))(f))) typedef struct kevent event_t; #endif static inline void *event_udata(const event_t *event) { #if HAVE_EPOLL return event->data.ptr; #elif HAVE_PORTS return event->portev_user; #else return (void *)event->udata; #endif } /* event_udata() */ static inline short event_pending(const event_t *event) { #if HAVE_EPOLL return event->events; #elif HAVE_PORTS return event->portev_events; #else return (event->filter == EVFILT_READ)? POLLIN : (event->filter == EVFILT_WRITE)? POLLOUT : 0; #endif } /* event_pending() */ struct kpollfd { int fd; short events; short revents; LIST_ENTRY(kpollfd) le; }; /* struct kpollfd */ struct kpoll { int fd; LIST_HEAD(, kpollfd) pending; LIST_HEAD(, kpollfd) polling; LIST_HEAD(, kpollfd) dormant; struct { struct kpollfd event; int fd[2]; } alert; }; /* struct kpoll */ static void kpoll_move(struct kpoll *kp, struct kpollfd *fd) { LIST_REMOVE(fd, le); if (fd->revents) LIST_INSERT_HEAD(&kp->pending, fd, le); else if (fd->events) LIST_INSERT_HEAD(&kp->polling, fd, le); else LIST_INSERT_HEAD(&kp->dormant, fd, le); } /* kpoll_move() */ static struct kpollfd *kpoll_next(struct kpoll *kp) { return LIST_FIRST(&kp->pending); } /* kpoll_next() */ static int kpoll_ctl(struct kpoll *kp, struct kpollfd *fd, short events) { int error = 0; if (fd->events == events) goto reset; #if HAVE_EPOLL struct epoll_event event; int op; op = (!fd->events)? EPOLL_CTL_ADD : (!events)? EPOLL_CTL_DEL : EPOLL_CTL_MOD; memset(&event, 0, sizeof event); event.events = events; event.data.ptr = fd; if (0 != epoll_ctl(kp->fd, op, fd->fd, &event)) goto error; fd->events = events; #elif HAVE_PORTS if (!events) { if (0 != port_dissociate(kp->fd, PORT_SOURCE_FD, fd->fd)) goto error; } else { if (0 != port_associate(kp->fd, PORT_SOURCE_FD, fd->fd, events, fd)) goto error; } fd->events = events; #else struct kevent event; if (events & POLLIN) { if (!(fd->events & POLLIN)) { EV_SETx(&event, fd->fd, EVFILT_READ, EV_ADD, 0, 0, fd); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) goto error; fd->events |= POLLIN; } } else if (fd->events & POLLIN) { EV_SETx(&event, fd->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) goto error; fd->events &= ~POLLIN; } if (events & POLLOUT) { if (!(fd->events & POLLOUT)) { EV_SETx(&event, fd->fd, EVFILT_WRITE, EV_ADD, 0, 0, fd); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) goto error; fd->events |= POLLOUT; } } else if (fd->events & POLLOUT) { EV_SETx(&event, fd->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) goto error; fd->events &= ~POLLOUT; } #endif reset: fd->revents = 0; kpoll_move(kp, fd); return error; error: error = errno; goto reset; } /* kpoll_ctl() */ static void kpoll_add(struct kpoll *kp, struct kpollfd *pfd, int fd) { pfd->fd = fd; pfd->events = 0; pfd->revents = 0; LIST_INSERT_HEAD(&kp->dormant, pfd, le); } /* kpoll_add() */ static void kpoll_del(struct kpoll *kp, struct kpollfd *fd) { kpoll_ctl(kp, fd, 0); LIST_REMOVE(fd, le); } /* kpoll_del() */ static int kpoll_alert(struct kpoll *kp) { #if HAVE_PORTS return (!port_alert(kp->fd, PORT_ALERT_SET, POLLIN, &kp->alert.event))? 0 : errno; #else while (1 != write(kp->alert.fd[1], "!", 1)) { switch (errno) { case EINTR: continue; case EAGAIN: return 0; default: return errno; } } return kpoll_ctl(kp, &kp->alert.event, POLLIN); #endif } /* kpoll_alert() */ static int kpoll_calm(struct kpoll *kp) { #if HAVE_PORTS return (!port_alert(kp->fd, PORT_ALERT_SET, 0, &kp->alert.event))? 0 : errno; #else char buf[512]; while (read(kp->alert.fd[0], buf, sizeof buf) > 0) ;; return kpoll_ctl(kp, &kp->alert.event, POLLIN); #endif } /* kpoll_calm() */ static inline struct timespec *ms2ts_(struct timespec *ts, int ms) { if (ms < 0) return 0; ts->tv_sec = ms / 1000; ts->tv_nsec = (ms % 1000) * 1000000; return ts; } /* ms2ts_() */ #define ms2ts(ms) (ms2ts_(&(struct timespec){ 0, 0 }, (ms))) static int kpoll_wait(struct kpoll *kp, int timeout) { event_t event[KPOLL_MAXWAIT]; struct kpollfd *fd; int error; if (!LIST_EMPTY(&kp->pending)) return 0; #if HAVE_EPOLL int i, n; if (-1 == (n = epoll_wait(kp->fd, event, (int)countof(event), timeout))) return (errno == EINTR)? 0 : errno; #elif HAVE_PORTS uint_t i, n = 1; if (0 != port_getn(kp->fd, event, countof(event), &n, ms2ts(timeout))) return (errno == ETIME || errno == EINTR)? 0 : errno; #else int i, n; if (-1 == (n = kevent(kp->fd, NULL, 0, event, (int)countof(event), ms2ts(timeout)))) return (errno == EINTR)? 0 : errno; #endif for (i = 0; i < n; i++) { fd = event_udata(&event[i]); #if HAVE_PORTS fd->events = 0; #endif if (unlikely(fd == &kp->alert.event)) { if ((error = kpoll_calm(kp))) return error; } else { fd->revents |= event_pending(&event[i]); kpoll_move(kp, fd); } } return 0; } /* kpoll_wait() */ static int alert_init(struct kpoll *kp) { #if HAVE_PORTS return 0; #else int error; #if HAVE_PIPE2 if (0 != pipe2(kp->alert.fd, O_CLOEXEC|O_NONBLOCK)) return errno; #else if (0 != pipe(kp->alert.fd)) return errno; for (int i = 0; i < 2; i++) { if ((error = setcloexec(kp->alert.fd[i])) || (error = setnonblock(kp->alert.fd[i]))) return error; } #endif kpoll_add(kp, &kp->alert.event, kp->alert.fd[0]); return kpoll_ctl(kp, &kp->alert.event, POLLIN); #endif } /* alert_init() */ static void alert_destroy(struct kpoll *kp) { #if HAVE_PORTS (void)0; #else closefd(&kp->alert.fd[0]); closefd(&kp->alert.fd[1]); #endif } /* alert_destroy() */ static void kpoll_destroy(struct kpoll *kp) { closefd(&kp->fd); alert_destroy(kp); LIST_INIT(&kp->pending); LIST_INIT(&kp->polling); LIST_INIT(&kp->dormant); } /* kpoll_destroy() */ static int kpoll_init(struct kpoll *kp) { int error; kp->fd = -1; kp->alert.fd[0] = -1; kp->alert.fd[1] = -1; LIST_INIT(&kp->pending); LIST_INIT(&kp->polling); LIST_INIT(&kp->dormant); #if HAVE_EPOLL_CREATE1 if (-1 == (kp->fd = epoll_create1(O_CLOEXEC))) goto syerr; #elif HAVE_EPOLL if (-1 == (kp->fd = epoll_create(0))) goto syerr; #elif HAVE_PORTS if (-1 == (kp->fd = port_create())) { if (errno == EAGAIN) { /* too confusing */ error = EMFILE; goto error; } else goto syerr; } #else if (-1 == (kp->fd = kqueue())) goto syerr; #endif #if !HAVE_EPOLL_CREATE1 if ((error = setcloexec(kp->fd))) goto error; #endif if ((error = alert_init(kp))) goto error; return 0; syerr: error = errno; error: kpoll_destroy(kp); return error; } /* kpoll_init() */ int main(void) { return 0; } cqueues-rel-20161214/src/lib/llrb.h000066400000000000000000000271571302435770500166710ustar00rootroot00000000000000/* ========================================================================== * llrb.h - Iterative Left-leaning Red-Black Tree. * -------------------------------------------------------------------------- * Copyright (c) 2011, 2013 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * -------------------------------------------------------------------------- * CREDITS: * o Algorithm courtesy of Robert Sedgewick, "Left-leaning Red-Black * Trees" (September 2008); and Robert Sedgewick and Kevin Wayne, * Algorithms (4th ed. 2011). * * Sedgewick touts the simplicity of the recursive implementation, * but at least for the 2-3 tree variant the iterative approach is * almost line-for-line identical. The magic of C pointers helps; * it'd be uglier with Java. * * A couple of missing NULL checks were added to Sedgewick's deletion * example, and insert was optimized to short-circuit rotations when * walking up the tree. * * o Code implemented in the fashion of Niels Provos' excellent *BSD * sys/tree.h pre-processor library. * * Regarding relative performance, I've refrained from sharing my own * benchmarks. Differences in run-time speed were too correlated to * compiler options and other external factors. * * Provos' delete implementation doesn't need to start at the root of * the tree. However, RB_REMOVE must be passed the actual node to be * removed. LLRB_REMOVE merely requires a key, much like * RB_FIND/LLRB_FIND. * ========================================================================== */ #ifndef LLRB_H #define LLRB_H #define LLRB_VENDOR "william@25thandClement.com" #define LLRB_VERSION 0x20130925 #ifndef LLRB_STATIC #ifdef __GNUC__ #define LLRB_STATIC __attribute__((__unused__)) static #else #define LLRB_STATIC static #endif #endif #define LLRB_HEAD(name, type) \ struct name { struct type *rbh_root; } #define LLRB_INITIALIZER(root) { 0 } #define LLRB_INIT(root) do { (root)->rbh_root = 0; } while (0) #define LLRB_BLACK 0 #define LLRB_RED 1 #define LLRB_ENTRY(type) \ struct { struct type *rbe_left, *rbe_right, *rbe_parent; _Bool rbe_color; } #define LLRB_LEFT(elm, field) (elm)->field.rbe_left #define LLRB_RIGHT(elm, field) (elm)->field.rbe_right #define LLRB_PARENT(elm, field) (elm)->field.rbe_parent #define LLRB_EDGE(head, elm, field) (((elm) == LLRB_ROOT(head))? &LLRB_ROOT(head) : ((elm) == LLRB_LEFT(LLRB_PARENT((elm), field), field))? &LLRB_LEFT(LLRB_PARENT((elm), field), field) : &LLRB_RIGHT(LLRB_PARENT((elm), field), field)) #define LLRB_COLOR(elm, field) (elm)->field.rbe_color #define LLRB_ROOT(head) (head)->rbh_root #define LLRB_EMPTY(head) ((head)->rbh_root == 0) #define LLRB_ISRED(elm, field) ((elm) && LLRB_COLOR((elm), field) == LLRB_RED) #define LLRB_PROTOTYPE(name, type, field, cmp) \ LLRB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define LLRB_PROTOTYPE_STATIC(name, type, field, cmp) \ LLRB_PROTOTYPE_INTERNAL(name, type, field, cmp, LLRB_STATIC) #define LLRB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr struct type *name##_LLRB_INSERT(struct name *, struct type *); \ attr struct type *name##_LLRB_DELETE(struct name *, struct type *); \ attr struct type *name##_LLRB_FIND(struct name *, struct type *); \ attr struct type *name##_LLRB_MIN(struct type *); \ attr struct type *name##_LLRB_MAX(struct type *); \ attr struct type *name##_LLRB_NEXT(struct type *); #define LLRB_GENERATE(name, type, field, cmp) \ LLRB_GENERATE_INTERNAL(name, type, field, cmp,) #define LLRB_GENERATE_STATIC(name, type, field, cmp) \ LLRB_GENERATE_INTERNAL(name, type, field, cmp, LLRB_STATIC) #define LLRB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ static inline void name##_LLRB_ROTL(struct type **pivot) { \ struct type *a = *pivot; \ struct type *b = LLRB_RIGHT(a, field); \ if ((LLRB_RIGHT(a, field) = LLRB_LEFT(b, field))) \ LLRB_PARENT(LLRB_RIGHT(a, field), field) = a; \ LLRB_LEFT(b, field) = a; \ LLRB_COLOR(b, field) = LLRB_COLOR(a, field); \ LLRB_COLOR(a, field) = LLRB_RED; \ LLRB_PARENT(b, field) = LLRB_PARENT(a, field); \ LLRB_PARENT(a, field) = b; \ *pivot = b; \ } \ static inline void name##_LLRB_ROTR(struct type **pivot) { \ struct type *b = *pivot; \ struct type *a = LLRB_LEFT(b, field); \ if ((LLRB_LEFT(b, field) = LLRB_RIGHT(a, field))) \ LLRB_PARENT(LLRB_LEFT(b, field), field) = b; \ LLRB_RIGHT(a, field) = b; \ LLRB_COLOR(a, field) = LLRB_COLOR(b, field); \ LLRB_COLOR(b, field) = LLRB_RED; \ LLRB_PARENT(a, field) = LLRB_PARENT(b, field); \ LLRB_PARENT(b, field) = a; \ *pivot = a; \ } \ static inline void name##_LLRB_FLIP(struct type *root) { \ LLRB_COLOR(root, field) = !LLRB_COLOR(root, field); \ LLRB_COLOR(LLRB_LEFT(root, field), field) = !LLRB_COLOR(LLRB_LEFT(root, field), field); \ LLRB_COLOR(LLRB_RIGHT(root, field), field) = !LLRB_COLOR(LLRB_RIGHT(root, field), field); \ } \ static inline void name##_LLRB_FIXUP(struct type **root) { \ if (LLRB_ISRED(LLRB_RIGHT(*root, field), field) && !LLRB_ISRED(LLRB_LEFT(*root, field), field)) \ name##_LLRB_ROTL(root); \ if (LLRB_ISRED(LLRB_LEFT(*root, field), field) && LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*root, field), field), field)) \ name##_LLRB_ROTR(root); \ if (LLRB_ISRED(LLRB_LEFT(*root, field), field) && LLRB_ISRED(LLRB_RIGHT(*root, field), field)) \ name##_LLRB_FLIP(*root); \ } \ attr struct type *name##_LLRB_INSERT(struct name *head, struct type *elm) { \ struct type **root = &LLRB_ROOT(head); \ struct type *parent = 0; \ while (*root) { \ int comp = (cmp)((elm), (*root)); \ parent = *root; \ if (comp < 0) \ root = &LLRB_LEFT(*root, field); \ else if (comp > 0) \ root = &LLRB_RIGHT(*root, field); \ else \ return *root; \ } \ LLRB_LEFT((elm), field) = 0; \ LLRB_RIGHT((elm), field) = 0; \ LLRB_COLOR((elm), field) = LLRB_RED; \ LLRB_PARENT((elm), field) = parent; \ *root = (elm); \ while (parent && (LLRB_ISRED(LLRB_LEFT(parent, field), field) || LLRB_ISRED(LLRB_RIGHT(parent, field), field))) { \ root = LLRB_EDGE(head, parent, field); \ parent = LLRB_PARENT(parent, field); \ name##_LLRB_FIXUP(root); \ } \ LLRB_COLOR(LLRB_ROOT(head), field) = LLRB_BLACK; \ return 0; \ } \ static inline void name##_LLRB_MOVL(struct type **pivot) { \ name##_LLRB_FLIP(*pivot); \ if (LLRB_ISRED(LLRB_LEFT(LLRB_RIGHT(*pivot, field), field), field)) { \ name##_LLRB_ROTR(&LLRB_RIGHT(*pivot, field)); \ name##_LLRB_ROTL(pivot); \ name##_LLRB_FLIP(*pivot); \ } \ } \ static inline void name##_LLRB_MOVR(struct type **pivot) { \ name##_LLRB_FLIP(*pivot); \ if (LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*pivot, field), field), field)) { \ name##_LLRB_ROTR(pivot); \ name##_LLRB_FLIP(*pivot); \ } \ } \ static inline struct type *name##_DELETEMIN(struct name *head, struct type **root) { \ struct type **pivot = root, *deleted, *parent; \ while (LLRB_LEFT(*pivot, field)) { \ if (!LLRB_ISRED(LLRB_LEFT(*pivot, field), field) && !LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*pivot, field), field), field)) \ name##_LLRB_MOVL(pivot); \ pivot = &LLRB_LEFT(*pivot, field); \ } \ deleted = *pivot; \ parent = LLRB_PARENT(*pivot, field); \ *pivot = 0; \ while (root != pivot) { \ pivot = LLRB_EDGE(head, parent, field); \ parent = LLRB_PARENT(parent, field); \ name##_LLRB_FIXUP(pivot); \ } \ return deleted; \ } \ attr struct type *name##_LLRB_DELETE(struct name *head, struct type *elm) { \ struct type **root = &LLRB_ROOT(head), *parent = 0, *deleted = 0; \ int comp; \ while (*root) { \ parent = LLRB_PARENT(*root, field); \ comp = (cmp)(elm, *root); \ if (comp < 0) { \ if (LLRB_LEFT(*root, field) && !LLRB_ISRED(LLRB_LEFT(*root, field), field) && !LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*root, field), field), field)) \ name##_LLRB_MOVL(root); \ root = &LLRB_LEFT(*root, field); \ } else { \ if (LLRB_ISRED(LLRB_LEFT(*root, field), field)) { \ name##_LLRB_ROTR(root); \ comp = (cmp)(elm, *root); \ } \ if (!comp && !LLRB_RIGHT(*root, field)) { \ deleted = *root; \ *root = 0; \ break; \ } \ if (LLRB_RIGHT(*root, field) && !LLRB_ISRED(LLRB_RIGHT(*root, field), field) && !LLRB_ISRED(LLRB_LEFT(LLRB_RIGHT(*root, field), field), field)) { \ name##_LLRB_MOVR(root); \ comp = (cmp)(elm, *root); \ } \ if (!comp) { \ struct type *orphan = name##_DELETEMIN(head, &LLRB_RIGHT(*root, field)); \ LLRB_COLOR(orphan, field) = LLRB_COLOR(*root, field); \ LLRB_PARENT(orphan, field) = LLRB_PARENT(*root, field); \ if ((LLRB_RIGHT(orphan, field) = LLRB_RIGHT(*root, field))) \ LLRB_PARENT(LLRB_RIGHT(orphan, field), field) = orphan; \ if ((LLRB_LEFT(orphan, field) = LLRB_LEFT(*root, field))) \ LLRB_PARENT(LLRB_LEFT(orphan, field), field) = orphan; \ deleted = *root; \ *root = orphan; \ parent = *root; \ break; \ } else \ root = &LLRB_RIGHT(*root, field); \ } \ } \ while (parent) { \ root = LLRB_EDGE(head, parent, field); \ parent = LLRB_PARENT(parent, field); \ name##_LLRB_FIXUP(root); \ } \ if (LLRB_ROOT(head)) \ LLRB_COLOR(LLRB_ROOT(head), field) = LLRB_BLACK; \ return deleted; \ } \ attr struct type *name##_LLRB_FIND(struct name *head, struct type *key) { \ struct type *elm = LLRB_ROOT(head); \ while (elm) { \ int comp = (cmp)(key, elm); \ if (comp < 0) \ elm = LLRB_LEFT(elm, field); \ else if (comp > 0) \ elm = LLRB_RIGHT(elm, field); \ else \ return elm; \ } \ return 0; \ } \ attr struct type *name##_LLRB_MIN(struct type *elm) { \ while (elm && LLRB_LEFT(elm, field)) \ elm = LLRB_LEFT(elm, field); \ return elm; \ } \ attr struct type *name##_LLRB_MAX(struct type *elm) { \ while (elm && LLRB_RIGHT(elm, field)) \ elm = LLRB_RIGHT(elm, field); \ return elm; \ } \ attr struct type *name##_LLRB_NEXT(struct type *elm) { \ if (LLRB_RIGHT(elm, field)) { \ return name##_LLRB_MIN(LLRB_RIGHT(elm, field)); \ } else if (LLRB_PARENT(elm, field)) { \ if (elm == LLRB_LEFT(LLRB_PARENT(elm, field), field)) \ return LLRB_PARENT(elm, field); \ while (LLRB_PARENT(elm, field) && elm == LLRB_RIGHT(LLRB_PARENT(elm, field), field)) \ elm = LLRB_PARENT(elm, field); \ return LLRB_PARENT(elm, field); \ } else return 0; \ } #define LLRB_INSERT(name, head, elm) name##_LLRB_INSERT((head), (elm)) #define LLRB_DELETE(name, head, elm) name##_LLRB_DELETE((head), (elm)) #define LLRB_REMOVE(name, head, elm) name##_LLRB_DELETE((head), (elm)) #define LLRB_FIND(name, head, elm) name##_LLRB_FIND((head), (elm)) #define LLRB_MIN(name, head) name##_LLRB_MIN(LLRB_ROOT((head))) #define LLRB_MAX(name, head) name##_LLRB_MAX(LLRB_ROOT((head))) #define LLRB_NEXT(name, head, elm) name##_LLRB_NEXT((elm)) #define LLRB_FOREACH(elm, name, head) \ for ((elm) = LLRB_MIN(name, head); (elm); (elm) = name##_LLRB_NEXT((elm))) #endif /* LLRB_H */ cqueues-rel-20161214/src/lib/notify.c000066400000000000000000000627771302435770500172500ustar00rootroot00000000000000/* ========================================================================== * notify.c - Kernel File Notification. * -------------------------------------------------------------------------- * Copyright (c) 2012 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include /* NAME_MAX */ #include /* offsetof */ #include /* intptr_t */ #include /* calloc(3) free(3) */ #include /* memcpy(3) memchr(3) strcmp(3) */ #include /* ffs(3) */ #include /* ENAMETOOLONG EINTR EAGAIN EMFILE EISDIR ENOTDIR */ #include /* LIST_* */ #include /* close(2) */ #include /* O_CLOEXEC O_DIRECTORY ... open(2) openat(2) fcntl(2) */ #include /* DIR fdopendir(3) opendir(3) readdir_r(3) closedir(3) */ #include /* POLLIN poll(2) */ #include "notify.h" #include "llrb.h" /* * F E A T U R E M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef ENABLE_INOTIFY #define ENABLE_INOTIFY HAVE_INOTIFY_INIT #endif #ifndef ENABLE_FEN #define ENABLE_FEN HAVE_PORT_H #endif #ifndef ENABLE_KQUEUE #define ENABLE_KQUEUE HAVE_KQUEUE #endif #ifndef HAVE_O_CLOEXEC #define HAVE_O_CLOEXEC (defined O_CLOEXEC) #endif #ifndef HAVE_O_DIRECTORY #define HAVE_O_DIRECTORY (defined O_DIRECTORY) #endif #ifndef HAVE_IN_CLOEXEC #define HAVE_IN_CLOEXEC (defined IN_CLOEXEC) #endif #ifndef HAVE_IN_NONBLOCK #define HAVE_IN_NONBLOCK (defined IN_NONBLOCK) #endif #if ENABLE_INOTIFY #include #elif ENABLE_FEN #include #include #include #ifndef NAME_MAX #define NAME_MAX 255 #endif #else #include #define NFY_SET(ev, id, filt, fl, ffl, d, ud) EV_SET((ev), (id), (filt), (fl), (ffl), (d), (__typeof__(((struct kevent *)0)->udata))(intptr_t)(ud)) #endif int notify_features(void) { return 0 #if ENABLE_INOTIFY | NOTIFY_INOTIFY #endif #if ENABLE_FEN | NOTIFY_FEN #endif #if HAVE_KQUEUE | NOTIFY_KQUEUE #endif #if HAVE_KQUEUE1 | NOTIFY_KQUEUE1 #endif #if HAVE_OPENAT | NOTIFY_OPENAT #endif #if HAVE_FDOPENDIR | NOTIFY_FDOPENDIR #endif #if HAVE_O_CLOEXEC | NOTIFY_O_CLOEXEC #endif #if HAVE_IN_CLOEXEC | NOTIFY_IN_CLOEXEC #endif ; } /* notify_features() */ const char *notify_strflag(int flag) { static const char *table[32] = { [0] = "CREATE", "ATTRIB", "MODIFY", "REVOKE", "DELETE", [16] = "inotify", "FEN", "kqueue", "kqueue1", "openat", "fdopendir", "O_CLOEXEC", "IN_CLOEXEC", }; return (ffs(0xFFFFFFFF & flag))? table[ffs(0xFFFFFFFF & flag) - 1] : NULL; } /* notify_strflag() */ /* * D I A G N O S T I C S & D E B U G G I N G * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if __GNUC__ || __clang__ #define NOTUSED __attribute__((unused)) #else #define NOTUSED #endif #if __clang__ #pragma clang diagnostic ignored "-Winitializer-overrides" #pragma clang diagnostic ignored "-Wunused-function" #pragma clang diagnostic ignored "-Wunused-label" #elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 #pragma GCC diagnostic ignored "-Woverride-init" #pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-label" #endif #include #define SAY_(file, func, line, fmt, ...) \ fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__) #define SAY(...) SAY_(__FILE__, __func__, __LINE__, __VA_ARGS__, "\n") #define HAI SAY("hai") /* * M A C R O R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define countof(a) (sizeof (a) / sizeof *(a)) #define NFY_LIST_MOVE(head, elm, le) do { \ LIST_REMOVE((elm), le); \ LIST_INSERT_HEAD((head), (elm), le); \ } while (0) /* * F I L E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int cloexec(int fd) { int flags; if (-1 == (flags = fcntl(fd, F_GETFD))) return errno; if (-1 == fcntl(fd, F_SETFD, flags|FD_CLOEXEC)) return errno; return 0; } /* cloexec() */ static int nonblock(int) NOTUSED; static int nonblock(int fd) { int flags; if (-1 == (flags = fcntl(fd, F_GETFL))) return errno; if (-1 == fcntl(fd, F_SETFL, flags|O_NONBLOCK)) return errno; return 0; } /* nonblock() */ static int closefd(int *fd) { while (*fd >= 0 && 0 != close(*fd)) { if (errno != EINTR) return errno; } *fd = -1; return 0; } /* closefd() */ #define nfy_openfd(fd, ...) nfy_openfd((fd), &(struct nfy_open){ .dirfd = -1, __VA_ARGS__ }) struct nfy_open { const char *path; const char *abspath; int dirfd; _Bool chdir; _Bool rdonly; _Bool rdwr; _Bool wronly; _Bool creat; _Bool trunc; _Bool nofollow; _Bool cloexec; _Bool directory; mode_t mode; }; static int (nfy_openfd)(int *_fd, const struct nfy_open *opts) { int fd = -1, wd = -1, flags = 0; int error; if (opts->rdwr) flags |= O_RDWR; else if (opts->wronly) flags |= O_WRONLY; else flags |= O_RDONLY; if (opts->creat) flags |= O_CREAT; if (opts->trunc) flags |= O_TRUNC; if (opts->nofollow) flags |= O_NOFOLLOW; #if HAVE_O_CLOEXEC if (opts->cloexec) flags |= O_CLOEXEC; #endif #if HAVE_O_DIRECTORY if (opts->directory) flags |= O_DIRECTORY; #endif if (opts->dirfd >= 0) { #if HAVE_OPENAT if (-1 == (fd = openat(opts->dirfd, opts->path, flags, opts->mode))) goto syerr; #else if (opts->chdir) { #if HAVE_O_CLOEXEC if (-1 == (wd = open(".", O_RDONLY|O_CLOEXEC))) goto syerr; #else if (-1 == (wd = open(".", O_RDONLY))) goto syerr; #endif if (0 != fchdir(opts->dirfd)) goto syerr; error = (-1 == (fd = open(opts->path, flags, opts->mode)))? errno : 0; if (0 != fchdir(wd)) goto syerr; if (fd == -1) goto error; if ((error = closefd(&wd))) goto error; } else { if (-1 == (fd = open(opts->abspath, flags, opts->mode))) goto syerr; } #endif } else { if (-1 == (fd = open(opts->path, flags, opts->mode))) goto syerr; } #if !defined O_CLOEXEC if (opts->cloexec && (error = cloexec(fd))) goto error; #endif #if !defined O_DIRECTORY if (opts->directory) { struct stat st; if (0 != fstat(fd, &st)) goto syerr; if (!S_ISDIR(st.st_mode)) { error = ENOTDIR; goto error; } } #endif *_fd = fd; return 0; syerr: error = errno; error: closefd(&fd); closefd(&wd); return error; } /* nfy_openfd() */ #if 0 static DIR *nfy_opendir(const char *path, int *error) { DIR *dir; #if HAVE_FDOPENDIR #else #endif return dir; } /* nfy_opendir() */ #endif #if ENABLE_FEN static void fenfo_init(struct file_obj *fo, char *path) { struct stat st; if (0 == stat(path, &st)) { fo->fo_atime = st.st_atim; fo->fo_mtime = st.st_mtim; fo->fo_ctime = st.st_ctim; } fo->fo_name = path; } /* fenfo_init() */ #endif /* * N O T I F I C A T I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct file { int fd; #if ENABLE_FEN struct file_obj fo; #endif int flags, changes, error; enum status { S_DEFUNCT = 0, S_POLLING = 1, S_REVOKED = 2, S_DELETED = 3, } status; LIST_ENTRY(file) le, sle; LLRB_ENTRY(file) rbe; #if ENABLE_FEN char *name; size_t namelen; size_t pathlen; char path[]; #else size_t namelen; char name[]; #endif }; /* struct file */ static inline int filecmp(const struct file *a, const struct file *b) { return strcmp(a->name, b->name); } struct notify { int fd; LLRB_HEAD(files, file) files; LIST_HEAD(, file) dormant; LIST_HEAD(, file) pending; LIST_HEAD(, file) changed; LIST_HEAD(, file) defunct; LIST_HEAD(, file) polling; LIST_HEAD(, file) revoked; LIST_HEAD(, file) deleted; int flags, changes; _Bool dirty; #if ENABLE_INOTIFY _Bool critical; #endif #if ENABLE_FEN struct file_obj dirfo; #endif int dirfd, dirwd; size_t dirlen; char dirpath[]; }; /* struct notify */ LLRB_GENERATE_STATIC(files, file, rbe, filecmp) static struct file *lookup(struct notify *nfy, const char *name, size_t namelen) { #if ENABLE_FEN struct file key = { .name = (char *)name, .namelen = namelen }; return LLRB_FIND(files, &nfy->files, &key); #else struct file *key = &((union { char pad[offsetof(struct file, name) + NAME_MAX + 1]; struct file file; }){ { 0 } }).file; if (namelen > NAME_MAX) return NULL; memcpy(key->name, name, namelen); key->namelen = namelen; return LLRB_FIND(files, &nfy->files, key); #endif } /* lookup() */ static void change(struct notify *nfy, struct file *file, int changes) { if (changes & file->flags) { file->changes |= (file->flags & changes); NFY_LIST_MOVE(&nfy->changed, file, le); } } /* change() */ static void status(struct notify *nfy, struct file *file, enum status status) { switch (status) { case S_DEFUNCT: NFY_LIST_MOVE(&nfy->defunct, file, sle); break; case S_POLLING: NFY_LIST_MOVE(&nfy->polling, file, sle); if (file->status != status) change(nfy, file, (file->status == S_REVOKED)? NOTIFY_ATTRIB : NOTIFY_CREATE); break; case S_REVOKED: NFY_LIST_MOVE(&nfy->revoked, file, sle); if (file->status != status) change(nfy, file, NOTIFY_REVOKE); break; case S_DELETED: NFY_LIST_MOVE(&nfy->deleted, file, sle); if (file->status != status) change(nfy, file, NOTIFY_DELETE); break; } /* switch() */ file->status = status; } /* status() */ static void discard(struct notify *nfy, struct file *file) { closefd(&file->fd); #if ENABLE_FEN port_dissociate(nfy->fd, PORT_SOURCE_FILE, (intptr_t)&file->fo); #endif LLRB_REMOVE(files, &nfy->files, file); LIST_REMOVE(file, le); LIST_REMOVE(file, sle); free(file); } /* discard() */ struct notify *notify_opendir(const char *dirpath, int flags, int *_error) { struct notify *nfy = NULL; size_t dirlen = strlen(dirpath); size_t padlen = NAME_MAX + 2; int error; while (dirlen > 1 && dirpath[dirlen - 1] == '/') --dirlen; if (~padlen < dirlen) { error = ENAMETOOLONG; goto error; } if (!(nfy = calloc(1, offsetof(struct notify, dirpath) + dirlen + padlen))) goto syerr; nfy->fd = -1; nfy->flags = flags; nfy->dirfd = -1; nfy->dirwd = -1; nfy->dirlen = dirlen; memcpy(nfy->dirpath, dirpath, dirlen); #if ENABLE_INOTIFY #if HAVE_INOTIFY_INIT1 && HAVE_IN_NONBLOCK && HAVE_IN_CLOEXEC if (-1 == (nfy->fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC))) goto syerr; #else if (-1 == (nfy->fd = inotify_init())) goto syerr; if ((error = cloexec(nfy->fd))) goto error; if ((error = nonblock(nfy->fd))) goto error; #endif if (-1 == (nfy->dirwd = inotify_add_watch(nfy->fd, nfy->dirpath, IN_ATTRIB|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MODIFY|IN_MOVE|IN_MOVE_SELF|IN_ONLYDIR))) goto syerr; #elif ENABLE_FEN if (-1 == (nfy->fd = port_create())) { if (errno == EAGAIN) errno = EMFILE; goto syerr; } if ((error = cloexec(nfy->fd))) goto error; if ((error = nfy_openfd(&nfy->dirfd, .path = nfy->dirpath, .rdonly = 1, .cloexec = 1, .directory = 1))) goto error; fenfo_init(&nfy->dirfo, nfy->dirpath); if (0 != port_associate(nfy->fd, PORT_SOURCE_FILE, (intptr_t)&nfy->dirfo, FILE_MODIFIED|FILE_ATTRIB|FILE_NOFOLLOW, nfy)) goto syerr; #else #if HAVE_KQUEUE1 && HAVE_O_CLOEXEC if (-1 == (nfy->fd = kqueue1(O_CLOEXEC))) goto syerr; #else if (-1 == (nfy->fd = kqueue())) goto syerr; if ((error = cloexec(nfy->fd))) goto error; #endif if ((error = nfy_openfd(&nfy->dirfd, .path = nfy->dirpath, .rdonly = 1, .cloexec = 1, .directory = 1))) goto error; struct kevent event; NFY_SET(&event, nfy->dirfd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_REVOKE, 0, nfy); if (0 != kevent(nfy->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) goto syerr; #endif return nfy; syerr: error = errno; error: *_error = error; notify_close(nfy); return NULL; } /* notify_opendir() */ void notify_close(struct notify *nfy) { struct file *file, *next; if (!nfy) return; for (file = LLRB_MIN(files, &nfy->files); file != NULL; file = next) { next = LLRB_NEXT(files, &nfy->files, file); discard(nfy, file); } closefd(&nfy->fd); closefd(&nfy->dirfd); #if ENABLE_FEN port_dissociate(nfy->fd, PORT_SOURCE_FILE, (intptr_t)&nfy->dirfo); #endif free(nfy); } /* notify_close() */ int notify_pollfd(struct notify *nfy) { return nfy->fd; } /* notify_pollfd() */ int notify_timeout(struct notify *nfy) { if (nfy->dirty || !LIST_EMPTY(&nfy->pending) || !LIST_EMPTY(&nfy->changed)) return 0; else return -1; } /* notify_timeout() */ static int decode(int flags) { #if ENABLE_INOTIFY static const int table[][2] = { { IN_ATTRIB, NOTIFY_ATTRIB }, { IN_CREATE, NOTIFY_CREATE }, { IN_DELETE, NOTIFY_DELETE }, { IN_DELETE_SELF, NOTIFY_DELETE }, { IN_MODIFY, NOTIFY_MODIFY }, { IN_MOVE_SELF, NOTIFY_DELETE }, { IN_MOVED_FROM, NOTIFY_DELETE }, { IN_MOVED_TO, NOTIFY_CREATE }, }; #elif ENABLE_FEN static const int table[][2] = { { FILE_MODIFIED, NOTIFY_MODIFY }, { FILE_ATTRIB, NOTIFY_ATTRIB }, { FILE_DELETE, NOTIFY_DELETE }, { FILE_RENAME_TO, NOTIFY_DELETE }, { FILE_RENAME_FROM, NOTIFY_DELETE }, }; #else static const int table[][2] = { { NOTE_DELETE, NOTIFY_DELETE }, { NOTE_WRITE, NOTIFY_MODIFY }, { NOTE_EXTEND, NOTIFY_MODIFY }, { NOTE_ATTRIB, NOTIFY_ATTRIB }, { NOTE_LINK, NOTIFY_ATTRIB }, { NOTE_RENAME, NOTIFY_DELETE }, { NOTE_REVOKE, NOTIFY_REVOKE }, }; #endif int events = 0; unsigned i; for (i = 0; i < countof(table); i++) { if (table[i][0] & flags) events |= table[i][1]; } return events; } /* decode() */ #define NOTIFY_MAXSTEP 32 #define ms2ts(ms) (((ms) >= 0)? &(struct timespec){ (ms) / 1000, (((ms) % 1000) * 1000000) } : NULL) #if ENABLE_INOTIFY #define NFY_STEP in_step #define NFY_POST in_post #define IN_BUFSIZ 2048 #define in_msgbuf(bufsiz) (&((union { char pad[bufsiz]; struct inotify_event event; }){ { 0 } }).event) #define in_msgend(msg, len) (struct inotify_event *)((unsigned char *)(msg) + (len)) #define in_msgnxt(msg) (struct inotify_event *)((unsigned char *)(msg) + offsetof(struct inotify_event, name) + (msg)->len) static int in_step1(struct notify *nfy) { struct inotify_event *buf, *msg, *end; ssize_t len; int count = 0; buf = in_msgbuf(IN_BUFSIZ); while ((len = read(nfy->fd, buf, IN_BUFSIZ)) > 0) { for (msg = buf, end = in_msgend(buf, len); msg < end; msg = in_msgnxt(msg)) { size_t namelen = strlen(msg->name); if (namelen) { struct file *file; if ((file = lookup(nfy, msg->name, namelen))) { file->changes |= decode(msg->mask); NFY_LIST_MOVE(&nfy->pending, file, le); } } else { nfy->changes |= decode(msg->mask); nfy->dirty = 1; if (msg->mask & (IN_Q_OVERFLOW|IN_IGNORED|IN_UNMOUNT)) nfy->critical = 1; } if (msg->mask & (IN_CREATE|IN_DELETE|IN_MOVE)) { nfy->changes |= decode(msg->mask & (IN_CREATE|IN_DELETE|IN_MOVE)); nfy->dirty = 1; } ++count; } if (count >= NOTIFY_MAXSTEP) return 0; } if (count > 0) return 0; else if (len == 0) return EPIPE; else return errno; } /* in_step1() */ static int in_step(struct notify *nfy, int timeout) { int error; if (!(error = in_step1(nfy))) return 0; else if (error != EAGAIN) goto error; else if (timeout == 0) return 0; if (-1 == poll(&(struct pollfd){ nfy->fd, POLLIN, 0 }, 1, timeout)) goto syerr; if ((error = in_step1(nfy))) goto error; return 0; syerr: error = errno; error: switch (error) { case EINTR: /* FALL THROUGH */ case EAGAIN: return 0; default: return error; } } /* in_step() */ static int in_post(struct notify *nfy) { struct file *file, *next; for (file = LIST_FIRST(&nfy->pending); file; file = next) { next = LIST_NEXT(file, le); file->changes &= file->flags; if (file->changes) NFY_LIST_MOVE(&nfy->changed, file, le); else NFY_LIST_MOVE(&nfy->dormant, file, le); } nfy->dirty = 0; nfy->changes &= nfy->flags; return 0; } /* in_post() */ #elif ENABLE_FEN #define NFY_STEP fen_step #define NFY_POST fen_post static int fen_step(struct notify *nfy, int timeout) { port_event_t event[NOTIFY_MAXSTEP]; uint_t count = 1, i; int error; if (0 != port_getn(nfy->fd, event, countof(event), &count, ms2ts(timeout))) goto syerr; for (i = 0; i < count; i++) { if (event[i].portev_source != PORT_SOURCE_FILE) continue; if (event[i].portev_user == nfy) { nfy->changes |= decode(event[i].portev_events); nfy->dirty = 1; } else { struct file *file = event[i].portev_user; file->changes |= decode(event[i].portev_events); NFY_LIST_MOVE(&nfy->pending, file, le); } } return 0; syerr: error = errno; switch (error) { case ETIME: /* FALL THROUGH */ case EINTR: return 0; default: return error; } } /* fen_step() */ static int fen_readd(struct notify *nfy, struct file *file) { int events, error; fenfo_init(&file->fo, file->path); events = FILE_ATTRIB|FILE_NOFOLLOW; if (file->flags & NOTIFY_MODIFY) events |= FILE_MODIFIED; error = port_associate(nfy->fd, PORT_SOURCE_FILE, (intptr_t)&file->fo, events, file)? errno : 0; switch (error) { case 0: status(nfy, file, S_POLLING); return 0; case ENOENT: status(nfy, file, S_DELETED); return 0; case EACCES: status(nfy, file, S_REVOKED); return 0; default: status(nfy, file, S_DEFUNCT); return file->error = error; } } /* fen_readd() */ static int fen_post(struct notify *nfy) { struct file *file, *next; int error; for (file = LIST_FIRST(&nfy->pending); file; file = next) { next = LIST_NEXT(file, le); if ((error = fen_readd(nfy, file))) goto error; file->changes &= file->flags; if (file->changes) NFY_LIST_MOVE(&nfy->changed, file, le); else NFY_LIST_MOVE(&nfy->dormant, file, le); } if (nfy->dirty) { for (file = LIST_FIRST(&nfy->revoked); file; file = next) { next = LIST_NEXT(file, sle); if ((error = fen_readd(nfy, file))) return error; } for (file = LIST_FIRST(&nfy->deleted); file; file = next) { next = LIST_NEXT(file, sle); if ((error = fen_readd(nfy, file))) return error; } fenfo_init(&nfy->dirfo, nfy->dirpath); if (0 != port_associate(nfy->fd, PORT_SOURCE_FILE, (intptr_t)&nfy->dirfo, FILE_MODIFIED|FILE_ATTRIB|FILE_NOFOLLOW, nfy)) goto syerr; nfy->changes &= nfy->flags; nfy->dirty = 0; } return 0; syerr: error = errno; error: return error; } /* fen_post() */ #else #define NFY_STEP kq_step #define NFY_POST kq_post static int kq_step(struct notify *nfy, int timeout) { struct kevent event[NOTIFY_MAXSTEP]; struct file *file; int i, count; if (-1 == (count = kevent(nfy->fd, NULL, 0, event, countof(event), ms2ts(timeout)))) return errno; for (i = 0; i < count; i++) { if ((void *)event[i].udata == nfy) { nfy->changes |= decode(event[i].fflags); nfy->dirty = 1; } else { file = (void *)event[i].udata; file->changes |= decode(event[i].fflags); NFY_LIST_MOVE(&nfy->pending, file, le); } } return 0; } /* kq_step() */ static int kq_readd(struct notify *nfy, struct file *file) { struct kevent event; int notes, error; closefd(&file->fd); nfy->dirpath[nfy->dirlen] = '/'; memcpy(&nfy->dirpath[nfy->dirlen + 1], file->name, file->namelen); nfy->dirpath[nfy->dirlen + 1 + file->namelen] = '\0'; error = nfy_openfd(&file->fd, .dirfd = nfy->dirfd, .path = file->name, .abspath = nfy->dirpath, .rdonly = 1, .cloexec = 1, .nofollow = 1); nfy->dirpath[nfy->dirlen] = '\0'; switch (error) { case 0: notes = NOTE_DELETE|NOTE_ATTRIB|NOTE_RENAME|NOTE_REVOKE; if (file->flags & NOTIFY_MODIFY) notes |= NOTE_WRITE|NOTE_EXTEND; NFY_SET(&event, file->fd, EVFILT_VNODE, EV_ADD|EV_CLEAR, notes, 0, file); if (0 != kevent(nfy->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) { error = errno; goto error; } status(nfy, file, S_POLLING); return 0; case ENOENT: status(nfy, file, S_DELETED); return 0; case EACCES: status(nfy, file, S_REVOKED); return 0; default: error: status(nfy, file, S_DEFUNCT); return file->error = error; } } /* kq_readd() */ static int kq_post(struct notify *nfy) { struct file *file, *next; struct kevent event; int error; for (file = LIST_FIRST(&nfy->pending); file; file = next) { next = LIST_NEXT(file, le); if (file->changes & (NOTIFY_DELETE|NOTIFY_REVOKE)) { if ((error = kq_readd(nfy, file))) return error; } file->changes &= file->flags; if (file->changes) NFY_LIST_MOVE(&nfy->changed, file, le); else NFY_LIST_MOVE(&nfy->dormant, file, le); } if (nfy->dirty) { for (file = LIST_FIRST(&nfy->revoked); file; file = next) { next = LIST_NEXT(file, sle); if ((error = kq_readd(nfy, file))) return error; } for (file = LIST_FIRST(&nfy->deleted); file; file = next) { next = LIST_NEXT(file, sle); if ((error = kq_readd(nfy, file))) return error; } NFY_SET(&event, nfy->dirfd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_REVOKE, 0, nfy); if (0 != kevent(nfy->fd, &event, 1, NULL, 0, ms2ts(0))) return errno; nfy->changes &= nfy->flags; nfy->dirty = 0; } return 0; } /* kq_post() */ #endif int notify_step(struct notify *nfy, int timeout) { int error; if (nfy->dirty || !LIST_EMPTY(&nfy->pending)) goto post; if (nfy->changes || !LIST_EMPTY(&nfy->changed)) return 0; if ((error = NFY_STEP(nfy, timeout))) return error; post: if ((error = NFY_POST(nfy))) return error; return 0; } /* notify_step() */ int notify_add(struct notify *nfy, const char *name, int flags) { size_t namelen = strlen(name); struct file *file; int error; if (namelen > NAME_MAX) return ENAMETOOLONG; if (memchr(name, '/', namelen)) return EISDIR; if ((file = lookup(nfy, name, namelen))) return 0; #if ENABLE_FEN size_t pathlen = nfy->dirlen + 1 + namelen; if (!(file = calloc(1, offsetof(struct file, path) + pathlen + 1))) return errno; memcpy(file->path, nfy->dirpath, nfy->dirlen); file->path[nfy->dirlen] = '/'; file->pathlen = pathlen; file->name = &file->path[nfy->dirlen + 1]; fenfo_init(&file->fo, file->path); #else if (!(file = calloc(1, offsetof(struct file, name) + namelen + 1))) return errno; #endif file->fd = -1; file->flags = flags; memcpy(file->name, name, namelen); file->namelen = namelen; LIST_INSERT_HEAD(&nfy->dormant, file, le); LIST_INSERT_HEAD(&nfy->defunct, file, sle); LLRB_INSERT(files, &nfy->files, file); #if ENABLE_KQUEUE if ((error = kq_readd(nfy, file))) goto error; NFY_LIST_MOVE(&nfy->dormant, file, le); nfy->changes = 0; #elif ENABLE_FEN if ((error = fen_readd(nfy, file))) goto error; NFY_LIST_MOVE(&nfy->dormant, file, le); nfy->changes = 0; #endif return 0; error: discard(nfy, file); return error; } /* notify_add() */ static int notify_del(struct notify *dir, const char *name) { size_t namelen = strlen(name); struct file *file; if (namelen > NAME_MAX) return ENAMETOOLONG; if (!(file = lookup(dir, name, namelen))) return 0; discard(dir, file); return 0; } /* notify_del() */ int notify_get(struct notify *nfy, const char **name) { struct file *file; int changes; if ((file = LIST_FIRST(&nfy->changed))) { NFY_LIST_MOVE(&nfy->dormant, file, le); if (name) *name = file->name; changes = file->changes; file->changes = 0; return changes; } if (!nfy->dirty && nfy->changes) { if (name) *name = "."; changes = nfy->changes; nfy->changes = 0; return changes; } return 0; } /* notify_get() */ #if NOTIFY_MAIN #include #include #define USAGE \ "notify [-fh] [DIR [FILE ...]]\n" \ " -f print kernel notification features\n" \ " -h print usage message\n" \ "\n" \ "Report bugs to \n" static void printfeat(void) { int features = notify_features(); int flag; while (features) { flag = 1 << (ffs(features) - 1); printf("%s\n", notify_strflag(flag)); features &= ~flag; } } /* printfeat() */ int main(int argc, char **argv) { extern int optind; const char *path; struct notify *notify; const char *file; int optc, i, error; while (-1 != (optc = getopt(argc, argv, "fh"))) { switch (optc) { case 'f': printfeat(); return 0; case 'h': fputs(USAGE, stdout); return 0; default: fputs(USAGE, stderr); return EXIT_FAILURE; } } /* while() */ argc -= optind; argv += optind; path = (argc > 0)? argv[0] : "/tmp"; if (!(notify = notify_opendir(path, NOTIFY_ALL, &error))) errx(1, "%s: %s", path, strerror(error)); if (argc > 1) { for (i = 1; i < argc; i++) { if ((error = notify_add(notify, argv[i], NOTIFY_ALL))) errx(1, "%s: %s", argv[i], strerror(error)); } } else if ((error = notify_add(notify, "test.file", NOTIFY_ALL))) errx(1, "test.file: %s", strerror(error)); while (!(error = notify_step(notify, -1))) { while (notify_get(notify, &file)) puts(file); } return 0; } /* main() */ #endif cqueues-rel-20161214/src/lib/notify.h000066400000000000000000000050271302435770500172360ustar00rootroot00000000000000/* ========================================================================== * notify.h - Kernel File Notification. * -------------------------------------------------------------------------- * Copyright (c) 2012 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef NOTIFY_H #define NOTIFY_H #define NOTIFY_VERSION 0x20120926 #define NOTIFY_CREATE 0x01 #define NOTIFY_ATTRIB 0x02 #define NOTIFY_MODIFY 0x04 #define NOTIFY_REVOKE 0x08 #define NOTIFY_DELETE 0x10 #define NOTIFY_ALL (NOTIFY_CREATE|NOTIFY_DELETE|NOTIFY_ATTRIB|NOTIFY_MODIFY|NOTIFY_REVOKE) #define NOTIFY_GLOB 0x20 #define NOTIFY_GREP 0x40 #define nfy_error_t int #define nfy_timeout_t int #define nfy_flags_t int struct notify *notify_opendir(const char *, nfy_flags_t, nfy_error_t *); void notify_close(struct notify *); int notify_pollfd(struct notify *); nfy_timeout_t notify_timeout(struct notify *); nfy_error_t notify_step(struct notify *, nfy_timeout_t); nfy_error_t notify_add(struct notify *, const char *, nfy_flags_t); nfy_flags_t notify_get(struct notify *, const char **); #define NOTIFY_INOTIFY 0x010000 #define NOTIFY_FEN 0x020000 #define NOTIFY_KQUEUE 0x040000 #define NOTIFY_KQUEUE1 0x080000 #define NOTIFY_OPENAT 0x100000 #define NOTIFY_FDOPENDIR 0x200000 #define NOTIFY_O_CLOEXEC 0x400000 #define NOTIFY_IN_CLOEXEC 0x800000 nfy_flags_t notify_features(void); const char *notify_strflag(nfy_flags_t); #endif /* NOTIFY_H */ cqueues-rel-20161214/src/lib/socket.c000066400000000000000000002172111302435770500172110ustar00rootroot00000000000000/* ========================================================================== * socket.c - Simple Sockets * -------------------------------------------------------------------------- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #if HAVE_CONFIG_H #include "config.h" #endif #include /* offsetof size_t */ #include /* INT_MAX LONG_MAX */ #include /* malloc(3) free(3) */ #include /* strdup(3) strlen(3) memset(3) strncpy(3) memcpy(3) strerror(3) */ #include /* EINVAL EAFNOSUPPORT EAGAIN EWOULDBLOCK EINPROGRESS EALREADY ENAMETOOLONG EOPNOTSUPP ENOTSOCK ENOPROTOOPT */ #include /* SIGPIPE SIG_BLOCK SIG_SETMASK sigset_t sigprocmask(2) pthread_sigmask(3) sigtimedwait(2) sigpending(2) sigemptyset(3) sigismember(3) sigaddset(3) */ #include /* assert(3) */ #include /* time(2) */ #include /* socklen_t mode_t in_port_t */ #include /* fchmod(2) fstat(2) S_IFSOCK S_ISSOCK */ #include /* FD_ZERO FD_SET fd_set select(2) */ #include /* AF_UNIX AF_INET AF_INET6 SO_TYPE SO_NOSIGPIPE MSG_EOR MSG_NOSIGNAL struct sockaddr_storage socket(2) connect(2) bind(2) listen(2) accept(2) getsockname(2) getpeername(2) */ #if defined(AF_UNIX) #include /* struct sockaddr_un struct unpcbid */ #endif #include /* IPPROTO_IPV6 IPPROTO_TCP IPV6_V6ONLY struct sockaddr_in struct sockaddr_in6 */ #include /* TCP_NODELAY TCP_NOPUSH TCP_CORK */ #include /* inet_ntop(3) inet_pton(3) ntohs(3) htons(3) */ #include /* struct addrinfo */ #include /* _POSIX_REALTIME_SIGNALS _POSIX_THREADS close(2) unlink(2) getpeereid(2) */ #include /* F_SETFD F_GETFD F_GETFL F_SETFL FD_CLOEXEC O_NONBLOCK O_NOSIGPIPE F_SETNOSIGPIPE F_GETNOSIGPIPE */ #include /* POLLIN POLLOUT */ #if defined __sun #include /* ucred_t getpeerucred(2) ucred_free(3) */ #endif #include #include #include #include #include "dns.h" #include "socket.h" /* * V E R S I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ const char *socket_vendor(void) { return SOCKET_VENDOR; } /* socket_vendor() */ int socket_v_rel(void) { return SOCKET_V_REL; } /* socket_v_rel() */ int socket_v_abi(void) { return SOCKET_V_ABI; } /* socket_v_abi() */ int socket_v_api(void) { return SOCKET_V_API; } /* socket_v_api() */ /* * F E A T U R E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if !defined SO_THREAD_SAFE #if (defined _REENTRANT || defined _THREAD_SAFE) && _POSIX_THREADS > 0 #define SO_THREAD_SAFE 1 #else #define SO_THREAD_SAFE 0 #endif #endif #ifndef HAVE_OPENSSL11_API #define HAVE_OPENSSL11_API (!(OPENSSL_VERSION_NUMBER < 0x10100001L || defined LIBRESSL_VERSION_NUMBER)) #endif #ifndef HAVE_BIO_CTRL_SET_CONNECTED_2ARY #define HAVE_BIO_CTRL_SET_CONNECTED_2ARY HAVE_OPENSSL11_API #endif #ifndef HAVE_BIO_SET_INIT #define HAVE_BIO_SET_INIT HAVE_OPENSSL11_API #endif #ifndef HAVE_BIO_SET_SHUTDOWN #define HAVE_BIO_SET_SHUTDOWN HAVE_OPENSSL11_API #endif #ifndef HAVE_BIO_SET_DATA #define HAVE_BIO_SET_DATA HAVE_OPENSSL11_API #endif #ifndef HAVE_BIO_GET_DATA #define HAVE_BIO_GET_DATA HAVE_OPENSSL11_API #endif #ifndef HAVE_BIO_UP_REF #define HAVE_BIO_UP_REF HAVE_OPENSSL11_API #endif #ifndef HAVE_SSL_IS_SERVER #define HAVE_SSL_IS_SERVER HAVE_OPENSSL11_API #endif #ifndef HAVE_SSL_UP_REF #define HAVE_SSL_UP_REF HAVE_OPENSSL11_API #endif /* * C O M P A T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if !HAVE_BIO_CTRL_SET_CONNECTED_2ARY #undef BIO_ctrl_set_connected #define BIO_ctrl_set_connected(b, peer) (int)BIO_ctrl(b, BIO_CTRL_DGRAM_SET_CONNECTED, 0, (char *)peer) #endif #if !HAVE_BIO_SET_INIT #define BIO_set_init(bio, val) ((void)((bio)->init = (val))) #endif #if !HAVE_BIO_SET_SHUTDOWN #define BIO_set_shutdown(bio, val) ((void)((bio)->shutdown = (val))) #endif #if !HAVE_BIO_SET_DATA #define BIO_set_data(bio, val) ((void)((bio)->ptr = (val))) #endif #if !HAVE_BIO_GET_DATA #define BIO_get_data(bio) ((bio)->ptr) #endif #if !HAVE_BIO_UP_REF #define BIO_up_ref(bio) CRYPTO_add(&(bio)->references, 1, CRYPTO_LOCK_BIO) #endif #if !HAVE_SSL_IS_SERVER #undef SSL_is_server #define SSL_is_server(ssl) compat_SSL_is_server(ssl) static _Bool compat_SSL_is_server(SSL *ssl) { const SSL_METHOD *method = SSL_get_ssl_method(ssl); /* * NOTE: SSLv23_server_method()->ssl_connect should be a reference to * OpenSSL's internal ssl_undefined_function(). * * Server methods such as TLSv1_2_server_method(), etc. should have * their .ssl_connect method set to this value. * * WARNING: SSL_is_server in OpenSSL 1.1 defaults to server mode * when both connect and accept methods are present (e.g. as * returned by SSLv23_method()), whereas we always defaulted to * client mode. We keep our old logic to avoid breaking any existing * code that relies on our behavior. Such code will break when * moving to OpenSSL 1.1, but it would be even more surprising if * their code broke when the only change was a minor version of * something using this library. */ if (!method->ssl_connect || method->ssl_connect == SSLv23_server_method()->ssl_connect) return 1; return 0; } /* compat_SSL_is_server() */ #endif #if !HAVE_SSL_UP_REF #define SSL_up_ref(...) compat_SSL_up_ref(__VA_ARGS__) static int compat_SSL_up_ref(SSL *ssl) { /* our caller should already have had a proper reference */ if (CRYPTO_add(&ssl->references, 1, CRYPTO_LOCK_SSL) < 2) return 0; /* fail */ return 1; } /* compat_SSL_up_ref() */ #endif /* * D E B U G R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ int socket_debug; enum so_trace { SO_T_CONNECT, SO_T_STARTTLS, SO_T_READ, SO_T_WRITE, }; /* enum so_trace */ static void so_trace(enum so_trace, int, const struct addrinfo *, ...); #if !defined(SOCKET_DEBUG) #define SOCKET_DEBUG 0 #endif #if SOCKET_DEBUG #include #include #include #undef SOCKET_DEBUG #define SOCKET_DEBUG socket_debug #if !defined(SAY) #define SAY_(fmt, ...) fprintf(stderr, fmt "%s", __FILE__, __LINE__, __func__, __VA_ARGS__); #define SAY(...) SAY_("@@ %s:%d:%s: " __VA_ARGS__, "\n"); #endif #if !defined(HAI) #define HAI SAY_("@@ %s:%d:%s", "\n"); #endif static void so_dump(const unsigned char *src, size_t len, FILE *fp) { static const unsigned char hex[] = "0123456789abcdef"; static const unsigned char tmp[] = " | |\n"; unsigned char ln[sizeof tmp]; const unsigned char *p, *pe; unsigned char *h, *g; unsigned i, n; p = src; pe = p + len; while (p < pe) { memcpy(ln, tmp, sizeof ln); h = &ln[2]; g = &ln[61]; n = p - src; h[0] = hex[0x0f & (n >> 20)]; h[1] = hex[0x0f & (n >> 16)]; h[2] = hex[0x0f & (n >> 12)]; h[3] = hex[0x0f & (n >> 8)]; h[4] = hex[0x0f & (n >> 4)]; h[5] = hex[0x0f & (n >> 0)]; h += 8; for (n = 0; n < 2; n++) { for (i = 0; i < 8 && pe - p > 0; i++, p++) { h[0] = hex[0x0f & (*p >> 4)]; h[1] = hex[0x0f & (*p >> 0)]; h += 3; *g++ = (isgraph(*p))? *p : '.'; } h++; } fputs((char *)ln, fp); } } /* so_dump() */ static void so_trace(enum so_trace event, int fd, const struct addrinfo *host, ...) { struct sockaddr_storage saddr = {0}; socklen_t saddr_len = sizeof saddr; char addr[64], who[256]; in_port_t port; va_list ap; SSL *ctx; const void *data; size_t count; const char *fmt; int error; if (!socket_debug) return; if (host) { sa_ntop(addr, sizeof addr, host->ai_addr, NULL, &error); port = *sa_port(host->ai_addr, SA_PORT_NONE, NULL); if (host->ai_canonname) snprintf(who, sizeof who, "%.96s/[%s]:%hu", host->ai_canonname, addr, ntohs(port)); else snprintf(who, sizeof who, "[%s]:%hu", addr, ntohs(port)); } else if (fd != -1 && 0 == getpeername(fd, (struct sockaddr *)&saddr, &saddr_len)) { sa_ntop(addr, saddr_len, &saddr, NULL, &error); port = *sa_port(&saddr, SA_PORT_NONE, NULL); snprintf(who, sizeof who, "[%s]:%hu", addr, ntohs(port)); } else dns_strlcpy(who, "[unknown]", sizeof who); va_start(ap, host); flockfile(stderr); switch (event) { case SO_T_CONNECT: fmt = va_arg(ap, char *); fprintf(stderr, "connect(%s): ", who); vfprintf(stderr, fmt, ap); fputc('\n', stderr); break; case SO_T_STARTTLS: ctx = va_arg(ap, SSL *); fmt = va_arg(ap, char *); (void)ctx; /* unused for now */ fprintf(stderr, "starttls(%s): ", who); vfprintf(stderr, fmt, ap); fputc('\n', stderr); break; case SO_T_READ: data = va_arg(ap, void *); count = va_arg(ap, size_t); fmt = va_arg(ap, char *); fprintf(stderr, "read(%s): ", who); vfprintf(stderr, fmt, ap); fputc('\n', stderr); so_dump(data, count, stderr); break; case SO_T_WRITE: data = va_arg(ap, void *); count = va_arg(ap, size_t); fmt = va_arg(ap, char *); fprintf(stderr, "write(%s): ", who); vfprintf(stderr, fmt, ap); fputc('\n', stderr); so_dump(data, count, stderr); break; } /* switch(event) */ funlockfile(stderr); va_end(ap); } /* so_trace() */ static void so_initdebug(void) { const char *debug; if ((debug = getenv("SOCKET_DEBUG")) || (debug = getenv("SO_DEBUG"))) { switch (*debug) { case 'Y': case 'y': case 'T': case 't': case '1': socket_debug = 1; break; case 'N': case 'n': case 'F': case 'f': case '0': socket_debug = 0; break; } /* switch() */ } } /* so_initdebug() */ #else #define so_trace(...) (void)0 #define so_initdebug() (void)0 #endif /* SOCKET_DEBUG */ /* * M A C R O R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef countof #define countof(a) (sizeof (a) / sizeof *(a)) #endif #ifndef endof #define endof(a) (&(a)[countof(a)]) #endif /* * E R R O R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if _WIN32 #define SO_EINTR WSAEINTR #define SO_EINPROGRESS WSAEINPROGRESS #define SO_EISCONN WSAEISCONN #define SO_EWOULDBLOCK WSAEWOULDBLOCK #define SO_EALREADY WSAEALREADY #define SO_EAGAIN WSAEWOULDBLOCK #define SO_ENOTCONN WSAENOTCONN #define SO_ECONNABORTED WSAECONNABORTED #define so_syerr() ((int)GetLastError()) #define so_soerr() ((int)WSAGetLastError()) #else #define SO_EINTR EINTR #define SO_EINPROGRESS EINPROGRESS #define SO_EISCONN EISCONN #define SO_EWOULDBLOCK EWOULDBLOCK #define SO_EALREADY EALREADY #define SO_EAGAIN EAGAIN #define SO_ENOTCONN ENOTCONN #define SO_ECONNABORTED ECONNABORTED #define so_syerr() errno #define so_soerr() errno #endif const char *so_strerror(int error) { static const char *errlist[] = { [SO_EOPENSSL - SO_ERRNO0] = "TLS/SSL error", [SO_EX509INT - SO_ERRNO0] = "X.509 certificate lookup interrupt", [SO_ENOTVRFD - SO_ERRNO0] = "Absent or unverified peer certificate", [SO_ECLOSURE - SO_ERRNO0] = "Peers elected to shutdown secure transport", [SO_ENOHOST - SO_ERRNO0] = "No host address available to complete operation", }; if (error >= 0) return strerror(error); if (error == SO_EOPENSSL) { #if SO_THREAD_SAFE && (!defined __NetBSD__ || __NetBSD_Version__ > 600000000) static __thread char sslstr[256]; #else static char sslstr[256]; #endif unsigned long code = ERR_peek_last_error(); if (!code) return "Unknown TLS/SSL error"; ERR_error_string_n(code, sslstr, sizeof sslstr); return sslstr; } else { int index = error - SO_ERRNO0; if (index >= 0 && index < (int)countof(errlist) && errlist[index]) return errlist[index]; else return "Unknown socket error"; } } /* so_strerror() */ /* * Translate SSL_get_error(3) errors into something sensible. */ static int ssl_error(SSL *ctx, int rval, short *events) { unsigned long code; switch (SSL_get_error(ctx, rval)) { case SSL_ERROR_ZERO_RETURN: return SO_ECLOSURE; case SSL_ERROR_WANT_READ: *events |= POLLIN; return SO_EAGAIN; case SSL_ERROR_WANT_WRITE: *events |= POLLOUT; return SO_EAGAIN; case SSL_ERROR_WANT_CONNECT: *events |= POLLOUT; return SO_EAGAIN; case SSL_ERROR_WANT_ACCEPT: *events |= POLLIN; return SO_EAGAIN; case SSL_ERROR_WANT_X509_LOOKUP: return SO_EX509INT; case SSL_ERROR_SYSCALL: if ((code = ERR_peek_last_error())) return SO_EOPENSSL; else if (rval == 0) return ECONNRESET; else if (rval == -1 && so_soerr() && so_soerr() != SO_EAGAIN) return so_soerr(); else return SO_EOPENSSL; case SSL_ERROR_SSL: /* FALL THROUGH */ default: return SO_EOPENSSL; } /* switch(SSL_get_error()) */ } /* ssl_error() */ /* * A D D R E S S R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ char *sa_ntop(char *dst, size_t lim, const void *src, const char *def, int *_error) { union sockaddr_any *any = (void *)src; const char *unspec = "0.0.0.0"; char text[SA_ADDRSTRLEN]; int error; switch (*sa_family(&any->sa)) { case AF_INET: unspec = "0.0.0.0"; if (!inet_ntop(AF_INET, &any->sin.sin_addr, text, sizeof text)) goto syerr; break; case AF_INET6: unspec = "::"; if (!inet_ntop(AF_INET6, &any->sin6.sin6_addr, text, sizeof text)) goto syerr; break; #if SA_UNIX case AF_UNIX: unspec = "/nonexistent"; memset(text, 0, sizeof text); memcpy(text, any->sun.sun_path, SO_MIN(sizeof text - 1, sizeof any->sun.sun_path)); break; #endif default: error = EAFNOSUPPORT; goto error; } /* switch() */ if (dns_strlcpy(dst, text, lim) >= lim) { error = ENOSPC; goto error; } return dst; syerr: error = so_syerr(); error: if (_error) *_error = error; /* * NOTE: Always write something in case caller ignores errors, such * as when caller is using the sa_ntoa() macro. */ dns_strlcpy(dst, (def)? def : unspec, lim); return (char *)def; } /* sa_ntop() */ void *sa_pton(void *dst, size_t lim, const char *src, const void *def, int *_error) { union sockaddr_any family[] = { { { .sa_family = AF_INET } }, { { .sa_family = AF_INET6 } } }, *fp; int error; memset(dst, 0, lim); for (fp = family; fp < endof(family); fp++) { switch (inet_pton(*sa_family(fp), src, sa_addr(fp, NULL, NULL))) { case -1: goto syerr; case 1: if (lim < sa_len(fp)) { error = ENOSPC; goto error; } memcpy(dst, fp, sa_len(fp)); return dst; } } error = EAFNOSUPPORT; goto error; syerr: error = so_syerr(); error: if (_error) *_error = error; return (void *)def; } /* sa_pton() */ /* * U T I L I T I Y R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void *sa_egress(void *lcl, size_t lim, sockaddr_arg_t rmt, int *_error) { static struct { sa_family_t pf; int fd;} udp4 = { PF_INET, -1 }, udp6 = { PF_INET6, -1 }, *udp; struct sockaddr_storage ss; int error; switch (*sa_family(rmt)) { case AF_INET: udp = &udp4; break; case AF_INET6: udp = &udp6; break; default: error = EINVAL; goto error; } if (udp->fd == -1) { #if defined SOCK_CLOEXEC if (-1 == (udp->fd = socket(udp->pf, SOCK_DGRAM|SOCK_CLOEXEC, 0))) goto syerr; #else if (-1 == (udp->fd = socket(udp->pf, SOCK_DGRAM, 0))) goto syerr; #endif if ((error = so_cloexec(udp->fd, 1))) { so_closesocket(&udp->fd, NULL); goto error; } } assert(sizeof ss >= sa_len(rmt)); memcpy(&ss, sockaddr_ref(rmt).sa, sa_len(rmt)); if (!*sa_port(&ss, SA_PORT_NONE, NULL)) *sa_port(&ss, SA_PORT_NONE, NULL) = htons(6970); if (0 != connect(udp->fd, (struct sockaddr *)&ss, sa_len(&ss))) goto syerr; memset(&ss, 0, sizeof ss); if (0 != getsockname(udp->fd, (struct sockaddr *)&ss, &(socklen_t){ sizeof ss })) goto syerr; if (lim < sa_len(&ss)) { error = ENOSPC; goto error; } memcpy(lcl, &ss, sa_len(&ss)); return lcl; syerr: error = so_syerr(); error: if (_error) *_error = error; return memset(lcl, 0, lim); } /* sa_egress() */ static so_error_t so_ffamily(int fd, int *family) { struct sockaddr_storage ss; if (0 != getsockname(fd, (struct sockaddr *)&ss, &(socklen_t){ sizeof ss })) return errno; *family = ss.ss_family; return 0; } /* so_ffamily() */ static so_error_t so_ftype(int fd, mode_t *mode, int *domain, int *type, int *protocol) { struct stat st; int error; if (0 != fstat(fd, &st)) return errno; *mode = S_IFMT & st.st_mode; if (!S_ISSOCK(*mode)) return 0; #if defined SO_DOMAIN if (0 != getsockopt(fd, SOL_SOCKET, SO_DOMAIN, domain, &(socklen_t){ sizeof *domain })) { if (errno != ENOPROTOOPT) return errno; if ((error = so_ffamily(fd, domain))) return error; } #else if ((error = so_ffamily(fd, domain))) return error; #endif if (0 != getsockopt(fd, SOL_SOCKET, SO_TYPE, type, &(socklen_t){ sizeof *type })) return errno; #if defined SO_PROTOCOL if (0 != getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, protocol, &(socklen_t){ sizeof *protocol })) { if (errno != ENOPROTOOPT) return errno; } #else (void)protocol; #endif return 0; } /* so_ftype() */ static int so_opts2flags(const struct so_options *, int *); static int so_type2mask(mode_t, int, int, int); int so_socket(int domain, int type, const struct so_options *opts, int *_error) { int error, fd, flags, mask, need; #if defined SOCK_CLOEXEC if (-1 == (fd = socket(domain, type|SOCK_CLOEXEC, 0))) goto syerr; #else if (-1 == (fd = socket(domain, type, 0))) goto syerr; #endif flags = so_opts2flags(opts, &mask); mask &= so_type2mask(S_IFSOCK, domain, type, 0); need = ~(SO_F_NODELAY|SO_F_NOPUSH|SO_F_NOSIGPIPE|SO_F_OOBINLINE); if ((error = so_setfl(fd, flags, mask, need))) goto error; return fd; syerr: error = so_syerr(); goto error; error: *_error = error; so_closesocket(&fd, opts); return -1; } /* so_socket() */ #define so_bind(...) SO_EXTENSION so_bind(__VA_ARGS__) int (so_bind)(int fd, sockaddr_arg_t arg, const struct so_options *opts) { #if SA_UNIX if (*sa_family(arg) == AF_UNIX) { char *path = strncpy((char [sizeof sockaddr_ref(arg).sun->sun_path + 1]){ 0 }, sockaddr_ref(arg).sun->sun_path, sizeof sockaddr_ref(arg).sun->sun_path); _Bool nochmod = 0; int error; if (opts->sun_unlink && *path) (void)unlink(path); if (opts->sun_mode) { if (0 == fchmod(fd, (opts->sun_mode & 0777))) nochmod = 1; else if (errno != EINVAL) /* BSDs return EINVAL */ return errno; } if (opts->sun_mask) { mode_t omask = umask(opts->sun_mask & 0777); error = (0 == bind(fd, sockaddr_ref(arg).sa, sa_len(arg)))? 0 : errno; umask(omask); } else { error = (0 == bind(fd, sockaddr_ref(arg).sa, sa_len(arg)))? 0 : errno; } if (error) return error; if (opts->sun_mode && !nochmod && *path) { if (0 != chmod(path, (opts->sun_mode & 0777))) return errno; } return 0; } #endif if (0 != bind(fd, sockaddr_ref(arg).sa, sa_len(arg))) return so_soerr(); return 0; } /* so_bind() */ void so_closesocket(int *fd, const struct so_options *opts) { if (opts && opts->fd_close.cb) opts->fd_close.cb(fd, opts->fd_close.arg); if (*fd != -1) { #if _WIN32 closesocket(*fd); #else close(*fd); #endif *fd = -1; } } /* so_closesocket() */ int so_cloexec(int fd, _Bool cloexec) { #if _WIN32 return 0; #else if (-1 == fcntl(fd, F_SETFD, cloexec)) return so_syerr(); return 0; #endif } /* so_cloexec() */ int so_nonblock(int fd, _Bool nonblock) { int flags, mask = (nonblock)? ~0 : (~O_NONBLOCK); if (-1 == (flags = fcntl(fd, F_GETFL)) || -1 == fcntl(fd, F_SETFL, mask & (flags | O_NONBLOCK))) return so_syerr(); return 0; } /* so_nonblock() */ static _Bool so_getboolopt(int fd, int lvl, int opt) { int val; if (0 != getsockopt(fd, lvl, opt, &val, &(socklen_t){ sizeof val })) return 0; return !!val; } /* so_getboolopt() */ static int so_setboolopt(int fd, int lvl, int opt, _Bool enable) { if (0 != setsockopt(fd, lvl, opt, &(int){ enable }, sizeof (int))) { switch (errno) { case ENOTSOCK: /* FALL THROUGH */ case ENOPROTOOPT: return EOPNOTSUPP; default: return errno; } } return 0; } /* so_setboolopt() */ int so_reuseaddr(int fd, _Bool reuseaddr) { return so_setboolopt(fd, SOL_SOCKET, SO_REUSEADDR, reuseaddr); } /* so_reuseaddr() */ int so_reuseport(int fd, _Bool reuseport) { int error; #if defined SO_REUSEPORT error = so_setboolopt(fd, SOL_SOCKET, SO_REUSEPORT, reuseport); #else (void)fd; error = EOPNOTSUPP; #endif if (error == EOPNOTSUPP && !reuseport) error = 0; /* already disabled */ return error; } /* so_reuseport() */ int so_broadcast(int fd, _Bool broadcast) { return so_setboolopt(fd, SOL_SOCKET, SO_BROADCAST, broadcast); } /* so_broadcast() */ int so_nodelay(int fd, _Bool nodelay) { return so_setboolopt(fd, IPPROTO_TCP, TCP_NODELAY, nodelay); } /* so_nodelay() */ #ifndef TCP_NOPUSH #ifdef TCP_CORK #define TCP_NOPUSH TCP_CORK #endif #endif int so_nopush(int fd, _Bool nopush) { #ifdef TCP_NOPUSH return so_setboolopt(fd, IPPROTO_TCP, TCP_NOPUSH, nopush); #else return EOPNOTSUPP; #endif } /* so_nopush() */ int so_nosigpipe(int fd, _Bool nosigpipe) { #if defined O_NOSIGPIPE int flags, mask = (nosigpipe)? ~0 : (~O_NOSIGPIPE); if (-1 == (flags = fcntl(fd, F_GETFL)) || -1 == fcntl(fd, F_SETFL, mask & (flags | O_NOSIGPIPE))) return errno; return 0; #elif defined F_SETNOSIGPIPE if (0 != fcntl(fd, F_SETNOSIGPIPE, nosigpipe)) return errno; return 0; #elif defined SO_NOSIGPIPE return so_setboolopt(fd, SOL_SOCKET, SO_NOSIGPIPE, nosigpipe); #else (void)fd; (void)nosigpipe; return EOPNOTSUPP; #endif } /* so_nosigpipe() */ int so_v6only(int fd, _Bool v6only) { /* * NOTE: OS X will return EINVAL if socket already connected. * Haven't checked other systems. Should we should suppress this * error? */ return so_setboolopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, v6only); } /* so_v6only() */ int so_oobinline(int fd, _Bool oobinline) { return so_setboolopt(fd, SOL_SOCKET, SO_OOBINLINE, oobinline); } /* so_oobinline() */ #define NO_OFFSET ((size_t)-1) #define optoffset(m) offsetof(struct so_options, m) static const struct flops { int flag; int (*set)(int, _Bool); size_t offset; } fltable[] = { { SO_F_CLOEXEC, &so_cloexec, optoffset(fd_cloexec), }, { SO_F_NONBLOCK, &so_nonblock, optoffset(fd_nonblock), }, { SO_F_REUSEADDR, &so_reuseaddr, optoffset(sin_reuseaddr), }, { SO_F_REUSEPORT, &so_reuseport, optoffset(sin_reuseport), }, { SO_F_BROADCAST, &so_broadcast, optoffset(sin_broadcast), }, { SO_F_NODELAY, &so_nodelay, optoffset(sin_nodelay), }, { SO_F_NOPUSH, &so_nopush, optoffset(sin_nopush), }, { SO_F_NOSIGPIPE, &so_nosigpipe, optoffset(fd_nosigpipe), }, { SO_F_V6ONLY, &so_v6only, NO_OFFSET, }, { SO_F_OOBINLINE, &so_oobinline, optoffset(sin_oobinline), }, }; static int so_opts2flags(const struct so_options *opts, int *mask) { const struct flops *f; int flags = 0; *mask = 0; for (f = fltable; f < endof(fltable); f++) { if (f->offset == NO_OFFSET) continue; flags |= (*(_Bool *)((char *)opts + f->offset))? f->flag : 0; *mask |= f->flag; } switch (opts->sin_v6only) { case SO_V6ONLY_DEFAULT: break; case SO_V6ONLY_ENABLE: flags |= SO_F_V6ONLY; *mask |= SO_F_V6ONLY; break; case SO_V6ONLY_DISABLE: *mask |= SO_F_V6ONLY; break; } return flags; } /* so_opts2flags() */ static int so_type2mask(mode_t mode, int family, int type, int protocol) { int mask = SO_F_CLOEXEC|SO_F_NONBLOCK|SO_F_NOSIGPIPE; if (S_ISSOCK(mode)) { mask |= SO_F_REUSEADDR|SO_F_REUSEPORT|SO_F_OOBINLINE; if (!protocol) { if (family == AF_INET || family == AF_INET6) { protocol = (type == SOCK_STREAM)? IPPROTO_TCP : IPPROTO_UDP; } } if (family == AF_INET6) { mask |= SO_F_V6ONLY; } if (type == SOCK_DGRAM) { mask |= SO_F_BROADCAST; } if (protocol == IPPROTO_TCP) { mask |= SO_F_NODELAY|SO_F_NOPUSH; } } return mask; } /* so_type2mask() */ int so_getfl(int fd, int which) { int flags = 0, getfl = 0, getfd; if ((which & SO_F_CLOEXEC) && -1 != (getfd = fcntl(fd, F_GETFD))) { if (getfd & FD_CLOEXEC) flags |= SO_F_CLOEXEC; } if ((which & SO_F_NONBLOCK) && -1 != (getfl = fcntl(fd, F_GETFL))) { if (getfl & O_NONBLOCK) flags |= SO_F_NONBLOCK; } if ((which & SO_F_REUSEADDR) && so_getboolopt(fd, SOL_SOCKET, SO_REUSEADDR)) flags |= SO_F_REUSEADDR; #if defined SO_REUSEPORT if ((which & SO_F_REUSEPORT) && so_getboolopt(fd, SOL_SOCKET, SO_REUSEPORT)) flags |= SO_F_REUSEPORT; #endif if ((which & SO_F_BROADCAST) && so_getboolopt(fd, SOL_SOCKET, SO_BROADCAST)) flags |= SO_F_BROADCAST; if ((which & SO_F_NODELAY) && so_getboolopt(fd, IPPROTO_TCP, TCP_NODELAY)) flags |= SO_F_NODELAY; #if defined TCP_NOPUSH if ((which & SO_F_NOPUSH) && so_getboolopt(fd, IPPROTO_TCP, TCP_NOPUSH)) flags |= SO_F_NOPUSH; #endif #if defined O_NOSIGPIPE || defined F_GETNOSIGPIPE || defined SO_NOSIGPIPE if ((which & SO_F_NOSIGPIPE)) { #if defined O_NOSIGPIPE if (getfl) { if (getfl != -1 && (getfl & O_NOSIGPIPE)) flags |= SO_F_NOSIGPIPE; } else if (-1 != (getfl = fcntl(fd, F_GETFL))) { if (getfl & O_NOSIGPIPE) flags |= SO_F_NOSIGPIPE; } #elif defined F_GETNOSIGPIPE int nosigpipe; if (-1 != (nosigpipe = fcntl(fd, F_GETNOSIGPIPE))) { if (nosigpipe) flags |= SO_F_NOSIGPIPE; } #else if (so_getboolopt(fd, SOL_SOCKET, SO_NOSIGPIPE)) flags |= SO_F_NOSIGPIPE; #endif } #endif if ((which & SO_F_V6ONLY) && so_getboolopt(fd, IPPROTO_IPV6, IPV6_V6ONLY)) flags |= SO_F_V6ONLY; if ((which & SO_F_OOBINLINE) && so_getboolopt(fd, SOL_SOCKET, SO_OOBINLINE)) flags |= SO_F_OOBINLINE; return flags; } /* so_getfl() */ int so_rstfl(int fd, int *oflags, int flags, int mask, int require) { const struct flops *f; int error; for (f = fltable; f < endof(fltable); f++) { if (!(f->flag & mask)) continue; if ((error = f->set(fd, !!(f->flag & flags)))) { if ((f->flag & require) || error != EOPNOTSUPP) return error; *oflags &= ~f->flag; } else { *oflags &= ~f->flag; *oflags |= (f->flag & flags); } } return 0; } /* so_rstfl() */ int so_setfl(int fd, int flags, int mask, int require) { return so_rstfl(fd, &(int){ 0 }, flags, mask, require); } /* so_setfl() */ int (so_addfl)(int fd, int flags, int require) { return so_rstfl(fd, &(int){ 0 }, flags, flags, require); } /* so_addfl() */ int (so_delfl)(int fd, int flags, int require) { return so_rstfl(fd, &(int){ 0 }, ~flags, flags, require); } /* so_delfl() */ static void x509_discard(X509 **cert) { if (*cert) X509_free(*cert); *cert = 0; } /* x509_discard() */ static void ssl_discard(SSL **ctx) { if (*ctx) SSL_free(*ctx); *ctx = 0; } /* ssl_discard() */ static int thr_sigmask(int how, const sigset_t *set, sigset_t *oset) { #if SO_THREAD_SAFE return pthread_sigmask(how, set, oset); #else return (0 == sigprocmask(how, set, oset))? 0 : errno; #endif } /* thr_sigmask() */ static int math_addull(unsigned long long *x, unsigned long long a, unsigned long long b) { if (~a < b) { *x = ~0ULL; return EOVERFLOW; } else { *x = a + b; return 0; } } /* math_addull() */ static void st_update(struct st_log *log, size_t len, const struct so_options *opts) { math_addull(&log->count, log->count, len); if (opts->st_time) time(&log->time); } /* st_update() */ /* * S O C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * NOTE: We give SO_S_SHUTWR higher precedence because on some systems * shutdown(SHUT_RD) will fail if EOF has already been sent by the peer. A * mitigation was already committed to address this issue (see so_shutrd_), * but it never made it downstream. This makes merging easier and is * otherwise sensible on its own terms. */ enum so_state { SO_S_INIT = 1<<0, SO_S_GETADDR = 1<<1, SO_S_SOCKET = 1<<2, SO_S_BIND = 1<<3, SO_S_LISTEN = 1<<4, SO_S_CONNECT = 1<<5, SO_S_STARTTLS = 1<<6, SO_S_SETREAD = 1<<7, SO_S_SETWRITE = 1<<8, SO_S_RSTLOWAT = 1<<9, SO_S_SHUTWR = 1<<10, /* see NOTE above */ SO_S_SHUTRD = 1<<11, SO_S_END, SO_S_ALL = ((SO_S_END - 1) << 1) - 1 }; /* enum so_state */ struct socket { struct so_options opts; struct dns_addrinfo *res; int fd; mode_t mode; /* file mode */ int domain; /* socket domain (address family) */ int type; /* socket type */ int protocol; /* socket protocol */ int flags; /* descriptor flags */ struct so_stat st; struct { _Bool rd; _Bool wr; } shut; struct addrinfo *host; short events; int done, todo; int lerror; int olowat; struct { SSL *ctx; int error; int state; _Bool accept; _Bool vrfd; } ssl; struct { BIO *ctx; // BIO *ctrl; /* proxy unknown DTLS BIO_ctrl commands */ int error; struct { void *data; unsigned char *p, *pe; } ahead; } bio; struct { int ncalls; sigset_t pending; sigset_t blocked; } pipeign; struct { pid_t pid; uid_t uid; gid_t gid; } cred; }; /* struct socket */ static _Bool so_needign(struct socket *so, _Bool rdonly) { if (!so->opts.fd_nosigpipe || (so->flags & SO_F_NOSIGPIPE)) return 0; if (so->ssl.ctx && !so->bio.ctx) return 1; if (rdonly) return 0; #if defined MSG_NOSIGNAL if (S_ISSOCK(so->mode)) return 0; #endif return 1; } /* so_needign() */ static int so_pipeign(struct socket *so, _Bool rdonly) { if (!so_needign(so, rdonly)) return 0; #if _POSIX_REALTIME_SIGNALS > 0 if (so->pipeign.ncalls++ > 0) return 0; sigemptyset(&so->pipeign.pending); sigpending(&so->pipeign.pending); if (sigismember(&so->pipeign.pending, SIGPIPE)) return 0; sigset_t piped; sigemptyset(&piped); sigaddset(&piped, SIGPIPE); sigemptyset(&so->pipeign.blocked); return thr_sigmask(SIG_BLOCK, &piped, &so->pipeign.blocked); #else return EOPNOTSUPP; #endif } /* so_pipeign() */ static int so_pipeok(struct socket *so, _Bool rdonly) { if (!so_needign(so, rdonly)) return 0; #if _POSIX_REALTIME_SIGNALS > 0 assert(so->pipeign.ncalls > 0); if (--so->pipeign.ncalls) return 0; if (sigismember(&so->pipeign.pending, SIGPIPE)) return 0; sigset_t piped; sigemptyset(&piped); sigaddset(&piped, SIGPIPE); while (-1 == sigtimedwait(&piped, NULL, &(struct timespec){ 0, 0 }) && errno == EINTR) ;; return thr_sigmask(SIG_SETMASK, &so->pipeign.blocked, NULL); #else return EOPNOTSUPP; #endif } /* so_pipeok() */ static int so_getaddr_(struct socket *so) { int error; if (!so->res) return SO_ENOHOST; so->events = 0; free(so->host); so->host = 0; if ((error = dns_ai_nextent(&so->host, so->res))) goto error; return 0; error: switch (error) { #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK: /* FALL THROUGH */ #endif case SO_EAGAIN: so->events = dns_ai_events(so->res); break; } /* switch() */ return error; } /* so_getaddr_() */ static int so_socket_(struct socket *so) { int error; if (!so->host) return SO_ENOHOST; so_closesocket(&so->fd, &so->opts); if (-1 == (so->fd = so_socket(so->host->ai_family, so->host->ai_socktype, &so->opts, &error))) return error; if ((error = so_ftype(so->fd, &so->mode, &so->domain, &so->type, &so->protocol))) return error; so->flags = so_getfl(so->fd, ~0); return 0; } /* so_socket_() */ static int so_bind_(struct socket *so) { struct sockaddr *saddr; if (so->todo & SO_S_LISTEN) { if (!so->host) return SO_ENOHOST; saddr = so->host->ai_addr; } else if (so->opts.sa_bind) { saddr = (struct sockaddr *)so->opts.sa_bind; } else { return 0; } return so_bind(so->fd, saddr, &so->opts); } /* so_bind_() */ static int so_listen_(struct socket *so) { if (!S_ISSOCK(so->mode) || (so->type != SOCK_STREAM && so->type != SOCK_SEQPACKET)) return 0; return (0 == listen(so->fd, SOMAXCONN))? 0 : so_soerr(); } /* so_listen_() */ static int so_connect_(struct socket *so) { int error; so->events &= ~POLLOUT; retry: if (!so->host) { error = SO_ENOHOST; goto error; } if (0 != connect(so->fd, so->host->ai_addr, so->host->ai_addrlen)) { error = so_soerr(); goto error; } ready: so_trace(SO_T_CONNECT, so->fd, so->host, "ready"); return 0; error: switch (error) { case SO_EISCONN: goto ready; case SO_EINTR: goto retry; case SO_EINPROGRESS: /* FALL THROUGH */ case SO_EALREADY: /* FALL THROUGH */ #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK /* FALL THROUGH */ #endif so->events |= POLLOUT; return SO_EAGAIN; default: so_trace(SO_T_CONNECT, so->fd, so->host, "%s", so_strerror(error)); return error; } /* switch() */ } /* so_connect_() */ static BIO *so_newbio(struct socket *, int *); static int so_starttls_(struct socket *so) { X509 *peer; int rval, error; if (so->ssl.error) return so->ssl.error; so_pipeign(so, 0); ERR_clear_error(); switch (so->ssl.state) { case 0: { /* * NOTE: For SOCK_DGRAM continue using OpenSSL's BIO until * we have time to reverse engineer the semantics necessary * for DTLS. */ if (S_ISSOCK(so->mode) && so->type == SOCK_DGRAM) { struct sockaddr_storage peer; BIO *bio; memset(&peer, 0, sizeof peer); if (0 != getpeername(so->fd, (struct sockaddr *)&peer, &(socklen_t){ sizeof peer })) { error = errno; goto error; } if (!(bio = BIO_new_dgram(so->fd, BIO_NOCLOSE))) { error = SO_EOPENSSL; goto error; } BIO_ctrl_set_connected(bio, &peer); SSL_set_bio(so->ssl.ctx, bio, bio); SSL_set_read_ahead(so->ssl.ctx, 1); } else { BIO *bio; if (!(bio = so_newbio(so, &error))) goto error; SSL_set_bio(so->ssl.ctx, bio, bio); } if (so->ssl.accept) { SSL_set_accept_state(so->ssl.ctx); } else { SSL_set_connect_state(so->ssl.ctx); } so->ssl.state++; } case 1: rval = SSL_do_handshake(so->ssl.ctx); if (rval > 0) { /* SUCCESS (continue to next state) */ ;; } else { /* ERROR (either need I/O or a plain error) or SHUTDOWN */ so->events &= ~(POLLIN|POLLOUT); error = ssl_error(so->ssl.ctx, rval, &so->events); goto error; } /* (rval) */ so->ssl.state++; case 2: /* * NOTE: Must call SSL_get_peer_certificate() first, which * processes the certificate. SSL_get_verify_result() merely * returns the result of this processing. */ peer = SSL_get_peer_certificate(so->ssl.ctx); so->ssl.vrfd = (peer && SSL_get_verify_result(so->ssl.ctx) == X509_V_OK); x509_discard(&peer); so->ssl.state++; case 3: if (so->opts.tls_verify && !so->ssl.vrfd) { error = SO_ENOTVRFD; goto error; } so->ssl.state++; case 4: break; } /* switch(so->ssl.state) */ #if SOCKET_DEBUG if (SOCKET_DEBUG) { const SSL_CIPHER *cipher = SSL_get_current_cipher(so->ssl.ctx); so_trace(SO_T_STARTTLS, so->fd, so->host, so->ssl.ctx, "%s-%s", SSL_get_version(so->ssl.ctx), SSL_CIPHER_get_name(cipher)); } #endif so_pipeok(so, 0); return 0; error: if (error != SO_EAGAIN) so_trace(SO_T_STARTTLS, so->fd, so->host, so->ssl.ctx, "%s", so_strerror(error)); so_pipeok(so, 0); return error; } /* so_starttls_() */ static int so_rstlowat_(struct socket *so) { if (0 != setsockopt(so->fd, SOL_SOCKET, SO_RCVLOWAT, &so->olowat, sizeof so->olowat)) return so_soerr(); return 0; } /* so_rstlowat_() */ static int so_shutwr_(struct socket *so) { if (so->fd != -1 && 0 != shutdown(so->fd, SHUT_WR)) return so_soerr(); so->shut.wr = 1; so->st.sent.eof = 1; return 0; } /* so_shutwr_() */ static _Bool so_isconn(int fd) { struct sockaddr sa; socklen_t slen = sizeof sa; return 0 == getpeername(fd, &sa, &slen) || so_soerr() != SO_ENOTCONN; } /* so_isconn() */ static int so_shutrd_(struct socket *so) { if (so->fd != -1 && 0 != shutdown(so->fd, SHUT_RD)) { /* * NOTE: OS X will fail with ENOTCONN if the requested * SHUT_RD or SHUT_WR flag is already set, including if the * SHUT_RD flag is set from the peer sending eof. Other OSs * just treat this as a noop and return successfully. */ if (so_soerr() != SO_ENOTCONN) return so_soerr(); else if (!so_isconn(so->fd)) return SO_ENOTCONN; } so->shut.rd = 1; return 0; } /* so_shutrd_() */ static inline int so_state(const struct socket *so) { if (so->todo & ~so->done) { int i = 1; while (i < SO_S_END && !(i & (so->todo & ~so->done))) i <<= 1; return (i < SO_S_END)? i : 0; } else return 0; } /* so_state() */ static int so_exec(struct socket *so) { int state, error_, error = 0; exec: switch (state = so_state(so)) { case SO_S_INIT: break; case SO_S_GETADDR: switch ((error_ = so_getaddr_(so))) { case 0: break; case ENOENT: /* NOTE: Return the last error if possible. */ if (error) error_ = error; else if (so->lerror) error_ = so->lerror; /* FALL THROUGH */ default: error = error_; goto error; } so->done |= state; goto exec; case SO_S_SOCKET: if ((error = so_socket_(so))) goto retry; so->done |= state; goto exec; case SO_S_BIND: if ((error = so_bind_(so))) goto retry; so->done |= state; goto exec; case SO_S_LISTEN: if ((error = so_listen_(so))) return error; so->done |= state; goto exec; case SO_S_CONNECT: if ((error = so_connect_(so))) { switch (error) { case SO_EAGAIN: goto error; default: goto retry; } /* switch() */ } so->done |= state; goto exec; case SO_S_STARTTLS: if ((error = so_starttls_(so))) goto error; so->done |= state; goto exec; case SO_S_SETREAD: so->events |= POLLIN; so->done |= state; goto exec; case SO_S_SETWRITE: so->events |= POLLOUT; so->done |= state; goto exec; case SO_S_RSTLOWAT: if ((error = so_rstlowat_(so))) goto error; so->todo &= ~state; goto exec; case SO_S_SHUTWR: if ((error = so_shutwr_(so))) goto error; so->done |= state; goto exec; case SO_S_SHUTRD: if ((error = so_shutrd_(so))) goto error; so->done |= state; goto exec; } /* so_exec() */ return 0; retry: /* * Jump back to the SO_S_GETADDR iterator if enabled. Otherwise, * this is a terminal error. */ if (so->todo & SO_S_GETADDR) { so->done = 0; so->lerror = error; goto exec; } /* FALL THROUGH */ error: return error; } /* so_exec() */ static struct socket *so_make(const struct so_options *opts, int *error) { static const struct socket so_initializer = { .fd = -1, .domain = PF_UNSPEC, .cred = { (pid_t)-1, (uid_t)-1, (gid_t)-1, } }; struct socket *so; size_t len; if (!(so = malloc(sizeof *so))) goto syerr; *so = so_initializer; so->opts = *opts; if (opts->sa_bind) { if (!(len = sa_len((void *)opts->sa_bind))) { *error = EAFNOSUPPORT; goto error; } if (!(so->opts.sa_bind = malloc(len))) goto syerr; memcpy((void *)so->opts.sa_bind, opts->sa_bind, len); } if (opts->tls_sendname && opts->tls_sendname != SO_OPTS_TLS_HOSTNAME) { if (!(so->opts.tls_sendname = strdup(opts->tls_sendname))) goto syerr; } return so; syerr: *error = so_syerr(); error: if (so) { if (so->opts.tls_sendname != opts->tls_sendname) free((void *)so->opts.tls_sendname); if (so->opts.sa_bind != opts->sa_bind) free((void *)so->opts.sa_bind); free(so); } return NULL; } /* so_make() */ static void so_resetssl(struct socket *); static int so_destroy(struct socket *so) { so_resetssl(so); dns_ai_close(so->res); so->res = NULL; free(so->host); so->host = NULL; so_closesocket(&so->fd, &so->opts); so->events = 0; if (so->opts.tls_sendname && so->opts.tls_sendname != SO_OPTS_TLS_HOSTNAME) { free((void *)so->opts.tls_sendname); so->opts.tls_sendname = NULL; } free((void *)so->opts.sa_bind); so->opts.sa_bind = NULL; return 0; } /* so_destroy() */ static _Bool sa_isnumeric(const char *host) { union sockaddr_any ip; return !!sa_pton(&ip, sizeof ip, host, NULL, NULL); } /* sa_isnumeric() */ struct socket *(so_open)(const char *host, const char *port, int qtype, int domain, int type, const struct so_options *opts, int *error_) { _Bool isnumeric = sa_isnumeric(host); struct dns_resolver *res = NULL; struct addrinfo hints; struct socket *so; int error; if (!(so = so_make(opts, &error))) goto error; /* * Copy host name as TLS server host name. * * NOTE: All the TLS RFCs (from RFC 3546 to RFC 6066) * declare * * Literal IPv4 and IPv6 addresses are not permitted in * "HostName". * * If the caller wants to send the IP address as the TLS * server host name, they can set .tls_hostname explicitly. */ if (so->opts.tls_sendname == SO_OPTS_TLS_HOSTNAME && !isnumeric) { if (!(so->opts.tls_sendname = strdup(host))) goto syerr; } memset(&hints, 0, sizeof hints); hints.ai_flags = AI_CANONNAME; hints.ai_family = domain; hints.ai_socktype = type; if (isnumeric) { hints.ai_flags |= AI_NUMERICHOST; } else { struct dns_options *opts = dns_opts(); opts->closefd.arg = so->opts.fd_close.arg; opts->closefd.cb = so->opts.fd_close.cb; if (!(res = dns_res_stub(opts, &error))) goto error; } if (!(so->res = dns_ai_open(host, port, qtype, &hints, res, &error))) goto error; so->todo = SO_S_GETADDR | SO_S_SOCKET | SO_S_BIND; dns_res_close(res); return so; syerr: error = so_syerr(); error: dns_res_close(res); so_close(so); *error_ = error; return 0; } /* so_open() */ struct socket *so_dial(const struct sockaddr *sa, int type, const struct so_options *opts, int *error_) { struct { struct addrinfo ai; struct sockaddr_storage ss; } *host; struct socket *so; int error; if (!(so = so_make(opts, &error))) goto error; if (!(host = malloc(sizeof *host))) goto syerr; memset(&host->ai, 0, sizeof host->ai); memcpy(&host->ss, sa, SO_MIN(af_len(sa->sa_family), sizeof host->ss)); so->host = &host->ai; so->host->ai_family = sa->sa_family; so->host->ai_socktype = type; so->host->ai_protocol = 0; so->host->ai_addrlen = af_len(sa->sa_family); so->host->ai_addr = (struct sockaddr *)&host->ss; so->todo = SO_S_SOCKET | SO_S_BIND; return so; syerr: error = so_syerr(); error: so_close(so); *error_ = error; return 0; } /* so_dial() */ struct socket *so_fdopen(int fd, const struct so_options *opts, int *error_) { struct socket *so; int flags, mask, need, error; if (!(so = so_make(opts, &error))) goto error; if ((error = so_ftype(fd, &so->mode, &so->domain, &so->type, &so->protocol))) goto error; flags = so_opts2flags(opts, &mask); mask &= so_type2mask(so->mode, so->domain, so->type, so->protocol); need = ~(SO_F_NODELAY|SO_F_NOPUSH|SO_F_NOSIGPIPE|SO_F_OOBINLINE); if ((error = so_rstfl(fd, &so->flags, flags, mask, need))) goto error; so->fd = fd; return so; error: so_close(so); *error_ = error; return 0; } /* so_fdopen() */ int so_close(struct socket *so) { if (!so) return EINVAL; so_destroy(so); free(so); return 0; } /* so_close() */ int so_family(struct socket *so, int *error_) { struct sockaddr_storage saddr; socklen_t slen = sizeof saddr; int error; if ((error = so_localaddr(so, (void *)&saddr, &slen))) { *error_ = error; return AF_UNSPEC; } return *sa_family(&saddr); } /* so_family() */ int so_localaddr(struct socket *so, void *saddr, socklen_t *slen) { int error; if ((so_state(so) < SO_S_STARTTLS) && (error = so_exec(so))) return error; if (0 != getsockname(so->fd, saddr, slen)) return errno; return 0; } /* so_localaddr() */ int so_remoteaddr(struct socket *so, void *saddr, socklen_t *slen) { int error; if ((so_state(so) < SO_S_STARTTLS) && (error = so_exec(so))) return error; if (0 != getpeername(so->fd, saddr, slen)) return errno; return 0; } /* so_remoteaddr() */ int so_connect(struct socket *so) { if (so->done & SO_S_CONNECT) return 0; so->todo |= SO_S_CONNECT; return so_exec(so); } /* so_connect() */ int so_listen(struct socket *so) { if (so->done & SO_S_LISTEN) return 0; so->todo |= SO_S_LISTEN; return so_exec(so); } /* so_listen() */ int so_accept(struct socket *so, struct sockaddr *saddr, socklen_t *slen, int *error_) { int fd = -1, error; if ((error = so_listen(so))) goto error; if ((error = so_exec(so))) goto error; so->events = POLLIN; retry: #if HAVE_ACCEPT4 && defined SOCK_CLOEXEC if (-1 == (fd = accept4(so->fd, saddr, slen, SOCK_CLOEXEC))) goto soerr; #elif HAVE_PACCEPT && defined SOCK_CLOEXEC if (-1 == (fd = paccept(so->fd, saddr, slen, NULL, SOCK_CLOEXEC))) goto soerr; #else if (-1 == (fd = accept(so->fd, saddr, slen))) goto soerr; if ((error = so_cloexec(fd, 1))) goto error; #endif return fd; soerr: switch ((error = so_soerr())) { case SO_EINTR: goto retry; #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK: error = SO_EAGAIN; break; #endif case SO_ECONNABORTED: error = SO_EAGAIN; break; } error: *error_ = error; so_closesocket(&fd, NULL); return -1; } /* so_accept() */ static void so_resetssl(struct socket *so) { ssl_discard(&so->ssl.ctx); so->ssl.state = 0; so->ssl.error = 0; so->ssl.accept = 0; so->ssl.vrfd = 0; if (so->bio.ctx) { BIO_free(so->bio.ctx); so->bio.ctx = NULL; } free(so->bio.ahead.data); so->bio.ahead.data = NULL; so->bio.ahead.p = NULL; so->bio.ahead.pe = NULL; } /* so_resetssl() */ int so_starttls(struct socket *so, const struct so_starttls *cfg) { SSL_CTX *ctx, *tmp = NULL; SSL *ssl = NULL; const SSL_METHOD *method; int error; if (so->done & SO_S_STARTTLS) return 0; if (so->todo & SO_S_STARTTLS) goto check; cfg = (cfg)? cfg : &(struct so_starttls){ 0 }; so_resetssl(so); /* * NOTE: Commit to the SO_S_STARTTLS state at this point, no matter * whether we can allocate the proper objects, so any errors will * persist. so_starttls_() immediately returns if so->ssl.error is * set. See NOTE at error label below. */ so->todo |= SO_S_STARTTLS; if (cfg->pushback.iov_len > 0) { if (!(so->bio.ahead.data = malloc(cfg->pushback.iov_len))) { error = errno; goto error; } memcpy(so->bio.ahead.data, cfg->pushback.iov_base, cfg->pushback.iov_len); so->bio.ahead.p = so->bio.ahead.data; so->bio.ahead.pe = so->bio.ahead.p + cfg->pushback.iov_len; } ERR_clear_error(); if ((ssl = cfg->instance)) { SSL_up_ref(ssl); } else { if (!(ctx = cfg->context)) { if (!(method = cfg->method)) { if (so_isbool(cfg->accept)) { method = SSLv23_method(); } else { method = SSLv23_client_method(); } } if (!(ctx = tmp = SSL_CTX_new((SSL_METHOD *)method))) goto eossl; } if (!(ssl = SSL_new(ctx))) goto eossl; } if (so_isbool(cfg->accept)) { so->ssl.accept = so_tobool(cfg->accept); } else { /* NB: see WARNING at compat_SSL_is_server() */ so->ssl.accept = SSL_is_server(ssl); } if (!so->ssl.accept && so->opts.tls_sendname && so->opts.tls_sendname != SO_OPTS_TLS_HOSTNAME) { if (!SSL_set_tlsext_host_name(ssl, so->opts.tls_sendname)) goto eossl; } SSL_set_mode(ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); so->ssl.ctx = ssl; ssl = NULL; if (tmp) SSL_CTX_free(tmp); check: return so_exec(so); eossl: error = SO_EOPENSSL; error: /* * NOTE: Store any error in so->ssl.error because callers expect to * call-and-forget this routine, similar to so_connect() and * so_listen(), but we still need to replay the error. */ so->ssl.error = error; if (ssl) SSL_free(ssl); if (tmp) SSL_CTX_free(tmp); return so->ssl.error; } /* so_starttls() */ SSL *so_checktls(struct socket *so) { return so->ssl.ctx; } /* so_checktls() */ int so_shutdown(struct socket *so, int how) { switch (how) { case SHUT_RD: so->todo |= SO_S_SHUTRD; break; case SHUT_WR: so->todo |= SO_S_SHUTWR; break; case SHUT_RDWR: so->todo |= SO_S_SHUTRD|SO_S_SHUTWR; break; } /* switch (how) */ return so_exec(so); } /* so_shutdown() */ static size_t so_sysread(struct socket *so, void *dst, size_t lim, int *error) { long len; retry: #if _WIN32 len = recv(so->fd, dst, SO_MIN(lim, LONG_MAX), 0); #else len = read(so->fd, dst, SO_MIN(lim, LONG_MAX)); #endif if (len == -1) goto error; if (len == 0) goto epipe; return len; epipe: *error = EPIPE; so->st.rcvd.eof = 1; return 0; error: *error = so_soerr(); switch (*error) { case SO_EINTR: goto retry; #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK: *error = SO_EAGAIN; /* FALL THROUGH */ #endif case SO_EAGAIN: so->events |= POLLIN; break; } /* switch() */ return 0; } /* so_sysread() */ static size_t so_syswrite(struct socket *so, const void *src, size_t len, int *error) { long count; int flags = 0; if (so->st.sent.eof) { *error = EPIPE; return 0; } // so_pipeign(so, 0); #if _WIN32 #else if (S_ISSOCK(so->mode)) { #if defined(MSG_NOSIGNAL) if (so->opts.fd_nosigpipe) flags |= MSG_NOSIGNAL; #endif if (so->type == SOCK_SEQPACKET) flags |= MSG_EOR; } #endif retry: #if _WIN32 if (1) { #else if (S_ISSOCK(so->mode)) { #endif count = send(so->fd, src, SO_MIN(len, LONG_MAX), flags); } else { count = write(so->fd, src, SO_MIN(len, LONG_MAX)); } if (count == -1) goto error; // so_pipeok(so, 0); return count; error: *error = so_soerr(); switch (*error) { case EPIPE: so->st.sent.eof = 1; break; case SO_EINTR: goto retry; #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK: *error = SO_EAGAIN; /* FALL THROUGH */ #endif case SO_EAGAIN: so->events |= POLLOUT; break; } /* switch() */ // so_pipeok(so, 0); return 0; } /* so_syswrite() */ static _Bool bio_nonfatal(int error) { switch (error) { case SO_EAGAIN: case SO_EALREADY: case SO_EINPROGRESS: case SO_EINTR: case SO_ENOTCONN: /* FIXME (bss_sock.c has this but is it correct?) */ return 1; default: return 0; } } /* bio_nonfatal() */ static int bio_read(BIO *bio, char *dst, int lim) { struct socket *so = BIO_get_data(bio); size_t count; assert(so); assert(lim >= 0); BIO_clear_retry_flags(bio); so->bio.error = 0; if (so->bio.ahead.p < so->bio.ahead.pe) { count = SO_MIN(so->bio.ahead.pe - so->bio.ahead.p, lim); memcpy(dst, so->bio.ahead.p, count); so->bio.ahead.p += count; return count; } if ((count = so_sysread(so, dst, lim, &so->bio.error))) return (int)count; if (bio_nonfatal(so->bio.error)) BIO_set_retry_read(bio); /* see note about SSL_ERROR_SYSCALL at bio_write */ errno = so->bio.error; return (so->bio.error == EPIPE)? 0 : -1; } /* bio_read() */ static int bio_write(BIO *bio, const char *src, int len) { struct socket *so = BIO_get_data(bio); size_t count; assert(so); assert(len >= 0); BIO_clear_retry_flags(bio); so->bio.error = 0; if ((count = so_syswrite(so, src, len, &so->bio.error))) return (int)count; if (bio_nonfatal(so->bio.error)) BIO_set_retry_write(bio); /* * SSL_get_error will return SSL_ERROR_SYSCALL, expecting errno to * be set. */ errno = so->bio.error; return -1; } /* bio_write() */ static int bio_puts(BIO *bio, const char *src) { size_t len = strlen(src); return bio_write(bio, src, (int)SO_MIN(len, INT_MAX)); } /* bio_puts() */ static long bio_ctrl(BIO *bio, int cmd, long udata_i, void *udata_p) { (void)bio; (void)udata_i; switch (cmd) { case BIO_CTRL_DUP: { /* * We'll permit duping, just like all the other BIOs do by * default and so we don't inadvertently break something. * But we won't copy our state because our memory management * assumes 1:1 mapping between BIO and socket objects. And * just to be safe, zero the state members. BIO_dup_chain, * for example, has a hack to always copy .init and .num. */ BIO *udata = udata_p; BIO_set_init(udata, 0); BIO_set_data(udata, NULL); return 1; } case BIO_CTRL_FLUSH: return 1; default: /* * BIO_ctrl manual page says * * Source/sink BIOs return an 0 if they do not * recognize the BIO_ctrl() operation. */ return 0; } /* switch() */ } /* bio_ctrl() */ static int bio_create(BIO *bio) { BIO_set_init(bio, 0); BIO_set_shutdown(bio, 0); BIO_set_data(bio, NULL); return 1; } /* bio_create() */ static int bio_destroy(BIO *bio) { BIO_set_init(bio, 0); BIO_set_shutdown(bio, 0); BIO_set_data(bio, NULL); return 1; } /* bio_destroy() */ #if !HAVE_OPENSSL11_API static BIO_METHOD bio_methods = { BIO_TYPE_SOURCE_SINK, "struct socket*", bio_write, bio_read, bio_puts, NULL, bio_ctrl, bio_create, bio_destroy, NULL, }; static BIO_METHOD *so_get_bio_methods() { return &bio_methods; } /* so_get_bio_methods() */ #else static BIO_METHOD *bio_methods = NULL; static CRYPTO_ONCE bio_methods_init_once = CRYPTO_ONCE_STATIC_INIT; static void bio_methods_init(void) { int type = BIO_get_new_index(); if (type == -1) return; bio_methods = BIO_meth_new(type|BIO_TYPE_SOURCE_SINK, "struct socket*"); if (bio_methods == NULL) return; BIO_meth_set_write(bio_methods, bio_write); BIO_meth_set_read(bio_methods, bio_read); BIO_meth_set_puts(bio_methods, bio_puts); BIO_meth_set_ctrl(bio_methods, bio_ctrl); BIO_meth_set_create(bio_methods, bio_create); BIO_meth_set_destroy(bio_methods, bio_destroy); } /* bio_methods_init() */ static BIO_METHOD *so_get_bio_methods() { if (bio_methods == NULL) { CRYPTO_THREAD_run_once(&bio_methods_init_once, bio_methods_init); } return bio_methods; } /* so_get_bio_methods() */ #endif static BIO *so_newbio(struct socket *so, int *error) { BIO *bio; BIO_METHOD *bio_methods = so_get_bio_methods(); if (bio_methods == NULL || !(bio = BIO_new(bio_methods))) { *error = SO_EOPENSSL; return NULL; } BIO_set_init(bio, 1); BIO_set_data(bio, so); /* * NOTE: Applications can acquire a reference to our BIO via the SSL * state object. The lifetime of the BIO could last longer than the * lifetime of our socket object, so we must keep our own reference * and zero any pointer to ourselves here and from so_destroy. */ if (so->bio.ctx) { BIO_set_init(so->bio.ctx, 0); BIO_set_data(so->bio.ctx, NULL); BIO_free(so->bio.ctx); } BIO_up_ref(bio); so->bio.ctx = bio; return bio; } /* so_newbio() */ size_t so_read(struct socket *so, void *dst, size_t lim, int *error_) { size_t len; int error; so_pipeign(so, 1); so->todo |= SO_S_SETREAD; if ((error = so_exec(so))) goto error; if (so->fd == -1) { error = ENOTCONN; goto error; } so->events &= ~POLLIN; retry: if (so->ssl.ctx) { int n; ERR_clear_error(); if ((n = SSL_read(so->ssl.ctx, dst, SO_MIN(lim, INT_MAX))) < 0) { if (SO_EINTR == (error = ssl_error(so->ssl.ctx, n, &so->events))) goto retry; goto error; } else if (n == 0) { error = EPIPE; /* FIXME: differentiate clean from unclean shutdown? */ so->st.rcvd.eof = 1; goto error; } len = n; } else { if (!(len = so_sysread(so, dst, lim, &error))) goto error; } so_trace(SO_T_READ, so->fd, so->host, dst, (size_t)len, "rcvd %zu bytes", (size_t)len); st_update(&so->st.rcvd, len, &so->opts); so_pipeok(so, 1); return len; error: *error_ = error; if (error != SO_EAGAIN) so_trace(SO_T_READ, so->fd, so->host, (void *)0, (size_t)0, "%s", so_strerror(error)); so_pipeok(so, 1); return 0; } /* so_read() */ size_t so_write(struct socket *so, const void *src, size_t len, int *error_) { size_t count; int error; so_pipeign(so, 0); so->todo |= SO_S_SETWRITE; if ((error = so_exec(so))) goto error; if (so->fd == -1) { error = ENOTCONN; goto error; } so->events &= ~POLLOUT; retry: if (so->ssl.ctx) { if (len > 0) { int n; ERR_clear_error(); if ((n = SSL_write(so->ssl.ctx, src, SO_MIN(len, INT_MAX))) < 0) { if (SO_EINTR == (error = ssl_error(so->ssl.ctx, n, &so->events))) goto retry; goto error; } else if (n == 0) { error = EPIPE; /* FIXME: differentiate clean from unclean shutdown? */ so->st.sent.eof = 1; goto error; } count = n; } else { count = 0; } } else { if (!(count = so_syswrite(so, src, len, &error))) goto error; } so_trace(SO_T_WRITE, so->fd, so->host, src, (size_t)count, "sent %zu bytes", (size_t)len); st_update(&so->st.sent, count, &so->opts); so_pipeok(so, 0); return count; error: *error_ = error; if (error != SO_EAGAIN) so_trace(SO_T_WRITE, so->fd, so->host, (void *)0, (size_t)0, "%s", so_strerror(error)); so_pipeok(so, 0); return 0; } /* so_write() */ size_t so_peek(struct socket *so, void *dst, size_t lim, int flags, int *_error) { int rstlowat = so->todo & SO_S_RSTLOWAT; long count; int lowat, error; so->todo &= ~SO_S_RSTLOWAT; error = so_exec(so); so->todo |= rstlowat; if (error) goto error; if (flags & SO_F_PEEKALL) so->events &= ~POLLIN; retry: count = recv(so->fd, dst, lim, MSG_PEEK); if (count == -1) goto soerr; if ((size_t)count == lim || !(flags & SO_F_PEEKALL)) return count; pollin: if (!(so->todo & SO_S_RSTLOWAT)) { if (0 != getsockopt(so->fd, SOL_SOCKET, SO_RCVLOWAT, &so->olowat, &(socklen_t){ sizeof so->olowat })) goto soerr; if (lim > INT_MAX) { error = EOVERFLOW; goto error; } lowat = (int)lim; if (0 != setsockopt(so->fd, SOL_SOCKET, SO_RCVLOWAT, &lowat, sizeof lowat)) goto soerr; so->todo |= SO_S_RSTLOWAT; } so->events |= POLLIN; *_error = SO_EAGAIN; return 0; soerr: error = so_soerr(); switch (error) { case SO_EINTR: goto retry; #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK: /* FALL THROUGH */ #endif case SO_EAGAIN: if (flags & SO_F_PEEKALL) goto pollin; break; } /* switch() */ error: *_error = error; return 0; } /* so_peek() */ int so_sendmsg(struct socket *so, const struct msghdr *msg, int flags) { ssize_t count; int error; so_pipeign(so, 0); so->todo |= SO_S_SETWRITE; if ((error = so_exec(so))) goto error; so->events &= ~POLLOUT; #if defined MSG_NOSIGNAL if (so->opts.fd_nosigpipe) flags |= MSG_NOSIGNAL; #endif retry: if (-1 == (count = sendmsg(so->fd, msg, flags))) goto syerr; st_update(&so->st.sent, count, &so->opts); so_pipeok(so, 0); return 0; syerr: error = errno; error: switch (error) { case SO_EINTR: goto retry; #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK: /* FALL THROUGH */ #endif case SO_EAGAIN: so->events |= POLLOUT; break; } /* switch() */ so_pipeok(so, 0); return error; } /* so_sendmsg() */ int so_recvmsg(struct socket *so, struct msghdr *msg, int flags) { ssize_t count; int error; so_pipeign(so, 1); so->todo |= SO_S_SETREAD; if ((error = so_exec(so))) goto error; so->events &= ~POLLIN; retry: if (-1 == (count = recvmsg(so->fd, msg, flags))) { goto syerr; } else if (!count) { so->st.rcvd.eof = 1; error = EPIPE; goto error; } st_update(&so->st.rcvd, count, &so->opts); /* RE .msg_iovlen type * * - Linux : size_t * - OS X : int * - OpenBSD : unsigned int * - Solaris : int * - FreeBSD : int * - NetBSD : int * - RFC 2292 : size_t */ for (size_t i = 0; i < (size_t)msg->msg_iovlen; i++) { if ((size_t)count < msg->msg_iov[i].iov_len) { msg->msg_iov[i].iov_len = count; break; } else { count -= (ssize_t)msg->msg_iov[i].iov_len; } } so_pipeok(so, 1); return 0; syerr: error = errno; error: switch (error) { case SO_EINTR: goto retry; #if SO_EWOULDBLOCK != SO_EAGAIN case SO_EWOULDBLOCK: /* FALL THROUGH */ #endif case SO_EAGAIN: so->events |= POLLIN; break; } /* switch() */ so_pipeok(so, 1); return error; } /* so_recvmsg() */ const struct so_stat *so_stat(struct socket *so) { return &so->st; } /* so_stat() */ void so_clear(struct socket *so) { so->todo &= ~(SO_S_SETREAD|SO_S_SETWRITE); so->events = 0; } /* so_clear() */ int so_events(struct socket *so) { short events; switch (so->opts.fd_events) { case SO_LIBEVENT: events = SO_POLL2EV(so->events); break; default: /* FALL THROUGH */ case SO_SYSPOLL: events = so->events; break; } /* switch (.fd_events) */ return events; } /* so_events() */ int so_pollfd(struct socket *so) { switch (so_state(so)) { case SO_S_GETADDR: return dns_ai_pollfd(so->res); default: return so->fd; } /* switch() */ } /* so_pollfd() */ int so_poll(struct socket *so, int timeout) { int nfds; #if 1 struct pollfd pfd = { .fd = so_pollfd(so), .events = so->events, }; if (!pfd.events) return 0; if (timeout != -1) timeout *= 1000; nfds = poll(&pfd, 1, timeout); #else fd_set rfds, wfds; int set, fd; FD_ZERO(&rfds); FD_ZERO(&wfds); if (!(set = so->events)) return 0; fd = so_pollfd(so); if ((set & POLLIN) && fd < FD_SETSIZE) FD_SET(fd, &rfds); if ((set & POLLOUT) && fd < FD_SETSIZE) FD_SET(fd, &wfds); nfds = select(fd + 1, &rfds, &wfds, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : 0); #endif switch (nfds) { case -1: return errno; case 0: return ETIMEDOUT; default: return 0; } } /* so_poll() */ int so_peerfd(struct socket *so) { return so->fd; } /* so_peerfd() */ int so_uncork(struct socket *so) { return so_nopush(so->fd, 0); } /* so_uncork() */ static int so_loadcred(struct socket *so) { if (so->cred.uid != (uid_t)-1) return 0; #if defined SO_PEERCRED #if defined __OpenBSD__ struct sockpeercred uc; #else struct ucred uc; #endif if (0 != getsockopt(so->fd, SOL_SOCKET, SO_PEERCRED, &uc, &(socklen_t){ sizeof uc })) return errno; so->cred.pid = uc.pid; so->cred.uid = uc.uid; so->cred.gid = uc.gid; return 0; #elif defined LOCAL_PEEREID struct unpcbid unp = { -1, -1, -1 }; if (0 != getsockopt(so->fd, 0, LOCAL_PEEREID, &unp, &(socklen_t){ sizeof unp })) return errno; so->cred.pid = unp.unp_pid; so->cred.uid = unp.unp_euid; so->cred.gid = unp.unp_egid; return 0; #elif defined __sun ucred_t *uc = NULL; if (0 != getpeerucred(so->fd, &uc)) return errno; so->cred.pid = ucred_getpid(uc); so->cred.uid = ucred_geteuid(uc); so->cred.gid = ucred_getegid(uc); ucred_free(uc); return 0; #else if (0 != getpeereid(so->fd, &so->cred.uid, &so->cred.gid)) return errno; return 0; #endif } /* so_loadcred() */ int so_peereid(struct socket *so, uid_t *uid, gid_t *gid) { int error; if ((error = so_loadcred(so))) return error; if (so->cred.uid == (uid_t)-1) return EOPNOTSUPP; if (uid) *uid = so->cred.uid; if (gid) *gid = so->cred.gid; return 0; } /* so_peereid() */ int so_peerpid(struct socket *so, pid_t *pid) { int error; if ((error = so_loadcred(so))) return error; if (so->cred.pid == (pid_t)-1) return EOPNOTSUPP; if (pid) *pid = so->cred.pid; return 0; } /* so_peerpid() */ /* * L I B R A R Y R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void socket_init(void) __attribute__((constructor, used)); void socket_init(void) { SSL_load_error_strings(); SSL_library_init(); so_initdebug(); } /* socket_init() */ #if SOCKET_MAIN #include #include #include #include #include #include #include #include "fifo.h" struct { char arg0[64]; struct { char scheme[32]; char authority[128]; char host[128]; char port[32]; char path[128]; char query[64]; char fragment[32]; } url; } MAIN = { .url = { .scheme = "http", .host = "google.com", .port = "80", .path = "/", }, }; static void panic(const char *fmt, ...) { va_list ap; va_start(ap, fmt); #if _WIN32 vfprintf(stderr, fmt, ap); exit(EXIT_FAILURE); #else verrx(EXIT_FAILURE, fmt, ap); #endif } /* panic() */ #define panic_(fn, ln, fmt, ...) \ panic(fmt "%0s", (fn), (ln), __VA_ARGS__) #define panic(...) \ panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "") void parseurl(const char *url) { static const char *expr = "^(([^:/?#]+):)?(//(([^/@]*@)?([^/?#:]*)(:([[:digit:]]*))?))?([^?#]*)(\\?([^#]*))?(#(.*))?"; regex_t re; regmatch_t match[16]; char errstr[128]; int error; struct { const char *name; char *dst; size_t lim; } part[16] = { [2] = { "scheme: ", MAIN.url.scheme, sizeof MAIN.url.scheme }, [4] = { "authority:", MAIN.url.authority, sizeof MAIN.url.authority }, [6] = { "host: ", MAIN.url.host, sizeof MAIN.url.host }, [8] = { "port: ", MAIN.url.port, sizeof MAIN.url.port }, [9] = { "path: ", MAIN.url.path, sizeof MAIN.url.path }, [11] = { "query: ", MAIN.url.query, sizeof MAIN.url.query }, [13] = { "fragment: ", MAIN.url.fragment, sizeof MAIN.url.fragment }, }; if ((error = regcomp(&re, expr, REG_EXTENDED))) goto error; if ((error = regexec(&re, url, countof(match), match, 0))) goto error; for (size_t i = 0; i < countof(match); i++) { if (match[i].rm_so == -1) continue; if (part[i].dst) { snprintf(part[i].dst, part[i].lim, "%.*s", (int)(match[i].rm_eo - match[i].rm_so), &url[match[i].rm_so]); // SAY("%s %s", part[i].name, part[i].dst); } else { // SAY("[%d]: %.*s", i, (int)(match[i].rm_eo - match[i].rm_so), &url[match[i].rm_so]); ;; } } regfree(&re); return; error: regerror(error, &re, errstr, sizeof errstr); regfree(&re); panic("%s", errstr); } /* parseurl() */ int httpget(const char *url) { const struct so_stat *st; struct socket *so; struct fifo *req; struct iovec iov; long n; int lc, error; parseurl(url); if (!(so = so_open(MAIN.url.host, MAIN.url.port, DNS_T_A, PF_INET, SOCK_STREAM, so_opts(), &error))) errx(EXIT_FAILURE, "so_open: %s", so_strerror(error)); so_connect(so); if (!strcasecmp("https", MAIN.url.scheme)) { SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method()); #if 0 /* example code if waiting for SSL negotiation */ while ((error = so_starttls(so, ctx))) { if (error != SO_EAGAIN || (error = so_poll(so, 3))) errx(EXIT_FAILURE, "so_starttls: %s", so_strerror(error)); } #else so_starttls(so, &(struct so_starttls){ .context = ctx }); #endif } req = fifo_new(1024); fifo_puts(req, "GET "); fifo_puts(req, MAIN.url.path); fifo_puts(req, " HTTP/1.0\r\n"); fifo_puts(req, "Host: "); fifo_puts(req, MAIN.url.host); fifo_putc(req, ':'); fifo_puts(req, MAIN.url.port); fifo_puts(req, "\r\n\r\n"); while (fifo_rvec(req, &iov)) { if (!(n = so_write(so, iov.iov_base, iov.iov_len, &error))) { switch (error) { case SO_EAGAIN: so_poll(so, 1); break; default: errx(EXIT_FAILURE, "so_write: %s", so_strerror(error)); } } else { fifo_discard(req, n); } } // so_shutdown(so, SHUT_WR); /* send EOF (but some servers don't like this) */ lc = 0; do { char res[512]; while (0 == (n = so_read(so, res, sizeof res, &error)) && error != EPIPE) { switch (error) { case SO_EAGAIN: so_poll(so, 1); continue; default: errx(EXIT_FAILURE, "so_read: %s", so_strerror(error)); } } if (n > 0) { fwrite(res, 1, n, stdout); lc = res[n-1]; } } while (n); if (isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) { if (lc != '\n') fputc('\n', stdout); fflush(stdout); fputs("--\n", stderr); } st = so_stat(so); fprintf(stderr, "sent: %llu bytes\n", st->sent.count); fprintf(stderr, "rcvd: %llu bytes\n", st->rcvd.count); so_close(so); return 0; } /* httpget() */ int echo(void) { struct socket *srv0, *srv, *cli; struct fifo out, in; char obuf[512], ibuf[512]; size_t olen, len; struct iovec iov; int fd, error; if (!(srv0 = so_open("127.0.0.1", "54321", DNS_T_A, PF_INET, SOCK_STREAM, so_opts(), &error))) panic("so_open: %s", so_strerror(error)); if (!(cli = so_open("127.0.0.1", "54321", DNS_T_A, PF_UNSPEC, SOCK_STREAM, so_opts(), &error))) panic("so_open: %s", so_strerror(error)); so_listen(srv0); while (-1 == (fd = so_accept(srv0, 0, 0, &error))) { if (error != SO_EAGAIN) panic("so_accept: %s", so_strerror(error)); if ((error = so_connect(cli)) && error != SO_EAGAIN) panic("so_connect: %s", so_strerror(error)); so_poll(cli, 1); } if (!(srv = so_fdopen(fd, so_opts(), &error))) panic("so_fdopen: %s", so_strerror(error)); while ((olen = fread(obuf, 1, sizeof obuf, stdin))) { fifo_from(&out, obuf, olen); fifo_init(&in, ibuf, sizeof ibuf); while (fifo_rlen(&in) < olen) { if (fifo_rvec(&out, &iov)) { so_poll(cli, 1); if (!(len = so_write(cli, iov.iov_base, iov.iov_len, &error)) && error != SO_EAGAIN) panic("so_write: %s", so_strerror(error)); else fifo_discard(&out, len); } so_poll(srv, 1); fifo_wvec(&in, &iov); if (!(len = so_read(srv, iov.iov_base, iov.iov_len, &error)) && error != SO_EAGAIN && error != EPIPE) panic("so_read: %s", so_strerror(error)); else fifo_update(&in, +len); } while (fifo_rvec(&in, &iov)) fifo_discard(&in, fwrite(iov.iov_base, 1, iov.iov_len, stdout)); } so_close(srv0); so_close(srv); so_close(cli); return 0; } /* echo() */ #define USAGE \ "%s [-h] echo | egress ADDR [PORT] | print ADDR [PORT] | get URI\n" \ " -v be verbose--trace input/output\n" \ " -V print version information\n" \ " -h print usage information\n" \ "\n" \ "Report bugs to william@25thandClement.com\n" int main(int argc, char **argv) { extern int optind; int opt; dns_strlcpy(MAIN.arg0, (strrchr(argv[0], '/')? strrchr(argv[0], '/') + 1 : argv[0]), sizeof MAIN.arg0); while (-1 != (opt = getopt(argc, argv, "vVh"))) { switch (opt) { case 'v': #if !defined(so_trace) /* macro expanding to void statement if no debug support */ socket_debug++; #else fprintf(stderr, "%s: not compiled with tracing support\n", MAIN.arg0); #endif break; case 'V': printf("%s (socket.c) %.8X\n", MAIN.arg0, socket_v_rel()); printf("vendor %s\n", socket_vendor()); printf("release %.8X\n", socket_v_rel()); printf("abi %.8X\n", socket_v_abi()); printf("api %.8X\n", socket_v_api()); printf("dns %.8X\n", dns_v_rel()); printf("ssl %s\n", OPENSSL_VERSION_TEXT); return 0; case 'h': /* FALL THROUGH */ usage: default: fprintf(stderr, USAGE, MAIN.arg0); return (opt == 'h')? 0: EXIT_FAILURE; } /* switch() */ } /* while () */ argc -= optind; argv += optind; socket_init(); if (!argc) { goto usage; } else if (!strcmp(*argv, "echo")) { return echo(); } else if (!strcmp(*argv, "egress") && argv[1]) { struct sockaddr *saddr; struct sockaddr_storage egress; int error; if (AF_UNSPEC == *sa_family(saddr = sa_aton(argv[1]))) { struct dns_resolver *res; struct dns_packet *ans; struct dns_rr rr; union dns_any rd; char addr[SA_ADDRSTRLEN]; if (!(res = dns_res_stub(dns_opts(), &error))) panic("dns_res_stub: %s", so_strerror(error)); if (!(ans = dns_res_query(res, argv[1], DNS_T_A, DNS_C_IN, 1, &error))) panic("dns_res_query: %s", so_strerror(error)); dns_res_close(res); if (!dns_rr_grep(&rr, 1, dns_rr_i_new(ans, .section = DNS_S_AN, .type = DNS_T_A), ans, &error)) panic("%s: no A record", argv[1]); if ((error = dns_any_parse(&rd, &rr, ans))) panic("%s: %s", argv[1], so_strerror(error)); dns_any_print(addr, sizeof addr, &rd, rr.type); free(ans); saddr = sa_aton(addr); if (AF_UNSPEC == sa_family(saddr)) goto usage; } if (argc > 2) { *sa_port(saddr, SA_PORT_NONE, NULL) = htons(atoi(argv[2])); printf("[%s]:%hu => %s\n", sa_ntoa(saddr), ntohs(*sa_port(saddr, SA_PORT_NONE, NULL)), sa_ntoa(sa_egress(&egress, sizeof egress, saddr, 0))); } else printf("%s => %s\n", sa_ntoa(saddr), sa_ntoa(sa_egress(&egress, sizeof egress, saddr, 0))); } else if (!strcmp(*argv, "print") && argv[1]) { struct sockaddr *saddr = sa_aton(argv[1]); if (AF_UNSPEC == sa_family(saddr)) goto usage; if (argc > 2) { *sa_port(saddr, SA_PORT_NONE, NULL) = htons(atoi(argv[2])); printf("[%s]:%hu\n", sa_ntoa(saddr), ntohs(*sa_port(saddr, SA_PORT_NONE, NULL))); } else { *sa_port(saddr, SA_PORT_NONE, NULL) = htons(6970); printf("%s\n", sa_ntoa(saddr)); } } else if (!strcmp(*argv, "get") && argv[1]) { return httpget(argv[1]); } else goto usage; return 0; } /* main() */ #endif cqueues-rel-20161214/src/lib/socket.h000066400000000000000000000416361302435770500172240ustar00rootroot00000000000000/* ========================================================================== * socket.h - Simple Sockets * -------------------------------------------------------------------------- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef SOCKET_H #define SOCKET_H #include /* time_t */ #include /* memcpy(3) */ #include /* EAFNOSUPPORT */ #include /* socklen_t in_port_t uid_t gid_t pid_t */ #include /* struct iovec */ #include /* AF_INET AF_INET6 AF_UNIX SOCK_STREAM SHUT_RD SHUT_WR SHUT_RDWR struct sockaddr struct msghdr struct cmsghdr */ #if defined(AF_UNIX) #include #endif #include /* POLLIN POLLOUT */ #include /* struct sockaddr_in struct sockaddr_in6 */ #include /* SSL_CTX SSL */ #include /* ERR_get_error() */ /* * V E R S I O N I N T E R F A C E S * * Vendor: Entity for which versions numbers are relevant. (If forking * change SOCKET_VENDOR to avoid confusion.) * * Three versions: * * REL Official "release"--bug fixes, new features, etc. * ABI Changes to existing object sizes or parameter types. * API Changes that might effect application source. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define SOCKET_VENDOR "william@25thandClement.com" #define SOCKET_V_REL 0x20161213 #define SOCKET_V_ABI 0x20161213 #define SOCKET_V_API 0x20161213 const char *socket_vendor(void); int socket_v_rel(void); int socket_v_abi(void); int socket_v_api(void); /* * T Y P E I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if _WIN32 typedef short sa_family_t; typedef unsigned short in_port_t; #endif /* * D E B U G I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ extern int socket_debug; /* * E R R O R I N T E R F A C E S * * System errors--always positive--are returned as-is. Internal errors are * returned as negative integers with a mask in the top 23 bits equivalent * to "sck". * * OpenSSL errors must be handled out-of-band because they're not simple * integer codes. When SO_EOPENSSL is encountered, the application must * query OpenSSL's per-thread error queue if it wants more information. * so_strerror(SO_EOPENSSL) will attempt to return something reasonable by * peeking into the OpenSSL error queue. * * TLS/SSL operations will clear the OpenSSL error queue before proceeding. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define SO_EBASE (-(('s' << 24) | ('c' << 16) | ('k' << 8) | '9')) enum so_errno { SO_EOPENSSL = SO_EBASE, SO_EX509INT, /* See SSL_ERROR_WANT_X509_LOOKUP in SSL_get_error(3). */ SO_ENOTVRFD, SO_ECLOSURE, SO_ENOHOST, SO_ELAST, }; /* enum so_errno */ const char *so_strerror(int); #define SO_ERRNO0 SO_EBASE #define SO_EEND SO_ELAST #define SO_ISERRNO(e) ((e) >= SO_ERRNO0 && (e) < SO_EEND) #define so_error_t int /* for documentation only */ /* * O P T I O N I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ typedef struct { enum { SO_OPT_UNSET = 0, SO_OPT_BOOLEAN, } type; union { _Bool boolean; }; } so_optional; struct so_options { const void *sa_bind; mode_t sun_mode; mode_t sun_mask; _Bool sun_unlink; _Bool sin_reuseaddr; _Bool sin_reuseport; _Bool sin_broadcast; _Bool sin_nodelay; _Bool sin_nopush; _Bool sin_oobinline; enum { SO_V6ONLY_DEFAULT = 0, /* system default */ SO_V6ONLY_ENABLE = 1, SO_V6ONLY_DISABLE = 2, } sin_v6only; _Bool fd_nonblock; _Bool fd_cloexec; _Bool fd_nosigpipe; enum { SO_SYSPOLL, SO_LIBEVENT, } fd_events; struct { void *arg; int (*cb)(int *fd, void *arg); } fd_close; _Bool tls_verify; const char *tls_sendname; _Bool st_time; }; /* struct so_options */ #define SO_OPTS_TLS_HOSTNAME ((char *)1) /* place holder for peer host name */ #define so_opts(...) (&(struct so_options){ .sin_reuseaddr = 1, .sin_v6only = SO_V6ONLY_DEFAULT, .fd_nonblock = 1, .fd_cloexec = 1, .fd_nosigpipe = 1, .tls_sendname = SO_OPTS_TLS_HOSTNAME, .st_time = 1, __VA_ARGS__ }) static inline _Bool so_isbool(const so_optional v) { return v.type == SO_OPT_BOOLEAN; } static inline _Bool so_optbool(const so_optional v, _Bool def) { return (so_isbool(v))? v.boolean : def; } static inline _Bool so_tobool(const so_optional v) { return so_optbool(v, 0); } static inline void so_setbool(so_optional *v, _Bool b) { v->type = SO_OPT_BOOLEAN; v->boolean = b; } /* * P R E - P R O C E S S O R I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define SO_MIN(a, b) (((a) < (b))? (a) : (b)) #define SO_MAX(a, b) (((a) > (b))? (a) : (b)) #define SO_NARG_(_15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N #define SO_NARG(...) SO_NARG_(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define SO_PASTE(a, b) a ## b #define SO_XPASTE(a, b) SO_PASTE(a, b) /* * A D D R E S S I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define SA_UNIX defined(AF_UNIX) && !_WIN32 union sockaddr_any { struct sockaddr sa; struct sockaddr_storage ss; struct sockaddr_in sin; struct sockaddr_in6 sin6; #if SA_UNIX struct sockaddr_un sun; #endif }; /* union sockaddr_any */ /* * GCC 4.4's strong aliasing constraints complain about casting through * intermediate void pointers before taking a reference to an object member. * Use sockaddr_arg_t where we might use the void pointer, and use the * accessor function sockaddr_ref() to return the union sockaddr_arg object. */ #if __GNUC__ #define SO_TRANSPARENT __attribute__((__transparent_union__)) #define SO_EXTENSION __extension__ #else #define SO_TRANSPARENT #define SO_EXTENSION #endif union sockaddr_arg { struct sockaddr *sa; const struct sockaddr *c_sa; struct sockaddr_storage *ss; struct sockaddr_storage *c_ss; struct sockaddr_in *sin; struct sockaddr_in *c_sin; struct sockaddr_in6 *sin6; struct sockaddr_in6 *c_sin6; #if SA_UNIX struct sockaddr_un *sun; struct sockaddr_un *c_sun; #endif union sockaddr_any *any; union sockaddr_any *c_any; void *ptr; void *c_ptr; } SO_TRANSPARENT; #if __GNUC__ typedef union sockaddr_arg sockaddr_arg_t; static inline union sockaddr_arg sockaddr_ref(sockaddr_arg_t arg) { return arg; } /* sockaddr_ref() */ #else typedef void *sockaddr_arg_t; static inline union sockaddr_arg sockaddr_ref(sockaddr_arg_t arg) { return (union sockaddr_arg){ arg }; } /* sockaddr_ref() */ #endif static inline socklen_t af_len(sa_family_t af) { switch (af) { case AF_INET: return sizeof (struct sockaddr_in); case AF_INET6: return sizeof (struct sockaddr_in6); #if SA_UNIX case AF_UNIX: return sizeof (struct sockaddr_un); #endif default: return 0; } } /* af_len() */ #define sa_family(...) SO_EXTENSION sa_family(__VA_ARGS__) static inline sa_family_t *(sa_family)(sockaddr_arg_t arg) { return &sockaddr_ref(arg).sa->sa_family; } /* sa_family() */ #define sa_len(...) SO_EXTENSION sa_len(__VA_ARGS__) static inline socklen_t (sa_len)(sockaddr_arg_t arg) { return af_len(*sa_family(arg)); } /* sa_len() */ #if SA_UNIX #define SA_ADDR_NONE (&(union { struct in_addr addr; struct in6_addr addr6; char path[sizeof ((struct sockaddr_un *)0)->sun_path]; })) #else #define SA_ADDR_NONE (&(union { struct in_addr addr; struct in6_addr addr6; })) #endif #define sa_addr(...) SO_EXTENSION sa_addr(__VA_ARGS__) static inline void *(sa_addr)(sockaddr_arg_t arg, const void *def, int *error) { switch (*sa_family(arg)) { case AF_INET: return &sockaddr_ref(arg).sin->sin_addr; case AF_INET6: return &sockaddr_ref(arg).sin6->sin6_addr; #if SA_UNIX case AF_UNIX: return &sockaddr_ref(arg).sun->sun_path; #endif default: if (error) *error = EAFNOSUPPORT; return (void *)def; } } /* sa_addr() */ #define sa_addrlen(...) SO_EXTENSION sa_addrlen(__VA_ARGS__) static inline socklen_t (sa_addrlen)(sockaddr_arg_t arg, int *error) { switch (*sa_family(arg)) { case AF_INET: return sizeof ((struct sockaddr_in *)0)->sin_addr; case AF_INET6: return sizeof ((struct sockaddr_in6 *)0)->sin6_addr; #if SA_UNIX case AF_UNIX: return sizeof ((struct sockaddr_un *)0)->sun_path; #endif default: if (error) *error = EAFNOSUPPORT; return 0; } } /* sa_addrlen() */ #define SA_PORT_NONE (&(in_port_t){ 0 }) #define sa_port(...) SO_EXTENSION sa_port(__VA_ARGS__) static inline in_port_t *(sa_port)(sockaddr_arg_t arg, const in_port_t *def, int *error) { switch (*sa_family(arg)) { case AF_INET: return &sockaddr_ref(arg).sin->sin_port; case AF_INET6: return &sockaddr_ref(arg).sin6->sin6_port; default: if (error) *error = EAFNOSUPPORT; return (in_port_t *)def; } } /* sa_port() */ #if SA_UNIX #define SA_ADDRSTRLEN SO_MAX(INET6_ADDRSTRLEN, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1) #else #define SA_ADDRSTRLEN INET6_ADDRSTRLEN #endif char *sa_ntop(char *, size_t, const void *, const char *, so_error_t *); void *sa_pton(void *, size_t, const char *, const void *, so_error_t *); static inline char *sa_ntoa_(char *dst, size_t lim, const void *src) { return sa_ntop(dst, lim, src, NULL, &(int){ 0 }), dst; } /* sa_ntoa_() */ static inline void *sa_aton_(void *dst, size_t lim, const char *src) { return sa_pton(dst, lim, src, NULL, &(int){ 0 }), dst; } /* sa_aton_() */ #define sa_ntoa(sa) sa_ntoa_((char [SA_ADDRSTRLEN]){ 0 }, SA_ADDRSTRLEN, (sa)) #define sa_aton(str) sa_aton_(&(struct sockaddr_storage){ 0 }, sizeof (struct sockaddr_storage), (str)) /* * U T I L I T Y I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define so_itoa_putc(c) do { if (p < lim) dst[p] = (c); p++; } while (0) static inline char *so_itoa(char *dst, size_t lim, long i) { size_t p = 0; long d = 1000000000L, n = 0, r; if (i < 0) { so_itoa_putc('-'); i *= -1; } if ((i = SO_MIN(2147483647L, i))) { do { if ((r = i / d) || n) { i -= r * d; n++; so_itoa_putc('0' + r); } } while (d /= 10); } else so_itoa_putc('0'); if (lim) dst[SO_MIN(p, lim - 1)] = '\0'; return dst; } /* so_itoa() */ #define so_itoa3(d, l, i) so_itoa((d), (l), (i)) #define so_itoa1(i) so_itoa3((char[32]){ 0 }, 32, (i)) #define so_itoa(...) SO_XPASTE(so_itoa, SO_NARG(__VA_ARGS__))(__VA_ARGS__) #define so_isint(T) \ (__builtin_types_compatible_p(char, T) || \ __builtin_types_compatible_p(signed char, T) || \ __builtin_types_compatible_p(unsigned char, T) || \ __builtin_types_compatible_p(signed short, T) || \ __builtin_types_compatible_p(unsigned short, T) || \ __builtin_types_compatible_p(signed int, T) || \ __builtin_types_compatible_p(unsigned int, T) || \ __builtin_types_compatible_p(signed long, T) || \ __builtin_types_compatible_p(unsigned long, T) || \ __builtin_types_compatible_p(signed long long, T) || \ __builtin_types_compatible_p(unsigned long long, T)) #define so_ytoa(y) \ __builtin_choose_expr(so_isint(__typeof__(y)), so_itoa((long)(y)), (y)) void *sa_egress(void *, size_t, sockaddr_arg_t, int *); int so_socket(int, int, const struct so_options *, int *); int so_bind(int, sockaddr_arg_t, const struct so_options *); void so_closesocket(int *, const struct so_options *); int so_nonblock(int, _Bool); int so_cloexec(int, _Bool); int so_reuseaddr(int, _Bool); int so_reuseport(int, _Bool); int so_broadcast(int, _Bool); int so_nodelay(int, _Bool); int so_nopush(int, _Bool); int so_nosigpipe(int, _Bool); int so_v6only(int, _Bool); int so_oobinline(int, _Bool); #define SO_F_CLOEXEC 0x0001 #define SO_F_NONBLOCK 0x0002 #define SO_F_REUSEADDR 0x0004 #define SO_F_REUSEPORT 0x0008 #define SO_F_BROADCAST 0x0010 #define SO_F_NODELAY 0x0020 #define SO_F_NOPUSH 0x0040 #define SO_F_NOSIGPIPE 0x0080 #define SO_F_V6ONLY 0x0100 #define SO_F_OOBINLINE 0x0200 int so_getfl(int fd, int which); /* no failure mode */ so_error_t so_rstfl(int fd, int *oflags, int flags, int mask, int require); so_error_t so_setfl(int fd, int flags, int mask, int require); so_error_t so_addfl(int fd, int flags, int require); #define so_addfl3(fd, flags, require, ...) so_addfl((fd), (flags), (require)) #define so_addfl(...) so_addfl3(__VA_ARGS__, ~0) so_error_t so_delfl(int fd, int flags, int require); #define so_delfl3(fd, flags, require, ...) so_delfl((fd), (flags), (require)) #define so_delfl(...) so_delfl3(__VA_ARGS__, ~0) /* * S O C K E T I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct socket; struct socket *so_open(const char *, const char *, int, int, int, const struct so_options *, int *); #if __GNUC__ /* Coerce port to string if not already. */ #define so_open(host, port, ...) so_open((host), so_ytoa((port)), __VA_ARGS__) #else #define so_open(...) so_open(__VA_ARGS__) #endif struct socket *so_dial(const struct sockaddr *, int, const struct so_options *, int *); struct socket *so_fdopen(int, const struct so_options *, int *); int so_close(struct socket *); int so_family(struct socket *, int *); int so_localaddr(struct socket *, void *, socklen_t *); int so_remoteaddr(struct socket *, void *, socklen_t *); int so_connect(struct socket *); int so_listen(struct socket *); int so_accept(struct socket *, struct sockaddr *, socklen_t *, int *); struct so_starttls { SSL_METHOD *method; SSL_CTX *context; SSL *instance; struct iovec pushback; so_optional accept; }; /* struct so_starttls */ int so_starttls(struct socket *, const struct so_starttls *); SSL *so_checktls(struct socket *); int so_shutdown(struct socket *, int /* SHUT_RD, SHUT_WR, SHUT_RDWR */); size_t so_read(struct socket *, void *, size_t, int *); size_t so_write(struct socket *, const void *, size_t, int *); #define SO_F_PEEKALL 0x01 size_t so_peek(struct socket *, void *, size_t, int, int *); #define so_peekall(so, dst, lim, ep) so_peek((so), (dst), (lim), SO_F_PEEKALL, (ep)) #define so_peekany(so, dst, lim, ep) so_peek((so), (dst), (lim), 0, (ep)) /* * NOTE: CMSG_SPACE does not evaluate to a constant on OS X or NetBSD, so * cannot be used to declare a compound literal. Instead, a largish constant * is used. It must be adjusted at run-time because some implementations * complain if .msg_controllen is too large. */ #define so_fdmsgbuf() (&(struct msghdr){ \ .msg_iov = &(struct iovec){ 0, 0 }, \ .msg_iovlen = 1, \ .msg_control = &(union { char buf[64]; struct cmsghdr hdr; }){ { 0 } }, \ .msg_controllen = 64 \ }) static inline struct msghdr *so_fdmsg_(struct msghdr *msg, const void *p, size_t n, int fd) { msg->msg_iov->iov_base = (void *)p; msg->msg_iov->iov_len = n; msg->msg_controllen = SO_MIN(msg->msg_controllen, CMSG_SPACE(sizeof (int))); struct cmsghdr *cmsg; cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof (int)); memcpy(CMSG_DATA(cmsg), &fd, sizeof fd); return msg; } /* so_fdmsg_() */ #define so_fdmsg(p, n, fd) so_fdmsg_(so_fdmsgbuf(), (p), (n), (fd)) int so_sendmsg(struct socket *, const struct msghdr *, int); int so_recvmsg(struct socket *, struct msghdr *, int); struct so_stat { struct st_log { unsigned long long count; _Bool eof; time_t time; } sent, rcvd; }; /* struct so_stat */ const struct so_stat *so_stat(struct socket *); #define SO_POLL2EV(set) \ (((set) & POLLIN)? 2 : 0) | (((set) & POLLOUT)? 4 : 0) int so_events(struct socket *); void so_clear(struct socket *); int so_pollfd(struct socket *); int so_poll(struct socket *, int); int so_peerfd(struct socket *); int so_uncork(struct socket *); int so_peereid(struct socket *, uid_t *, gid_t *); int so_peerpid(struct socket *, pid_t *); /* * L I B R A R Y I N T E R F A C E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ void socket_init(void); #endif /* SOCKET_H */ cqueues-rel-20161214/src/notify.c000066400000000000000000000145051302435770500164640ustar00rootroot00000000000000/* ========================================================================== * notify.c - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include "lib/notify.h" #include "cqueues.h" struct luanotify { struct notify *notify; }; /* luanotify */ static int ln_step(lua_State *L) { struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); int error; if ((error = notify_step(N->notify, 0))) { lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } else { lua_pushboolean(L, 1); return 1; } } /* ln_step() */ static int ln_get(lua_State *L) { struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); const char *name = 0; int changes; if (!(changes = notify_get(N->notify, &name))) return 0; lua_pushinteger(L, changes); lua_pushstring(L, name); return 2; } /* ln_get() */ static int ln_add(lua_State *L) { struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); const char *name = luaL_checkstring(L, 2); int flags = luaL_optinteger(L, 3, NOTIFY_ALL); int error; if ((error = notify_add(N->notify, name, flags))) { lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } else { lua_pushboolean(L, 1); return 1; } } /* ln_add() */ static int ln_pollfd(lua_State *L) { struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); lua_pushinteger(L, notify_pollfd(N->notify)); return 1; } /* ln_pollfd() */ static int ln_events(lua_State *L) { luaL_checkudata(L, 1, CQS_NOTIFY); lua_pushliteral(L, "r"); return 1; } /* ln_events() */ static int ln_timeout(lua_State *L) { struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); int timeout; if ((timeout = notify_timeout(N->notify)) >= 0) { lua_pushnumber(L, (lua_Number)timeout / 1000); return 1; } else { return 0; } } /* ln_timeout() */ static const luaL_Reg ln_methods[] = { { "step", &ln_step }, { "get", &ln_get }, { "add", &ln_add }, { "pollfd", &ln_pollfd }, { "events", &ln_events }, { "timeout", &ln_timeout }, { NULL, NULL }, }; /* ln_methods[] */ static int ln__gc(lua_State *L) { struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); notify_close(N->notify); N->notify = 0; return 0; } /* ln__gc() */ static const luaL_Reg ln_metatable[] = { { "__gc", &ln__gc }, { NULL, NULL }, }; /* ln_metatable[] */ static int ln_opendir(lua_State *L) { const char *path = luaL_checkstring(L, 1); struct luanotify *N = 0; int error; N = lua_newuserdata(L, sizeof *N); N->notify = 0; luaL_setmetatable(L, CQS_NOTIFY); if (!(N->notify = notify_opendir(path, NOTIFY_ALL, &error))) goto error; return 1; error: lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* ln_opendir */ static int ln_type(lua_State *L) { if (luaL_testudata(L, 1, CQS_NOTIFY)) { lua_pushstring(L, "file notifier"); } else { lua_pushnil(L); } return 1; } /* ln_type() */ static int ln_interpose(lua_State *L) { return cqs_interpose(L, CQS_NOTIFY); } /* ln_interpose() */ static int ln_strflag(lua_State *L) { int flags = luaL_checkint(L, 1); int flag, count = 0; const char *name; while (ffs(flags)) { flag = 1 << (ffs(flags) - 1); flags &= ~flag; if ((name = notify_strflag(flag))) { luaL_checkstack(L, 1, "too many results"); lua_pushstring(L, name); count++; } } return count; } /* ln_strflag() */ static int ln_nxtflag(lua_State *L) { int flags = (int)lua_tointeger(L, lua_upvalueindex(1)); int flag; if (ffs(flags)) { flag = 1 << (ffs(flags) - 1); lua_pushinteger(L, flags & ~flag); lua_replace(L, lua_upvalueindex(1)); lua_pushinteger(L, flag); return 1; } return 0; } /* ln_nxtflag() */ static int ln_flags(lua_State *L) { int i, flags = 0; for (i = 1; i <= lua_gettop(L); i++) flags |= luaL_checkint(L, i); lua_pushinteger(L, flags); lua_pushcclosure(L, &ln_nxtflag, 1); return 1; } /* ln_flags() */ static const luaL_Reg ln_globals[] = { { "opendir", &ln_opendir }, { "type", &ln_type }, { "interpose", &ln_interpose }, { "strflag", &ln_strflag }, { "flags", &ln_flags }, { NULL, NULL } }; int luaopen__cqueues_notify(lua_State *L) { static const struct { const char *name; int value; } flag[] = { { "CREATE", NOTIFY_CREATE }, { "DELETE", NOTIFY_DELETE }, { "ATTRIB", NOTIFY_ATTRIB }, { "MODIFY", NOTIFY_MODIFY }, { "REVOKE", NOTIFY_REVOKE }, { "ALL", NOTIFY_ALL }, { "INOTIFY", NOTIFY_INOTIFY }, { "FEN", NOTIFY_FEN }, { "KQUEUE", NOTIFY_KQUEUE }, { "KQUEUE1", NOTIFY_KQUEUE1 }, { "OPENAT", NOTIFY_OPENAT }, { "FDOPENDIR", NOTIFY_FDOPENDIR}, { "O_CLOEXEC", NOTIFY_O_CLOEXEC }, { "IN_CLOEXEC", NOTIFY_IN_CLOEXEC }, }; unsigned i; if (luaL_newmetatable(L, CQS_NOTIFY)) { luaL_setfuncs(L, ln_metatable, 0); luaL_newlib(L, ln_methods); lua_setfield(L, -2, "__index"); } luaL_newlib(L, ln_globals); for (i = 0; i < countof(flag); i++) { lua_pushinteger(L, flag[i].value); lua_setfield(L, -2, flag[i].name); lua_pushinteger(L, flag[i].value); lua_pushstring(L, flag[i].name); lua_settable(L, -3); } lua_pushinteger(L, notify_features()); lua_setfield(L, -2, "FEATURES"); return 1; } /* luaopen__cqueues_notify() */ cqueues-rel-20161214/src/notify.lua000066400000000000000000000025241302435770500170210ustar00rootroot00000000000000local loader = function(loader, ...) local cqueues = require("cqueues") local notify = require("_cqueues.notify") local strerror = require("cqueues.errno").strerror local ALL = notify.ALL local function oops(self, op, why) local msg = string.format("notify.%s: %s", op, strerror(why)) error(msg) end -- -- notify:get -- local get; get = notify.interpose("get", function(self, timeout) local deadline = timeout and (cqueues.monotime() + timeout) local changes, filename local ready = function() local okay, why = self:step() if not okay then oops(self, "get", why) end changes, filename = get(self) return changes end while not ready() do if deadline then local curtime = cqueues.monotime() if curtime >= deadline then return nil else cqueues.poll(self, deadline - curtime) end else cqueues.poll(self) end end return changes, filename end) -- -- notify:changes -- notify.interpose("changes", function(self, timeout) return function() return self:get(timeout) end end) -- -- notify:add -- local add; add = notify.interpose("add", function(self, name, flags) local okay, why = add(self, name, flags or ALL) if not okay then oops(self, "add", why) end return true end) notify.loader = loader return notify end return loader(loader, ...) cqueues-rel-20161214/src/promise.lua000066400000000000000000000037441302435770500171740ustar00rootroot00000000000000local loader = function(loader, ...) local cqueues = require"cqueues" local condition = require"cqueues.condition" local auxlib = require"cqueues.auxlib" local assert3 = auxlib.assert3 local unpack = assert(table.unpack or unpack) local pcall = pcall local error = error local getmetatable = getmetatable local tostring = auxlib.tostring local promise = {} promise.__index = promise -- keep things simple function promise.new(f, ...) local self = setmetatable({ pollfd = condition.new(), state = "pending", }, promise) if f then cqueues.running():wrap(function(f, ...) self:set(pcall(f, ...)) end, f, ...) end return self end -- promise.new function promise.type(self) local mt = getmetatable(self) return (mt == promise and "promise") or nil end -- promise.type function promise:set(ok, ...) assert3(self.state == "pending", "attempt to set value of resolved promise") if not ok then self.state = "rejected" self.reason = ... else self.state = "fulfilled" self.n = select("#", ...) if self.n == 1 then self.tuple = ... else self.tuple = { ... } end end self.pollfd:signal() self.timeout = 0 end -- promise:set function promise:wait(timeout) if self.state == "pending" then self.pollfd:wait(timeout) end return (self.state ~= "pending" and self) or nil end -- promise:wait function promise:get(timeout) self:wait(timeout) if self.state == "fulfilled" then if self.n == 1 then return self.tuple else return unpack(self.tuple) end elseif self.state == "rejected" then return error(self.reason, 2) end end -- promise:get function promise:status() return self.state end -- promise:status -- NOTE: Only LuaJIT supports metamethod yielding. function promise:__tostring() return tostring(self:get()) end -- promise:__tostring function promise:__call() return self:get() end -- promise:__call promise.loader = loader return promise end return loader(loader, ...) cqueues-rel-20161214/src/signal.c000066400000000000000000000335301302435770500164300ustar00rootroot00000000000000/* ========================================================================== * signal.c - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include #include #include #include #if HAVE_SYS_EVENT_H #include #endif #if HAVE_SYS_SIGNALFD_H #include #endif #include #include #include #include #include "cqueues.h" /* * S I G N A L L I S T E N E R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef ENABLE_SIGNALFD #define ENABLE_SIGNALFD HAVE_SIGNALFD #endif #ifndef ENABLE_EVFILT_SIGNAL #define ENABLE_EVFILT_SIGNAL (ENABLE_KQUEUE && defined EVFILT_SIGNAL) #endif #define LSL_CLASS "CQS Signal" #define SIGNAL_SIGNALFD 0x01 #define SIGNAL_EVFILT_SIGNAL 0x02 #define SIGNAL_SIGTIMEDWAIT 0x04 #define SIGNAL_KQUEUE 0x08 #define SIGNAL_KQUEUE1 0x10 static int signal_features(void) { return 0 #if ENABLE_SIGNALFD | SIGNAL_SIGNALFD #endif #if ENABLE_EVFILT_SIGNAL | SIGNAL_EVFILT_SIGNAL #endif #if HAVE_SIGTIMEDWAIT | SIGNAL_SIGTIMEDWAIT #endif #if HAVE_KQUEUE | SIGNAL_KQUEUE #endif #if HAVE_KQUEUE1 | SIGNAL_KQUEUE1 #endif ; } static const char *signal_strflag(int flag) { static const char *const table[32] = { [0] = "signalfd", "EVFILT_SIGNAL", "sigtimedwait", "kqueue", "kqueue1", }; int i = ffs(0xFFFFFFFF & flag); return (i)? table[i - 1] : NULL; } static int signal_flags(int *flags) { while (0xFFFFFFFF & *flags) { int flag = 1 << (ffs(0xFFFFFFFF & *flags) - 1); *flags &= ~flag; if (signal_strflag(flag)) return flag; } return 0; } struct signalfd { int features; int fd; sigset_t desired; sigset_t polling; sigset_t pending; double timeout; }; /* struct signalfd */ static void sfd_preinit(struct signalfd *S) { S->features = 0; S->fd = -1; sigemptyset(&S->desired); sigemptyset(&S->polling); sigemptyset(&S->pending); #if ENABLE_SIGNALFD || ENABLE_EVFILT_SIGNAL S->timeout = NAN; #else S->timeout = 1.1; #endif } /* sfd_preinit() */ static int sfd_init(struct signalfd *S) { #if ENABLE_SIGNALFD S->features |= SIGNAL_SIGNALFD; if (-1 == (S->fd = signalfd(-1, &S->desired, SFD_NONBLOCK|SFD_CLOEXEC))) return errno; S->polling = S->desired; return 0; #elif ENABLE_EVFILT_SIGNAL && HAVE_KQUEUE1 S->features |= SIGNAL_EVFILT_SIGNAL | SIGNAL_KQUEUE1; if (-1 == (S->fd = kqueue1(O_CLOEXEC))) return errno; return 0; #elif ENABLE_EVFILT_SIGNAL int flags, error; S->features |= SIGNAL_EVFILT_SIGNAL | SIGNAL_KQUEUE; if (-1 == (S->fd = kqueue())) goto syerr; if (-1 == (flags = fcntl(S->fd, F_GETFL))) goto syerr; if (-1 == fcntl(S->fd, F_SETFD, flags|FD_CLOEXEC)) goto syerr; return 0; syerr: error = errno; cqs_closefd(&S->fd); return error; #elif ENABLE_SIGTIMEDWAIT S->features |= SIGNAL_SIGTIMEDWAIT; return 0; #else (void)S; return 0; #endif } /* sfd_init() */ static void sfd_destroy(struct signalfd *S) { cqs_closefd(&S->fd); sfd_preinit(S); } /* sfd_destroy() */ static int sfd_diff(const sigset_t *a, const sigset_t *b) { for (int signo = 1; signo < 32; signo++) { if (!!sigismember(a, signo) ^ !!sigismember(b, signo)) return signo; } return 0; } /* sfd_diff() */ static int sfd_update(struct signalfd *S) { #if ENABLE_SIGNALFD if (sfd_diff(&S->desired, &S->polling)) { if (-1 == signalfd(S->fd, &S->desired, 0)) return errno; S->polling = S->desired; } return 0; #elif ENABLE_EVFILT_SIGNAL int signo; while ((signo = sfd_diff(&S->desired, &S->polling))) { struct kevent event; if (sigismember(&S->desired, signo)) { EV_SET(&event, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); if (0 != kevent(S->fd, &event, 1, 0, 0, 0)) return errno; sigaddset(&S->polling, signo); } else { EV_SET(&event, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, 0); if (0 != kevent(S->fd, &event, 1, 0, 0, 0)) return errno; sigdelset(&S->polling, signo); } } return 0; #else (void)S; return 0; #endif } /* sfd_update() */ static int sfd_query(struct signalfd *S) { #if ENABLE_SIGNALFD struct signalfd_siginfo info; long n; retry: if ((n = read(S->fd, &info, sizeof info)) > 0) { sigaddset(&S->pending, info.ssi_signo); } else if (n == -1) { goto syerr; } return 0; syerr: switch (errno) { case EAGAIN: return 0; case EINTR: goto retry; default: break; } return errno; #elif ENABLE_EVFILT_SIGNAL struct kevent event; int n; retry: if (1 == (n = kevent(S->fd, 0, 0, &event, 1, &(struct timespec){ 0, 0 }))) { if (event.filter == EVFILT_SIGNAL) { sigaddset(&S->pending, event.ident); sigdelset(&S->polling, event.ident); } } else if (n == -1) { if (errno == EINTR) goto retry; return errno; } return sfd_update(S); #elif HAVE_SIGTIMEDWAIT int signo; if (-1 != (signo = sigtimedwait(&S->desired, NULL, &(struct timespec){ 0, 0 }))) sigaddset(&S->pending, signo); return 0; #else (void)S; return EOPNOTSUPP; #endif } /* sfd_query() */ static int lsl_listen(lua_State *L) { struct signalfd *S; int index, error; S = lua_newuserdata(L, sizeof *S); sfd_preinit(S); for (index = 1; index <= lua_gettop(L) - 1; index++) sigaddset(&S->desired, luaL_checkint(L, index)); luaL_getmetatable(L, LSL_CLASS); lua_setmetatable(L, -2); if ((error = sfd_init(S)) || (error = sfd_update(S))) return luaL_error(L, "signal.listen: %s", cqs_strerror(error)); return 1; } /* lsl_listen() */ static int lsl__gc(lua_State *L) { struct signalfd *S = luaL_checkudata(L, 1, LSL_CLASS); sfd_destroy(S); return 0; } /* lsl__gc() */ static int lsl_features(lua_State *L) { struct signalfd *S = luaL_checkudata(L, 1, LSL_CLASS); lua_pushinteger(L, S->features); return 1; } /* lsl_features() */ static int lsl_wait(lua_State *L) { struct signalfd *S = luaL_checkudata(L, 1, LSL_CLASS); sigset_t none; int error, signo; if ((error = sfd_query(S))) return luaL_error(L, "signal:get: %s", cqs_strerror(error)); sigemptyset(&none); if ((signo = sfd_diff(&S->pending, &none))) { lua_pushinteger(L, signo); sigdelset(&S->pending, signo); return 1; } return 0; } /* lsl_wait() */ static int lsl_pollfd(lua_State *L) { struct signalfd *S = luaL_checkudata(L, 1, LSL_CLASS); lua_pushinteger(L, S->fd); return 1; } /* lsl_pollfd() */ static int lsl_events(lua_State *L) { luaL_checkudata(L, 1, LSL_CLASS); lua_pushliteral(L, "r"); return 1; } /* lsl_events() */ static int lsl_timeout(lua_State *L) { struct signalfd *S = luaL_checkudata(L, 1, LSL_CLASS); sigset_t none; sigemptyset(&none); if (sfd_diff(&S->pending, &none)) { lua_pushnumber(L, 0.0); } else if (isnormal(S->timeout) && !signbit(S->timeout)) { lua_pushnumber(L, S->timeout); } else { lua_pushnil(L); } return 1; } /* lsl_timeout() */ static int lsl_settimeout(lua_State *L) { struct signalfd *S = luaL_checkudata(L, 1, LSL_CLASS); lua_settop(L, 2); lua_pushnumber(L, S->timeout); S->timeout = luaL_optnumber(L, 2, NAN); return 1; } /* lsl_settimeout() */ static int lsl_type(lua_State *L) { if (luaL_testudata(L, 1, LSL_CLASS)) { lua_pushstring(L, "signal listener"); } else { lua_pushnil(L); } return 1; } /* lsl_type() */ static int lsl_interpose(lua_State *L) { return cqs_interpose(L, LSL_CLASS); } /* lsl_interpose() */ static int lsl_strflag(lua_State *L) { int top = lua_gettop(L), count = 0; for (int i = 1; i <= top; i++) { int flags = luaL_checkint(L, i); int flag; while ((flag = signal_flags(&flags))) { const char *txt; if ((txt = signal_strflag(flag))) { luaL_checkstack(L, 1, "too many results"); lua_pushstring(L, txt); count++; } } } return count; } /* lsl_strflag() */ static int lsl_nxtflag(lua_State *L) { int flags = (int)lua_tointeger(L, lua_upvalueindex(1)); int flag; if ((flag = signal_flags(&flags))) { lua_pushinteger(L, flags); lua_replace(L, lua_upvalueindex(1)); lua_pushinteger(L, flag); return 1; } return 0; } /* lsl_nxtflag() */ static int lsl_flags(lua_State *L) { int i, flags = 0; for (i = 1; i <= lua_gettop(L); i++) flags |= luaL_checkint(L, i); lua_pushinteger(L, flags); lua_pushcclosure(L, &lsl_nxtflag, 1); return 1; } /* lsl_flags() */ static const luaL_Reg lsl_methods[] = { { "features", &lsl_features }, { "wait", &lsl_wait }, { "pollfd", &lsl_pollfd }, { "events", &lsl_events }, { "timeout", &lsl_timeout }, { "settimeout", &lsl_settimeout }, { NULL, NULL }, }; /* lsl_methods[] */ static const luaL_Reg lsl_metatable[] = { { "__gc", &lsl__gc }, { NULL, NULL }, }; /* lsl_metatable[] */ /* * S I G N A L D I S P O S I T I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int ls_ignore(lua_State *L) { struct sigaction sa; int index; for (index = 1; index <= lua_gettop(L); index++) { sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (0 != sigaction(luaL_checkint(L, index), &sa, 0)) return luaL_error(L, "signal.ignore: %s", cqs_strerror(errno)); } lua_pushboolean(L, 1); return 1; } /* ls_ignore() */ static int ls_default(lua_State *L) { struct sigaction sa; int index; for (index = 1; index <= lua_gettop(L); index++) { sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (0 != sigaction(luaL_checkint(L, index), &sa, 0)) return luaL_error(L, "signal.default: %s", cqs_strerror(errno)); } lua_pushboolean(L, 1); return 1; } /* ls_default() */ static void ls_noop() { return; } /* ls_noop() */ static int ls_discard(lua_State *L) { struct sigaction sa; int index; for (index = 1; index <= lua_gettop(L); index++) { sa.sa_handler = &ls_noop; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (0 != sigaction(luaL_checkint(L, index), &sa, 0)) return luaL_error(L, "signal.discard: %s", cqs_strerror(errno)); } lua_pushboolean(L, 1); return 1; } /* ls_discard() */ static int ls_block(lua_State *L) { sigset_t set; int index, error; sigemptyset(&set); for (index = 1; index <= lua_gettop(L); index++) { sigaddset(&set, luaL_checkint(L, index)); } if ((error = cqs_sigmask(SIG_BLOCK, &set, 0))) return luaL_error(L, "signal.block: %s", cqs_strerror(error)); lua_pushboolean(L, 1); return 1; } /* ls_block() */ static int ls_unblock(lua_State *L) { sigset_t set; int index, error; sigemptyset(&set); for (index = 1; index <= lua_gettop(L); index++) { sigaddset(&set, luaL_checkint(L, index)); } if ((error = cqs_sigmask(SIG_UNBLOCK, &set, 0))) return luaL_error(L, "signal.unblock: %s", cqs_strerror(error)); lua_pushboolean(L, 1); return 1; } /* ls_unblock() */ static int ls_raise(lua_State *L) { int index; for (index = 1; index <= lua_gettop(L); index++) { raise(luaL_checkint(L, index)); } lua_pushboolean(L, 1); return 1; } /* ls_raise() */ static int ls_strsignal(lua_State *L) { lua_pushstring(L, strsignal(luaL_checkint(L, 1))); return 1; } /* ls_strsignal() */ static const luaL_Reg ls_globals[] = { { "listen", &lsl_listen }, { "type", &lsl_type }, { "interpose", &lsl_interpose }, { "strflag", &lsl_strflag }, { "flags", &lsl_flags }, { "interpose", &lsl_interpose }, { "ignore", &ls_ignore }, { "default", &ls_default }, { "discard", &ls_discard }, { "block", &ls_block }, { "unblock", &ls_unblock }, { "raise", &ls_raise }, { "strsignal", &ls_strsignal }, { NULL, NULL } }; int luaopen__cqueues_signal(lua_State *L) { static const struct { const char *name; int value; } siglist[] = { { "SIGALRM", SIGALRM }, { "SIGCHLD", SIGCHLD }, { "SIGHUP", SIGHUP }, { "SIGINT", SIGINT }, { "SIGKILL", SIGKILL }, { "SIGPIPE", SIGPIPE }, { "SIGQUIT", SIGQUIT }, { "SIGTERM", SIGTERM }, { "SIGUSR1", SIGUSR1 }, { "SIGUSR2", SIGUSR2 }, }, flag[] = { { "SIGNALFD", SIGNAL_SIGNALFD }, { "EVFILT_SIGNAL", SIGNAL_EVFILT_SIGNAL }, { "SIGTIMEDWAIT", SIGNAL_SIGTIMEDWAIT }, { "KQUEUE", SIGNAL_KQUEUE }, { "KQUEUE1", SIGNAL_KQUEUE1 }, }; unsigned i; if (luaL_newmetatable(L, LSL_CLASS)) { luaL_setfuncs(L, lsl_metatable, 0); luaL_newlib(L, lsl_methods); lua_setfield(L, -2, "__index"); } luaL_newlib(L, ls_globals); for (i = 0; i < sizeof siglist / sizeof *siglist; i++) { lua_pushinteger(L, siglist[i].value); lua_setfield(L, -2, siglist[i].name); lua_pushstring(L, siglist[i].name); lua_rawseti(L, -2, siglist[i].value); } for (i = 0; i < sizeof flag / sizeof *flag; i++) { lua_pushinteger(L, flag[i].value); lua_setfield(L, -2, flag[i].name); lua_pushstring(L, flag[i].name); lua_rawseti(L, -2, flag[i].value); } lua_pushinteger(L, signal_features()); lua_setfield(L, -2, "FEATURES"); return 1; } /* luaopen__cqueues_signal() */ cqueues-rel-20161214/src/signal.lua000066400000000000000000000012601302435770500167620ustar00rootroot00000000000000local loader = function(loader, ...) local cqueues = require("cqueues") local signal = require("_cqueues.signal") -- -- signal:wait -- local wait; wait = signal.interpose("wait", function(self, timeout) local deadline = timeout and (cqueues.monotime() + timeout) local signo local ready = function() signo = wait(self) return signo end while not ready() do if deadline then local curtime = cqueues.monotime() if curtime >= deadline then return nil else cqueues.poll(self, deadline - curtime) end else cqueues.poll(self) end end return signo end) signal.loader = loader return signal end return loader(loader, ...) cqueues-rel-20161214/src/socket.c000066400000000000000000002050641302435770500164460ustar00rootroot00000000000000/* ========================================================================== * socket.c - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012, 2013, 2014 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include /* NULL offsetof size_t */ #include /* va_list va_start va_arg va_end */ #include /* strtol(3) */ #include /* memset(3) memchr(3) memcpy(3) memmem(3) */ #include /* NAN */ #include /* EBADF ENOTSOCK EOPNOTSUPP EOVERFLOW EPIPE */ #include #include /* AF_UNIX MSG_CMSG_CLOEXEC SOCK_CLOEXEC SOCK_STREAM SOCK_SEQPACKET SOCK_DGRAM PF_UNSPEC socketpair(2) */ #include /* struct sockaddr_un */ #include /* dup(2) */ #include /* F_DUPFD_CLOEXEC fcntl(2) */ #include /* ntohs(3) */ #include /* SSL_CTX, SSL_CTX_free(), SSL_CTX_up_ref(), SSL, SSL_up_ref() */ #include /* CRYPTO_LOCK_SSL CRYPTO_add() */ #include #include #include "lib/socket.h" #include "lib/fifo.h" #include "lib/dns.h" #include "cqueues.h" /* * F E A T U R E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define HAVE_OPENSSL11_API (!(OPENSSL_VERSION_NUMBER < 0x10100001L || defined LIBRESSL_VERSION_NUMBER)) #ifndef HAVE_SSL_CTX_UP_REF #define HAVE_SSL_CTX_UP_REF HAVE_OPENSSL11_API #endif #ifndef HAVE_SSL_UP_REF #define HAVE_SSL_UP_REF HAVE_OPENSSL11_API #endif /* * C O M P A T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #if !HAVE_SSL_CTX_UP_REF #define SSL_CTX_up_ref(ctx) CRYPTO_add(&(ctx)->references, 1, CRYPTO_LOCK_SSL_CTX) #endif #if !HAVE_SSL_UP_REF #define SSL_up_ref(ssl) CRYPTO_add(&(ssl)->references, 1, CRYPTO_LOCK_SSL) #endif /* * T E X T M U N G I N G R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static inline _Bool mime_isblank(unsigned char ch) { return ch == 32 || ch == 9; } /* mime_isblank() */ static inline _Bool mime_isfname(unsigned char ch) { return ch >= 33 && ch <= 126 && ch != 58 /* : */; } /* mime_isfname() */ static int iov_chr(const struct iovec *iov, size_t p) { return (p < iov->iov_len)? ((unsigned char *)iov->iov_base)[p] : -1; } /* iov_chr() */ static int iov_lc(const struct iovec *iov) { return (iov->iov_len)? iov_chr(iov, iov->iov_len - 1) : -1; } /* iov_lc() */ static int iov_addzu(size_t *r, size_t a, size_t b) { int error; if ((error = cqs_addzu(r, a, b))) return error; return (*r == (size_t)-1)? EOVERFLOW : 0; } /* iov_addzu() */ /* * Find end of MIME header. Returns 0 < length <= .iov_len to end of header, * 0 if not found. If length is >.iov_len then needs more data. Returns -1 * on error. */ #define IOV_F_EMPTYFNAME 1 static size_t iov_eoh(const struct iovec *iov, _Bool eof, int flags, int *_error) { const char *tp, *p, *pe; size_t n; int error; tp = iov->iov_base; p = tp; pe = tp + iov->iov_len; while (p < pe && mime_isfname(*p)) p++; if (p == tp && p < pe && !(flags & IOV_F_EMPTYFNAME)) return 0; /* not allowing empty field names */ while (p < pe && mime_isblank(*p)) p++; if (p < pe && *p != ':') return 0; /* not a valid field name */ while (p < pe && (p = memchr(p, '\n', pe - p))) { if (++p < pe && !mime_isblank(*p)) return p - tp; /* found */ } if (eof) return 0; /* do not allow truncated headers */ if ((error = iov_addzu(&n, iov->iov_len, 1))) goto error; return n; /* need more */ error: *_error = error; return -1; } /* iov_eoh() */ /* * Find end of MIME boundary marker. Returns length to end of marker, or 0 * if not found. */ static size_t iov_eob(const struct iovec *iov, const char *eob, size_t eoblen) { const char *p; if (iov->iov_len < eoblen) return 0; if ((p = memmem(iov->iov_base, iov->iov_len, eob, eoblen))) return (p + eoblen) - (char *)iov->iov_base; return 0; } /* iov_eob() */ /* * Find end of text region which would fill >= minbuf and <= maxbuf after * calling iov_trimcr, and without leaving a trailing \r unless EOF. If * return value > iov.iov_len, then need more data. Returns -1 on error. */ static size_t iov_eot(const struct iovec *iov, size_t minbuf, size_t maxbuf, _Bool eof, int *_error) { const char *p, *pe; size_t n = 0, eot; int lc = -1, error; p = iov->iov_base; pe = p + iov->iov_len; for (; p < pe && n < maxbuf; ++n) { lc = *p++; if (lc == '\r' && p < pe && *p == '\n') { lc = *p++; /* skip \n so we don't ++n */ } } if ((size_t)-1 == (eot = p - (char *)iov->iov_base)) { error = EOVERFLOW; goto error; } if (n < maxbuf) { if (!eof) { if (n >= minbuf && lc != '\r') { /* * just continue as we're not splitting a * \r\n pair */ (void)0; } else if (n > minbuf && lc == '\r') { /* * just exclude it. we might end up * returning a trailing \r, but we know for * a fact it's not part of a \r\n pair. */ --eot; } else if ((error = iov_addzu(&eot, eot, maxbuf - n))) { goto error; } } } else if (lc == '\r') { if (n > minbuf) { --eot; /* see comment ~10 lines above */ } else if ((error = iov_addzu(&eot, eot, 1))) { goto error; } } return eot; error: *_error = error; return -1; } /* iov_eot() */ static size_t iov_eol(const struct iovec *iov) { const char *p, *pe; size_t eol = 0; p = iov->iov_base; pe = p + iov->iov_len; while (p < pe && (p = memchr(p, '\n', pe - p))) { eol = ++p - (char *)iov->iov_base; } return (eol)? eol : iov->iov_len; } /* iov_eol() */ /* strip \r from \r\n sequences */ static size_t iov_trimcr(struct iovec *iov, _Bool chomp) { char *p, *pe; p = iov->iov_base; pe = p + iov->iov_len; if (chomp) { if (pe - p >= 2 && pe[-1] == '\n' && pe[-2] == '\r') *(--pe - 1) = '\n'; } else { while (p < pe && (p = memchr(p, '\r', pe - p))) { if (++p >= pe) { break; } else if (*p == '\n') { memmove(p - 1, p, pe - p); --pe; } } } return iov->iov_len = pe - (char *)iov->iov_base; } /* iov_trimcr() */ /* strip \r?\n from \r?\n sequences */ static size_t iov_trimcrlf(struct iovec *iov, _Bool chomp) { char *sp, *p, *pe; sp = iov->iov_base; p = iov->iov_base; pe = p + iov->iov_len; if (chomp) { if (p < pe && pe[-1] == '\n') { --pe; if (p < pe && pe[-1] == '\r') --pe; } } else { while (p < pe && (p = memchr(p, '\n', pe - p))) { if (p > sp && p[-1] == '\r') { ++p; memmove(p - 2, p, pe - p); pe -= 2; } else { memmove(p, p + 1, pe - p - 1); --pe; } } } return iov->iov_len = pe - (char *)iov->iov_base; } /* iov_trimcrlf() */ /* * L U A S O C K E T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define lso_error_t int #define lso_nargs_t int #define LSO_CLASS "CQS Socket" #define LSO_INDEX 1 #define LSO_UPVALUES 1 #define LSO_MAXERRS 100 #define LSO_BUFSIZ 4096 #define LSO_MAXLINE 4096 #define LSO_INFSIZ ((size_t)-1) #define LSO_LINEBUF 0x01 #define LSO_FULLBUF 0x02 #define LSO_NOBUF 0x04 #define LSO_ALLBUF (LSO_LINEBUF|LSO_FULLBUF|LSO_NOBUF) #define LSO_TEXT 0x08 #define LSO_BINARY 0x10 #define LSO_AUTOFLUSH 0x20 #define LSO_PUSHBACK 0x40 #define LSO_INITMODE (LSO_LINEBUF|LSO_TEXT|LSO_AUTOFLUSH|LSO_PUSHBACK) #define LSO_RDMASK (~(LSO_ALLBUF|LSO_AUTOFLUSH)) #define LSO_WRMASK (~LSO_PUSHBACK) /* * A placeholder until we make it optional. Some Microsoft services have * buggy line buffering and will choke if, e.g., an SMTP command is * fragmented across TCP packets. */ #define LSO_DEFRAG 1 #if !defined __NetBSD__ || NETBSD_PREREQ(6,0) #define LSO_NAN (NAN) #else #define LSO_NAN (__builtin_nan("")) #endif #define LSO_DO_FLUSH 0x01 /* flush output buffer */ #define LSO_DO_STARTTLS 0x02 /* initiate starttls */ struct luasocket { int todo, done; struct { _Bool once; struct so_starttls config; } tls; struct { int mode; size_t maxline; size_t bufsiz; struct fifo fifo; _Bool eof; _Bool eom; int error; size_t numerrs; size_t maxerrs; } ibuf; struct { int mode; size_t maxline; size_t bufsiz; struct fifo fifo; _Bool eof; size_t eol; int error; size_t numerrs; size_t maxerrs; } obuf; int type; struct socket *socket; cqs_ref_t onerror; lua_State *mainthread; double timeout; }; /* struct luasocket */ static struct luasocket lso_initializer = { .ibuf = { .mode = (LSO_RDMASK & LSO_INITMODE), .maxline = LSO_MAXLINE, .bufsiz = LSO_BUFSIZ, .maxerrs = LSO_MAXERRS }, .obuf = { .mode = (LSO_WRMASK & LSO_INITMODE), .maxline = LSO_MAXLINE, .bufsiz = LSO_BUFSIZ, .maxerrs = LSO_MAXERRS }, .type = SOCK_STREAM, .onerror = LUA_NOREF, .timeout = LSO_NAN, }; static size_t lso_optsize(struct lua_State *L, int index, size_t def) { lua_Number size; if (lua_isnoneornil(L, index)) return def; size = luaL_checknumber(L, index); if (size < 0 || isinf(size)) return LSO_INFSIZ; return (size > 0)? (size_t)size : def; } /* lso_optsize() */ static size_t lso_checksize(struct lua_State *L, int index) { lua_Number size = luaL_checknumber(L, index); if (size < 0 || isinf(size)) return LSO_INFSIZ; return (size_t)size; } /* lso_checksize() */ static void lso_pushsize(struct lua_State *L, size_t size) { if (size == LSO_INFSIZ) { lua_pushnumber(L, INFINITY); } else { lua_pushinteger(L, size); } } /* lso_pushsize() */ static int lso_tofileno(lua_State *L, int index) { struct luasocket *so; luaL_Stream *fh; if (lua_isnumber(L, index)) { return lua_tointeger(L, index); } else if ((so = luaL_testudata(L, index, LSO_CLASS))) { return so_peerfd(so->socket); } else if ((fh = luaL_testudata(L, index, LUA_FILEHANDLE))) { return (fh->f)? fileno(fh->f) : -1; } else { return -1; } } /* lso_tofileno() */ static _Bool lso_getfield(lua_State *L, int index, const char *k) { lua_getfield(L, index, k); if (lua_isnil(L, -1)) { lua_pop(L, 1); return 0; } else { return 1; } } /* lso_getfield() */ static _Bool lso_rawgeti(lua_State *L, int index, int k) { lua_rawgeti(L, index, k); if (lua_isnil(L, -1)) { lua_pop(L, 1); return 0; } else { return 1; } } /* lso_rawgeti() */ static _Bool lso_altfield(lua_State *L, int index, ...) { const char *k; va_list ap; va_start(ap, index); while ((k = va_arg(ap, const char *))) { if (lso_getfield(L, index, k)) break; } va_end(ap); return !!k; } /* lso_altfield() */ #define lso_altfield(...) lso_altfield(__VA_ARGS__, (const char *)0) static _Bool lso_popbool(lua_State *L) { _Bool val; luaL_checktype(L, -1, LUA_TBOOLEAN); val = lua_toboolean(L, -1); lua_pop(L, 1); return val; } /* lso_popbool() */ static void *lso_singleton(lua_State *L, const void *key, const void *init, size_t len) { void *p; lua_rawgetp(L, LUA_REGISTRYINDEX, key); p = lua_touserdata(L, -1); lua_pop(L, 1); if (p) return p; p = lua_newuserdata(L, len); if (init) memcpy(p, init, len); else memset(p, 0, len); lua_rawsetp(L, LUA_REGISTRYINDEX, key); return p; } /* lso_singleton() */ static mode_t lso_checkperm(lua_State *L, int index) { const char *mode = luaL_checkstring(L, index); mode_t perm = 0; if (*mode >= '0' && *mode <= '9') { perm = strtol(mode, NULL, 0); } else { int i = 9, flag, ch; while ((ch = *mode++) && i > 0) { if (ch == 'r' || ch == 'R') flag = 04; else if (ch == 'w' || ch == 'W') flag = 02; else if (ch == 'x' || ch == 'X') flag = 01; else if (ch == '-') flag = 00; else continue; perm |= (flag << (3 * (--i / 3))); } } return perm; } /* lso_checkperm() */ static struct so_options lso_checkopts(lua_State *L, int index) { struct so_options opts = *so_opts(); /* TODO: Support explicit interface name via .if_name/.name */ if (lso_altfield(L, index, "bind", "sa_bind")) { static const int regindex; struct sockaddr_storage *ss = lso_singleton(L, ®index, NULL, sizeof *ss); const char *addr = NULL; int port = -1, error; if (lua_istable(L, -1)) { if (lso_altfield(L, -1, "addr", "address", "sin_addr", "sin6_addr") || lso_rawgeti(L, -1, 1)) { addr = luaL_checkstring(L, -1); lua_pop(L, 1); } if (lso_altfield(L, -1, "port", "sin_port", "sin6_port") || lso_rawgeti(L, -1, 2)) { port = luaL_checkint(L, -1); lua_pop(L, 1); } } else { addr = luaL_checkstring(L, -1); } luaL_argcheck(L, addr != NULL, index, "no bind address specified"); if (!sa_pton(ss, sizeof *ss, addr, NULL, &error)) luaL_argerror(L, index, lua_pushfstring(L, "%s: unable to parse bind address (%s)", addr, cqs_strerror(error))); if (port >= 0) *sa_port(ss, &(unsigned short){ 0 }, NULL) = htons((unsigned short)port); opts.sa_bind = ss; lua_pop(L, 1); } if (lso_altfield(L, index, "mode", "sun_mode")) { opts.sun_mode = S_IFSOCK | lso_checkperm(L, -1); lua_pop(L, 1); } if (lso_altfield(L, index, "mask", "sun_mask")) { opts.sun_mask = S_IFSOCK | lso_checkperm(L, -1); lua_pop(L, 1); } if (lso_altfield(L, index, "unlink", "sun_unlink")) opts.sun_unlink = lso_popbool(L); if (lso_altfield(L, index, "reuseaddr", "sin_reuseaddr")) opts.sin_reuseaddr = lso_popbool(L); if (lso_altfield(L, index, "reuseport", "sin_reuseport")) opts.sin_reuseport = lso_popbool(L); if (lso_altfield(L, index, "broadcast", "sin_broadcast")) opts.sin_broadcast = lso_popbool(L); if (lso_altfield(L, index, "nodelay", "sin_nodelay")) opts.sin_nodelay = lso_popbool(L); if (lso_altfield(L, index, "nopush", "sin_nopush")) opts.sin_nopush = lso_popbool(L); if (lso_altfield(L, index, "v6only", "sin_v6only")) opts.sin_v6only = (lso_popbool(L))? SO_V6ONLY_ENABLE : SO_V6ONLY_DISABLE; if (lso_altfield(L, index, "oobinline", "sin_oobinline")) opts.sin_oobinline = lso_popbool(L); if (lso_altfield(L, index, "nonblock", "fd_nonblock")) opts.fd_nonblock = lso_popbool(L); if (lso_altfield(L, index, "cloexec", "fd_cloexec")) opts.fd_cloexec = lso_popbool(L); if (lso_altfield(L, index, "nosigpipe", "fd_nosigpipe")) opts.fd_nosigpipe = lso_popbool(L); if (lso_altfield(L, index, "verify", "tls_verify")) opts.tls_verify = lso_popbool(L); if (lso_altfield(L, index, "sendname", "tls_sendname")) { if (lua_isboolean(L, -1)) { opts.tls_sendname = (lua_toboolean(L, -1))? SO_OPTS_TLS_HOSTNAME : NULL; } else { opts.tls_sendname = luaL_checkstring(L, -1); } lua_pop(L, 1); } if (lso_altfield(L, index, "time", "st_time")) opts.st_time = lso_popbool(L); return opts; } /* lso_checkopts() */ static int lso_closefd(int *fd, void *arg) { struct luasocket *S = arg; if (S->mainthread) { cqs_cancelfd(S->mainthread, *fd); cqs_closefd(fd); } return 0; } /* lso_closefd() */ static struct luasocket *lso_testself(lua_State *L, int index) { return cqs_testudata(L, index, LSO_INDEX); } /* lso_testself() */ static struct luasocket *lso_checkvalid(lua_State *L, int index, struct luasocket *S) { luaL_argcheck(L, !!S->socket, index, "socket closed"); return S; } /* lso_checkvalid() */ static struct luasocket *lso_checkself(lua_State *L, int index) { return lso_checkvalid(L, index, cqs_checkudata(L, index, LSO_INDEX, LSO_CLASS)); } /* lso_checkself() */ static int lso_imode(const char *str, int init) { int mode = init, ch; while ((ch = *str++)) { switch (ch) { case 'n': mode = LSO_NOBUF | (mode & ~LSO_ALLBUF); break; case 'l': mode = LSO_LINEBUF | (mode & ~LSO_ALLBUF); break; case 'f': mode = LSO_FULLBUF | (mode & ~LSO_ALLBUF); break; case 't': mode = LSO_TEXT | (mode & ~LSO_BINARY); break; case 'b': mode = LSO_BINARY | (mode & ~LSO_TEXT); break; case 'a': mode |= LSO_AUTOFLUSH; break; case 'A': mode &= ~LSO_AUTOFLUSH; break; case 'p': mode |= LSO_PUSHBACK; break; case 'P': mode &= ~LSO_PUSHBACK; break; } /* switch() */ } /* while() */ return mode; } /* lso_imode() */ static void lso_pushmode(lua_State *L, int mode, int mask, _Bool libc) { if (libc) { if (mode & LSO_NOBUF) lua_pushstring(L, "no"); else if (mode & LSO_LINEBUF) lua_pushstring(L, "line"); else if (mode & LSO_FULLBUF) lua_pushstring(L, "full"); else lua_pushnil(L); /* XXX: shouldn't happen */ } else { char flag[8], *p = flag; if (mode & LSO_TEXT) *p++ = 't'; else if (mode & LSO_BINARY) *p++ = 'b'; else *p++ = '-'; if (mode & LSO_NOBUF) *p++ = 'n'; else if (mode & LSO_LINEBUF) *p++ = 'l'; else if (mode & LSO_FULLBUF) *p++ = 'f'; else *p++ = '-'; if (mask & LSO_AUTOFLUSH) *p++ = (mode & LSO_AUTOFLUSH)? 'a' : 'A'; if (mask & LSO_PUSHBACK) *p++ = (mode & LSO_PUSHBACK)? 'p' : 'P'; lua_pushlstring(L, flag, p - flag); } } /* lso_pushmode() */ //static lso_nargs_t lso_throw(lua_State *L, struct luasocket *S, int error) { // return luaL_error(L, "socket: %s", cqs_strerror(error)); //} /* lso_throw() */ static struct luasocket *lso_prototype(lua_State *L) { static const int regindex; return lso_singleton(L, ®index, &lso_initializer, sizeof lso_initializer); } /* lso_prototype() */ static struct luasocket *lso_newsocket(lua_State *L, int type) { struct luasocket *S; S = lua_newuserdata(L, sizeof *S); *S = *lso_prototype(L); S->type = type; fifo_init(&S->ibuf.fifo); fifo_init(&S->obuf.fifo); if (S->onerror != LUA_NOREF && S->onerror != LUA_REFNIL) { cqs_getref(L, S->onerror); S->onerror = LUA_NOREF; cqs_ref(L, &S->onerror); } #if defined LUA_RIDX_MAINTHREAD lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); S->mainthread = lua_tothread(L, -1); lua_pop(L, 1); #endif luaL_getmetatable(L, LSO_CLASS); lua_setmetatable(L, -2); return S; } /* lso_newsocket() */ static lso_error_t lso_adjbuf(struct fifo *buf, size_t size) { if (size == LSO_INFSIZ) return 0; return fifo_realloc(buf, size); } /* lso_adjbuf() */ static lso_error_t lso_adjbufs(struct luasocket *S) { int error; if ((error = lso_adjbuf(&S->ibuf.fifo, S->ibuf.bufsiz))) return error; if ((error = lso_adjbuf(&S->obuf.fifo, S->obuf.bufsiz))) return error; return 0; } /* lso_adjbufs() */ static lso_error_t lso_prepsocket(struct luasocket *S) { return lso_adjbufs(S); } /* lso_prepsocket() */ static lso_error_t lso_doflush(struct luasocket *, int); static lso_error_t lso_checktodo(struct luasocket *S) { int todo, error; while ((todo = (S->todo & ~S->done))) { if (todo & LSO_DO_FLUSH) { so_clear(S->socket); if ((error = lso_doflush(S, LSO_NOBUF))) return error; S->done |= LSO_DO_FLUSH; } else if (todo & LSO_DO_STARTTLS) { so_clear(S->socket); if (!S->tls.once) { S->tls.once = 1; if (S->ibuf.mode & LSO_PUSHBACK) fifo_rvec(&S->ibuf.fifo, &S->tls.config.pushback, 1); error = so_starttls(S->socket, &S->tls.config); if (S->ibuf.mode & LSO_PUSHBACK) { fifo_purge(&S->ibuf.fifo); S->ibuf.eom = 0; } } else { error = so_starttls(S->socket, NULL); } if (S->tls.config.instance) { SSL_free(S->tls.config.instance); S->tls.config.instance = NULL; } if (S->tls.config.context) { SSL_CTX_free(S->tls.config.context); S->tls.config.context = NULL; } if (error) return error; S->done |= LSO_DO_STARTTLS; } } return 0; } /* lso_checktodo() */ static lso_nargs_t lso_connect2(lua_State *L) { const char *host NOTUSED = NULL, *port NOTUSED = NULL; const char *path = NULL; struct so_options opts; struct luasocket *S; size_t plen; int family, type, error; if (lua_istable(L, 1)) { opts = lso_checkopts(L, 1); lua_getfield(L, 1, "family"); family = luaL_optinteger(L, -1, AF_UNSPEC); lua_pop(L, 1); lua_getfield(L, 1, "type"); type = luaL_optinteger(L, -1, SOCK_STREAM); lua_pop(L, 1); if (lso_getfield(L, 1, "path")) { path = luaL_checklstring(L, -1, &plen); family = AF_UNIX; } else { lua_getfield(L, 1, "host"); host = luaL_checkstring(L, -1); lua_getfield(L, 1, "port"); port = luaL_checkstring(L, -1); } } else { opts = *so_opts(); host = luaL_checkstring(L, 1); port = luaL_checkstring(L, 2); family = luaL_optinteger(L, 3, AF_UNSPEC); type = luaL_optinteger(L, 4, SOCK_STREAM); } S = lso_newsocket(L, type); opts.fd_close.arg = S; opts.fd_close.cb = &lso_closefd; if (path) { struct sockaddr_un sun; memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; memcpy(sun.sun_path, path, MIN(plen, sizeof sun.sun_path)); if (!(S->socket = so_dial((struct sockaddr *)&sun, type, &opts, &error))) goto error; } else { if (!(S->socket = so_open(host, port, 0, family, type, &opts, &error))) goto error; } if ((error = lso_prepsocket(S))) goto error; (void)so_connect(S->socket); return 1; error: lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* lso_connect2() */ static lso_nargs_t lso_connect1(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); int error; so_clear(S->socket); if (!(error = so_connect(S->socket))) { lua_pushvalue(L, 1); return 1; } else { lua_pushnil(L); lua_pushinteger(L, error); return 2; } } /* lso_connect1() */ static lso_nargs_t lso_listen2(lua_State *L) { const char *host NOTUSED = NULL, *port NOTUSED = NULL; const char *path = NULL; struct so_options opts; struct luasocket *S; size_t plen; int family, type, error; if (lua_istable(L, 1)) { opts = lso_checkopts(L, 1); lua_getfield(L, 1, "family"); family = luaL_optinteger(L, -1, AF_UNSPEC); lua_pop(L, 1); lua_getfield(L, 1, "type"); type = luaL_optinteger(L, -1, SOCK_STREAM); lua_pop(L, 1); if (lso_getfield(L, 1, "path")) { path = luaL_checklstring(L, -1, &plen); family = AF_UNIX; } else { lua_getfield(L, 1, "host"); host = luaL_checkstring(L, -1); lua_getfield(L, 1, "port"); port = luaL_checkstring(L, -1); } } else { opts = *so_opts(); host = luaL_checkstring(L, 1); port = luaL_checkstring(L, 2); family = luaL_optinteger(L, 3, AF_UNSPEC); type = luaL_optinteger(L, 4, SOCK_STREAM); } S = lso_newsocket(L, type); opts.fd_close.arg = S; opts.fd_close.cb = &lso_closefd; if (path) { struct sockaddr_un sun; memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; memcpy(sun.sun_path, path, MIN(plen, sizeof sun.sun_path)); if (!(S->socket = so_dial((struct sockaddr *)&sun, type, &opts, &error))) goto error; } else { if (!(S->socket = so_open(host, port, 0, family, type, &opts, &error))) goto error; } if ((error = lso_prepsocket(S))) goto error; (void)so_listen(S->socket); return 1; error: lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* lso_listen2() */ static lso_nargs_t lso_listen1(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); int error; so_clear(S->socket); if (!(error = so_listen(S->socket))) { lua_pushvalue(L, 1); return 1; } else { lua_pushnil(L); lua_pushinteger(L, error); return 2; } } /* lso_listen1() */ /* luasec compat */ #define LSEC_MODE_INVALID 0 #define LSEC_MODE_SERVER 1 #define LSEC_MODE_CLIENT 2 typedef struct { SSL_CTX *context; lua_State *L; DH *dh_param; int mode; } lsec_context; static lso_nargs_t lso_starttls(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); SSL_CTX **ctx = NULL; SSL **ssl = NULL; int error; /* * NB: short-circuit if we've already started so we don't * unnecessarily check for or take a reference to the SSL_CTX * object. */ if ((S->todo & LSO_DO_STARTTLS)) goto check; if ((ssl = luaL_testudata(L, 2, "SSL*"))) { /* accept-mode check handled by so_starttls() */ } else if ((ctx = luaL_testudata(L, 2, "SSL_CTX*"))) { /* accept-mode check handled by so_starttls() */ } else if ((ctx = luaL_testudata(L, 2, "SSL:Context"))) { /* luasec compatability */ luaL_argcheck(L, ((lsec_context*)ctx)->mode != LSEC_MODE_INVALID, 2, "invalid mode"); so_setbool(&S->tls.config.accept, ((((lsec_context*)ctx)->mode) == LSEC_MODE_SERVER)); } if (ssl && *ssl && *ssl != S->tls.config.instance) { if (S->tls.config.instance) SSL_free(S->tls.config.instance); SSL_up_ref(*ssl); S->tls.config.instance = *ssl; } if (ctx && *ctx && *ctx != S->tls.config.context) { if (S->tls.config.context) SSL_CTX_free(S->tls.config.context); SSL_CTX_up_ref(*ctx); S->tls.config.context = *ctx; } S->todo |= LSO_DO_STARTTLS; if (S->obuf.mode & LSO_AUTOFLUSH) S->todo |= LSO_DO_FLUSH; check: if ((error = lso_checktodo(S))) goto error; lua_pushvalue(L, 1); return 1; error: lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* lso_starttls() */ static lso_nargs_t lso_checktls(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); SSL **ssl; ssl = lua_newuserdata(L, sizeof *ssl); if (!(*ssl = so_checktls(S->socket))) return 0; luaL_getmetatable(L, "SSL*"); if (lua_isnil(L, -1)) return 0; lua_setmetatable(L, -2); SSL_up_ref(*ssl); return 1; } /* lso_checktls() */ lso_error_t cqs_socket_fdopen(lua_State *L, int fd, const struct so_options *_opts) { struct so_options opts = *((_opts)? _opts : so_opts()); struct luasocket *S; int type = SOCK_STREAM, error; if (0 != getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &(socklen_t){ sizeof type })) { switch (errno) { case ENOTSOCK: case EOPNOTSUPP: break; default: goto syerr; } } S = lso_newsocket(L, type); if ((error = lso_prepsocket(S))) goto error; opts.fd_close.arg = S; opts.fd_close.cb = &lso_closefd; if (!(S->socket = so_fdopen(fd, &opts, &error))) goto error; return 0; syerr: error = errno; error: lua_pop(L, 1); return error; } /* cqs_socket_fdopen() */ static lso_nargs_t lso_dup(lua_State *L) { struct so_options opts; int ofd, fd = -1, error; if (lua_istable(L, 1)) { opts = lso_checkopts(L, 1); if (!lso_altfield(L, 1, "fd", "file", "socket")) lua_rawgeti(L, 1, 1); ofd = lso_tofileno(L, -1); lua_pop(L, 1); } else { opts = *so_opts(); ofd = lso_tofileno(L, 1); } if (ofd < 0) goto badfd; #if defined F_DUPFD_CLOEXEC if (-1 == (fd = fcntl(ofd, F_DUPFD_CLOEXEC, 0))) goto syerr; #else if (-1 == (fd = dup(ofd))) goto syerr; #endif if ((error = cqs_socket_fdopen(L, fd, &opts))) goto error; return 1; badfd: error = EBADF; goto error; syerr: error = errno; error: cqs_closefd(&fd); lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* lso_dup() */ /* * NOTE: Only permit integer descriptors to mitigate the risk that we wrap a * descriptor still owned by a GC-able object. Cf. socket.dup. */ static lso_nargs_t lso_fdopen(lua_State *L) { struct so_options opts; int fd, error; if (lua_istable(L, 1)) { opts = lso_checkopts(L, 1); if (lso_altfield(L, 1, "fd")) { fd = luaL_checkint(L, -1); } else { lua_rawgeti(L, 1, 1); fd = luaL_checkint(L, -1); } lua_pop(L, 1); } else { opts = *so_opts(); fd = luaL_checkint(L, 1); } if (fd < 0) goto badfd; if ((error = cqs_socket_fdopen(L, fd, &opts))) goto error; return 1; badfd: error = EBADF; goto error; error: lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* lso_fdopen() */ static lso_nargs_t lso_pair(lua_State *L) { struct luasocket *a = NULL, *b = NULL; struct so_options opts; int fd[2] = { -1, -1 }; int type, error; if (lua_istable(L, 1)) { opts = lso_checkopts(L, 1); lua_getfield(L, 1, "type"); type = luaL_optinteger(L, -1, SOCK_STREAM); lua_pop(L, 1); } else { opts = *so_opts(); type = luaL_optinteger(L, 1, SOCK_STREAM); } a = lso_newsocket(L, type); b = lso_newsocket(L, type); #if defined SOCK_CLOEXEC if (0 != socketpair(AF_UNIX, type|SOCK_CLOEXEC, PF_UNSPEC, fd)) goto syerr; #else if (0 != socketpair(AF_UNIX, type, PF_UNSPEC, fd)) goto syerr; #endif opts.fd_close.arg = a; opts.fd_close.cb = &lso_closefd; if (!(a->socket = so_fdopen(fd[0], &opts, &error))) goto error; fd[0] = -1; if ((error = lso_prepsocket(a))) goto error; opts.fd_close.arg = b; opts.fd_close.cb = &lso_closefd; if (!(b->socket = so_fdopen(fd[1], &opts, &error))) goto error; fd[1] = -1; if ((error = lso_prepsocket(b))) goto error; return 2; syerr: error = errno; error: cqs_closefd(&fd[0]); cqs_closefd(&fd[1]); lua_pushnil(L); lua_pushnil(L); lua_pushinteger(L, error); return 3; } /* lso_pair() */ static int lso_checkvbuf(struct lua_State *L, int index) { switch (luaL_checkoption(L, index, "line", (const char *[]){ "line", "full", "nobuf", "no", NULL })) { case 0: /* "line" */ return LSO_LINEBUF; case 1: /* "full" */ return LSO_FULLBUF; case 2: /* "nobuf" */ /* FALL THROUGH */ case 3: /* "no" */ /* FALL THROUGH */ default: return LSO_NOBUF; } } /* lso_checkvbuf() */ static lso_nargs_t lso_setvbuf_(struct lua_State *L, struct luasocket *S, int modeidx, int bufidx) { lso_pushmode(L, S->obuf.mode, LSO_WRMASK, 1); lua_pushinteger(L, S->obuf.bufsiz); S->obuf.mode = lso_checkvbuf(L, modeidx) | (S->obuf.mode & ~LSO_ALLBUF); if (S->obuf.mode & (LSO_LINEBUF|LSO_FULLBUF)) S->obuf.bufsiz = lso_optsize(L, bufidx, LSO_BUFSIZ); return 2; } /* lso_setvbuf_() */ static lso_nargs_t lso_setvbuf2(struct lua_State *L) { lua_settop(L, 2); return lso_setvbuf_(L, lso_prototype(L), 1, 2); } /* lso_setvbuf2() */ static lso_nargs_t lso_setvbuf3(struct lua_State *L) { lua_settop(L, 3); return lso_setvbuf_(L, lso_checkself(L, 1), 2, 3); } /* lso_setvbuf3() */ static lso_nargs_t lso_setmode_(struct lua_State *L, struct luasocket *S, int ridx, int widx) { lso_pushmode(L, S->ibuf.mode, LSO_RDMASK, 0); lso_pushmode(L, S->obuf.mode, LSO_WRMASK, 0); if (!lua_isnil(L, ridx)) S->ibuf.mode = LSO_RDMASK & lso_imode(luaL_checkstring(L, ridx), LSO_INITMODE); if (!lua_isnil(L, widx)) S->obuf.mode = LSO_WRMASK & lso_imode(luaL_checkstring(L, widx), LSO_INITMODE); return 2; } /* lso_setmode_() */ static lso_nargs_t lso_setmode2(struct lua_State *L) { lua_settop(L, 2); return lso_setmode_(L, lso_prototype(L), 1, 2); } /* lso_setmode2() */ static lso_nargs_t lso_setmode3(struct lua_State *L) { lua_settop(L, 3); return lso_setmode_(L, lso_checkself(L, 1), 2, 3); } /* lso_setmode3() */ static lso_nargs_t lso_setbufsiz_(struct lua_State *L, struct luasocket *S, int ridx, int widx) { lso_pushsize(L, S->ibuf.bufsiz); lso_pushsize(L, S->obuf.bufsiz); S->ibuf.bufsiz = lso_optsize(L, ridx, S->ibuf.bufsiz); S->obuf.bufsiz = lso_optsize(L, widx, S->obuf.bufsiz); return 2; } /* lso_setbufsiz_() */ static lso_nargs_t lso_setbufsiz2(struct lua_State *L) { lua_settop(L, 2); return lso_setbufsiz_(L, lso_prototype(L), 1, 2); } /* lso_setbufsiz2() */ static lso_nargs_t lso_setbufsiz3(struct lua_State *L) { struct luasocket *S = lso_checkself(L, 1); int n, error; lua_settop(L, 3); n = lso_setbufsiz_(L, S, 2, 3); if ((error = lso_adjbufs(S))) goto error; return n; error: lua_pushnil(L); lua_pushnil(L); lua_pushinteger(L, error); return 3; } /* lso_setbufsiz3() */ static lso_nargs_t lso_setmaxline_(struct lua_State *L, struct luasocket *S, int ridx, int widx) { lso_pushsize(L, S->ibuf.maxline); lso_pushsize(L, S->obuf.maxline); S->ibuf.maxline = lso_optsize(L, ridx, S->ibuf.maxline); S->obuf.maxline = lso_optsize(L, widx, S->obuf.maxline); return 2; } /* lso_setmaxline_() */ static lso_nargs_t lso_setmaxline2(struct lua_State *L) { lua_settop(L, 2); return lso_setmaxline_(L, lso_prototype(L), 1, 2); } /* lso_setmaxline2() */ static lso_nargs_t lso_setmaxline3(struct lua_State *L) { lua_settop(L, 3); return lso_setmaxline_(L, lso_checkself(L, 1), 2, 3); } /* lso_setmaxline3() */ static lso_nargs_t lso_settimeout_(struct lua_State *L, struct luasocket *S, int index) { double timeout; if (isnormal(S->timeout) || S->timeout == 0) { lua_pushnumber(L, S->timeout); } else { lua_pushnil(L); } timeout = luaL_optnumber(L, index, NAN); S->timeout = (isnormal(timeout) || timeout == 0)? timeout : NAN; return 1; } /* lso_settimeout_() */ static lso_nargs_t lso_settimeout1(struct lua_State *L) { lua_settop(L, 1); return lso_settimeout_(L, lso_prototype(L), 1); } /* lso_settimeout1() */ static lso_nargs_t lso_settimeout2(struct lua_State *L) { lua_settop(L, 2); return lso_settimeout_(L, lso_checkself(L, 1), 2); } /* lso_settimeout2() */ static lso_nargs_t lso_setmaxerrs_(struct lua_State *L, struct luasocket *S, int index) { const char *what = "rw"; int nret = 0; if (lua_type(L, index) == LUA_TSTRING) { what = luaL_checkstring(L, index); index++; } for (; *what; what++) { switch (*what) { case 'r': lua_pushinteger(L, S->ibuf.maxerrs); nret++; S->ibuf.maxerrs = luaL_optunsigned(L, index, S->ibuf.maxerrs); break; case 'w': lua_pushinteger(L, S->obuf.maxerrs); nret++; S->obuf.maxerrs = luaL_optunsigned(L, index, S->obuf.maxerrs); break; default: return luaL_argerror(L, 1, lua_pushfstring(L, "%s: %c: only `r' or `w' accepted", what, *what)); } } return nret; } /* lso_setmaxerrs_() */ static lso_nargs_t lso_setmaxerrs1(struct lua_State *L) { return lso_setmaxerrs_(L, lso_prototype(L), 1); } /* lso_setmaxerrs1() */ static lso_nargs_t lso_setmaxerrs2(struct lua_State *L) { return lso_setmaxerrs_(L, lso_checkself(L, 1), 2); } /* lso_setmaxerrs2() */ static lso_nargs_t lso_onerror_(struct lua_State *L, struct luasocket *S, int fidx) { cqs_getref(L, S->onerror); if (lua_gettop(L) > fidx) { if (!lua_isnil(L, fidx)) luaL_checktype(L, fidx, LUA_TFUNCTION); lua_pushvalue(L, fidx); cqs_ref(L, &S->onerror); } return 1; } /* lso_onerror_() */ static lso_nargs_t lso_onerror1(struct lua_State *L) { return lso_onerror_(L, lso_prototype(L), 1); } /* lso_onerror1() */ static lso_nargs_t lso_onerror2(struct lua_State *L) { return lso_onerror_(L, lso_checkself(L, 1), 2); } /* lso_onerror2() */ static void lso_pusherror(struct lua_State *L, int error) { if (error) lua_pushinteger(L, error); else lua_pushnil(L); } /* lso_pusherror() */ static lso_nargs_t lso_seterror_(struct lua_State *L, struct luasocket *S, const char *what, int error) { int nret = 0; for (; *what; what++) { switch (*what) { case 'r': lso_pusherror(L, S->ibuf.error); nret++; if (!(S->ibuf.error = error)) S->ibuf.numerrs = 0; break; case 'w': lso_pusherror(L, S->obuf.error); nret++; if (!(S->obuf.error = error)) S->obuf.numerrs = 0; break; default: return luaL_argerror(L, 2, lua_pushfstring(L, "%s: %c: only `r' or `w' accepted", what, *what)); } /* switch() */ } /* for() */ return nret; } /* lso_seterror_() */ static lso_nargs_t lso_seterror(struct lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const char *what = luaL_checkstring(L, 2); int error = luaL_optint(L, 3, 0); return lso_seterror_(L, S, what, error); } /* lso_seterror() */ static lso_nargs_t lso_error(struct lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const char *what = luaL_optstring(L, 2, "rw"); int nret = 0; for (; *what; what++) { switch (*what) { case 'r': lso_pusherror(L, S->ibuf.error); nret++; break; case 'w': lso_pusherror(L, S->obuf.error); nret++; break; default: return luaL_argerror(L, 2, lua_pushfstring(L, "%s: %c: only `r' or `w' accepted", what, *what)); } /* switch() */ } /* for() */ return nret; } /* lso_error() */ static lso_nargs_t lso_clearerr(struct lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const char *what = luaL_optstring(L, 2, "rw"); return lso_seterror_(L, S, what, 0); } /* lso_clearerr() */ static lso_error_t lso_fill(struct luasocket *S, size_t limit) { struct iovec iov; size_t prepbuf, count; int error; if (S->ibuf.eom && fifo_rlen(&S->ibuf.fifo) > 0) return 0; prepbuf = (S->type == SOCK_DGRAM)? (SO_MIN(limit, 65536)) : 1; while (fifo_rlen(&S->ibuf.fifo) < limit) { if ((error = fifo_wbuf(&S->ibuf.fifo, &iov, prepbuf))) return error; if ((count = so_read(S->socket, iov.iov_base, iov.iov_len, &error))) { fifo_update(&S->ibuf.fifo, count); if (S->type == SOCK_DGRAM || S->type == SOCK_SEQPACKET) { S->ibuf.eom = 1; return 0; } } else { switch (error) { case EPIPE: S->ibuf.eof = 1; default: return error; } /* switch() */ } } return 0; } /* lso_fill() */ static _Bool lso_nomore(struct luasocket *S, size_t limit) { return S->ibuf.eof || S->ibuf.eom || fifo_rlen(&S->ibuf.fifo) >= limit; } /* lso_nomore() */ static lso_error_t lso_asserterror(int error) { return (error)? error : EFAULT; } /* lso_asserterror() */ static lso_error_t lso_getline(struct luasocket *S, struct iovec *iov) { int error; while (!fifo_lvec(&S->ibuf.fifo, iov)) { error = lso_fill(S, S->ibuf.maxline); if (fifo_lvec(&S->ibuf.fifo, iov)) break; if (fifo_rlen(&S->ibuf.fifo) > 0 && lso_nomore(S, S->ibuf.maxline)) { fifo_slice(&S->ibuf.fifo, iov, 0, S->ibuf.maxline); break; } return lso_asserterror(error); } iov->iov_len = MIN(iov->iov_len, S->ibuf.maxline); return 0; } /* lso_getline() */ static lso_error_t lso_getheader(struct luasocket *S, struct iovec *iov) { size_t eoh; int error; fifo_slice(&S->ibuf.fifo, iov, 0, S->ibuf.maxline); if ((size_t)-1 == (eoh = iov_eoh(iov, lso_nomore(S, S->ibuf.maxline), 0, &error))) goto error; if (!eoh || eoh > iov->iov_len) { error = lso_fill(S, S->ibuf.maxline); fifo_slice(&S->ibuf.fifo, iov, 0, S->ibuf.maxline); if ((size_t)-1 == (eoh = iov_eoh(iov, lso_nomore(S, S->ibuf.maxline), 0, &error))) goto error; else if (!eoh) goto nomore; else if (eoh > iov->iov_len) goto error; /* lso_fill should have returned error */ } iov->iov_len = eoh; return 0; nomore: iov->iov_len = 0; return 0; error: return lso_asserterror(error); } /* lso_getheader() */ static lso_error_t lso_getbody(struct luasocket *S, struct iovec *iov, int *eom, const char *eob, size_t eoblen, int mode) { size_t bufsiz, maxbuf, n; int error; bufsiz = (mode & LSO_TEXT)? MAX(S->ibuf.bufsiz, S->ibuf.maxline) : S->ibuf.bufsiz; bufsiz = MAX(bufsiz, 2); /* see comment in text-mode handling below wrt >=2 */ /* * Adjust window. We need at least 1 + "\r\n" + eoblen to make * forward progress. But we actually want to return bufsiz-sized * intermediate chunks. So we want a temporary buffer of bufsiz + * "\r\n" + eoblen. */ if ((error = cqs_addzu(&maxbuf, bufsiz, 2))) return error; if ((error = cqs_addzu(&maxbuf, maxbuf, eoblen))) return error; error = lso_fill(S, maxbuf); fifo_slice(&S->ibuf.fifo, iov, 0, maxbuf); if ((n = iov_eob(iov, eob, eoblen))) { iov->iov_len = n - eoblen; /* n >= eoblen */ *eom = 1; } else if (iov->iov_len >= maxbuf) { /* * Because maxbuf is >= bufsiz + 2 + eoblen we can be sure * that returning bufsiz bytes won't cause problems trimming * the \r\n preceding the boundary marker. It's inelegant * but very simple. In the case of a stall or broken * connection we may be leaving more bytes in the buffer * than strictly necessary, but in those cases something is * broken, anyhow. */ iov->iov_len = bufsiz; if (mode & LSO_TEXT) { iov->iov_len = iov_eol(iov); /* trim if might be part of \r\n sequence */ if (iov_lc(iov) == '\r') --iov->iov_len; /* * NOTE: we guaranteed above that bufsiz >= 2 so we * don't accidentally return an empty string if we * trimmed a \r here. */ } } return 0; } /* lso_getbody() */ static lso_error_t lso_getblock(struct luasocket *S, struct iovec *iov, size_t minbuf, size_t maxbuf, int mode) { int error; if (mode & LSO_TEXT) { size_t fillsz = maxbuf, n; do { error = lso_fill(S, fillsz); fifo_slice(&S->ibuf.fifo, iov, 0, -1); if ((size_t)-1 == (n = iov_eot(iov, minbuf, maxbuf, (S->ibuf.eof || S->ibuf.eom), &error))) { goto error; } else if (n > iov->iov_len) { if (fillsz < n) error = 0; fillsz = n; } else { iov->iov_len = n; return 0; } } while (!error); } else { error = lso_fill(S, maxbuf); if (fifo_slice(&S->ibuf.fifo, iov, 0, maxbuf) >= minbuf) return 0; if ((S->ibuf.eof || S->ibuf.eom) && iov->iov_len > 0) return 0; } error: return lso_asserterror(error); } /* lso_getblock() */ struct lso_rcvop { int index; enum { LSO_NONE, LSO_NUMBER, LSO_SLURP, LSO_CHOMP, LSO_LINE, LSO_FIELD, LSO_HEADER, LSO_BODY, LSO_BLOCK, LSO_LIMIT, } type; int mode; EXTENSION union { size_t size; struct { const char *eob; size_t eoblen; } body; }; }; /* struct lso_rcvop */ static struct lso_rcvop lso_checkrcvop(lua_State *L, int index, int mode) { struct lso_rcvop op = { index, LSO_NONE, mode }; lua_Number size; if (!lua_isnumber(L, index)) { size_t len; const char *fmt = luaL_optlstring(L, index, "*l", &len); if (fmt[0] == '*' && len == 2) { switch (fmt[1]) { case 'n': op.type = LSO_NUMBER; break; case 'a': op.type = LSO_SLURP; break; case 'l': op.type = LSO_CHOMP; break; case 'L': op.type = LSO_LINE; break; case 'h': op.type = LSO_FIELD; break; case 'H': op.type = LSO_HEADER; break; } } else if (fmt[0] == '-' && fmt[1] == '-') { op.type = LSO_BODY; op.body.eob = fmt; op.body.eoblen = len; } } else { if ((size = luaL_checknumber(L, index)) < 0) { op.type = LSO_LIMIT; op.size = -size; } else { op.type = LSO_BLOCK; op.size = size; } } if (op.type == LSO_NONE) luaL_argerror(L, index, lua_pushfstring(L, "invalid format %s", luaL_checkstring(L, index))); return op; } /* lso_checkrcvop() */ #define LSO_CHECKERRS(L, iobuf) do { \ if (!(iobuf).error) \ return 0; \ if (++(iobuf).numerrs > (iobuf).maxerrs) \ luaL_error((L), "exceeded unchecked error limit (%s)", cqs_strerror((iobuf).error)); \ return (iobuf).error; \ } while (0) static lso_error_t lso_checkrcverrs(lua_State *L, struct luasocket *S) { LSO_CHECKERRS(L, S->ibuf); } /* lso_checkrcverrs() */ static lso_error_t lso_checksnderrs(lua_State *L, struct luasocket *S) { LSO_CHECKERRS(L, S->obuf); } /* lso_checksnderrs() */ static lso_error_t lso_preprcv(lua_State *L, struct luasocket *S) { int error; if ((error = lso_checkrcverrs(L, S))) return error; if ((error = lso_checktodo(S))) return error; so_clear(S->socket); if (S->obuf.mode & LSO_AUTOFLUSH) { switch ((error = lso_doflush(S, LSO_NOBUF))) { case EAGAIN: break; case EPIPE: break; default: return error; } } return 0; } /* lso_preprcv() */ static lso_error_t lso_prepsnd(lua_State *L, struct luasocket *S) { int error; if ((error = lso_checksnderrs(L, S))) return error; return lso_checktodo(S); } /* lso_prepsnd() */ static lso_nargs_t lso_recv3(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); struct lso_rcvop op; struct iovec iov; size_t count; int error; if ((error = lso_preprcv(L, S))) goto error; lua_settop(L, 3); op = lso_checkrcvop(L, 2, lso_imode(luaL_optstring(L, 3, ""), S->ibuf.mode)); switch (op.type) { case LSO_NUMBER: return luaL_argerror(L, op.index, "*n not implemented yet"); case LSO_SLURP: error = lso_fill(S, (size_t)-1); if (!(S->ibuf.eom || S->ibuf.eof)) goto error; fifo_rvec(&S->ibuf.fifo, &iov, 1); if ((count = iov.iov_len)) { if (op.mode & LSO_TEXT) iov_trimcr(&iov, 0); lua_pushlstring(L, iov.iov_base, iov.iov_len); fifo_discard(&S->ibuf.fifo, count); } else { lua_pushnil(L); } break; case LSO_CHOMP: if ((error = lso_getline(S, &iov))) goto error; count = iov.iov_len; if (op.mode & LSO_TEXT) iov_trimcr(&iov, 1); if (iov_lc(&iov) == '\n') --iov.iov_len; lua_pushlstring(L, iov.iov_base, iov.iov_len); fifo_discard(&S->ibuf.fifo, count); break; case LSO_LINE: if ((error = lso_getline(S, &iov))) goto error; count = iov.iov_len; if (op.mode & LSO_TEXT) iov_trimcr(&iov, 1); lua_pushlstring(L, iov.iov_base, iov.iov_len); fifo_discard(&S->ibuf.fifo, count); break; case LSO_FIELD: if ((error = lso_getheader(S, &iov))) goto error; if ((count = iov.iov_len)) { iov_trimcrlf(&iov, 0); lua_pushlstring(L, iov.iov_base, iov.iov_len); fifo_discard(&S->ibuf.fifo, count); } else { lua_pushnil(L); } break; case LSO_HEADER: if ((error = lso_getheader(S, &iov))) goto error; if ((count = iov.iov_len)) { if (op.mode & LSO_TEXT) iov_trimcr(&iov, 0); lua_pushlstring(L, iov.iov_base, iov.iov_len); fifo_discard(&S->ibuf.fifo, count); } else { lua_pushnil(L); } break; case LSO_BODY: { int eom = 0; /* it would be confusing to overload ibuf.eom */ if ((error = lso_getbody(S, &iov, &eom, op.body.eob, op.body.eoblen, op.mode))) goto error; if ((count = iov.iov_len)) { if (eom) { /* trim any \r\n preceding the boundary */ iov_trimcrlf(&iov, 1); if (op.mode & LSO_TEXT) iov_trimcr(&iov, 0); if (iov.iov_len) lua_pushlstring(L, iov.iov_base, iov.iov_len); else lua_pushnil(L); } else { if (op.mode & LSO_TEXT) iov_trimcr(&iov, 0); lua_pushlstring(L, iov.iov_base, iov.iov_len); } fifo_discard(&S->ibuf.fifo, count); } else { lua_pushnil(L); } break; } case LSO_BLOCK: if (op.size == 0) { lua_pushlstring(L, "", 0); break; } if ((error = lso_getblock(S, &iov, op.size, op.size, op.mode))) goto error; if ((count = iov.iov_len)) { if (op.mode & LSO_TEXT) iov_trimcr(&iov, 0); lua_pushlstring(L, iov.iov_base, iov.iov_len); fifo_discard(&S->ibuf.fifo, count); } else { lua_pushnil(L); } break; case LSO_LIMIT: if (op.size == 0) { lua_pushlstring(L, "", 0); break; } if ((error = lso_getblock(S, &iov, 1, op.size, op.mode))) goto error; if ((count = iov.iov_len)) { if (op.mode & LSO_TEXT) iov_trimcr(&iov, 0); lua_pushlstring(L, iov.iov_base, iov.iov_len); fifo_discard(&S->ibuf.fifo, count); } else { lua_pushnil(L); } break; default: error = EFAULT; goto error; } /* switch(op) */ if (!fifo_rlen(&S->ibuf.fifo)) S->ibuf.eom = 0; return 1; error: lua_pushnil(L); lua_pushinteger(L, lso_asserterror(error)); return 2; } /* lso_recv3() */ static lso_nargs_t lso_unget2(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const void *src; size_t len; struct iovec iov; int error; src = luaL_checklstring(L, 2, &len); if ((error = fifo_grow(&S->ibuf.fifo, len))) goto error; fifo_rewind(&S->ibuf.fifo, len); fifo_slice(&S->ibuf.fifo, &iov, 0, len); memcpy(iov.iov_base, src, len); S->ibuf.eof = 0; lua_pushboolean(L, 1); return 1; error: lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } /* lso_unget2() */ static lso_error_t lso_doflush(struct luasocket *S, int mode) { size_t amount = 0, n; struct iovec iov; int error; if (mode & LSO_LINEBUF) { if (S->obuf.eol > 0) { amount = S->obuf.eol; } else if (fifo_rlen(&S->obuf.fifo) >= S->obuf.maxline) { amount = S->obuf.maxline; } } else if (mode & LSO_FULLBUF) { amount = fifo_rlen(&S->obuf.fifo); amount -= amount % S->obuf.bufsiz; } else if (mode & LSO_NOBUF) { amount = fifo_rlen(&S->obuf.fifo); } while (amount) { if (!fifo_slice(&S->obuf.fifo, &iov, 0, amount)) break; /* should never happen */ if (!(n = so_write(S->socket, iov.iov_base, iov.iov_len, &error))) goto error; fifo_discard(&S->obuf.fifo, n); amount -= n; S->obuf.eol -= MIN(S->obuf.eol, n); } return 0; error: switch (error) { case EPIPE: S->obuf.eof = 1; break; } /* switch() */ return error; } /* lso_doflush() */ static lso_nargs_t lso_send5(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const unsigned char *src, *lf; size_t tp, p, pe, end, n; int mode, byline, error; if ((error = lso_prepsnd(L, S))) { lua_pushinteger(L, 0); lua_pushinteger(L, error); return 2; } lua_settop(L, 5); src = (const void *)luaL_checklstring(L, 2, &end); tp = lso_checksize(L, 3) - 1; pe = lso_checksize(L, 4); mode = lso_imode(luaL_optstring(L, 5, ""), S->obuf.mode); byline = (mode & (LSO_TEXT|LSO_LINEBUF)) || (S->obuf.mode & LSO_LINEBUF); luaL_argcheck(L, tp <= end, 3, "start index beyond object boundary"); luaL_argcheck(L, pe <= end, 4, "end index beyond object boundary"); p = tp; so_clear(S->socket); while (p < pe) { if (byline) { n = MIN(pe - p, S->obuf.maxline); if ((lf = memchr(&src[p], '\n', n))) { n = lf - &src[p]; if ((error = fifo_write(&S->obuf.fifo, &src[p], n))) goto error; if ((mode & LSO_TEXT) && (error = fifo_putc(&S->obuf.fifo, '\r'))) goto error; if ((error = fifo_putc(&S->obuf.fifo, '\n'))) goto error; p += n + 1; S->obuf.eol = fifo_rlen(&S->obuf.fifo); } else { if ((error = fifo_write(&S->obuf.fifo, &src[p], n))) goto error; p += n; } } else { n = MIN(pe - p, LSO_BUFSIZ); if ((error = fifo_write(&S->obuf.fifo, &src[p], n))) goto error; p += n; } if (fifo_rlen(&S->obuf.fifo) > S->obuf.bufsiz) { if ((error = lso_doflush(S, mode))) goto error; } } if ((error = lso_doflush(S, mode))) goto error; lua_pushinteger(L, p - tp); return 1; error: lua_pushinteger(L, p - tp); lua_pushinteger(L, error); return 2; } /* lso_send5() */ static lso_nargs_t lso_flush(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); int mode = lso_imode(luaL_optstring(L, 2, "n"), S->obuf.mode); int error; if ((error = lso_prepsnd(L, S)) || (error = lso_doflush(S, mode))) { lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } else { lua_pushboolean(L, 1); return 1; } } /* lso_flush() */ static lso_nargs_t lso_uncork(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); int error; if ((error = so_uncork(S->socket))) { lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } else { lua_pushboolean(L, 1); return 1; } } /* lso_uncork() */ static lso_nargs_t lso_pending(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); lua_pushunsigned(L, fifo_rlen(&S->ibuf.fifo)); lua_pushunsigned(L, fifo_rlen(&S->obuf.fifo)); return 2; } /* lso_pending() */ static lso_nargs_t lso_sendfd3(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const void *src; size_t len; int fd, error; if ((error = lso_prepsnd(L, S))) goto error; lua_settop(L, 3); src = luaL_checklstring(L, 2, &len); if ((fd = lso_tofileno(L, 3)) < 0) goto badfd; so_clear(S->socket); if ((error = so_sendmsg(S->socket, so_fdmsg(src, len, fd), 0))) goto error; lua_pushboolean(L, 1); return 1; badfd: error = EBADF; error: lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } /* lso_sendfd3() */ static lso_nargs_t lso_recvfd2(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); size_t bufsiz = luaL_optunsigned(L, 2, S->ibuf.maxline); struct msghdr *msg; struct cmsghdr *cmsg; struct iovec iov; struct so_options opts; int fd = -1, error; if ((error = lso_preprcv(L, S))) goto error; if ((error = fifo_grow(&S->ibuf.fifo, bufsiz))) goto error; fifo_wvec(&S->ibuf.fifo, &iov, 1); msg = so_fdmsg(iov.iov_base, iov.iov_len, -1); #if defined MSG_CMSG_CLOEXEC if ((error = so_recvmsg(S->socket, msg, MSG_CMSG_CLOEXEC))) goto error; #else if ((error = so_recvmsg(S->socket, msg, 0))) goto error; #endif for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) continue; cqs_closefd(&fd); memcpy(&fd, CMSG_DATA(cmsg), sizeof fd); } if (msg->msg_flags & (MSG_TRUNC|MSG_CTRUNC)) goto trunc; if (msg->msg_iovlen > 0 && msg->msg_iov[0].iov_len > 0) lua_pushlstring(L, msg->msg_iov[0].iov_base, msg->msg_iov[0].iov_len); else lua_pushliteral(L, ""); if (fd == -1) lua_pushnil(L); else if ((error = cqs_socket_fdopen(L, fd, so_opts()))) goto error; return 2; trunc: error = ENOBUFS; error: cqs_closefd(&fd); lua_pushnil(L); lua_pushnil(L); lua_pushinteger(L, error); return 3; } /* lso_recvfd2() */ static lso_nargs_t lso_pack4(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); lua_Number value; unsigned count; int mode, error; if ((error = lso_prepsnd(L, S))) goto error; lua_settop(L, 4); value = luaL_checknumber(L, 2); count = luaL_optunsigned(L, 3, 32); mode = lso_imode(luaL_optstring(L, 4, ""), S->obuf.mode); if ((error = fifo_pack(&S->obuf.fifo, (unsigned long long)(long long)value, count))) goto error; so_clear(S->socket); if ((error = lso_doflush(S, mode))) goto error; lua_pushboolean(L, 1); return 1; error: lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } /* lso_pack4() */ static lso_nargs_t lso_unpack2(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); unsigned long long value; unsigned count; int error; if ((error = lso_preprcv(L, S))) goto error; lua_settop(L, 2); count = luaL_optunsigned(L, 2, 32); if (fifo_rbits(&S->ibuf.fifo) < count) { size_t rem = ((count - fifo_rbits(&S->ibuf.fifo)) + 7U) / 8U; if ((error = lso_fill(S, rem))) { if (fifo_rbits(&S->ibuf.fifo) < count) goto error; } } value = fifo_unpack(&S->ibuf.fifo, count); if (value == (unsigned long long)(lua_Integer)value) lua_pushinteger(L, (lua_Integer)value); else if (value == (unsigned long long)(lua_Number)value) lua_pushnumber(L, (lua_Number)value); else goto range; return 1; range: error = ERANGE; error: lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* lso_unpack2() */ static lso_nargs_t lso_fill2(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); size_t size = lso_checksize(L, 2); int error; if ((error = lso_preprcv(L, S)) || (error = lso_fill(S, size))) { lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } lua_pushboolean(L, 1); return 1; } /* lso_fill2() */ static lso_nargs_t lso_clear(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); so_clear(S->socket); lua_pushboolean(L, 1); return 1; } /* lso_clear() */ int cqs_socket_pollfd(lua_State *L, int index) { struct luasocket *S = lso_checkvalid(L, index, lua_touserdata(L, index)); return so_pollfd(S->socket); } /* cqs_socket_pollfd() */ static lso_nargs_t lso_pollfd(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); lua_pushinteger(L, so_pollfd(S->socket)); return 1; } /* lso_pollfd() */ int cqs_socket_events(lua_State *L, int index) { struct luasocket *S = lso_checkvalid(L, index, lua_touserdata(L, index)); return so_events(S->socket); } /* cqs_socket_events() */ static lso_nargs_t lso_events(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); short events = so_events(S->socket); char mode[3], *p = mode; if ((events & POLLIN)) *p++ = 'r'; if ((events & POLLOUT)) *p++ = 'w'; lua_pushlstring(L, mode, p - mode); return 1; } /* lso_events() */ double cqs_socket_timeout(lua_State *L NOTUSED, int index NOTUSED) { struct luasocket *S = lso_checkvalid(L, index, lua_touserdata(L, index)); return S->timeout; } /* cqs_socket_timeout() */ static lso_nargs_t lso_timeout(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); if (isnormal(S->timeout) || S->timeout == 0) { lua_pushnumber(L, S->timeout); return 1; } return 0; } /* lso_timeout() */ static lso_nargs_t lso_shutdown(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); int how, error; switch (luaL_checkoption(L, 2, "rw", (const char *[]){ "r", "w", "rw", "wr", 0 })) { case 0: how = SHUT_RD; break; case 1: how = SHUT_WR; break; default: how = SHUT_RDWR; break; } /* switch() */ if ((error = so_shutdown(S->socket, how))) { lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } else { lua_pushboolean(L, 1); return 1; } } /* lso_shutdown() */ static lso_nargs_t lso_eof(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const char *which = luaL_optstring(L, 2, "rw"); int nret = 0; for (; *which; which++) { switch (*which) { case 'r': lua_pushboolean(L, S->ibuf.eof); nret++; break; case 'w': lua_pushboolean(L, S->obuf.eof); nret++; break; } } /* for() */ return nret; } /* lso_eof() */ static lso_nargs_t lso_accept(lua_State *L) { struct luasocket *A = lso_checkself(L, 1); struct so_options opts; int fd, error; if (lua_istable(L, 2)) { opts = lso_checkopts(L, 2); } else { opts = *so_opts(); } so_clear(A->socket); if (-1 == (fd = so_accept(A->socket, 0, 0, &error))) goto error; if ((error = cqs_socket_fdopen(L, fd, &opts))) goto error; return 1; error: lua_pushnil(L); lua_pushinteger(L, error); return 2; } /* lso_accept() */ static lso_nargs_t lso_pushname(lua_State *L, struct sockaddr_storage *ss, socklen_t salen) { switch (ss->ss_family) { case AF_INET: /* FALL THROUGH */ case AF_INET6: lua_pushinteger(L, ss->ss_family); lua_pushstring(L, sa_ntoa(ss)); lua_pushinteger(L, ntohs(*sa_port(ss, SA_PORT_NONE, NULL))); return 3; case AF_UNIX: lua_pushinteger(L, ss->ss_family); /* support nameless sockets and Linux's abstract namespace */ if (salen > offsetof(struct sockaddr_un, sun_path)) { struct sockaddr_un *sun = (struct sockaddr_un *)ss; char *pe = (char *)sun + SO_MIN(sizeof *sun, salen); size_t plen; while (pe > sun->sun_path && pe[-1] == '\0') --pe; if ((plen = (pe - sun->sun_path)) > 0) { lua_pushlstring(L, sun->sun_path, plen); } else { lua_pushnil(L); } } else { lua_pushnil(L); } return 2; default: lua_pushinteger(L, ss->ss_family); return 1; } } /* lso_pushname() */ static lso_nargs_t lso_peername(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); struct sockaddr_storage ss; socklen_t salen = sizeof ss; int error; memset(&ss, 0, sizeof ss); if ((error = so_remoteaddr(S->socket, &ss, &salen))) { lua_pushnil(L); lua_pushinteger(L, error); return 2; } return lso_pushname(L, &ss, salen); } /* lso_peername() */ static lso_nargs_t lso_peereid(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); uid_t uid; gid_t gid; int error; if ((error = so_peereid(S->socket, &uid, &gid))) { lua_pushnil(L); lua_pushinteger(L, error); return 2; } lua_pushinteger(L, uid); lua_pushinteger(L, gid); return 2; } /* lso_peereid() */ static lso_nargs_t lso_peerpid(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); pid_t pid; int error; if ((error = so_peerpid(S->socket, &pid))) { lua_pushnil(L); lua_pushinteger(L, error); return 2; } lua_pushinteger(L, pid); return 1; } /* lso_peerpid() */ static lso_nargs_t lso_localname(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); struct sockaddr_storage ss; socklen_t salen = sizeof ss; int error; memset(&ss, 0, sizeof ss); if ((error = so_localaddr(S->socket, &ss, &salen))) { lua_pushnil(L); lua_pushinteger(L, error); return 2; } return lso_pushname(L, &ss, salen); } /* lso_localname() */ static lso_nargs_t lso_stat(lua_State *L) { struct luasocket *S = lso_checkself(L, 1); const struct so_stat *st = so_stat(S->socket); lua_newtable(L); lua_newtable(L); lua_pushinteger(L, st->sent.count); lua_setfield(L, -2, "count"); lua_pushboolean(L, st->sent.eof); lua_setfield(L, -2, "eof"); lua_pushinteger(L, st->sent.time); lua_setfield(L, -2, "time"); lua_setfield(L, -2, "sent"); lua_newtable(L); lua_pushinteger(L, st->rcvd.count); lua_setfield(L, -2, "count"); lua_pushboolean(L, st->rcvd.eof); lua_setfield(L, -2, "eof"); lua_pushinteger(L, st->rcvd.time); lua_setfield(L, -2, "time"); lua_setfield(L, -2, "rcvd"); return 1; } /* lso_stat() */ static void lso_destroy(lua_State *L, struct luasocket *S) { cqs_unref(L, &S->onerror); if (S->tls.config.instance) { SSL_free(S->tls.config.instance); S->tls.config.instance = NULL; } if (S->tls.config.context) { SSL_CTX_free(S->tls.config.context); S->tls.config.context = NULL; } fifo_reset(&S->ibuf.fifo); fifo_reset(&S->obuf.fifo); /* Hack for Lua 5.1 and LuaJIT */ if (!S->mainthread) { S->mainthread = L; so_close(S->socket); S->mainthread = 0; } else { so_close(S->socket); } S->socket = 0; } /* lso_destroy() */ static lso_nargs_t lso_close(lua_State *L) { struct luasocket *S = luaL_checkudata(L, 1, LSO_CLASS); lso_destroy(L, S); return 0; } /* lso_close() */ static lso_nargs_t lso__gc(lua_State *L) { struct luasocket *S = luaL_checkudata(L, 1, LSO_CLASS); S->mainthread = NULL; // disable poll cancellation lso_destroy(L, S); return 0; } /* lso__gc() */ static int lso_type(lua_State *L) { struct luasocket *S; if ((S = lso_testself(L, 1))) { lua_pushstring(L, (S->socket)? "socket" : "closed socket"); } else { lua_pushnil(L); } return 1; } /* lso_type() */ static int lso_interpose(lua_State *L) { return cqs_interpose(L, LSO_CLASS); } /* lso_interpose() */ static luaL_Reg lso_methods[] = { { "connect", &lso_connect1 }, { "listen", &lso_listen1 }, { "starttls", &lso_starttls }, { "checktls", &lso_checktls }, { "setvbuf", &lso_setvbuf3 }, { "setmode", &lso_setmode3 }, { "setbufsiz", &lso_setbufsiz3 }, { "setmaxline", &lso_setmaxline3 }, { "settimeout", &lso_settimeout2 }, { "seterror", &lso_seterror }, { "setmaxerrs", &lso_setmaxerrs2 }, { "error", &lso_error }, { "clearerr", &lso_clearerr }, { "onerror", &lso_onerror2 }, { "recv", &lso_recv3 }, { "unget", &lso_unget2 }, { "send", &lso_send5 }, { "flush", &lso_flush }, { "uncork", &lso_uncork }, { "pending", &lso_pending }, { "sendfd", &lso_sendfd3 }, { "recvfd", &lso_recvfd2 }, { "pack", &lso_pack4 }, { "unpack", &lso_unpack2 }, { "fill", &lso_fill2 }, { "clear", &lso_clear }, { "pollfd", &lso_pollfd }, { "events", &lso_events }, { "timeout", &lso_timeout }, { "shutdown", &lso_shutdown }, { "eof", &lso_eof }, { "accept", &lso_accept }, { "peername", &lso_peername }, { "peereid", &lso_peereid }, { "peerpid", &lso_peerpid }, { "localname", &lso_localname }, { "stat", &lso_stat }, { "close", &lso_close }, { 0, 0 } }; /* lso_methods[] */ static luaL_Reg lso_metamethods[] = { { "__gc", &lso__gc }, { 0, 0 } }; /* lso_metamethods[] */ static luaL_Reg lso_globals[] = { { "connect", &lso_connect2 }, { "listen", &lso_listen2 }, { "dup", &lso_dup }, { "fdopen", &lso_fdopen }, { "pair", &lso_pair }, { "type", &lso_type }, { "interpose", &lso_interpose }, { "setvbuf", &lso_setvbuf2 }, { "setmode", &lso_setmode2 }, { "setbufsiz", &lso_setbufsiz2 }, { "setmaxline", &lso_setmaxline2 }, { "settimeout", &lso_settimeout1 }, { "setmaxerrs", &lso_setmaxerrs1 }, { "onerror", &lso_onerror1 }, { 0, 0 } }; /* lso_globals[] */ lso_nargs_t luaopen__cqueues_socket(lua_State *L) { static const struct cqs_macro macros[] = { { "AF_UNSPEC", AF_UNSPEC }, { "AF_INET", AF_INET }, { "AF_INET6", AF_INET6 }, { "AF_UNIX", AF_UNIX }, { "SOCK_STREAM", SOCK_STREAM }, { "SOCK_SEQPACKET", SOCK_SEQPACKET }, { "SOCK_DGRAM", SOCK_DGRAM }, }; cqs_pushnils(L, LSO_UPVALUES); /* initial upvalues */ cqs_newmetatable(L, LSO_CLASS, lso_methods, lso_metamethods, LSO_UPVALUES); lua_pushvalue(L, -1); /* push self as replacement upvalue */ cqs_setmetaupvalue(L, -2, LSO_INDEX); /* insert self as upvalue */ luaL_newlibtable(L, lso_globals); cqs_pushnils(L, LSO_UPVALUES); /* initial upvalues */ luaL_setfuncs(L, lso_globals, LSO_UPVALUES); lua_pushvalue(L, -2); /* push metatable */ cqs_setfuncsupvalue(L, -2, LSO_INDEX); cqs_setmacros(L, -1, macros, countof(macros), 0); return 1; } /* luaopen__cqueues_socket() */ /* * D E B U G & U N I T T E S T I N G R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static size_t dbg_checksize(lua_State *L, int index) { lua_Number n = luaL_checknumber(L, index); return (n < 0)? (size_t)0 - (size_t)-n : (size_t)n; } /* dbg_checksize() */ static size_t dbg_checkbool(lua_State *L, int index) { luaL_checktype(L, index, LUA_TBOOLEAN); return lua_toboolean(L, index); } /* dbg_checkbool() */ static struct iovec dbg_checkstring(lua_State *L, int index) { struct iovec iov; iov.iov_base = (void *)luaL_checklstring(L, index, &iov.iov_len); return iov; } /* dbg_checkstring() */ static int dbg_iov_eoh(lua_State *L) { struct iovec iov = dbg_checkstring(L, 1); _Bool eof = dbg_checkbool(L, 2); size_t eoh; int error; if ((size_t)-1 == (eoh = iov_eoh(&iov, eof, 0, &error))) { lua_pushnil(L); lua_pushstring(L, cqs_strerror(error)); lua_pushinteger(L, error); return 3; } else { lua_pushinteger(L, eoh); return 1; } } /* dbg_iov_eoh() */ static int dbg_iov_eob(lua_State *L) { struct iovec haystack = dbg_checkstring(L, 1); struct iovec needle = dbg_checkstring(L, 2); lua_pushinteger(L, iov_eob(&haystack, needle.iov_base, needle.iov_len)); return 1; } /* dbg_iov_eob() */ static int dbg_iov_eot(lua_State *L) { struct iovec iov = dbg_checkstring(L, 1); size_t minbuf = dbg_checksize(L, 2); size_t maxbuf = dbg_checksize(L, 3); _Bool eof = dbg_checkbool(L, 4); size_t n; int error; if ((size_t)-1 == (n = iov_eot(&iov, minbuf, maxbuf, eof, &error))) { lua_pushnil(L); lua_pushstring(L, cqs_strerror(error)); lua_pushinteger(L, error); return 3; } else { lua_pushinteger(L, n); return 1; } } /* dbg_iov_eot() */ static int dbg_iov_trimcr(lua_State *L) { struct iovec src = dbg_checkstring(L, 1); _Bool chomp = dbg_checkbool(L, 2); struct iovec dst = { memcpy(lua_newuserdata(L, src.iov_len), src.iov_base, src.iov_len), src.iov_len }; iov_trimcr(&dst, chomp); lua_pushlstring(L, dst.iov_base, dst.iov_len); return 1; } /* dbg_iov_trimcr() */ static int dbg_iov_trimcrlf(lua_State *L) { struct iovec src = dbg_checkstring(L, 1); _Bool chomp = dbg_checkbool(L, 2); struct iovec dst = { memcpy(lua_newuserdata(L, src.iov_len), src.iov_base, src.iov_len), src.iov_len }; iov_trimcrlf(&dst, chomp); lua_pushlstring(L, dst.iov_base, dst.iov_len); return 1; } /* dbg_iov_trimcrlf() */ static luaL_Reg dbg_globals[] = { { "iov_eoh", &dbg_iov_eoh }, { "iov_eob", &dbg_iov_eob }, { "iov_eot", &dbg_iov_eot }, { "iov_trimcr", &dbg_iov_trimcr }, { "iov_trimcrlf", &dbg_iov_trimcrlf }, { NULL, NULL } }; /* dbg_globals[] */ lso_nargs_t luaopen__cqueues_socket_debug(lua_State *L) { luaL_newlib(L, dbg_globals); return 1; } /* luaopen__cqueues_socket_debug() */ cqueues-rel-20161214/src/socket.debug.lua000066400000000000000000000237531302435770500200750ustar00rootroot00000000000000local debug = require"_cqueues.socket.debug" -- -- toviz - binary to visible ASCII string -- local _viz = { ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\\"] = "\\\\", ['"'] = '\\"', ["'"] = "\\'" } for i = 0,255 do local c = string.char(i) _viz[c] = _viz[c] or (i > 31 and i < 127 and c) or string.format("\\%.3d", i) end local function toviz(txt) return (string.gsub(tostring(txt), ".", _viz)) end -- toviz -- -- huh - print arguments to stderr before returning to caller -- local function huh(...) local function _huh(...) if select("#", ...) > 0 then local x = ... if type(x) == "string" or type(x) == "table" or type(x) == "userdata" then x = string.format('"%s"', toviz(x)) end io.stderr:write(tostring(x), " ") return (...), _huh(select(2, ...)) end end io.stderr:write"[ " _huh(...) io.stderr:write("] (line ", _G.debug.getinfo(2, "l").currentline , ")\n") return ... end -- huh -- -- Simple semaphore -- local semaphore = {} function semaphore.new() return setmetatable({ counter = 0, condvar = assert(require"cqueues.condition".new()), }, { __index = semaphore }) end -- semaphore.new function semaphore:post() self.counter = self.counter + 1 self.condvar:signal() end -- semaphore:post function semaphore:wait() while self.counter == 0 do self.condvar:wait() end self.counter = self.counter - 1 end -- semaphore:wait -- -- iobox - provide environment for running cqueues code -- local function iobox(f) return function() local cqueues = require"cqueues" local loop = cqueues.running() if loop then f(loop) else local loop = cqueues.new() assert(loop:wrap(function() f(loop) end):loop()) end end end -- iobox -- -- debug.units - table of unit tests -- debug.units = setmetatable({ }, { __index = { new = function(name, f) debug.units[name] = f debug.units[#debug.units + 1] = { name = name, f = f } end, run = function(patt) patt = patt or "." for _, unit in ipairs(debug.units) do if string.match(unit.name, patt) then unit.f() end end end } }) -- -- iov_eoh consumes a multi-line MIME header at the beginning of the source -- text. Returns the prefix length of the header. A return value greater -- than #text means more text is necessary. -- -- On error returns nil, error string, and error number. -- debug.units.new("iov_eoh", function() local iov_eoh = debug.iov_eoh -- (text, eof) local txt = "Foo: bar\n" local n = assert(iov_eoh(txt, false)) assert(n > #txt) -- -- 2014-05-26: Only headers with a valid termination condition will -- parse. A header which is followed by EOF, even with a trailing -- newline, is not considered a valid header. Likewise for headers -- which reach the maximum line length. -- local txt = "Foo: bar\n \n\tbaz\n\n" local n = assert(iov_eoh(txt, true)) assert(n == #txt - 1) local txt = "Foo: bar\n\n" local n = assert(iov_eoh(txt, false)) assert(n == #txt - 1) -- skips over spaces before colon local txt = "Foo : bar\n\n" local n = assert(iov_eoh(txt, false)) print(n) assert(n == #txt - 1) -- make sure we handle end-of-headers linebreak local txt = "\n" local n = assert(iov_eoh(txt, false)) assert(n == 0) -- make sure we stop at first non-header local txt = "foo\n" local n = assert(iov_eoh(txt, false)) assert(n == 0) end) -- -- iov_eob consumes data up and including the specified MIME boundary -- marker. Returns the prefix length of the chunk. A return value greater -- than #text means more text is necessary. 0 means the boundary was -- not found. -- debug.units.new("iov_eob", function() local iov_eob = debug.iov_eob -- (text, boundary) local txt = "123--AAAA" local n = assert(iov_eob(txt, "--AAAA")) assert(n == #txt) local txt = "123--AAAA\n" local n = assert(iov_eob(txt, "--AAAA")) assert(n == #txt - 1) local txt = "123--AAAA" local n = assert(iov_eob(txt, "--AAAAA")) assert(n == 0) local txt = "--AAAA" local n = assert(iov_eob(txt, "--AAA")) assert(n == #txt - 1) end) -- -- iov_eot attempts to fit \r\n:\n translated text into the specified lower -- and upper output string length bounds, without leaving any trailing \r, -- unless EOF is true. Returns the prefix length of the input text necessary -- to meet the minimum bound. A return value greater than #text means more -- text is necessary. Specifically, the return value represents the minimum -- string length needded to fit the lower bound. It could be more if the new -- text has more \r\n-pairs. -- -- On error returns nil, error string, and error number. -- debug.units.new("iov_eot", function() local iov_eot = debug.iov_eot -- (text, minbuf, maxbuf, eof) local txt = "12345678\r" local n = assert(iov_eot(txt, #txt, #txt, false)) assert(n > #txt) local txt = "\r12345678" local n = assert(iov_eot(txt, #txt, #txt, false)) assert(n == #txt) local txt = "12345678\r" local n = assert(iov_eot(txt, #txt - 1, #txt, false)) assert(n == #txt - 1) local txt = "\r\n" local n = assert(iov_eot(txt, #txt + 1, #txt + 1, false)) assert(n == #txt + 2) local txt = "\r\n" local n = assert(iov_eot(txt, #txt + 1, #txt + 1, true)) assert(n == #txt) local txt = string.rep("\r\n", 8192) local _, overflow = iov_eot(txt, -4096, -4096, false) assert(overflow) end) -- -- iov_trimcr removes \r from \r\n pairs. If chomp is true, then only removes -- \r from ending \r\n pair. Returns translated string. -- debug.units.new("iov_trimcr", function() local iov_trimcr = debug.iov_trimcr -- (text, chomp) for _, txt in pairs{ "1\r\n2\r\n3\r\n", "\r\n\r" } do local gs = string.gsub(txt, "\r\n", "\n") assert(gs == iov_trimcr(txt, false), string.format("%s != %s", toviz(gs), toviz(txt))) end end) -- -- iov_trimcrlf removes \r from \r\n pairs. If chomp is true, then only -- removes \r\n from end of string. Returns translated string. -- debug.units.new("iov_trimcrlf", function() local iov_trimcrlf = debug.iov_trimcrlf -- (text, chomp) for _, txt in pairs{ "\r\n.\r\r\r\n", "\r\n\r\r\r", "\r\r.\n\n" } do local gs = string.gsub(txt, "\r?\n", "") assert(gs == iov_trimcrlf(txt, false), string.format("%s != %s", toviz(gs), toviz(txt))) end for _, txt in pairs{ "\r\n.\r\n", "\n", "\n\r\n\n" } do local gs = string.gsub(txt, "\r?\n$", "") assert(gs == iov_trimcrlf(txt, true), string.format("%s != %s", toviz(gs), toviz(txt))) end end) -- -- io.boundary - test various aspects of buffered MIME reader -- debug.units.new("io.boundary.text", iobox(function(loop) local cqueues = require"cqueues" local socket = require"cqueues.socket" local snd, rcv = assert(socket.pair()) local boundary = "--AAAA" local message = string.rep(".\r\n.\r", 123456) snd:setmode(nil, "bf") snd:setbufsiz(nil, 1024) rcv:setmode("tf") rcv:setbufsiz(256) cqueues.running():wrap(function () local sent = 1 while sent <= #message do local n = math.random(math.min(1024, #message - sent)) local buf = string.sub(message, sent, sent + n) snd:write(buf) sent = sent + #buf end snd:write(boundary) snd:flush() snd:shutdown() end) local buff = {} for b in rcv:lines(boundary) do if #buff > 0 then -- in text mode the implementation tries to break -- chunks along line boundaries, as long as they -- fall within MAX(maxline, bufsiz). assert(buff[#buff]:sub(-1) == "\n") end buff[#buff + 1] = b end assert(table.concat(buff) == string.gsub(message, "\r\n", "\n")) assert(rcv:read() == boundary) end)) -- -- io.block - test various aspects of buffered block reader -- debug.units.new("io.block.text", iobox(function(loop) local cqueues = require"cqueues" local socket = require"cqueues.socket" local snd, rcv = assert(socket.pair()) local message = string.rep(".\r\n.\r", 123456) .. "@" snd:setmode(nil, "bf") snd:setbufsiz(nil, math.random(7, 1024)) rcv:setmode("tf") rcv:setbufsiz(256) cqueues.running():wrap(function () local sent = 0 while sent < #message do local n = math.random(math.min(1024, #message - sent)) local buf = string.sub(message, sent + 1, sent + n) snd:write(buf) sent = sent + #buf end snd:flush() snd:shutdown"w" end) local buff = {} for b in rcv:lines(127) do buff[#buff + 1] = b end assert(table.concat(buff) == string.gsub(message, "\r\n", "\n")) end)) -- -- sys.reuseport - test SO_REUSEPORT -- debug.units.new("sys.reuseport", iobox(function(loop) local socket = require"cqueues.socket" local A = assert(assert(socket.listen{ host = "127.0.0.1", port = 0, sin_reuseport = true }):listen()) local _, _, port = assert(A:localname()) local B = assert(assert(socket.listen{ host = "127.0.0.1", port = port, sin_reuseport = true }):listen()) local sem = semaphore.new() local behavior = nil loop:wrap(function() sem:wait() assert(socket.connect("127.0.0.1", port)):connect(1) sem:wait() assert(socket.connect("127.0.0.1", port)):connect(1) end) loop:wrap(function() sem:post() if B:accept(1) then behavior = "bsd" end sem:post() if A:accept(1) then behavior = "linux" end io.stderr:write(string.format("sys.reuseport: %s\n", assert(behavior))) A:close() B:close() end) end)) -- -- opts.cloexec -- test that socket.connect obeys .cloexec -- debug.units.new("opts.cloexec", iobox(function (loop) local ok, unix = pcall(require, "unix") if not ok or not unix then return end local assert = require"cqueues.auxlib".assert local socket = require"cqueues.socket" local A = assert(assert(socket.listen{ host = "127.0.0.1", port = 0, sin_reuseport = true }):listen()) local _, _, port = assert(A:localname()) loop:wrap(function () for _, cloexec in ipairs{ true, false } do local B = assert(assert(socket.connect{ host = "127.0.0.1", port = port, cloexec = cloexec }):connect()) local flags = assert(unix.fcntl(B:pollfd(), unix.F_GETFD)) --io.stderr:write(string.format("opts.cloexec(%s): 0x%.2x\n", (cloexec and "true" or "false"), flags)) assert((flags == unix.FD_CLOEXEC) == cloexec) end end) end)) debug.units.run"^iov.*" --> these are always safe to run debug.units.run"^opts.*" --> "" --debug.units.run"^io%..*" return debug cqueues-rel-20161214/src/socket.lua000066400000000000000000000377311302435770500170110ustar00rootroot00000000000000local loader = function(loader, ...) local socket = require("_cqueues.socket") local cqueues = require("cqueues") local errno = require("cqueues.errno") local poll = cqueues.poll local monotime = cqueues.monotime local AF_INET = socket.AF_INET local AF_INET6 = socket.AF_INET6 local AF_UNIX = socket.AF_UNIX local SOCK_STREAM = socket.SOCK_STREAM local SOCK_DGRAM = socket.SOCK_DGRAM local EAGAIN = errno.EAGAIN local EPIPE = errno.EPIPE local ETIMEDOUT = errno.ETIMEDOUT local ENOTCONN = errno.ENOTCONN local ENOTSOCK = errno.ENOTSOCK local strerror = errno.strerror local format = string.format -- -- H E L P E R R O U T I N E S -- -- ======================================================================== local function timed_poll(self, deadline) if deadline then local curtime = monotime() if deadline <= curtime then return false end poll(self, deadline - curtime) return true else poll(self) return true end end -- timed_poll local function logname(so) local af, addr, port = so:peername() if af == AF_INET or af == AF_INET6 then return format("%s.%s", addr, port) elseif af == AF_UNIX then return format("unix:%s", addr or "unnamed") end end -- logname -- -- E R R O R M A N A G E M E N T -- -- All errors in the I/O routines are first passed to a per-socket error -- handler, which can choose to return or throw them. -- -- The default error handler is not actually installed with any socket, as -- that would create needless churn in the registry index on socket -- instantiation. Instead we interpose socket.onerror and socket:onerror and -- return our default handler if none was previously installed. -- -- ======================================================================== -- default error handler local function def_onerror(self, op, why, lvl) if why == EPIPE then return EPIPE elseif why == ETIMEDOUT then return ETIMEDOUT else local addr = logname(self) local msg if addr then msg = format("[%s]:%s: %s", addr, op, strerror(why)) else msg = format("socket:%s: %s", op, strerror(why)) end error(msg, lvl) end end -- def_onerror local _onerror = socket.onerror; socket.onerror = function(...) return _onerror(...) or def_onerror end local _onerror; _onerror = socket.interpose("onerror", function(...) return _onerror(...) or def_onerror end) -- -- On buffered I/O we need to preserve errors across calls, otherwise -- unchecked transient errors might lead to unexpected behavior by -- application code. This is particularly true regarding timeouts, and -- especially so when mixed with iterators like socket:lines--doubly so when -- reading MIME headers, which could terminate on ETIMEDOUT, EPIPE, or just -- when reaching the end of the headers section. -- -- Why not just always throw on such errors? One reason is that we partially -- mimic Lua's file objects, which will return such errors. (And we might -- change our semantics to fully mimic Lua in the future.) -- -- Another reason is that it's very common to want to deal with timeouts -- inline. For example, maybe you want to write a keep-alive message after a -- read timeout. Timeouts are exceptional but not necessarily errors. -- local preserve = { read = "r", lines = "r", fill = "r", unpack = "r", write = "w", flush = "w", pack = "w", -- these too for good measure, even though they're not buffered recvfd = "r", sendfd = "w", } -- drop EPIPE errors on input channel local nopipe = { read = true, lines = true, fill = true, unpack = true, recvfd = true } local function oops(self, op, why, level) local onerror = self:onerror() or def_onerror if why == EPIPE and nopipe[op] then return -- EOF elseif preserve[op] then self:seterror(preserve[op], why) end -- NOTE: There's normally no need to increment on a tail-call -- (except when directly calling the error() routine), but we -- increment here so the callee has the correct stack level to pass -- to error() directly, without making adjustments for its own -- activation record. return onerror(self, op, why, (level or 2) + 1) end -- oops -- -- A P I E X T E N S I O N S -- -- The core sockets implementation in C will not yield on I/O, or throw -- recoverable errors. These things are done in Lua code for simplicitly and -- portability--Lua 5.1/LuaJIT doesn't support resumption of C routines. -- -- ======================================================================== -- -- Extended socket.pair -- local _pair = socket.pair; socket.pair = function(type) if type == "stream" then type = SOCK_STREAM elseif type == "dgram" then type = SOCK_DGRAM end return _pair(type) end -- -- Throwable socket:setbufsiz -- local _setbufsiz; _setbufsiz = socket.interpose("setbufsiz", function(self, input, output) local input, output, why = _setbufsiz(self, input, output) if not input then return nil, nil, oops(self, "setbufsiz", why) end return input, output end) -- -- Yielding socket:listen -- local _listen; _listen = socket.interpose("listen", function(self, timeout) local timeout = timeout or self:timeout() local deadline = timeout and (monotime() + timeout) local ok, why = _listen(self) while not ok do if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, "listen", ETIMEDOUT) end else return nil, oops(self, "listen", why) end ok, why = _listen(self) end return self end) -- -- Yielding socket:accept -- local _accept; _accept = socket.interpose("accept", function(self, opts, timeout) -- :accept used to take just a timeout as argument if type(opts) == "number" then timeout, opts = opts, nil else timeout = timeout or self:timeout() end local deadline = timeout and (monotime() + timeout) local con, why = _accept(self, opts) while not con do if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, "accept", ETIMEDOUT) end else return nil, oops(self, "accept", why) end con, why = _accept(self, opts) end return con end) -- -- Add socket:clients -- socket.interpose("clients", function(self, opts, timeout) return function() return self:accept(opts, timeout) end end) -- -- Yielding socket:connect -- local _connect; _connect = socket.interpose("connect", function(self, timeout) local timeout = timeout or self:timeout() local deadline = timeout and (monotime() + timeout) local ok, why = _connect(self) while not ok do if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, "connect", ETIMEDOUT) end else return nil, oops(self, "connect", why) end ok, why = _connect(self) end return self end) -- -- Yielding socket:starttls -- local _starttls; _starttls = socket.interpose("starttls", function(self, arg1, arg2) local ctx, timeout if type(arg1) == "userdata" then ctx = arg1 elseif type(arg2) == "userdata" then ctx = arg2 end if type(arg1) == "number" then timeout = arg1 elseif type(arg2) == "number" then timeout = arg2 else timeout = self:timeout() end local deadline = timeout and monotime() + timeout local ok, why = _starttls(self, ctx) while not ok do if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, "starttls", ETIMEDOUT) end else return nil, oops(self, "starttls", why) end ok, why = _starttls(self, ctx) end return self end) -- -- Smarter socket:checktls -- local havessl, whynossl local _checktls; _checktls = socket.interpose("checktls", function(self) if not havessl then if havessl == false then return nil, whynossl end local havessl, whynossl = pcall(require, "openssl.ssl") if not havessl then return nil, whynossl end end return _checktls(self) end) -- -- Yielding socket:flush -- local _flush; local function timed_flush(self, mode, timeout, level) local ok, why = _flush(self, mode) if not ok then local deadline = timeout and (monotime() + timeout) repeat if why == EAGAIN then if not timed_poll(self, deadline) then return false, oops(self, "flush", ETIMEDOUT, level + 1) end else return false, oops(self, "flush", why, level + 1) end ok, why = _flush(self, mode) until ok end return true end -- timed_flush _flush = socket.interpose("flush", function (self, arg1, arg2) local mode, timeout if type(arg1) == "string" then mode = arg1 elseif type(arg2) == "string" then mode = arg2 end if type(arg1) == "number" then timeout = arg1 elseif type(arg2) == "number" then timeout = arg2 else timeout = self:timeout() end return timed_flush(self, mode, timeout, 2) end) -- -- Yielding socket:read -- local function read(self, func, what, ...) if not what then return end local data, why = self:recv(what) if not data then local timeout = self:timeout() local deadline = timeout and (monotime() + timeout) repeat if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, func, ETIMEDOUT, 2) end elseif why then return nil, oops(self, func, why, 2) else return -- EOF or end-of-headers end data, why = self:recv(what) until data end return data, read(self, func, ...) end socket.interpose("read", function(self, what, ...) if what then return read(self, "read", what, ...) else return read(self, "read", "*l") end end) -- -- Yielding socket:write -- -- This is complicated by the fact that we want error messages to get the -- correct stack trace, and also because on failure we want to return a list -- of error values of indeterminate length. -- local writeall; writeall = function(self, data, ...) if not data then return self end data = tostring(data) local i = 1 while i <= #data do -- use only full buffering mode here to minimize socket I/O local n, why = self:send(data, i, #data, "f") i = i + n if i <= #data then if why == EAGAIN then local timeout = self:timeout() local deadline = timeout and (monotime() + timeout) if not timed_poll(self, deadline) then return nil, oops(self, "write", ETIMEDOUT, 3) end else return nil, oops(self, "write", why, 3) end end end return writeall(self, ...) end local function fileresult(self, ok, ...) if ok then return self else return nil, ... end end -- fileresult local function flushwrite(self, ok, ...) if not ok then return nil, ... end -- Flush the buffer here because we used full buffering mode in -- writeall. But pass empty mode so it uses the configured flushing -- mode instead of an implicit flush all. return fileresult(self, timed_flush(self, "", nil, 2)) end -- flushwrite socket.interpose("write", function (self, ...) return flushwrite(self, writeall(self, ...)) end) -- -- Add socket:lines -- -- We optimize single-mode case so we're not unpacking tables all the time. -- local unpack = assert(table.unpack or unpack) socket.interpose("lines", function (self, mode, ...) local args = select("#", ...) > 0 and { ... } if mode then if select("#", ...) > 0 then local args = { ... } return function () return read(self, "lines", mode, unpack(args)) end end else mode = "*l" end return function () return read(self, "lines", mode) end end) -- -- Smarter socket:read -- local function xswap(arg1, arg2) if tonumber(arg1) then return arg2, arg1 else return arg1, arg2 end end -- xswap local function xopts(self, ...) local mode, timeout = xswap(...) return mode, timeout end -- xopts local function xdeadline(self, timeout) timeout = timeout or self:timeout() return timeout and (monotime() + timeout) end -- xdeadline socket.interpose("xread", function (self, what, ...) local mode, timeout = xopts(self, ...) local data, why = self:recv(what, mode) if not data then local deadline = xdeadline(self, timeout) repeat if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, "read", ETIMEDOUT) end elseif why then return nil, oops(self, "read", why) else return --> EOF end data, why = self:recv(what, mode) until data end return data end) -- xread -- -- Smarter socket:write -- socket.interpose("xwrite", function (self, data, ...) local mode, timeout = xopts(self, ...) local i = 1 -- -- should we default to full-buffering here (and the :send below) if -- mode is nil? -- local n, why = self:send(data, i, #data, mode) i = i + n if i <= #data then local deadline = xdeadline(self, timeout) repeat if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, "write", ETIMEDOUT) end else return nil, oops(self, "write", why) end n, why = self:send(data, i, #data, mode) i = i + n until i > #data timeout = deadline and math.max(0, deadline - monotime()) end return fileresult(self, self:flush(mode or "", timeout)) end) -- -- Smarter socket:lines -- socket.interpose("xlines", function (self, what, ...) local mode, timeout = xopts(self, ...) return function () return self:xread(what, mode, timeout) end end) -- -- Yielding socket:sendfd -- local _sendfd; _sendfd = socket.interpose("sendfd", function (self, msg, fd, timeout) local timeout = timeout or self:timeout() local deadline = timeout and (monotime() + timeout) local ok, why repeat ok, why = _sendfd(self, msg, fd) if not ok then if why == EAGAIN then if not timed_poll(self, deadline) then return false, oops(self, "sendfd", ETIMEDOUT) end else return false, oops(self, "sendfd", why) end end until ok return ok end) -- -- Yielding socket:recvfd -- local _recvfd; _recvfd = socket.interpose("recvfd", function (self, prepbufsiz, timeout) local timeout = timeout or self:timeout() local deadline = timeout and (monotime() + timeout) local msg, fd, why repeat msg, fd, why = _recvfd(self, prepbufsiz) if not msg then if why == EAGAIN then if not timed_poll(self, deadline) then return nil, nil, oops(self, "recvfd", ETIMEDOUT) end else return nil, nil, oops(self, "recvfd", why) end end until msg return msg, fd end) -- -- Yielding socket:pack -- local _pack; _pack = socket.interpose("pack", function (self, num, nbits, mode) local ok, why = _pack(self, num, nbits, mode) if not ok then local timeout = self:timeout() local deadline = timeout and (monotime() + timeout) repeat if why == EAGAIN then if not timed_poll(self, deadline) then return false, oops(self, "pack", ETIMEDOUT) end else return false, oops(self, "pack", why) end ok, why = _pack(self, num, nbits, mode) until ok end return ok end) -- -- Yielding socket:unpack -- local _unpack; _unpack = socket.interpose("unpack", function (self, nbits) local num, why = _unpack(self, nbits) if not num then local timeout = self:timeout() local deadline = timeout and (monotime() + timeout) repeat if why == EAGAIN then if not timed_poll(self, deadline) then return nil, oops(self, "unpack", ETIMEDOUT) end else return nil, oops(self, "unpack", why) end num, why = _unpack(self, nbits) until num end return num end) -- -- Yielding socket:fill -- local _fill; _fill = socket.interpose("fill", function (self, size, timeout) local ok, why = _fill(self, size) if not ok then local timeout = timeout or self:timeout() local deadline = timeout and (monotime() + timeout) repeat if why == EAGAIN then if not timed_poll(self, deadline) then return false, oops(self, "fill", ETIMEDOUT) end else return false, oops(self, "fill", why) end ok, why = _fill(self, size) until ok end return true end) -- -- Extend socket:peername -- local function getname(get, self) local af, r1, r2 = get(self) if af then return af, r1, r2 elseif r1 == ENOTCONN or r1 == ENOTSOCK or r1 == EAGAIN then return 0 else return nil, r1 end end local _peername; _peername = socket.interpose("peername", function (self) return getname(_peername, self) end) -- -- Extend socket:localname -- local _localname; _localname = socket.interpose("localname", function (self) return getname(_localname, self) end) socket.loader = loader return socket end -- loader return loader(loader, ...) cqueues-rel-20161214/src/thread.c000066400000000000000000000466341302435770500164330ustar00rootroot00000000000000/* ========================================================================== * thread.c - Lua Continuation Queues * -------------------------------------------------------------------------- * Copyright (c) 2012, 2014, 2015 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "cqueues.h" #include "lib/llrb.h" #ifndef ENABLE_PTHREAD_MUTEX_ROBUST #define ENABLE_PTHREAD_MUTEX_ROBUST ((HAVE_DECL_PTHREAD_MUTEX_ROBUST+0) || (HAVE_PTHREAD_MUTEX_ROBUST+0)) #endif #if defined EOWNERDEAD #define CT_EOWNERDEAD EOWNERDEAD #else #define CT_EOWNERDEAD EBUSY #endif struct cthread_arg { int type; int iscfunction:1; int isinteger:1; /* * NB: The value representation below is not a simple mapping to the * Lua type. Ex: Lua functions are serialized to a Lua string stored * in .v.string, but the argument type is still LUA_TFUNCTION. */ union { struct iovec string; lua_Number number; lua_Integer integer; _Bool boolean; void *pointer; } v; }; /* struct cthread_arg */ struct cthread_lib { Dl_info info; void *ref; LLRB_ENTRY(cthread_lib) rbe; }; /* struct cthread_lib */ struct cthread_handle { sig_atomic_t held; #if ENABLE_PTHREAD_MUTEX_ROBUST pthread_mutex_t hold; #endif }; /* struct cthread_handle */ struct cthread { int refs, error, status; char *msg; pthread_t id; pthread_mutex_t mutex; pthread_cond_t cond; pthread_attr_t attr; jmp_buf trap; struct cthread_handle handle; int pipe[2]; LLRB_HEAD(libs, cthread_lib) libs; struct { struct cthread_arg *arg; unsigned argc; int fd[2]; } tmp; }; /* struct cthread */ static const int selfindex; static struct { pthread_once_t once; pthread_key_t key; int error; } atpanic = { PTHREAD_ONCE_INIT, }; static void atpanic_once(void) { atpanic.error = pthread_key_create(&atpanic.key, 0); } /* atpanic_once() */ static int atpanic_trap(lua_State *L NOTUSED) { struct cthread *ct; if ((ct = pthread_getspecific(atpanic.key))) _longjmp(ct->trap, EINVAL); return 0; } /* atpanic_trap() */ static int hdl_init(struct cthread_handle *h) { #if ENABLE_PTHREAD_MUTEX_ROBUST pthread_mutexattr_t attr; int error; h->held = 0; if ((error = pthread_mutexattr_init(&attr))) return error; if ((error = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST))) goto error; if ((error = pthread_mutex_init(&h->hold, &attr))) goto error; pthread_mutexattr_destroy(&attr); return 0; error: pthread_mutexattr_destroy(&attr); return error; #else h->held = 0; return 0; #endif } /* hdl_init() */ static void hdl_destroy(struct cthread_handle *h) { #if ENABLE_PTHREAD_MUTEX_ROBUST pthread_mutex_destroy(&h->hold); #else (void)h; return; #endif } /* hdl_destroy() */ static int hdl_hold(struct cthread_handle *h) { #if ENABLE_PTHREAD_MUTEX_ROBUST int error; if ((error = pthread_mutex_lock(&h->hold))) return error; #endif h->held = 1; return 0; } /* hdl_hold() */ static _Bool hdl_isheld(struct cthread_handle *h) { #if ENABLE_PTHREAD_MUTEX_ROBUST int error; switch ((error = pthread_mutex_trylock(&h->hold))) { case EBUSY: return 1; case EOWNERDEAD: pthread_mutex_consistent(&h->hold); /* FALL THROUGH */ case 0: pthread_mutex_unlock(&h->hold); return 0; default: return 1; } #else return h->held; #endif } /* hdl_isheld() */ static int lib_cmp(struct cthread_lib *a, struct cthread_lib *b) { if ((intptr_t)a->info.dli_fbase < (intptr_t)b->info.dli_fbase) return -1; if ((intptr_t)a->info.dli_fbase > (intptr_t)b->info.dli_fbase) return 1; return 0; } /* lib_cmp() */ LLRB_GENERATE_STATIC(libs, cthread_lib, rbe, lib_cmp) static int ct_addfunc(struct cthread *ct, lua_CFunction f) { struct cthread_lib key, *ent; void *ref = NULL; if (!dladdr(EXTENSION (void *)f, &key.info)) goto dlerr; if ((ent = LLRB_FIND(libs, &ct->libs, &key))) return 0; if (!(ref = dlopen(key.info.dli_fname, RTLD_NOW|RTLD_LOCAL))) goto dlerr; if (!(ent = calloc(1, sizeof *ent))) goto syerr; ent->info = key.info; ent->ref = ref; LLRB_INSERT(libs, &ct->libs, ent); return 0; dlerr: return -1; syerr: if (ref) dlclose(ref); return errno; } /* ct_addfunc() */ static struct cthread *ct_checkthread(lua_State *L, int index) { struct cthread **ct = luaL_checkudata(L, index, CQS_THREAD); luaL_argcheck(L, *ct, index, CQS_THREAD " expected, got NULL"); return *ct; } /* ct_checkthread() */ static void ct_release(struct cthread *ct) { _Bool destroy; struct cthread_lib *ent, *nxt; pthread_mutex_lock(&ct->mutex); destroy = !--ct->refs; pthread_mutex_unlock(&ct->mutex); if (!destroy) return; hdl_destroy(&ct->handle); pthread_attr_destroy(&ct->attr); pthread_cond_destroy(&ct->cond); pthread_mutex_destroy(&ct->mutex); cqs_closefd(&ct->pipe[0]); cqs_closefd(&ct->pipe[1]); for (ent = LLRB_MIN(libs, &ct->libs); ent; ent = nxt) { nxt = LLRB_NEXT(libs, &ct->libs, ent); LLRB_REMOVE(libs, &ct->libs, ent); dlclose(ent->ref); free(ent); } cqs_closefd(&ct->tmp.fd[0]); cqs_closefd(&ct->tmp.fd[1]); free(ct->tmp.arg); free(ct->msg); free(ct); } /* ct_release() */ static void *ct_enter(void *arg) { struct cthread *ct = arg, **ud; lua_State *L = NULL; int error; /* * Hold down deadman switch so ct_join can detect (on some systems) * whether thread was killed or cancelled. */ hdl_hold(&ct->handle); /* * Procedure for bootstrapping into a new Lua VM. Order is important * because arg[0..N] are interned strings from the parent Lua VM. * * 1) Acquire lock. * -- BEGIN CRITICAL SECTION -- * 2) Grab struct cthread reference. * 3) Open new main Lua thread. * 4) Set Lua panic trap. * 5) Load low-level components from memory as we might be * chroot'd and unable to load them from disk. * 6) Load arg[0] as our Lua start routine. * 7) Push reference to struct cthread. * 8) Push reference to our socket. * 9) Push strings arg[1..N]. * -- END CRITICAL SECTION -- * 10) Release lock and signal parent. * 11) Reset Lua panic trap. * 12) Call Lua start routine. * * NOTE: Lua user code perceives this process differently. See * thread.lua. */ pthread_mutex_lock(&ct->mutex); ct->refs++; if (!(L = luaL_newstate())) goto syerr; if ((error = pthread_once(&atpanic.once, &atpanic_once))) goto error; if ((error = pthread_setspecific(atpanic.key, ct))) goto error; lua_atpanic(L, &atpanic_trap); if ((error = _setjmp(ct->trap))) goto error; luaL_openlibs(L); cqs_openlibs(L); if (ct->tmp.arg[0].iscfunction) { lua_pushcfunction(L, EXTENSION (lua_CFunction)ct->tmp.arg[0].v.pointer); } else { luaL_loadbuffer(L, ct->tmp.arg[0].v.string.iov_base, ct->tmp.arg[0].v.string.iov_len, "[thread enter]"); } ud = lua_newuserdata(L, sizeof *ud); *ud = NULL; luaL_getmetatable(L, CQS_THREAD); lua_setmetatable(L, -2); ct->refs++; *ud = ct; lua_pushvalue(L, -1); lua_rawsetp(L, LUA_REGISTRYINDEX, &selfindex); if ((error = cqs_socket_fdopen(L, ct->tmp.fd[1], NULL))) goto error; ct->tmp.fd[1] = -1; for (struct cthread_arg *arg = &ct->tmp.arg[1]; arg < &ct->tmp.arg[ct->tmp.argc]; arg++) { switch (arg->type) { case LUA_TNUMBER: if (arg->isinteger) { lua_pushinteger(L, arg->v.integer); } else { lua_pushnumber(L, arg->v.number); } break; case LUA_TBOOLEAN: lua_pushboolean(L, arg->v.boolean); break; case LUA_TLIGHTUSERDATA: lua_pushlightuserdata(L, arg->v.pointer); break; case LUA_TSTRING: lua_pushlstring(L, arg->v.string.iov_base, arg->v.string.iov_len); break; case LUA_TFUNCTION: if (arg->iscfunction) { lua_pushcfunction(L, EXTENSION (lua_CFunction)arg->v.pointer); } else { luaL_loadbuffer(L, arg->v.string.iov_base, arg->v.string.iov_len, NULL); } break; default: lua_pushnil(L); break; } } free(ct->tmp.arg); ct->tmp.arg = NULL; ct->tmp.argc = 0; pthread_mutex_unlock(&ct->mutex); pthread_cond_signal(&ct->cond); if ((error = _setjmp(ct->trap))) { ct->error = error; goto close; } ct->status = lua_pcall(L, lua_gettop(L) - 1, 0, 0); if (ct->status != LUA_OK && lua_isstring(L, -1)) { if (!(ct->msg = strdup(lua_tostring(L, -1)))) { ct->error = errno; } } close: if (L) { if (!(error = _setjmp(ct->trap))) { lua_close(L); } else if (!ct->error) { ct->error = error; } } cqs_closefd(&ct->pipe[1]); ct_release(ct); return 0; syerr: error = errno; error: /* NOTE: Only critical section errors reach here. */ ct->error = error; pthread_mutex_unlock(&ct->mutex); pthread_cond_signal(&ct->cond); goto close; } /* ct_enter() */ static int dump_add(lua_State *L NOTUSED, const void *p, size_t sz, void *ud) { luaL_addlstring(((luaL_Buffer *)ud), p, sz); return 0; } /* dump_add() */ static int ct_setfarg(lua_State *L, struct cthread *ct, struct cthread_arg *arg, int index) { lua_Debug info; lua_CFunction f; int error; lua_pushvalue(L, index); lua_getinfo(L, ">u", &info); if ((f = lua_tocfunction(L, index))) { if (info.nups > 0) goto uperr; if ((error = ct_addfunc(ct, f))) { if (error == -1) return luaL_argerror(L, index, dlerror()); return error; } arg->v.pointer = EXTENSION (void *)f; arg->iscfunction = 1; } else { lua_State *T; luaL_Buffer B; /* _ENV is always first upvalue (if any) in Lua 5.2+ */ if ((LUA_VERSION_NUM < 502 && info.nups > 0) || info.nups > 1) goto uperr; luaL_checkstack(L, 2, "too many arguments"); /* * NOTE: Must put luaL_Buffer on a different stack because * luaL_Buffer has stack constraints that lua_dump is not * guaranteed to meet--we don't know if and how lua_dump * will keep intermediate objects on top of the stack. */ T = lua_newthread(L); luaL_buffinit(T, &B); lua_pushvalue(L, index); #if LUA_VERSION_NUM >= 503 lua_dump(L, &dump_add, &B, 0); #else lua_dump(L, &dump_add, &B); #endif luaL_pushresult(&B); arg->v.string.iov_base = (char *)luaL_checklstring(T, -1, &arg->v.string.iov_len); } arg->type = LUA_TFUNCTION; return 0; uperr: return luaL_argerror(L, index, "function has upvalues"); } /* ct_setfarg() */ /* on success destroy object with ct_release(); on failure use free(3) */ static int ct_init(struct cthread *ct) { int progress = 0; int error; ct->refs = 1; ct->pipe[0] = -1; ct->pipe[1] = -1; ct->tmp.fd[0] = -1; ct->tmp.fd[1] = -1; if ((error = pthread_mutex_init(&ct->mutex, NULL))) goto error; progress++; if ((error = pthread_cond_init(&ct->cond, NULL))) goto error; progress++; if ((error = pthread_attr_init(&ct->attr))) goto error; progress++; if ((error = hdl_init(&ct->handle))) goto error; progress++; return 0; error: switch (progress) { case 4: hdl_destroy(&ct->handle); case 3: pthread_attr_destroy(&ct->attr); case 2: pthread_cond_destroy(&ct->cond); case 1: pthread_mutex_destroy(&ct->mutex); case 0: break; } return error; } /* ct_init() */ static struct cthread *ct_create(int *_error) { struct cthread *ct = NULL; int error; if (!(ct = calloc(1, sizeof *ct))) goto syerr; if ((error = ct_init(ct))) { free(ct); ct = NULL; goto error; } if ((error = pthread_attr_setdetachstate(&ct->attr, PTHREAD_CREATE_DETACHED))) goto error; if ((error = cqs_pipe(ct->pipe, O_NONBLOCK|O_CLOEXEC))) goto error; return ct; syerr: error = errno; error: *_error = error; ct_release(ct); return NULL; } /* ct_create() */ static int ct_start(lua_State *L) { struct cthread **ud, *ct; sigset_t mask, omask; int top, error; top = lua_gettop(L); ud = lua_newuserdata(L, sizeof *ud); *ud = NULL; luaL_getmetatable(L, CQS_THREAD); lua_setmetatable(L, -2); if (!(ct = *ud = ct_create(&error))) goto error; luaL_checktype(L, 1, LUA_TFUNCTION); if (!(ct->tmp.arg = calloc(sizeof *ct->tmp.arg, top))) goto syerr; for (int index = 1; index <= top; index++) { struct cthread_arg *arg = &ct->tmp.arg[ct->tmp.argc]; switch (lua_type(L, index)) { case LUA_TNIL: arg->type = LUA_TNIL; break; case LUA_TNUMBER: #if LUA_VERSION_NUM >= 503 if (lua_isinteger(L, index)) { arg->v.integer = lua_tointeger(L, index); arg->isinteger = 1; arg->type = LUA_TNUMBER; break; } #endif arg->v.number = lua_tonumber(L, index); arg->type = LUA_TNUMBER; break; case LUA_TBOOLEAN: arg->v.boolean = lua_toboolean(L, index); arg->type = LUA_TBOOLEAN; break; case LUA_TLIGHTUSERDATA: arg->v.pointer = lua_touserdata(L, index); arg->type = LUA_TLIGHTUSERDATA; break; case LUA_TFUNCTION: if ((error = ct_setfarg(L, ct, arg, index))) goto error; break; default: /* FALL THROUGH (maybe has __tostring metamethod) */ case LUA_TSTRING: arg->v.string.iov_base = (char *)luaL_checklstring(L, index, &arg->v.string.iov_len); arg->type = LUA_TSTRING; break; } ct->tmp.argc++; } /* we may have added more stack objects above the thread object */ luaL_checkstack(L, 2, "too many arguments"); lua_pushvalue(L, top + 1); if (0 != cqs_socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, ct->tmp.fd, O_NONBLOCK|O_CLOEXEC)) goto syerr; if ((error = cqs_socket_fdopen(L, ct->tmp.fd[0], NULL))) goto error; ct->tmp.fd[0] = -1; sigfillset(&mask); sigemptyset(&omask); if ((error = pthread_sigmask(SIG_SETMASK, &mask, &omask))) goto error; pthread_mutex_lock(&ct->mutex); if (!(error = pthread_create(&ct->id, &ct->attr, &ct_enter, ct))) pthread_cond_wait(&ct->cond, &ct->mutex); pthread_mutex_unlock(&ct->mutex); pthread_sigmask(SIG_SETMASK, &omask, NULL); if (error) goto error; return 2; syerr: error = errno; error: lua_settop(L, 0); lua_pushnil(L); lua_pushnil(L); lua_pushinteger(L, error); return 3; } /* ct_start() */ static int ct_join(lua_State *L) { struct cthread *ct = ct_checkthread(L, 1); int error; if (pthread_equal(ct->id, pthread_self())) return luaL_error(L, "thread.join: cannot join self"); if (0 == read(ct->pipe[0], &(char){ 0 }, 1)) { lua_pushboolean(L, 1); if (ct->error) lua_pushinteger(L, ct->error); else if (ct->msg) lua_pushstring(L, ct->msg); else lua_pushnil(L); return 2; } else { error = errno; if (error == EAGAIN && !hdl_isheld(&ct->handle)) error = CT_EOWNERDEAD; lua_pushboolean(L, 0); lua_pushinteger(L, error); return 2; } } /* ct_join() */ static int ct_pollfd(lua_State *L) { struct cthread *ct = ct_checkthread(L, 1); lua_pushinteger(L, ct->pipe[0]); return 1; } /* ct_pollfd() */ static int ct_events(lua_State *L) { ct_checkthread(L, 1); lua_pushliteral(L, "r"); return 1; } /* ct_events() */ static int ct_timeout(lua_State *L) { ct_checkthread(L, 1); return 0; } /* ct_timeout() */ static int ct__eq(lua_State *L) { struct cthread **a = luaL_testudata(L, 1, CQS_THREAD); struct cthread **b = luaL_testudata(L, 2, CQS_THREAD); lua_pushboolean(L, a && b && (*a == *b)); return 1; } /* ct__eq() */ static int ct__gc(lua_State *L) { struct cthread **ud = luaL_checkudata(L, 1, CQS_THREAD); ct_release(*ud); *ud = NULL; return 0; } /* ct__gc() */ static int ct_type(lua_State *L) { if (luaL_testudata(L, 1, CQS_THREAD)) { lua_pushstring(L, "thread"); } else { lua_pushnil(L); } return 1; } /* ct_type() */ static int ct_interpose(lua_State *L) { return cqs_interpose(L, CQS_THREAD); } /* ct_interpose() */ static int ct_self(lua_State *L) { lua_rawgetp(L, LUA_REGISTRYINDEX, &selfindex); return 1; } /* ct_self() */ static const luaL_Reg ct_methods[] = { { "join", &ct_join }, { "pollfd", &ct_pollfd }, { "events", &ct_events }, { "timeout", &ct_timeout }, { NULL, NULL } }; static const luaL_Reg ct_metamethods[] = { { "__eq", &ct__eq }, { "__gc", &ct__gc }, { NULL, NULL } }; static const luaL_Reg ct_globals[] = { { "start", &ct_start }, { "type", &ct_type }, { "interpose", &ct_interpose }, { "self", &ct_self }, { NULL, NULL } }; static int ct_protectssl(void); int luaopen__cqueues_thread(lua_State *L) { int error; if ((error = ct_protectssl())) { if (error == -1) { return luaL_error(L, "%s", dlerror()); } else { return luaL_error(L, "%s", cqs_strerror(error)); } } cqs_newmetatable(L, CQS_THREAD, ct_methods, ct_metamethods, 0); luaL_newlib(L, ct_globals); return 1; } /* luaopen__cqueues_thread() */ /* * OpenSSL is not thread-safe without explicit locking handlers installed. */ #include static struct { pthread_mutex_t *lock; int count; void *dlref; } openssl; static void ct_lockssl(int mode, int type, const char *file NOTUSED, int line NOTUSED) { if (mode & CRYPTO_LOCK) pthread_mutex_lock(&openssl.lock[type]); else pthread_mutex_unlock(&openssl.lock[type]); } /* ct_lockssl() */ /* * Sources include Google and especially the Wine Project. See get_unix_tid * at http://source.winehq.org/git/wine.git/?a=blob;f=dlls/ntdll/server.c. */ #if __FreeBSD__ #include /* thr_self(2) */ #elif __NetBSD__ #include /* _lwp_self(2) */ #endif static unsigned long ct_selfid(void) { #if __APPLE__ return pthread_mach_thread_np(pthread_self()); #elif __DragonFly__ return lwp_gettid(); #elif __FreeBSD__ long id; thr_self(&id); return id; #elif __NetBSD__ return _lwp_self(); #else /* * pthread_t is an integer on Solaris and Linux, and a unique pointer * on OpenBSD. */ return (unsigned long)pthread_self(); #endif } /* ct_selfid() */ static int ct_protectssl(void) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int bound = 0, error = 0; pthread_mutex_lock(&mutex); if (!CRYPTO_get_locking_callback()) { if (!openssl.lock) { int i; openssl.count = CRYPTO_num_locks(); if (!(openssl.lock = malloc(openssl.count * sizeof *openssl.lock))) { error = errno; goto leave; } for (i = 0; i < openssl.count; i++) { pthread_mutex_init(&openssl.lock[i], NULL); } } CRYPTO_set_locking_callback(&ct_lockssl); bound = 1; } if (!CRYPTO_get_id_callback()) { CRYPTO_set_id_callback(&ct_selfid); bound = 1; } /* * Prevent loader from unlinking us if we've registered a callback * with OpenSSL. */ if (bound && !openssl.dlref) { Dl_info info; if (!dladdr(EXTENSION (void *)&luaopen__cqueues_thread, &info)) { error = -1; goto leave; } if (!(openssl.dlref = dlopen(info.dli_fname, RTLD_NOW|RTLD_LOCAL))) { error = -1; goto leave; } } leave: pthread_mutex_unlock(&mutex); return error; } /* ct_protectssl() */ cqueues-rel-20161214/src/thread.lua000066400000000000000000000043651302435770500167650ustar00rootroot00000000000000local loader = function(loader, ...) local thread = require"_cqueues.thread" -- -- thread.start -- local cache = {} local function dump(fn) if type(fn) == "string" then return fn else if not cache[fn] then cache[fn] = string.dump(fn) end return cache[fn] end end local include = { "cqueues", "cqueues.errno", "cqueues.socket", "cqueues.signal", "cqueues.thread", "cqueues.notify", } local start = thread.start; thread.start = function(enter, ...) local function init(self, pipe, nloaders, ...) local function loadblob(chunk, source, ...) if _VERSION == "Lua 5.1" then return loadstring(chunk, source) else return load(chunk, source, ...) end end local function preload(name, code) local loader = loadblob(code, nil, "bt", _ENV) package.loaded[name] = loader(loader, name) end local function unpack(n, ...) if n > 0 then local name, code = select(1, ...) preload(name, code) return unpack(n - 1, select(3, ...)) else return ... end end nloaders = tonumber(nloaders) local enter = unpack(nloaders, ...) return enter(pipe, select(nloaders * 2 + 2, ...)) end local function pack(i, enter, ...) if i == 1 then return init, #include, pack(i + 1, enter, ...) elseif include[i - 1] then return include[i - 1], dump(require(include[i - 1]).loader), pack(i + 1, enter, ...) else return enter, ... end end return start(pack(1, enter, ...)) end -- -- thread:join -- local monotime = require"cqueues".monotime local poll = require"cqueues".poll local EAGAIN = require"cqueues.errno".EAGAIN local ETIMEDOUT = require"cqueues.errno".ETIMEDOUT local join; join = thread.interpose("join", function (self, timeout) local deadline = timeout and (monotime() + timeout) while true do local ok, why = join(self) if ok then return true, why elseif why ~= EAGAIN then return false, why else if deadline then local curtime = monotime() if curtime >= deadline then return false, ETIMEDOUT else poll(self, deadline - curtime) end else poll(self) end end end end) thread.loader = loader return thread end -- loader return loader(loader, ...) cqueues-rel-20161214/util/000077500000000000000000000000001302435770500151715ustar00rootroot00000000000000cqueues-rel-20161214/util/Makefile000066400000000000000000000000541302435770500166300ustar00rootroot00000000000000CPPFLAGS= CFLAGS=-Wall -Wextra tcp-urgent: cqueues-rel-20161214/util/tcp-urgent.c000066400000000000000000000225031302435770500174270ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #if BSD #include #endif #include #include #include #include #include #if __clang__ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #elif __GNUC__ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif #define countof(a) (sizeof (a) / sizeof *(a)) static struct { const char *progname; _Bool ipv4; _Bool ipv6; int verbose; } MAIN = { .progname = __FILE__, .verbose = 1, }; #define panic(...) do { \ say(0, __LINE__, __VA_ARGS__); \ exit(EXIT_FAILURE); \ } while (0) #define info(...) do { \ say(1, __LINE__, __VA_ARGS__); \ } while (0) #define debug(...) do { \ say(2, __LINE__, __VA_ARGS__); \ } while (0) static void say(int level, int lineno, const char *fmt, ...) { va_list ap; if (level > MAIN.verbose) return; fprintf(stderr, "%s:", MAIN.progname); if (MAIN.verbose > 1) fprintf(stderr, "%d:", lineno); fputc(' ', stderr); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr); } /* say() */ static const char *progname(const char *progname) { const char *basename; if (!progname || !*progname) progname = __FILE__; if ((basename = strrchr(progname, '/')) && basename[1]) return &basename[1]; return progname; } /* progname() */ static socklen_t getegress(void *dst, socklen_t lim, int family, int type, int protocol, const char *host, const char *port) { struct addrinfo hints = { 0, family, type, protocol }; struct addrinfo *ent0 = NULL, *ent; socklen_t len; int fd = -1, error; if (0 != (error = getaddrinfo(host, port, &hints, &ent0))) panic("[%s]:%s: %s", host, port, gai_strerror(error)); for (ent = ent0; ent != NULL; ent = ent->ai_next) { if (-1 == (fd = socket(ent->ai_family, ent->ai_socktype, ent->ai_protocol))) panic("socket: %s", strerror(errno)); if (0 == connect(fd, ent->ai_addr, ent->ai_addrlen)) break; error = errno; close(fd); fd = -1; } freeaddrinfo(ent0); if (fd == -1) panic("connect([%s]:%s): %s", host, port, strerror(error)); len = lim; if (0 != getsockname(fd, dst, &len)) panic("getsockname: %s", strerror(errno)); if (len > lim) panic("getsockname: output buffer too small"); close(fd); return len; } /* getegress() */ static in_port_t *sa_port(void *any) { if (((struct sockaddr *)any)->sa_family == AF_INET6) { return &((struct sockaddr_in6 *)any)->sin6_port; } else { return &((struct sockaddr_in *)any)->sin_port; } } /* sa_port() */ static void opentcp(int fd[2], int family) { struct { const char *host, *port; } egress; union { struct sockaddr_storage any; struct sockaddr_in sin; struct sockaddr_in6 sin6; } saddr; socklen_t slen; int lfd = -1; memset(&saddr, 0, sizeof saddr); /* OS X needs this 0'd out */ /* find egress address instead of using loopback */ egress.host = (family == AF_INET6)? "2001:4860:4860::8888" : "8.8.8.8"; egress.port = "53"; slen = getegress(&saddr, sizeof saddr, family, SOCK_STREAM, IPPROTO_TCP, egress.host, egress.port); *sa_port(&saddr) = 0; if (-1 == (lfd = socket(family, SOCK_STREAM, IPPROTO_TCP))) panic("socket: %s", strerror(errno)); if (0 != bind(lfd, (struct sockaddr *)&saddr, slen)) panic("bind: %s", strerror(errno)); if (0 != listen(lfd, SOMAXCONN)) panic("listen: %s", strerror(errno)); memset(&saddr, 0, sizeof saddr); slen = sizeof saddr; if (0 != getsockname(lfd, (struct sockaddr *)&saddr, &slen)) panic("getsockname: %s", strerror(errno)); if (-1 == (fd[1] = socket(family, SOCK_STREAM, IPPROTO_TCP))) panic("socket: %s", strerror(errno)); if (0 != connect(fd[1], (struct sockaddr *)&saddr, slen)) panic("connect: %s", strerror(errno)); if (-1 == (fd[0] = accept(lfd, NULL, NULL))) panic("accept: %s", strerror(errno)); close(lfd); return; } /* opentcp() */ static void closetcp(int fd[2]) { close(fd[0]); fd[0] = -1; close(fd[1]); fd[1] = -1; } /* closetcp() */ static char *strevents(char *dst, size_t lim, short events) { static const struct { char text[16]; int flag; } event[] = { { "POLLIN", POLLIN }, { "POLLOUT", POLLOUT }, { "POLLRDNORM", POLLRDNORM }, { "POLLWRNORM", POLLWRNORM }, { "POLLRDBAND", POLLRDBAND }, { "POLLWRBAND", POLLWRBAND }, { "POLLPRI", POLLPRI }, }; char *p, *pe; size_t i; p = dst; pe = dst + lim; #define p_putc(c) do { if (p < pe) *p++ = (c); } while (0) for (i = 0; i < countof(event); i++) { const char *tp; if (!(event[i].flag & events)) continue; if (p > dst) { p_putc(','); p_putc(' '); } for (tp = event[i].text; *tp; tp++) p_putc(*tp); } p_putc('\0'); assert(lim > 0); dst[lim - 1] = '\0'; #undef p_putc return dst; } /* strevents() */ #define POLLALL (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM|POLLRDBAND|POLLWRBAND|POLLPRI) static short pollpending(int fd, short events) { struct pollfd fds[1] = { { .fd = fd, .events = events } }; int nfd; if (-1 == (nfd = poll(fds, 1, 0))) panic("poll: %s", strerror(errno)); return (nfd > 0)? fds[0].revents : 0; } /* pollpending() */ static short selectpending(int fd, short events) { fd_set rd, wr, ex; short revents; FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); if (POLLIN & events) FD_SET(fd, &rd); if (POLLOUT & events) FD_SET(fd, &wr); if (POLLPRI & events) FD_SET(fd, &ex); if (-1 == select(fd + 1, &rd, &wr, &ex, &(struct timeval){ 0, 0 })) panic("select"); revents = 0; if (FD_ISSET(fd, &rd)) revents |= POLLIN; if (FD_ISSET(fd, &wr)) revents |= POLLOUT; if (FD_ISSET(fd, &ex)) revents |= POLLPRI; return revents; } /* selectpending() */ static short keventpending(int fd, short events) { #if BSD struct kevent event[3], *ep = event; short revents; int kq, i, n; #if defined EV_OOBAND if (events & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)) { int flags = EV_ADD|EV_ONESHOT; /* * NB: This is pointless as the xnu kernel discards * EV_OOBAND by doing * * kev.flags &= ~EV_SYSFLAGS * * during changelist processing in bsd/kern/kern_event.c. */ if (events & (POLLRDBAND|POLLPRI)) flags |= EV_OOBAND; EV_SET(ep, fd, EVFILT_READ, flags, 0, 0, 0); ep++; } #else if (events & (POLLIN|POLLRDNORM)) { EV_SET(ep, fd, EVFILT_READ, EV_ADD|EV_ONESHOT, 0, 0, 0); ep++; } #endif if (events & POLLOUT) { EV_SET(ep, fd, EVFILT_WRITE, EV_ADD|EV_ONESHOT, 0, 0, 0); ep++; } #if defined EVFILT_EXCEPT if (events & (POLLRDBAND|POLLPRI)) { EV_SET(ep, fd, EVFILT_EXCEPT, EV_ADD|EV_ONESHOT, NOTE_OOB, 0, 0); ep++; } #endif if (-1 == (kq = kqueue())) panic("kqueue"); if (-1 == (n = kevent(kq, event, ep - event, event, countof(event), &(struct timespec){ 0, 0 }))) panic("kevent"); close(kq); revents = 0; for (i = 0; i < n; i++) { if (event[i].filter == EVFILT_READ) { #if defined EV_OOBAND if (event[i].flags & EV_OOBAND) revents |= events & (POLLPRI|POLLRDBAND); /* NB: no way to know whether _only_ OOB available */ #endif revents |= events & (POLLIN|POLLRDNORM); } else if (event[i].filter == EVFILT_WRITE) { revents |= POLLOUT; #if defined EVFILT_EXCEPT } else if (event[i].filter == EVFILT_EXCEPT) { revents |= POLLPRI; #endif } } return revents; #else return 0; #endif } /* keventpending() */ static void showpending(int fd, short events) { char text[128]; short revents; info("checking events: %s", strevents(text, sizeof text, events)); revents = pollpending(fd, events); info(" poll: %s", strevents(text, sizeof text, revents)); revents = selectpending(fd, events); info(" select: read:%d write:%d except:%d", !!(revents & POLLIN), !!(revents & POLLOUT), !!(revents & POLLPRI)); revents = keventpending(fd, events); info(" kevent: %s", strevents(text, sizeof text, revents)); } /* showpending() */ static void checktcp(int fd[2]) { char data[32], urgent[1]; ssize_t n; memset(data, 'A', sizeof data); memset(urgent, '!', sizeof urgent); if (-1 == (n = send(fd[1], urgent, sizeof urgent, MSG_OOB))) panic("send"); info("sent %d bytes of OOB data", n); showpending(fd[0], POLLALL); if (-1 == (n = send(fd[1], data, sizeof data, 0))) panic("send"); info("sent %d bytes of normal data", n); showpending(fd[0], POLLALL); return; } /* checktcp() */ #define SHORTOPTS "64qvh" static void usage(FILE *fp) { fprintf(fp, \ "Usage: %s [-" SHORTOPTS "]\n" \ " -4 use IPv4\n" \ " -6 use IPv6\n" \ " -q do not show information messages\n" \ " -v enable verbose logging\n" \ " -h print this usage message\n" \ "\n" \ "Report bugs to \n", MAIN.progname); } /* usage() */ int main(int argc, char **argv) { int fd[2] = { -1, -1 }; int optc; MAIN.progname = progname((argc > 0)? argv[0] : NULL); while (-1 != (optc = getopt(argc, argv, SHORTOPTS))) { switch (optc) { case '4': MAIN.ipv4 = 1; break; case '6': MAIN.ipv6 = 1; break; case 'q': MAIN.verbose = 0; break; case 'v': MAIN.verbose++; break; case 'h': usage(stdout); return 0; default: usage(stderr); return EXIT_FAILURE; } } if (!(MAIN.ipv4 || MAIN.ipv6)) MAIN.ipv4 = MAIN.ipv6 = 1; if (MAIN.ipv4) { opentcp(fd, AF_INET); checktcp(fd); closetcp(fd); } if (MAIN.ipv6) { opentcp(fd, AF_INET6); checktcp(fd); closetcp(fd); } return 0; } /* main() */