summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BUILD1
-rw-r--r--CHANGELOG69
-rwxr-xr-xCONTENT63
-rw-r--r--FILES162
-rw-r--r--LICENSE42
-rw-r--r--Makefile123
-rw-r--r--README.md132
-rw-r--r--TARGETS79
-rw-r--r--VERSION1
-rw-r--r--alloc.c50
-rw-r--r--base64.c124
-rw-r--r--buffer.c226
-rw-r--r--byte.c99
-rw-r--r--case.c134
-rw-r--r--cdbmake.c158
-rw-r--r--cdbread.c169
-rw-r--r--conf-build30
-rwxr-xr-xconfigure69
-rw-r--r--constmap.c169
-rw-r--r--dnsstub/Makefile23
-rw-r--r--dnsstub/README.md171
-rw-r--r--dnsstub/TARGETS18
-rw-r--r--dnsstub/dns_cname.c59
-rw-r--r--dnsstub/dns_dfd.c76
-rw-r--r--dnsstub/dns_domain.c80
-rw-r--r--dnsstub/dns_dtda.c43
-rw-r--r--dnsstub/dns_ip.c198
-rw-r--r--dnsstub/dns_ipq.c236
-rw-r--r--dnsstub/dns_mx.c63
-rw-r--r--dnsstub/dns_name.c80
-rw-r--r--dnsstub/dns_nd.c48
-rw-r--r--dnsstub/dns_packet.c85
-rw-r--r--dnsstub/dns_random.c70
-rw-r--r--dnsstub/dns_rcip.c114
-rw-r--r--dnsstub/dns_rcrw.c141
-rw-r--r--dnsstub/dns_resolve.c39
-rw-r--r--dnsstub/dns_sortip.c45
-rw-r--r--dnsstub/dns_transmit.c436
-rw-r--r--dnsstub/dns_txt.c64
-rw-r--r--env.c136
-rw-r--r--errstr.c163
-rw-r--r--fd.c30
-rw-r--r--fmt.c85
-rw-r--r--getln.c43
-rw-r--r--getoptb.c100
-rw-r--r--include/alloc.h12
-rw-r--r--include/base64.h7
-rw-r--r--include/buffer.h63
-rw-r--r--include/byte.h21
-rw-r--r--include/case.h17
-rw-r--r--include/cdbmake.h39
-rw-r--r--include/cdbread.h38
-rw-r--r--include/close.h12
-rw-r--r--include/constmap.h21
-rw-r--r--include/direntry.h10
-rw-r--r--include/dnsresolv.h202
-rw-r--r--include/env.h29
-rw-r--r--include/error.h57
-rw-r--r--include/exit.h13
-rw-r--r--include/fd.h8
-rw-r--r--include/fifo.h12
-rw-r--r--include/fmt.h36
-rw-r--r--include/genalloc.h74
-rw-r--r--include/getln.h10
-rw-r--r--include/getoptb.h28
-rw-r--r--include/iopause.h28
-rw-r--r--include/ip.h107
-rw-r--r--include/lock.h14
-rw-r--r--include/logmsg.h31
-rw-r--r--include/ndelay.h13
-rw-r--r--include/open.h16
-rw-r--r--include/pathexec.h10
-rw-r--r--include/prot.h7
-rw-r--r--include/readclose.h14
-rw-r--r--include/readwrite.h9
-rw-r--r--include/rename.h6
-rw-r--r--include/scan.h12
-rw-r--r--include/seek.h15
-rw-r--r--include/select.h13
-rw-r--r--include/sig.h63
-rw-r--r--include/socket_if.h97
-rw-r--r--include/str.h25
-rw-r--r--include/stralloc.h49
-rw-r--r--include/tai.h74
-rw-r--r--include/taia.h40
-rw-r--r--include/timeout.h7
-rw-r--r--include/timeoutconn.h10
-rw-r--r--include/uint_t.h76
-rw-r--r--include/wait.h20
-rwxr-xr-xinstall14
-rw-r--r--iopause.c84
-rw-r--r--ip4.c166
-rw-r--r--ip6.c360
-rw-r--r--lock.c22
-rw-r--r--logmsg.c97
-rw-r--r--man/INSTALL12
-rw-r--r--man/alloc.368
-rw-r--r--man/buffer.3185
-rw-r--r--man/byte.382
-rw-r--r--man/case.3124
-rw-r--r--man/cdbmake.361
-rw-r--r--man/cdbread.3177
-rw-r--r--man/constmap.356
-rw-r--r--man/dns.3249
-rw-r--r--man/dnsstub.3109
-rw-r--r--man/env.368
-rw-r--r--man/error.375
-rw-r--r--man/fd.379
-rw-r--r--man/fmt.390
-rw-r--r--man/getln.3102
-rw-r--r--man/getoptb.322
-rw-r--r--man/iopause.387
-rw-r--r--man/ip4.373
-rw-r--r--man/ip6.398
-rw-r--r--man/logmsg.3109
-rw-r--r--man/pathexec.3124
-rw-r--r--man/scan.381
-rw-r--r--man/socket_bind.3117
-rw-r--r--man/socket_connect.3110
-rw-r--r--man/socket_if.397
-rw-r--r--man/socket_info.347
-rw-r--r--man/socket_recv.356
-rw-r--r--man/socket_send.376
-rw-r--r--man/socket_setup.3105
-rw-r--r--man/socket_tcp.358
-rw-r--r--man/socket_udp.358
-rw-r--r--man/str.361
-rw-r--r--man/stralloc.3160
-rw-r--r--man/taia.391
-rw-r--r--man/timeout.336
-rw-r--r--man/timeoutconn.352
-rw-r--r--man/wait.396
-rw-r--r--man/x.html99
-rw-r--r--ndelay.c24
-rw-r--r--open.c25
-rw-r--r--pathexec.c121
-rw-r--r--prot.c31
-rw-r--r--readclose.c43
-rw-r--r--scan.c120
-rw-r--r--seek.c30
-rwxr-xr-xsharedlib2
-rw-r--r--sig.c110
-rw-r--r--socket_bind.c79
-rw-r--r--socket_connect.c66
-rw-r--r--socket_if.c36
-rw-r--r--socket_info.c58
-rw-r--r--socket_recv.c39
-rw-r--r--socket_send.c62
-rw-r--r--socket_setup.c99
-rw-r--r--socket_tcp.c63
-rw-r--r--socket_udp.c57
-rw-r--r--str.c135
-rw-r--r--stralloc.c126
-rw-r--r--tai.c59
-rw-r--r--taia.c104
-rw-r--r--timeout.c59
-rw-r--r--timeoutconn.c112
-rw-r--r--uint128p.c100
-rw-r--r--uint16p.c40
-rw-r--r--uint32p.c47
-rw-r--r--uint64p.c62
-rw-r--r--uint8p.c39
-rw-r--r--wait.c25
163 files changed, 12177 insertions, 0 deletions
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..da0237c
--- /dev/null
+++ b/BUILD
@@ -0,0 +1 @@
+20230916182859
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..229e41e
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,69 @@
+fehQlibs-09: First public release based on Qlibs as
+ a common project of Kai Peter and Erwin Hoffmann.
+fehQlibs-10: Added IPV6_V6ONLY setsockoptions call.
+ Improved poll handling in iopause.c considering poll RC.
+ Added building of shared libraries + updated docs.
+fehQlibs-11: Added ia4_fmt, ia6_fmt and dns_cname (for *qmail).
+ Changed dns_transmit lookup time constants.
+fehQlibs-12: dns_ip, dns_cname, dns_txt, dns_name and dns_mx
+ return now the number of DNS answers received
+ unlike the number of bytes for the given output.
+ Added convenience routines for forthcoming s/qmail.
+fehQlibs-12a: Fixed missing DNS A lookup in dns_ip.c.
+fehQlibs-12b: Removed (one) obsolete return value check
+ for iopause in timeoutconn.c.
+fehQlibs-12c: Checking carefully all dnsstub return codes
+ while using a coherent scheme.
+fehQlibs-13: Bugs fixed: str_copy (wrong paranthesis setting)
+ UI changed: log() -> log_who()
+ Added: man str(3)
+fehQlibs-13a: Fixed wrong input boundary checking for multiple
+ DNSCACHE variables settings in dns_rcip.c. (Tx. J.W.)
+fehQlibs-13b: Added missing str_copyb() function in str.c.
+fehQlibs-13c: iopause it RC < 0 forces end of connection loop.
+ DNS stub return codes straightend with djbdnscurve6-36.
+fehQlibs-13d: Fixed integration bugs not displaying IPv6 addresses
+ (dns_ip.c, dns_mx.c).
+ Wrong dependency check in Makefile for SHAREDLIBS (tx. Alan S.).
+fehQlibs-14: Added 'const' for most input arguments + updated man:
+ byte, logmsg, str, pathexec. Patch contributed by Alan S.
+ Added byte_fill and case_upper function (potentially for SRS).
+ Included CLFAGS and LDFLAGS macros in Makefile.
+fehQlibs-15: Added the Guninski patch for alloc from Qualys (CVE-2005-1513).
+ dns_ipq (IP qualify) now with unified IPv4/IPv6 handling and evaluation
+ of DNS well-know names 'localhost' and 'ipv[4|6]-loopback' (RFC 6761).
+ Fixed DNS lookup for IPv6 addresses. Adjusted DNS man pages.
+fehQlibs-15a: Again, changes for dns_ipq.c concerning return codes.
+ Should have only impact in case using DJB's qualification format.
+ Documentation and man page fixes for DNS stub resolver.
+ GCC 10 linker warning for external 'ipv4socket' solved.
+fehQlibs-15b: GCC 10 compiler issues solved. DNS TLSA record correct value.
+fehQlibs-16: 19961114 change: dns_ip() now recognizes [1.2.3.4] and [f80::a]. tnx DS.
+ scan_long() included for mess822x support.
+ constmap() added from qmail and written man page.
+ Updated getln man page to refer buffer.
+ Changed ipv4/6-localhost and ipv4/6-loopback to ip4/6-localhost and ip4/6-loopback.
+fehQlibs-16+: GCC 10.2 conformance; ipv4socket definitition removed in socket_send()
+ and socket_connect().
+ This is a major desaster: 'external' definitions depend on time-of-usage.
+fehQlibs-17: Removed dependency on ipv4socket entirely.
+ Reworked socket interface + doc. Removed obsolete dns_sortip() function.
+ genalloc.h is now separate. Installation of man pages described.
+fehQlibs-18
+ Due to problems binding with IPv4-mapped IPv6 addresses for a DNS lookup,
+ splitted up randombind into randombind4 and randombind6.
+ socket operations on IPv4 use IPv4 address only. (tx. Kouichi).
+fehQlibs-19
+ Provided socket_accept4 again to avoid IPv4-mapped IPv6 address binding problems.
+fehQlibs-20
+ Added uint8p.c functions. Fixed dns_txt.c for multiple labels in RDATA section.
+fehQlibs-21
+ Fixed wrong return code for DNS_COM (tx Franz).
+ Fixed header and man page for env functions and included fd_coe in man fd.
+fehQlibs-22
+ Changed ipX_bytestring to return correctly the number of bytes processed.
+fehQlibs-22a
+ dns_ip A lookup recognizes now 'composit' ip/fqdn correctly; ie. 1.2.3.4.example.com.
+fehQlibs-23
+ More documentation for dns resolver routines.
+ Added function stralloc_free() which was alreday in the header.
diff --git a/CONTENT b/CONTENT
new file mode 100755
index 0000000..25fa27d
--- /dev/null
+++ b/CONTENT
@@ -0,0 +1,63 @@
+Content of fehQlibs
+-------------------
+
+This list contains the generated archive and supplementary
+object files together with their main header files (to be
+found under ./include) and man pages (located at ./man).
+The list is incomplete.
+
+Archive/Object | Headers | Description
+-----------------------------------------------
+alloc.a alloc.h alloc.3
+buffer.a buffer.h buffer.3
+ byte.h byte.3
+case.a case.h case.3
+cdb.a cdbmake.h cdbmake.3
+ cdbread.h cdbread.3
+constmap.a constmap.h constmap.3
+dnsresolv.a dnsresolv.h dns.3, dnsstub.3
+env.a env.h env.3
+fd.a fd.h fd.3
+fs.a fmt.h fmt.3
+ scan.h scan.3
+getln.a getln.h getln.3
+getopt.a getoptb.h getoptb.3
+ip.a ip.h ip4.3, ip6.3
+lock.a lock.h
+logmsg.a logmsg.h logmsg.3
+ error.h error.3
+ndelay.a ndelay.h
+open.a open.h
+seek.a seek.h
+sig.a sig.h
+socket.a socket_if.h socket_bind.3, socket_connect.3
+ ip.h socket_if.3, socket_info.3
+ socket_recv.3, socket_send.3
+ socket_setup.3, socket_tcp.3
+ socket_udp.3
+str.a str.h
+stralloc.a stralloc.h stralloc.3
+time.a tai.h, taia.3
+ timeout.h timeout.3
+ timeoutconn.h timeoutconn.3
+ iopause.h iopause.3
+ select.h
+wait.a wait.h wait.3
+
+pathexec.o pathexec.h pathexec.3
+prot.o prot.h
+byte.o byte.h byte.3
+ base64.h
+readclose.o readclose.h
+uint8p.o uint_t.h
+uint16p.o uint_t.h
+uint32p.o uint_t.h
+uint64p.o uint_t.h
+uint128p.o uint_t.h
+
+
+Two main archives are generated:
+
+- (lib)qlibs.a -- including all above, except for
+- (lib)dnsresolv.a -- routines located at ./dnsstub.
+
diff --git a/FILES b/FILES
new file mode 100644
index 0000000..85b59ef
--- /dev/null
+++ b/FILES
@@ -0,0 +1,162 @@
+./BUILD = 3119c820453717278177af253188e95c
+./CHANGELOG = ba424a3dd1cdb12d4c345a11de0a0817
+./CONTENT = 3478f3a562d003b32aa0762e93a75b26
+./LICENSE = 43df073a04022f63913c379646905da6
+./Makefile = abba48d9d52ca48f050208231de5c783
+./README.md = 60875952ce23f26102f84757329b9b31
+./TARGETS = a3800d82d6b95a77536b9c719b519b73
+./VERSION = 6cfc8c14f10d3a39524fed09959cdffa
+./alloc.c = 514e44b39a3c964d0e17efda78e9b8c7
+./base64.c = 3a2830988bc99a672f09557b288dfd81
+./buffer.c = 04489d0d0961e69081381983bbe26f56
+./byte.c = 67806bc236105f085b46ae4327e437ae
+./case.c = f03a5e250d1aff49d1304670f5b7254a
+./cdbmake.c = b038d803422618d1f7eaade9716a858e
+./cdbread.c = 5eeb568fb122813c7ac0a3d273b98591
+./conf-build = 52f94f184b80b29aa7800265da87de55
+./configure = 145494889bebdcc519320472d84566b6
+./constmap.c = e49f40971441ca0187a19a1feba012f1
+./dnsstub/Makefile = 8dcd8f647b1f8d7ec755cea81ed57a37
+./dnsstub/README.md = d50e01dbf9223e49ec52f131fa5916d8
+./dnsstub/TARGETS = 723bd9a97f59f9091fbe2fba10ef8e5e
+./dnsstub/dns_cname.c = f70db88df864eab7694b747fd77dbb6d
+./dnsstub/dns_dfd.c = 149da24f7b57761d9c0eec3278b63fb0
+./dnsstub/dns_domain.c = e79ee91862f5d06ec892a228489aca3f
+./dnsstub/dns_dtda.c = f9fc0ba7ea3cdaad5e85c0d0edd479a7
+./dnsstub/dns_ip.c = 811eec1cfe97536d6ea9a1d5e48801ce
+./dnsstub/dns_ipq.c = 273479eeae125ffbddaf4e95fbcfe0f0
+./dnsstub/dns_mx.c = 01c29dde656fbaf7ab19c4c3a28664ac
+./dnsstub/dns_name.c = 31cb8e08404ab22dca6c6098a2fc9317
+./dnsstub/dns_nd.c = 8d1c5bf17f2242d22d64903393a73768
+./dnsstub/dns_packet.c = d6e4035938817a47f10578265d52b0ab
+./dnsstub/dns_random.c = 7429f11a2181d0771046ffd85f4acb38
+./dnsstub/dns_rcip.c = 2762d62ad3fffe4caaadbafd3c007211
+./dnsstub/dns_rcrw.c = c57be390844bb9f26fb44dbce2837136
+./dnsstub/dns_resolve.c = 8ec11fea2c0bb173b73230e0f41b055b
+./dnsstub/dns_sortip.c = aa8effa053c3d6899a17242d3eebae40
+./dnsstub/dns_transmit.c = 8c2b07aa13b0abcc7697cb37d9fef821
+./dnsstub/dns_txt.c = 4a8d105d506ec11ac3c307f405393e42
+./env.c = 2aabee824c86ed685a17cc8b6eb6e1cf
+./errstr.c = e89bde5dd0320f24a399967ef64c4a27
+./fd.c = e1643064b0f9571c92cb17f5e6c2c3c9
+./fmt.c = b2627c34a39b7a492ec07969931c6bc9
+./getln.c = bb694220f8bedb7f7f8a8dd8a6d91347
+./getoptb.c = 92c4305401f59359b8dac69e1df84647
+./include/alloc.h = fc20a6d23d60eeaa84a913b19cea2a6f
+./include/base64.h = 518e08181673aff5e497eb526a427b1d
+./include/buffer.h = 4ff0a37e7b614e0d80123782f706f54a
+./include/byte.h = 6a7dc0cca790d6cbac23731e71c665fb
+./include/case.h = b0f7e31bd1e121850c3d1c3dc4c38d5c
+./include/cdbmake.h = 44c1444111c3fabd59ed63258b5d54ad
+./include/cdbread.h = d40f94c976c1cadb575044a3ef965978
+./include/close.h = a1a84cfde83c8cef39c5ca5f52796aee
+./include/constmap.h = f217b422c4d74890b6675e72dca753bf
+./include/direntry.h = ed9e2af8fed1975b017883488b0db2f7
+./include/dnsresolv.h = 717941c00c4e369241000ef70cf848f6
+./include/env.h = 70f96abf159cedaad9bbbfa4c27bc8a7
+./include/error.h = 7886039fc72d57919c1c6db56b5fc324
+./include/exit.h = 45e2e3e56314e2332837a61e1c866423
+./include/fd.h = d76deb5c0ed33bf690e25f4e5fc94359
+./include/fifo.h = 4aac02038e14acac8c2b6e28ec992406
+./include/fmt.h = 71b6a242a848044e65e407a5647d21eb
+./include/genalloc.h = 7d88ca8ee9f933f1cbb9ed0597dddf60
+./include/getln.h = 040da2a02c37a8aa50ea614410f2ed0c
+./include/getoptb.h = 988a7314fca7dff960dcffc134cc404c
+./include/iopause.h = b7680ea87e84b568b8f1f7c281ae739a
+./include/ip.h = a66be1788f98dc795cceeb4b0f0c9bb2
+./include/lock.h = 224879d2b1671dad51f8bbcd6f7c2426
+./include/logmsg.h = d90310844d3fa6cfbab0b37b54170d92
+./include/ndelay.h = cc914c481a7652e9aef10acdf69de7ff
+./include/open.h = 4f4d7371bbefccf2cec365f7811e874e
+./include/pathexec.h = 37af5ae960eb91892744c7a3802737d1
+./include/prot.h = 0f6816695dc713f927e09270696835a2
+./include/readclose.h = cfe01c7d37be0a17a492534faf2717b2
+./include/readwrite.h = 58ab91bb5433b835e97fe78fd6780633
+./include/rename.h = 3d13ce7562cd4f87578b94adaa5857f0
+./include/scan.h = 49a8521c05e6c06436637af85e3f7234
+./include/seek.h = be3607921c13b338365ecbf1e9e24f38
+./include/select.h = 019f9aadad2f1a5d14aa5ec8cdee6065
+./include/sig.h = 3012ce2f2f452e3b822aeb7c9765640a
+./include/socket_if.h = ba79b96dcdcd50983e7328ee28b209ec
+./include/str.h = 9b010e0767b85c44804424ad9436ab5a
+./include/stralloc.h = c9a7b068f19c896891c30fd12bd078bb
+./include/tai.h = ca3390aa0aa229108a4864c0f4a40e6f
+./include/taia.h = 4e9a6bd8f7f6884932d72a48d34301ef
+./include/timeout.h = be7d9294fe124d22abdd20204abcaa2f
+./include/timeoutconn.h = 3bc13384d2d2355d7327a73428027822
+./include/uint_t.h = 2bdc1c29b1a66a792dc41004b1e4d2fe
+./include/wait.h = fc9c628f22a862bb9af69fdd7a6b61a8
+./install = f6468f1ce0a61e581df2d3278d4f9d24
+./iopause.c = 7cbd896fbcca2c26b218a3299543c905
+./ip4.c = 8cf7ae24e43da943e4695f5cb4a50f6c
+./ip6.c = 16192261767a3056adc84c10bbcaf8ce
+./lock.c = 50aa9fca4611c86f943c49256b7a001a
+./logmsg.c = 7335546b87caf29a8fbf41a8ac89169c
+./man/INSTALL = c83cc4663e122994ea65d269982df554
+./man/alloc.3 = 2f666bfc6cb2d755e4047be92853c4c9
+./man/buffer.3 = 14df7a93cc6131f83e84ee438ec29f05
+./man/byte.3 = 3927c67098f880846b6d71c8d69d63af
+./man/case.3 = 6ee29d92e49bb6f91b2e8029c7a5ad04
+./man/cdbmake.3 = 54e604b19d139b0c5b451b86c58802d5
+./man/cdbread.3 = 81a48e1258f4919861b7537a7759c0a5
+./man/constmap.3 = 3308860b3c8b2032dff86d64aab332d0
+./man/dns.3 = 3a3834e729315537509ddfd06d76ecd7
+./man/dnsstub.3 = 63081eeab017fbf754f854010d8b1dc4
+./man/env.3 = 27c3a784e43a5ec37c2918014bcac3f8
+./man/error.3 = 2d2b4e9800b73983848581814065b074
+./man/fd.3 = c6115dbec98169a66af436ea929cc0bb
+./man/fmt.3 = 419b2e143438a3351f6a21828823a367
+./man/getln.3 = 5c4930df89e1132257de8cfa7dbbac3a
+./man/getoptb.3 = eb4ff31f223bcf561043fd96e8bf7fcd
+./man/iopause.3 = 4705db96ec459124a7cac08c75621b43
+./man/ip4.3 = 0ffc1d509fec9b8432638ba2e244599e
+./man/ip6.3 = 480b6a5f03a5a07c122c0dd427b5f671
+./man/logmsg.3 = 1aac69f1603735534b831b062a982b96
+./man/pathexec.3 = 0f320b89031f14099389f245ff0082c4
+./man/scan.3 = f838564ca05d7eb75e0f94601da0e247
+./man/socket_bind.3 = 603252820735a0586d2473d60b5368ba
+./man/socket_connect.3 = 7ede000da47f9d050b4a8b4a548c3fba
+./man/socket_if.3 = 939ee90dbc06e46530bdebb76f7868b7
+./man/socket_info.3 = 25bb53af29cacae415052b795bc9dbbf
+./man/socket_recv.3 = c7fbce83d73180901bacfd42329a1961
+./man/socket_send.3 = 559b6ca0e4e488c961adfec91298cb57
+./man/socket_setup.3 = 06509120a17c0fdc1e74183e4fdeec6e
+./man/socket_tcp.3 = 88a7db7c3f87f6e55676a8bbea04bebb
+./man/socket_udp.3 = 83ef36e87a0616a1f89c62ee10443d20
+./man/str.3 = 8aada587fbeaa6642b92f8259253e864
+./man/stralloc.3 = 62f213c041e32f3b9c807e69b9c2b0a4
+./man/taia.3 = ca40e8e64a68b1452629067f7f5ba82e
+./man/timeout.3 = c70003889aeb948b0083d6ecaf9a78fa
+./man/timeoutconn.3 = 2fa5bc7c6c042bb7c80c431bf5eac5b2
+./man/wait.3 = 13f3301b18e333666659b8e49864d389
+./man/x.html = 4eda1f156fe2348daebfbddf7a33c022
+./ndelay.c = 15de43ad20cb35ec2c80df851f606bd9
+./open.c = 1a793911a2e57e01ffa7bda1d08db8f4
+./pathexec.c = 23c0c5d5013a19eb4f6b4b03c87db8ec
+./prot.c = e1568767fcfb5fc119da935d9d3cf27f
+./readclose.c = deabaa3b8b50201d560bfc3c584ca1c3
+./scan.c = 6792e3f60344e4cf5fcee4fad55fae38
+./seek.c = 5c2ee16ea4ec99584d702ff55d9c9d19
+./sharedlib = bde12e503174244d57ad35135b5d25e9
+./sig.c = a8a98f1c8c27eece5f341ec05f46e80e
+./socket_bind.c = b463068c7160e01d6fb8c3fb903d5c8e
+./socket_connect.c = 96c784f10d7ea96720b10b5844fd4cd0
+./socket_if.c = c841f0681878e90fbac87dc4ce7ab6fe
+./socket_info.c = 623a97efe396c56ac31e0964b4b8025a
+./socket_recv.c = 66914af66c9bda8c2239f5308648cbac
+./socket_send.c = 80647ec33d769a5f5a7ead0b7e881015
+./socket_setup.c = 229bca06ab2f68f483b525b8e8e13886
+./socket_tcp.c = 5c8565ee6668341730d3b9eac7de0a7b
+./socket_udp.c = 50a0bb63dcea40f7c146e17b1aa1050b
+./str.c = ff3952e9e6bf49cd34a2bcafaf168208
+./stralloc.c = 85b65c1193b0d93039530edad5e1cab5
+./tai.c = 823b0274b23207ebade293fd14a1ab31
+./taia.c = 63de9eb6672c1c43296b383224fccc74
+./timeout.c = 35e6965f54a50838b6d3ae61ee8fd962
+./timeoutconn.c = 6acef79e4c36a795da73f2c0eb010d39
+./uint128p.c = 83da4a537b781df80316da49e54735e8
+./uint16p.c = 61e6fed3598e3ceaa41a75d91657bef0
+./uint32p.c = 5a1965fbf0e288aebbae066f341a442b
+./uint64p.c = b95d3d869c5ecc6fe65f65c04dcceeb2
+./uint8p.c = 4db811c33b483cd9fe08ca72c254bb0c
+./wait.c = 4048ebc18bc71a91e21da7ccab205558
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3b1bddd
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,42 @@
+AUTHOR
+======
+
+Author:
+ Dr. Erwin Hoffmann - FEHCom Germany
+Web-Site:
+ https://www.fehcom.de
+E-Mail:
+ feh@fehcom.de
+
+
+LICENSE
+=======
+
+fehQlibs is free software placed into the Public Domain.
+fehQlibs is based on D.J. Bernstein's 'qmail' and 'ucspi-tcp' also put in the Public Domain.
+
+This includes:
+ You can download and use fehQlibs (and parts of it) as you like.
+ You can modify the source code without notification to or permission by the author.
+Please check:
+ http://www.cr.yp.to/softwarelaw.html
+Note:
+ fehQlibs may use/may depend on third party software with different
+ license and/or distribution conditions.
+
+
+FITNESS
+=======
+
+The Author does not guarantee a specific fitness of fehQlibs.
+If you use fehQlibs, it's on your own risk.
+
+
+DISTRIBUTION
+============
+
+fehQlibs may be included in ports and packages under the following conditions:
+
+ - The files VERSION and BUILD has to be part of the distribution.
+ - This LICENSE file has to be included in the distribution.
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8426968
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,123 @@
+# Makefile for qlibs
+
+SHELL=/bin/sh
+
+# static flags - DO NOT EDIT!
+CCFLAGS=-Iinclude
+
+SRCS=*.c
+OBJS=*.o
+COMPILE=./compile
+MAKELIB=./makelib
+SHAREDLIB=./sharedlib
+
+default: clean check libs
+
+check:
+ @[ -f $(COMPILE) ] && [ -f $(MAKELIB) ] || ./configure
+
+clean:
+ @echo -n Cleaning up libs ...
+ @rm -f `cat TARGETS`
+ @cd dnsstub ; make -s clean ; cd ..
+ @echo " done!"
+
+core: alloc.a buffer.a case.a cdb.a constmap.a env.a \
+fd.a fs.a getln.a getopt.a lock.a logmsg.a ndelay.a open.a \
+seek.a sig.a str.a stralloc.a time.a wait.a \
+ip.a socket.a dnsresolv.a
+
+libs: obj core qlibs.a dnsresolv.a
+
+obj:
+ @echo Making all in qlibs ...
+ $(COMPILE) $(CCFLAGS) $(CFLAGS) $(SRCS)
+
+install: setup
+
+setup:
+ @./install
+
+shared: $(COMPILE) $(SHAREDLIB) $(OBJS) ./dnsstub/$(OBJS)
+ @echo Building shared qlibs ...
+ $(SHAREDLIB) $(LDFLAGS) $(OBJS) -o libqlibs.so
+ $(SHAREDLIB) $(LDFLAGS) ./dnsstub/$(OBJS) -o libdnsresolv.so
+
+qlibs.a: obj socket.a
+ $(MAKELIB) $(LDFLAGS) qlibs.a *.o
+ ln -sf qlibs.a libqlibs.a
+
+dnsresolv.a:
+ @echo "Building @dnsresolv lib ..."
+ @cd dnsstub ; make
+ @cp dnsstub/dnsresolv.a dnsresolv.a
+ ln -sf dnsresolv.a libdnsresolv.a
+
+socket.a: ip.a
+ @echo "Building socket lib ..."
+ $(MAKELIB) $(LDFLAGS) socket.a socket_if.o socket_bind.o socket_connect.o \
+ socket_info.o socket_setup.o socket_tcp.o socket_udp.o ip.a
+
+# build each file separately (backwards compat) - better use recipe qlibs.a
+alloc.a: alloc.o
+ $(MAKELIB) $(LDFLAGS) alloc.a alloc.o
+
+buffer.a: buffer.o
+ $(MAKELIB) $(LDFLAGS) buffer.a buffer.o
+
+case.a: case.o
+ $(MAKELIB) $(LDFLAGS) case.a case.o
+
+cdb.a: cdbread.o cdbmake.o uint32p.o seek.o buffer.o
+ $(MAKELIB) $(LDFLAGS) cdb.a cdbread.o cdbmake.o uint32p.o seek.o buffer.o
+
+constmap.a: constmap.o alloc.o buffer.o scan.o
+ $(MAKELIB) $(LDFLAGS) constmap.a constmap.o alloc.o buffer.o scan.o
+
+env.a: env.o
+ $(MAKELIB) $(LDFLAGS) env.a env.o
+
+fd.a: fd.o
+ $(MAKELIB) $(LDFLAGS) fd.a fd.o
+
+fs.a: fmt.o scan.o
+ $(MAKELIB) $(LDFLAGS) fs.a fmt.o scan.o
+
+getln.a: getln.o
+ $(MAKELIB) $(LDFLAGS) getln.a getln.o
+
+getopt.a: getoptb.o
+ $(MAKELIB) $(LDFLAGS) getopt.a getoptb.o
+
+ip.a: ip4.o ip6.o socket_if.o
+ $(MAKELIB) $(LDFLAGS) ip.a ip4.o ip6.o socket_if.o
+
+lock.a: lock.o
+ $(MAKELIB) $(LDFLAGS) lock.a lock.o
+
+ndelay.a: ndelay.o
+ $(MAKELIB) $(LDFLAGS) ndelay.a ndelay.o
+
+open.a: open.o
+ $(MAKELIB) $(LDFLAGS) open.a open.o
+
+seek.a: seek.o
+ $(MAKELIB) $(LDFLAGS) seek.a seek.o
+
+sig.a: sig.o
+ $(MAKELIB) $(LDFLAGS) sig.a sig.o
+
+str.a: str.o byte.o stralloc.o alloc.o
+ $(MAKELIB) $(LDFLAGS) str.a str.o byte.o stralloc.o alloc.o
+
+stralloc.a: stralloc.o alloc.o
+ $(MAKELIB) $(LDFLAGS) stralloc.a stralloc.o alloc.o
+
+logmsg.a: errstr.o logmsg.o
+ $(MAKELIB) $(LDFLAGS) logmsg.a errstr.o logmsg.o
+
+time.a: iopause.o tai.o taia.o timeout.o timeoutconn.o
+ $(MAKELIB) $(LDFLAGS) time.a iopause.o tai.o taia.o timeout.o timeoutconn.o
+
+wait.a: wait.o
+ $(MAKELIB) $(LDFLAGS) wait.a wait.o
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0498792
--- /dev/null
+++ b/README.md
@@ -0,0 +1,132 @@
+/*! \mainpage
+
+qlibs - C library and headers based on "djb's libs" and libowfat
+================================================================
+
+Initially, these libs were excluded from *qmail and forseen to use with eQmail.
+As the packages of djb (D.J. Bernstein) uses these libs in different versions,
+there comes up the need to have a consistent state.
+As time was going on, there was also a need to include additional functionality
+and up-to-date best practices.
+Short - this was the point where some parts from libowfat were included.
+
+The current fehQlibs rather accustom to the needs of different OS.
+
+In theorie - these libs can be used with any djb software.
+
+Achievements
+------------
+
+- IPv4/IPv6 unification:
+
+The current package supports unified IPv4/IPv6 socket calls, while IPv6 is the default.
+
+- DNS stub-resolver:
+
+The provided DNS stub-resolver is IPv4 and IPv6 aware.
+See the README in the ./dnsstub directory for details.
+
+
+Installation
+------------
+
+Some build options can be configured through 'conf-build'.
+Read the comments in this file for more information.
+
+* Building:
+
+a) Static libs:
+
+Execute
+
+ $ make
+
+which does the steps of './configure && make'.
+Don't forget to make sure that libs and headers will be found by the compiler/linker.
+For convenience, link the current version of fehQlibs to qlibs
+
+ $ ln -s fehQlibs-XY qlibs
+
+b) Shared objects libs:
+
+You can also build dynamic libaries for qlibs and dnsresolv. After
+
+ $ make
+
+call
+
+ $ make shared
+
+* Deployment:
+
+In case conf-build has been customized to include the location of
+
+- HDRDIR
+- LIBDIR
+
+issue
+
+ $ make install
+
+to deploy the include files and libs at the given location.
+By default, for the libs the parent directory is used.
+
+* Verification:
+
+Check the ELFCLASS of the generated binaries in the source directory while calling
+
+ $ file open.o
+
+They need to comply with your OS. See 'conf-build' for options.
+
+* Resulting libraries:
+
+The following libraries are generated:
+
+- qlibs.a - static basic [w/o DNS] lib (linked to libqlibs.a)
+- dnsresolv.a - static DNS resolver lib (linked to libdnsresolv.a)
+
+and perhaps
+
+- libqlibs.so - dynamic, position independent (PIC) 'shared object' lib
+- libdnsresolv.so - dynamic, position independent (PIC) 'shared object' lib
+
+These libs are of type ELF64 or ELF32 depending on the system.
+
+Use 'ar -t' to get the included members of the static libs.
+Use 'nm' to view the members and symbols of the libs.
+
+The shared object libs are known NOT not work on MacOS.
+
+
+Man pages
+---------
+
+In the ./man directory the current man pages for the basic qlibs routines are included.
+Determine the current $mandir and (as root) install the man pages:
+
+ $ cp *.3 $mandir/man3/
+
+
+Packaging
+---------
+
+The fehQlibs are incompatible with Gentoo's parallel compilation flag '-jN' for N > 1.
+
+
+Compilation
+-----------
+
+Starting with version fehQlibs-15b, GCC 10 and Clang compatibility is provided,
+which means ISO C conformance.
+Since fehQlibs-17 strong coupling of application routines previously introduced
+by the global (external) 'ipv4socket' variable are avoided.
+stralloc.h is decoupled from genalloc.h (to be included separately).
+This respects the GCC 10 compilation unit behavior.
+
+
+-----
+
+Updated: 20170329, Kai Peter
+
+Updated: 20220706, Erwin Hoffmann
diff --git a/TARGETS b/TARGETS
new file mode 100644
index 0000000..df6d6ee
--- /dev/null
+++ b/TARGETS
@@ -0,0 +1,79 @@
+alloc.a
+alloc.o
+base64.o
+buffer.a
+buffer.o
+byte.o
+case.a
+case.o
+cdb.a
+cdbmake.o
+cdbread.o
+constmap.a
+constmap.o
+compile
+dnsresolv.a
+env.a
+env.o
+errstr.o
+fd.a
+fd.o
+fmt.o
+fs.a
+getln.a
+getln.o
+getopt.a
+getoptb.o
+iopause.o
+ip.a
+ip4.o
+ip6.o
+libdnsresolv.a
+libqlibs.a
+lock.a
+lock.o
+logmsg.a
+logmsg.o
+makelib
+ndelay.a
+ndelay.o
+open.a
+open.o
+pathexec.o
+prot.o
+qlibs.a
+readclose.o
+scan.o
+seek.a
+seek.o
+sig.a
+sig.o
+socket.a
+socket_bind.o
+socket_connect.o
+socket_if.o
+socket_info.o
+socket_recv.o
+socket_send.o
+socket_setup.o
+socket_tcp.o
+socket_udp.o
+str.a
+str.o
+stralloc.a
+stralloc.o
+sysmsg.o
+tai.o
+taia.o
+time.a
+timeout.o
+timeoutconn.o
+uint128p.o
+uint16p.o
+uint32p.o
+uint64p.o
+uint8p.o
+wait.a
+wait.o
+libdnsresolv.so
+libqlibs.so
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..cf310ee
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+fehQlibs-21
diff --git a/alloc.c b/alloc.c
new file mode 100644
index 0000000..8ec0a7e
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include "byte.h"
+#include "alloc.h"
+
+#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
+#define SPACE 4096 /* must be multiple of ALIGNMENT */
+
+typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
+static aligned realspace[SPACE / ALIGNMENT];
+#define space ((char *) realspace)
+static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
+
+/*@null@*//*@out@*/char *alloc(unsigned int n) {
+ char *x;
+
+/* Guninski exploit + patch from Qualys (CVE-2005-1513) */
+
+ if (n >= (INT_MAX >> 3)) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
+ if (n <= avail) { avail -= n; return space + avail; }
+ x = malloc(n);
+ if (!x) errno = ENOMEM;
+ return x;
+}
+
+void alloc_free(char *x)
+{
+ if (x >= space)
+ if (x < space + SPACE)
+ return; /* XXX: assuming that pointers are flat */
+ free(x);
+}
+
+int alloc_re(char **x,unsigned int m,unsigned int n)
+{
+ char *y;
+
+ y = alloc(n);
+ if (!y) return 0;
+ byte_copy(y,m,*x);
+ qfree(*x);
+ *x = y;
+ return 1;
+}
diff --git a/base64.c b/base64.c
new file mode 100644
index 0000000..986201e
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,124 @@
+#include "base64.h"
+#include "stralloc.h"
+#include "str.h"
+
+/**
+ @file base64.c
+ @author unkown
+ @source unknown
+ @brief base64 en+decoding of strings
+*/
+
+static char *b64alpha =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+#define B64PAD '='
+
+/* returns 0 ok, 1 illegal, -1 problem */
+
+int b64decode(const unsigned char *in,int l,stralloc *out) /* not null terminated */
+{
+ int p = 0;
+ int n;
+ unsigned int x;
+ int i, j;
+ char *s;
+ unsigned char b[3];
+
+ if (l == 0) {
+ if (!stralloc_copys(out,"")) return -1;
+ return 0;
+ }
+
+ while (in[l-1] == B64PAD) {
+ p ++;
+ l--;
+ }
+
+ n = (l + p) / 4;
+ i = (n * 3) - p;
+ if (!stralloc_ready(out,i)) return -1;
+ out->len = i;
+ s = out->s;
+
+ for (i = 0; i < n - 1 ; i++) {
+ x = 0;
+ for (j = 0; j < 4; j++) {
+ if(in[j] >= 'A' && in[j] <= 'Z')
+ x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+ else if(in[j] >= 'a' && in[j] <= 'z')
+ x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+ else if(in[j] >= '0' && in[j] <= '9')
+ x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+ else if(in[j] == '+')
+ x = (x << 6) + 62;
+ else if(in[j] == '/')
+ x = (x << 6) + 63;
+ else if(in[j] == '=')
+ x = (x << 6);
+ }
+
+ s[2] = (unsigned char)(x & 255); x >>= 8;
+ s[1] = (unsigned char)(x & 255); x >>= 8;
+ s[0] = (unsigned char)(x & 255); x >>= 8;
+ s += 3; in += 4;
+ }
+
+ x = 0;
+ for (j = 0; j < 4; j++) {
+ if(in[j] >= 'A' && in[j] <= 'Z')
+ x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
+ else if(in[j] >= 'a' && in[j] <= 'z')
+ x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
+ else if(in[j] >= '0' && in[j] <= '9')
+ x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
+ else if(in[j] == '+')
+ x = (x << 6) + 62;
+ else if(in[j] == '/')
+ x = (x << 6) + 63;
+ else if(in[j] == '=')
+ x = (x << 6);
+ }
+
+ b[2] = (unsigned char)(x & 255); x >>= 8;
+ b[1] = (unsigned char)(x & 255); x >>= 8;
+ b[0] = (unsigned char)(x & 255); x >>= 8;
+
+ for (i = 0; i < 3 - p; i++)
+ s[i] = b[i];
+
+ return 0;
+}
+
+int b64encode(stralloc *in,stralloc *out) /* not null terminated */
+{
+ unsigned char a, b, c;
+ int i;
+ char *s;
+
+ if (in->len == 0) {
+ if (!stralloc_copys(out,"")) return -1;
+ return 0;
+ }
+
+ i = in->len / 3 * 4 + 4;
+ if (!stralloc_ready(out,i)) return -1;
+ s = out->s;
+
+ for (i = 0; i < in->len; i += 3) {
+ a = in->s[i];
+ b = i + 1 < in->len ? in->s[i + 1] : 0;
+ c = i + 2 < in->len ? in->s[i + 2] : 0;
+
+ *s++ = b64alpha[a >> 2];
+ *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
+
+ if (i + 1 >= in->len) *s++ = B64PAD;
+ else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
+
+ if (i + 2 >= in->len) *s++ = B64PAD;
+ else *s++ = b64alpha[c & 63];
+ }
+ out->len = s - out->s;
+
+ return 0;
+}
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000..92d72b1
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,226 @@
+#include <unistd.h>
+#include "buffer.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+
+/**
+ @file buffer.c
+ @author djb
+ @brief input/output routines
+*/
+
+void buffer_init(buffer *s,ssize_t (*op)(),int fd,char *buf,size_t len)
+{
+ s->x = buf;
+ s->fd = fd;
+ s->op = op;
+ s->p = 0;
+ s->n = len;
+}
+
+ssize_t buffer_0_read(int fd,char *buf,size_t len)
+{
+ if (buffer_flush(buffer_1) == -1) return -1;
+ return read(fd,buf,len);
+}
+
+char buffer_0_space[BUFFER_INSIZE];
+static buffer it0 = BUFFER_INIT(buffer_0_read,0,buffer_0_space,sizeof(buffer_0_space));
+buffer *buffer_0 = &it0;
+
+char buffer_1_space[BUFFER_OUTSIZE];
+static buffer it1 = BUFFER_INIT(write,1,buffer_1_space,sizeof(buffer_1_space));
+buffer *buffer_1 = &it1;
+
+char buffer_2_space[BUFFER_OUTSIZE];
+static buffer it2 = BUFFER_INIT(write,2,buffer_2_space,sizeof(buffer_2_space));
+buffer *buffer_2 = &it2;
+
+char buffer_0_small[BUFFER_SMALL];
+static buffer is0 = BUFFER_INIT(buffer_0_read,0,buffer_0_small,sizeof(buffer_0_small));
+buffer *buffer_0small = &is0;
+
+char buffer_1_small[BUFFER_SMALL];
+static buffer is1 = BUFFER_INIT(write,1,buffer_1_small,sizeof(buffer_1_small));
+buffer *buffer_1small = &is1;
+
+char buffer_2_small[BUFFER_SMALL];
+static buffer is2 = BUFFER_INIT(write,2,buffer_2_small,sizeof(buffer_2_small));
+buffer *buffer_2small = &is2;
+
+ssize_t buffer_unixread(int fd,char *buf,size_t len)
+{
+ return read(fd,buf,len);
+}
+
+ssize_t buffer_unixwrite(int fd,char *buf,size_t len)
+{
+ return write(fd,buf,len);
+}
+
+int buffer_copy(buffer *bout,buffer *bin)
+{
+ int n;
+ char *x;
+
+ for (;;) {
+ n = buffer_feed(bin);
+ if (n < 0) return -2;
+ if (!n) return 0;
+ x = buffer_PEEK(bin);
+ if (buffer_put(bout,x,n) == -1) return -3;
+ buffer_SEEK(bin,n);
+ }
+}
+
+static int oneread(ssize_t (*op)(),int fd,char *buf,size_t len)
+{
+ int r;
+
+ for (;;) {
+ r = op(fd,buf,len);
+ if (r == -1) if (errno == EINTR) continue;
+ return r;
+ }
+}
+
+static int getthis(buffer *s,char *buf,size_t len)
+{
+ if (len > s->p) len = s->p;
+ s->p -= len;
+ byte_copy(buf,len,s->x + s->n);
+ s->n += len;
+ return len;
+}
+
+int buffer_feed(buffer *s)
+{
+ int r;
+
+ if (s->p) return s->p;
+ r = oneread(s->op,s->fd,s->x,s->n);
+ if (r <= 0) return r;
+ s->p = r;
+ s->n -= r;
+ if (s->n > 0) byte_copyr(s->x + s->n,r,s->x);
+ return r;
+}
+
+int buffer_bget(buffer *s,char *buf,size_t len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,s->n);
+ r = buffer_feed(s);
+ if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+int buffer_get(buffer *s,char *buf,size_t len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+ r = buffer_feed(s);
+ if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+char *buffer_peek(buffer *s)
+{
+ return s->x + s->n;
+}
+
+void buffer_seek(buffer *s,size_t len)
+{
+ s->n += len;
+ s->p -= len;
+}
+
+static int allwrite(ssize_t (*op)(),int fd,const char *buf,size_t len)
+{
+ int w;
+
+ while (len) {
+ w = op(fd,buf,len);
+ if (w == -1) {
+ if (errno == EINTR) continue;
+ return -1; /* note that some data may have been written */
+ }
+ if (w == 0) /* luser's fault */
+ ;
+ buf += w;
+ len -= w;
+ }
+ return 0;
+}
+
+int buffer_flush(buffer *s)
+{
+ int p;
+
+ p = s->p;
+ if (!p) return 0;
+ s->p = 0;
+ return allwrite(s->op,s->fd,s->x,p);
+}
+
+int buffer_putalign(buffer *s,const char *buf,size_t len)
+{
+ unsigned int n;
+
+ while (len > (n = s->n - s->p)) {
+ byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n;
+ if (buffer_flush(s) == -1) return -1;
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int buffer_put(buffer *s,const char *buf,size_t len)
+{
+ unsigned int n;
+
+ n = s->n;
+ if (len > n - s->p) {
+ if (buffer_flush(s) == -1) return -1;
+ /* now s->p == 0 */
+ if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE;
+ while (len > s->n) {
+ if (n > len) n = len;
+ if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+ buf += n;
+ len -= n;
+ }
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int buffer_putflush(buffer *s,const char *buf,size_t len)
+{
+ if (buffer_flush(s) == -1) return -1;
+ return allwrite(s->op,s->fd,buf,len);
+}
+
+int buffer_putsalign(buffer *s,const char *buf)
+{
+ return buffer_putalign(s,buf,str_len(buf));
+}
+
+int buffer_puts(buffer *s,const char *buf)
+{
+ return buffer_put(s,buf,str_len(buf));
+}
+
+int buffer_putsflush(buffer *s,const char *buf)
+{
+ return buffer_putflush(s,buf,str_len(buf));
+}
diff --git a/byte.c b/byte.c
new file mode 100644
index 0000000..c9d6d23
--- /dev/null
+++ b/byte.c
@@ -0,0 +1,99 @@
+#include "byte.h"
+
+/**
+ @file byte.c
+ @author djb
+ @brief byte manipulation functions
+*/
+
+unsigned int byte_chr(char *s,register unsigned int n,int c)
+{
+ register char ch;
+ register char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!n) { break; } if (*t == ch) { break; } ++t; --n;
+ if (!n) { break; } if (*t == ch) { break; } ++t; --n;
+ if (!n) { break; } if (*t == ch) { break; } ++t; --n;
+ if (!n) { break; } if (*t == ch) { break; } ++t; --n;
+ }
+ return t - s;
+}
+
+void byte_copy(register char *to,register unsigned int n,register char *from)
+{
+ for (;;) {
+ if (!n) { return; } *to++ = *from++; --n;
+ if (!n) { return; } *to++ = *from++; --n;
+ if (!n) { return; } *to++ = *from++; --n;
+ if (!n) { return; } *to++ = *from++; --n;
+ }
+}
+
+void byte_copyr(register char *to,register unsigned int n,register char *from)
+{
+ to += n;
+ from += n;
+ for (;;) {
+ if (!n) { return; } *--to = *--from; --n;
+ if (!n) { return; } *--to = *--from; --n;
+ if (!n) { return; } *--to = *--from; --n;
+ if (!n) { return; } *--to = *--from; --n;
+ }
+}
+
+int byte_diff(register char *s,register unsigned int n,register char *t)
+{
+ for (;;) {
+ if (!n) { return 0; } if (*s != *t) { break; } ++s; ++t; --n;
+ if (!n) { return 0; } if (*s != *t) { break; } ++s; ++t; --n;
+ if (!n) { return 0; } if (*s != *t) { break; } ++s; ++t; --n;
+ if (!n) { return 0; } if (*s != *t) { break; } ++s; ++t; --n;
+ }
+ return ((int)(unsigned int)(unsigned char) *s)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
+
+unsigned int byte_rchr(char *s,register unsigned int n,int c)
+{
+ register char ch;
+ register char *t;
+ register char *u;
+
+ ch = c;
+ t = s;
+ u = 0;
+ for (;;) {
+ if (!n) { break; } if (*t == ch) { u = t; } ++t; --n;
+ if (!n) { break; } if (*t == ch) { u = t; } ++t; --n;
+ if (!n) { break; } if (*t == ch) { u = t; } ++t; --n;
+ if (!n) { break; } if (*t == ch) { u = t; } ++t; --n;
+ }
+ if (!u) { u = t; }
+ return u - s;
+}
+
+void byte_zero(char *s,register unsigned int n)
+{
+ for (;;) {
+ if (!n) { break; } *s++ = 0; --n;
+ if (!n) { break; } *s++ = 0; --n;
+ if (!n) { break; } *s++ = 0; --n;
+ if (!n) { break; } *s++ = 0; --n;
+ }
+}
+
+void byte_fill(char *s,register unsigned int n,const int c)
+{
+ register char ch;
+
+ ch = c;
+ for (;;) {
+ if (!n) { break; } *s++ = ch; --n;
+ if (!n) { break; } *s++ = ch; --n;
+ if (!n) { break; } *s++ = ch; --n;
+ if (!n) { break; } *s++ = ch; --n;
+ }
+}
diff --git a/case.c b/case.c
new file mode 100644
index 0000000..47eb8ed
--- /dev/null
+++ b/case.c
@@ -0,0 +1,134 @@
+#include "case.h"
+#include "str.h"
+
+/**
+ @file case.c
+ @author djb
+ @brief string comparison and helper functions; case insensitive
+*/
+
+int case_diffb(register char *s,unsigned int len,register char *t)
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ while (len > 0) {
+ --len;
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (x != y)
+ return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+ }
+ return 0;
+}
+
+int case_diffs(register char *s,register char *t)
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ for (;;) {
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (x != y) break;
+ if (!x) break;
+ }
+ return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+}
+
+int case_diffrs(register char *s,register char *t)
+{
+ register unsigned char x = 0;
+ register unsigned char y = 0;
+ unsigned int lens = str_len(s);
+ unsigned int lent = str_len(t);
+
+ while (lens > 0 && lent > 0) {
+ x = s[--lens] - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = t[--lent] - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (x != y) break;
+ if (!x) break;
+ if (!y) break;
+ }
+ return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+}
+
+void case_lowerb(char *s,unsigned int len)
+{
+ unsigned char x;
+ while (len > 0) {
+ --len;
+ x = *s - 'A';
+ if (x <= 'Z' - 'A') *s = x + 'a';
+ ++s;
+ }
+}
+
+void case_lowers(char *s)
+{
+ unsigned char x;
+ while ((x = *s)) {
+ x -= 'A';
+ if (x <= 'Z' - 'A') *s = x + 'a';
+ ++s;
+ }
+}
+
+void case_upperb(char *s,unsigned int len)
+{
+ unsigned char x;
+ while (len > 0) {
+ --len;
+ x = *s - 'a';
+ if (x <= 'z' - 'a') *s = x + 'A';
+ ++s;
+ }
+}
+
+void case_uppers(char *s)
+{
+ unsigned char x;
+ while ((x = *s)) {
+ x -= 'a';
+ if (x <= 'z' - 'a') *s = x + 'A';
+ ++s;
+ }
+}
+
+int case_startb(register char *s,unsigned int len,register char *t)
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ for (;;) {
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (!y) return 1;
+ if (!len) return 0;
+ --len;
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ if (x != y) return 0;
+ }
+}
+
+int case_starts(register char *s,register char *t)
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ for (;;) {
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (!y) return 1;
+ if (x != y) return 0;
+ }
+}
diff --git a/cdbmake.c b/cdbmake.c
new file mode 100644
index 0000000..28e170c
--- /dev/null
+++ b/cdbmake.c
@@ -0,0 +1,158 @@
+#include <unistd.h>
+#include "seek.h"
+#include "error.h"
+#include "alloc.h"
+#include "cdbread.h"
+#include "cdbmake.h"
+
+/**
+ @file cdbmake.c
+ @author djb
+ @source ucspi-tcp
+ @brief constant data base (cdb) generation
+*/
+
+int cdb_make_start(struct cdb_make *c,int fd)
+{
+ c->head = 0;
+ c->split = 0;
+ c->hash = 0;
+ c->numentries = 0;
+ c->fd = fd;
+ c->pos = sizeof(c->final);
+ buffer_init(&c->b,write,fd,c->bspace,sizeof(c->bspace));
+ return seek_set(fd,c->pos);
+}
+
+static int posplus(struct cdb_make *c,uint32 len)
+{
+ uint32 newpos = c->pos + len;
+ if (newpos < len) { errno = ENOMEM; return -1; }
+ c->pos = newpos;
+ return 0;
+}
+
+int cdb_make_addend(struct cdb_make *c,unsigned int keylen,unsigned int datalen,uint32 h)
+{
+ struct cdb_hplist *head;
+
+ head = c->head;
+ if (!head || (head->num >= CDB_HPLIST)) {
+ head = (struct cdb_hplist *) alloc(sizeof(struct cdb_hplist));
+ if (!head) return -1;
+ head->num = 0;
+ head->next = c->head;
+ c->head = head;
+ }
+ head->hp[head->num].h = h;
+ head->hp[head->num].p = c->pos;
+ ++head->num;
+ ++c->numentries;
+ if (posplus(c,8) == -1) return -1;
+ if (posplus(c,keylen) == -1) return -1;
+ if (posplus(c,datalen) == -1) return -1;
+ return 0;
+}
+
+int cdb_make_addbegin(struct cdb_make *c,unsigned int keylen,unsigned int datalen)
+{
+ char buf[8];
+
+ if (keylen > 0xffffffff) { errno = ENOMEM; return -1; }
+ if (datalen > 0xffffffff) { errno = ENOMEM; return -1; }
+
+ uint32_pack(buf,keylen);
+ uint32_pack(buf + 4,datalen);
+ if (buffer_putalign(&c->b,buf,8) == -1) return -1;
+ return 0;
+}
+
+int cdb_make_add(struct cdb_make *c,char *key,unsigned int keylen,char *data,unsigned int datalen)
+{
+ if (cdb_make_addbegin(c,keylen,datalen) == -1) return -1;
+ if (buffer_putalign(&c->b,key,keylen) == -1) return -1;
+ if (buffer_putalign(&c->b,data,datalen) == -1) return -1;
+ return cdb_make_addend(c,keylen,datalen,cdb_hash(key,keylen));
+}
+
+int cdb_make_finish(struct cdb_make *c)
+{
+ char buf[8];
+ int i;
+ uint32 len;
+ uint32 u;
+ uint32 memsize;
+ uint32 count;
+ uint32 where;
+ struct cdb_hplist *x;
+ struct cdb_hp *hp;
+
+ for (i = 0; i < 256; ++i)
+ c->count[i] = 0;
+
+ for (x = c->head; x; x = x->next) {
+ i = x->num;
+ while (i--)
+ ++c->count[255 & x->hp[i].h];
+ }
+
+ memsize = 1;
+ for (i = 0; i < 256; ++i) {
+ u = c->count[i] * 2;
+ if (u > memsize)
+ memsize = u;
+ }
+
+ memsize += c->numentries; /* no overflow possible up to now */
+ u = (uint32) 0 - (uint32) 1;
+ u /= sizeof(struct cdb_hp);
+ if (memsize > u) { errno = ENOMEM; return -1; }
+
+ c->split = (struct cdb_hp *) alloc(memsize * sizeof(struct cdb_hp));
+ if (!c->split) return -1;
+
+ c->hash = c->split + c->numentries;
+
+ u = 0;
+ for (i = 0; i < 256; ++i) {
+ u += c->count[i]; /* bounded by numentries, so no overflow */
+ c->start[i] = u;
+ }
+
+ for (x = c->head; x; x = x->next) {
+ i = x->num;
+ while (i--)
+ c->split[--c->start[255 & x->hp[i].h]] = x->hp[i];
+ }
+
+ for (i = 0; i < 256; ++i) {
+ count = c->count[i];
+
+ len = count + count; /* no overflow possible */
+ uint32_pack(c->final + 8 * i,c->pos);
+ uint32_pack(c->final + 8 * i + 4,len);
+
+ for (u = 0; u < len; ++u)
+ c->hash[u].h = c->hash[u].p = 0;
+
+ hp = c->split + c->start[i];
+ for (u = 0; u < count; ++u) {
+ where = (hp->h >> 8) % len;
+ while (c->hash[where].p)
+ if (++where == len)
+ where = 0;
+ c->hash[where] = *hp++;
+ }
+
+ for (u = 0; u < len; ++u) {
+ uint32_pack(buf,c->hash[u].h);
+ uint32_pack(buf + 4,c->hash[u].p);
+ if (buffer_putalign(&c->b,buf,8) == -1) return -1;
+ if (posplus(c,8) == -1) return -1;
+ }
+ }
+
+ if (buffer_flush(&c->b) == -1) return -1;
+ if (seek_begin(c->fd) == -1) return -1;
+ return buffer_putflush(&c->b,c->final,sizeof(c->final));
+}
diff --git a/cdbread.c b/cdbread.c
new file mode 100644
index 0000000..c8ffa42
--- /dev/null
+++ b/cdbread.c
@@ -0,0 +1,169 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "error.h"
+#include "seek.h"
+#include "byte.h"
+#include "cdbread.h"
+
+/**
+ @file cdbread.c
+ @author djb
+ @source ucspi-tcp, fastforward
+ @brief read entries from a cdb
+*/
+
+uint32 cdb_unpack(unsigned char *buf)
+{
+ uint32 num;
+ num = buf[3]; num <<= 8;
+ num += buf[2]; num <<= 8;
+ num += buf[1]; num <<= 8;
+ num += buf[0];
+ return num;
+}
+
+void cdb_free(struct cdb *c)
+{
+ if (c->map) {
+ munmap(c->map,c->size);
+ c->map = 0;
+ }
+}
+
+void cdb_findstart(struct cdb *c)
+{
+ c->loop = 0;
+}
+
+void cdb_init(struct cdb *c,int fd)
+{
+ struct stat st;
+ char *x;
+
+ cdb_free(c);
+ cdb_findstart(c);
+ c->fd = fd;
+
+ if (fstat(fd,&st) == 0)
+ if (st.st_size <= 0xffffffff) {
+ x = mmap(0,st.st_size,PROT_READ,MAP_SHARED,fd,0);
+ if (x + 1) {
+ c->size = st.st_size;
+ c->map = x;
+ }
+ }
+}
+
+int cdb_read(struct cdb *c,char *buf,unsigned int len,uint32 pos)
+{
+ if (c->map) {
+ if ((pos > c->size) || (c->size - pos < len)) goto FORMAT;
+ byte_copy(buf,len,c->map + pos);
+ }
+ else {
+ if (seek_set(c->fd,pos) == -1) return -1;
+ while (len > 0) {
+ int r;
+ do
+ r = read(c->fd,buf,len);
+ while ((r == -1) && (errno == EINTR));
+ if (r == -1) return -1;
+ if (r == 0) goto FORMAT;
+ buf += r;
+ len -= r;
+ }
+ }
+ return 0;
+
+ FORMAT:
+ errno = EPROTO;
+ return -1;
+}
+
+static int match(struct cdb *c,char *key,unsigned int len,uint32 pos)
+{
+ char buf[32];
+ int n;
+
+ while (len > 0) {
+ n = sizeof(buf);
+ if (n > len) n = len;
+ if (cdb_read(c,buf,n,pos) == -1) return -1;
+ if (byte_diff(buf,n,key)) return 0;
+ pos += n;
+ key += n;
+ len -= n;
+ }
+ return 1;
+}
+
+int cdb_findnext(struct cdb *c,char *key,unsigned int len)
+{
+ char buf[8];
+ uint32 pos;
+ uint32 u;
+
+ if (!c->loop) {
+ u = cdb_hash(key,len);
+ if (cdb_read(c,buf,8,(u << 3) & 2047) == -1) return -1;
+ uint32_unpack(buf + 4,&c->hslots);
+ if (!c->hslots) return 0;
+ uint32_unpack(buf,&c->hpos);
+ c->khash = u;
+ u >>= 8;
+ u %= c->hslots;
+ u <<= 3;
+ c->kpos = c->hpos + u;
+ }
+
+ while (c->loop < c->hslots) {
+ if (cdb_read(c,buf,8,c->kpos) == -1) return -1;
+ uint32_unpack(buf + 4,&pos);
+ if (!pos) return 0;
+ c->loop += 1;
+ c->kpos += 8;
+ if (c->kpos == c->hpos + (c->hslots << 3)) c->kpos = c->hpos;
+ uint32_unpack(buf,&u);
+ if (u == c->khash) {
+ if (cdb_read(c,buf,8,pos) == -1) return -1;
+ uint32_unpack(buf,&u);
+ if (u == len)
+ switch(match(c,key,len,pos + 8)) {
+ case -1:
+ return -1;
+ case 1:
+ uint32_unpack(buf + 4,&c->dlen);
+ c->dpos = pos + 8 + len;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int cdb_find(struct cdb *c,char *key,unsigned int len)
+{
+ cdb_findstart(c);
+ return cdb_findnext(c,key,len);
+}
+
+uint32 cdb_hashadd(uint32 h,unsigned char c)
+{
+ h += (h << 5);
+ return h ^ c;
+}
+
+uint32 cdb_hash(char *buf,unsigned int len)
+{
+ uint32 h;
+
+ h = CDB_HASHSTART;
+ while (len) {
+ h = cdb_hashadd(h,*buf++);
+ --len;
+ }
+ return h;
+}
diff --git a/conf-build b/conf-build
new file mode 100644
index 0000000..a2428c4
--- /dev/null
+++ b/conf-build
@@ -0,0 +1,30 @@
+#!/bin/sh
+# Build and install options for qlibs
+#
+#********************************************************************************
+# default compiler flags - usually this shouldn't be changed
+CFLAGS='-O2 -Iinclude -fPIC -Wall'
+
+# under OmniOS the binaries are build as 32 bit version; except enabling this:
+#CFLAGS='-O2 -Iinclude -Wall -fPIC -m64'
+
+# You can check the ELFCLASS while calling eg.: 'file open.o'.
+#********************************************************************************
+# destination folder(s) (install options of qlibs)
+#
+# Define a destination where libs and/or header files will be installed into. If
+# a var is empty, then nothing will be installed (copied). The term 'libs' here
+# means all '*.a' files and additional all files given by $OFILES (see below).
+# Example 1:
+# LIBDIR=.. - copy all libs into the upper folder
+# HDRDIR= - don't copy header files ('*.h')
+# Example 2:
+# LIBDIR=/usr/local/lib - copy all libs into /usr/local/lib
+# HDRDIR=$LIBDIR/include - copy all header files into /usr/local/lib/include
+#
+LIBDIR=..
+HDRDIR=
+
+#********************************************************************************
+# Additional libs (object files, delimited by space, enclosed in "'")
+OFILES='base64.o byte.o fmt.o prot.o pathexec.o readclose.o scan.o constmap.o'
diff --git a/configure b/configure
new file mode 100755
index 0000000..34785ac
--- /dev/null
+++ b/configure
@@ -0,0 +1,69 @@
+#!/bin/sh
+#********************************************************************************
+# Very simple configure script for qlibs
+
+. ./conf-build
+# Add $HDRDIR as include option to default $CFLAGS
+[ "$HDRDIR" ] && CFLAGS="$CFLAGS -I$HDRDIR"
+
+#********************************************************************************
+# check for system header files
+#
+# Usually, 'select.h' should (have to) be in this location ...
+SELECT_H="/usr/include/sys/select.h"
+echo -n "Checking for select.h ..."
+if [ -f $SELECT_H ] ; then CFLAGS="$CFLAGS -DHAS_SELECT_H"
+ else echo -n " not" ; fi
+echo " found!"
+
+# On linux we have flock, other systems have lockf instead
+echo -n "Checking for flock/lockf ..."
+which flock 2>/dev/null >/dev/null
+if [ $? -eq 0 ] ; then CCOPTS="$CFLAGS -DHASFLOCK=1" ; echo -n " flock"
+ else echo -n " lockf" ; fi
+echo " found!"
+
+# Current systems provide a poll interface ... via
+POLL_H="/usr/include/poll.h"
+POLL_H1="/usr/include/sys/poll.h"
+echo -n "Checking for poll.h ..."
+if [ -f $POLL_H -o -f $POLL_H1 ] ; then CFLAGS="$CFLAGS -DHAS_POLL_H"
+ else echo -n " not" ; fi
+echo " found!"
+
+# Current systems support for 8 and 64 bit integers ... via
+STDINT_H="/usr/include/stdint.h"
+echo -n "Checking for uint8_t ..."
+if [ `grep -c uint_least8_t $STDINT_H` ] ; then CFLAGS="$CFLAGS -DHAS_UINT8_H"
+ else echo -n " not" ; fi
+echo " found!"
+echo -n "Checking for uint64_t ..."
+if [ `grep -c uint_least64_t $STDINT_H` ] ; then CFLAGS="$CFLAGS -DHAS_UINT64_H"
+ else echo -n " not" ; fi
+echo " found!"
+
+#********************************************************************************
+# Create compile, load, makelib, sharedlib
+echo -n "Checking for compile ... "
+CC="cc"
+( echo '#!/bin/sh'
+ echo exec "$CC" "$CFLAGS" -c '${1+"$@"}' ) > compile
+chmod 755 compile
+echo " created!"
+
+echo -n "Checking for makelib ... "
+( echo '#!/bin/sh' ;
+ echo "" ;
+ echo 'main="$1"; shift' ; \
+ echo 'rm -f "$main"' ; \
+ echo 'ar cr "$main" ${1+"$@"}' ; \
+ echo 'ranlib "$main"') > makelib
+chmod 755 makelib
+echo " created!"
+
+echo -n "Checking for sharedlib ... "
+CC="cc"
+( echo '#!/bin/sh'
+ echo exec "$CC" -shared '${1+"$@"}' ) > sharedlib
+chmod 755 sharedlib
+echo " created!"
diff --git a/constmap.c b/constmap.c
new file mode 100644
index 0000000..ecd5a92
--- /dev/null
+++ b/constmap.c
@@ -0,0 +1,169 @@
+#include "constmap.h"
+#include "alloc.h"
+#include "case.h"
+
+static constmap_hash hash(char *s,int len)
+{
+ unsigned char ch;
+ constmap_hash h;
+ h = 5381;
+ while (len > 0) {
+ ch = *s++ - 'A';
+ if (ch <= 'Z' - 'A') ch += 'a' - 'A';
+ h = ((h << 5) + h) ^ ch;
+ --len;
+ }
+ return h;
+}
+
+char *constmap(struct constmap *cm,char *s,int len)
+{
+ constmap_hash h;
+ int pos;
+ h = hash(s,len);
+ pos = cm->first[h & cm->mask];
+ while (pos != -1) {
+ if (h == cm->hash[pos])
+ if (len == cm->inputlen[pos])
+ if (!case_diffb(cm->input[pos],len,s))
+ return cm->input[pos] + cm->inputlen[pos] + 1;
+ pos = cm->next[pos];
+ }
+ return 0;
+}
+
+int constmap_init(struct constmap *cm,char *s,int len,int flagcolon)
+{
+ int i;
+ int j;
+ int k;
+ int pos;
+ constmap_hash h;
+
+ cm->num = 0;
+ for (j = 0; j < len; ++j) if (!s[j]) ++cm->num;
+
+ h = 64;
+ while (h && (h < cm->num)) h += h;
+ cm->mask = h - 1;
+
+ cm->first = (int *) alloc(sizeof(int) * h);
+ if (cm->first) {
+ cm->input = (char **) alloc(sizeof(char *) * cm->num);
+ if (cm->input) {
+ cm->inputlen = (int *) alloc(sizeof(int) * cm->num);
+ if (cm->inputlen) {
+ cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num);
+ if (cm->hash) {
+ cm->next = (int *) alloc(sizeof(int) * cm->num);
+ if (cm->next) {
+ for (h = 0; h <= cm->mask; ++h)
+ cm->first[h] = -1;
+ pos = 0;
+ i = 0;
+ for (j = 0; j < len; ++j)
+ if (!s[j]) {
+ k = j - i;
+ if (flagcolon) {
+ for (k = i; k < j; ++k)
+ if (s[k] == ':') break;
+ if (k >= j) { i = j + 1; continue; }
+ k -= i;
+ }
+ cm->input[pos] = s + i;
+ cm->inputlen[pos] = k;
+ h = hash(s + i,k);
+ cm->hash[pos] = h;
+ h &= cm->mask;
+ cm->next[pos] = cm->first[h];
+ cm->first[h] = pos;
+ ++pos;
+ i = j + 1;
+ }
+ return 1;
+ }
+ alloc_free(cm->hash);
+ }
+ alloc_free(cm->inputlen);
+ }
+ alloc_free(cm->input);
+ }
+ alloc_free(cm->first);
+ }
+ return 0;
+}
+
+int constmap_init_char(struct constmap *cm,char *s,int len,int flagcolon,char flagchar)
+{
+ int i;
+ int j;
+ int k;
+ int pos;
+ constmap_hash h;
+
+ if (!flagchar || flagchar == 0 || flagchar == '\0') {
+ flagchar = ':';
+ }
+
+ cm->num = 0;
+ for (j = 0; j < len; ++j) if (!s[j]) ++cm->num;
+
+ h = 64;
+ while (h && (h < cm->num)) h += h;
+ cm->mask = h - 1;
+
+ cm->first = (int *) alloc(sizeof(int) * h);
+ if (cm->first) {
+ cm->input = (char **) alloc(sizeof(char *) * cm->num);
+ if (cm->input) {
+ cm->inputlen = (int *) alloc(sizeof(int) * cm->num);
+ if (cm->inputlen) {
+ cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num);
+ if (cm->hash) {
+ cm->next = (int *) alloc(sizeof(int) * cm->num);
+ if (cm->next) {
+ for (h = 0; h <= cm->mask; ++h)
+ cm->first[h] = -1;
+ pos = 0;
+ i = 0;
+ for (j = 0; j < len; ++j) {
+ if (!s[j]) {
+ k = j - i;
+ if (flagcolon) {
+ for (k = i; k < j; ++k)
+ if (s[k] == flagchar) break;
+ if (k >= j) { i = j + 1; continue; }
+ k -= i;
+ }
+ cm->input[pos] = s + i;
+ cm->inputlen[pos] = k;
+ h = hash(s + i,k);
+ cm->hash[pos] = h;
+ h &= cm->mask;
+ cm->next[pos] = cm->first[h];
+ cm->first[h] = pos;
+ ++pos;
+ i = j + 1;
+ }
+ }
+ return 1;
+ }
+ alloc_free(cm->hash);
+ }
+ alloc_free(cm->inputlen);
+ }
+ alloc_free(cm->input);
+ }
+ alloc_free(cm->first);
+ }
+ return 0;
+}
+
+void constmap_free(struct constmap *cm)
+{
+ alloc_free(cm->next);
+ alloc_free(cm->hash);
+ alloc_free(cm->inputlen);
+ alloc_free(cm->input);
+ alloc_free(cm->first);
+}
diff --git a/dnsstub/Makefile b/dnsstub/Makefile
new file mode 100644
index 0000000..6c3bab3
--- /dev/null
+++ b/dnsstub/Makefile
@@ -0,0 +1,23 @@
+
+COMPILE=../compile
+MAKELIB=../makelib
+CCFLAGS=-I../include
+
+default: clean check dnsresolv.a
+
+check:
+ @[ -f $(COMPILE) ] && [ -f $(MAKELIB) ] || ( cd .. ; ./configure ; )
+
+clean:
+ rm -f `cat TARGETS`
+
+dnsresolv.a: \
+dns_domain.c dns_dtda.c dns_ip.c dns_ipq.c dns_name.c \
+dns_nd.c dns_packet.c dns_random.c dns_rcip.c dns_rcrw.c dns_resolve.c \
+dns_sortip.c dns_transmit.c dns_txt.c
+ $(COMPILE) $(CCFLAGS) dns_domain.c dns_dfd.c dns_dtda.c dns_ip.c dns_ipq.c \
+ dns_mx.c dns_name.c dns_nd.c dns_packet.c dns_random.c dns_rcip.c \
+ dns_rcrw.c dns_resolve.c dns_sortip.c dns_transmit.c dns_txt.c dns_cname.c
+ $(MAKELIB) dnsresolv.a dns_domain.o dns_dfd.o dns_dtda.o dns_ip.o dns_ipq.o \
+ dns_mx.o dns_name.o dns_nd.o dns_packet.o dns_random.o dns_rcip.o \
+ dns_rcrw.o dns_resolve.o dns_sortip.o dns_transmit.o dns_txt.o dns_cname.o
diff --git a/dnsstub/README.md b/dnsstub/README.md
new file mode 100644
index 0000000..95cd11c
--- /dev/null
+++ b/dnsstub/README.md
@@ -0,0 +1,171 @@
+/*! \mainpage
+
+Stub Resolver
+=============
+
+Simple DJBDNS stub-resolver based on 'djbdns-1.05(IPv6)' allowing for each
+calling application individually to include up to 16 DNSCACHEIP(s)
+as DNS forwarding/resolving servers to be tried sequentially.
+
+IP Addresses
+------------
+
+Here,
+ - global IPv6,
+ - IPv6 ULA, and
+ - IPv6 LLU addresses with a given Interface-Id
+can be specified. The IPv4 format could be either a
+ - legacy dotted-decimal or a
+ - IPv4-mapped IPv6 address.
+
+In any case, compactified IPv6 addresses are understood.
+IPv4/IPv6 addresses in brackets are understood by dns_ip.
+
+Resolver Call
+-------------
+
+If $DNSCACHEIP is not provided as environment variable, the stub-resolver
+will use the system-wide
+ - /etc/resolv.conf
+file; however now without the capability for IPv6 LLU addresses.
+While IPv4-mapped IPv6 addresses are supported here by default as well,
+care has to taken not to jeopardize other client's usage.
+
+Name Qualification
+------------------
+
+If provided, the stub-resolver uses either a system-wide configuration file
+ - /etc/dnsrewritefile or assumes this file to available as given in
+ - $DNSREWRITEFILE
+in order to define persistent mapping-rules of local domain names to public
+ones (for lookup) or IP addresses (for direct matching).
+
+Well-known domain names 'localhost', 'ip4-loopback' and 'ip6-loopback'
+are handled locally, thus no DNS query is used (RFC 6761).
+'localhost' is advertised as '::1' and '::ff:127.0.0.1' in it's native
+IPv6 format. It is up to the caller to convert the IPv6-mapped IPv4
+address to the IPv4 format.
+
+Local domain names can be alternatively specified (per application) using
+the environment variable
+ - $LOCALDOMAIN
+to be appended to unqualified hostnames dynamically. This is roughly equivalent
+with the 'search' string in /etc/resolv. Several domains names may be
+specified within $LOCALDOMAIN separated by blanks.
+
+See: https://cr.yp.to/djbdns/qualify.html
+
+
+Specific DNS Record type lookup
+-------------------------------
+
+* dns_ip (A, AAAAA)
+* dns_name (PTR)
+* dns_cname (CNAME)
+* dns_txt (TXT) -- now considering several 'labels'
+* dns_mx (MX)
+
+
+Internals
+---------
+
+* UDP message size:
+Unlike other implementations, this DNS stub-resolver supports UDP packet
+sizes up to 1028 byte without the need for (E)DNS0 packet enhancements.
+
+* DNS UDP query retrials:
+In case the NS is not able to initally reply to the query,
+it is retried again at the intervalls {1, 2, 4, 8, 16} secs.
+
+* DNS name qualification (dns_ip_qualify):
+Well-known domain names are qualified locally without invoking a DNS query
+while handling IPv4 and IPv6 addresses separately.
+
+* NS qualification/sorting for NS replies:
+NS qualification is not supported (yet), thus we use a randomly sorted
+list of NS IP addresses.
+
+* Query/Reply to/from DNS Cache servers/forwarders:
+Neither message (CurveDNS) nor transport layer (TLS) encryption is provided;
+the sub-resolver 'trusts' it's upstream caches/forwarders. We recommend to
+setup communication on private IPv4/IPv6 addresses; if applicable.
+
+* DNS TXT Records:
+The label substructure is now recognized in the RDATA section;
+each label may have the size of 255 byte.
+The length information is excluded from the output.
+Only printable characters are recognized in the output.
+
+* Return Codes:
+Different from DJB's initial routines, the DNS front-end routines
+ dns_cname*, dns_ip*, dns_mx*, dns_name*, dns dns_txt*
+return now the number of replies received (not bytes!).
+Thus, three cases need to be considered:
+
+ - rc < 0: Problem occured (SOFTFAIL, HARDFAIL)
+ - rc = 0: No answer obtained (but query was successful) = NXDOMAIN
+ - rc > 0: rc answers received; positive reply
+
+For return codes < 0, the following conventions have been applied:
+
+ include/dnsresolv.h
+
+\#define DNS_NXD 0
+\#define DNS_MEM -1
+\#define DNS_ERR -2 /* parsing errors and others */
+\#define DNS_COM -3 /* (socket) communication errors */
+\#define DNS_INT -4 /* internal errors */
+\#define DNS_SOFT -5 /* either -2 or -3 */
+\#define DNS_HARD -6 /* CNAME loop problem */
+
+The modification of the return code is typically not problematic,
+since mostly just rc = -1 is checked.
+
+In the future, these return codes are subject of change.
+Thus, instead of
+
+ if (dns_XX(...) == -1)
+
+one shoud use the more general syntax
+
+ if (dns_XX(...) < 0)
+
+to check for 'negative' results, allowing further actions
+and refinements given the calling sequence.
+
+
+
+Environment Variables Read
+--------------------------
+
+$DNSCACHEPIP The upstream resolver's IP[v4|v6] addresses (up to 32).
+ IPv6 LLU addresses may be suffixed with the interface name.
+$DNSREWRITEFILE Alternate location for the system-wide
+ /etc/dnsrewrite
+file
+$LOCALDOMAIN Additional local domain name appended to unqualified
+ hostnames dynamically.
+
+Sample for the file /etc/dnsrewrite:
+
+\#annything.local -> me
+\-.example.com:me
+\# me -> 127.0.0.1
+\=me:127.0.0.1
+\# any.name.a -> any.name.af.mil
+\*.a:.af.mil
+\# any-name-without-dots -> any-name-without-dots.heaven.af.mil
+\?:.heaven.af.mil
+\# remove trailing dot
+\*.:
+
+and DJB's explanations are given here:
+
+Instructions are followed in order, each at most once. There are four types of instructions:
+
+\=post:new means that the host name post is replaced by new.
+\*post:new means that any name of the form prepost is replaced by prenew.
+\?post:new means that any name of the form prepost, where pre does not contain dots or brackets, is replaced by prenew.
+\-post:new means that any name of the form prepost is replaced by new.
+
+Erwin Hoffmann, June 2023.
diff --git a/dnsstub/TARGETS b/dnsstub/TARGETS
new file mode 100644
index 0000000..62f40be
--- /dev/null
+++ b/dnsstub/TARGETS
@@ -0,0 +1,18 @@
+dns_cname.o
+dns_dfd.o
+dns_domain.o
+dns_dtda.o
+dns_ip.o
+dns_ipq.o
+dns_mx.o
+dns_name.o
+dns_nd.o
+dns_packet.o
+dns_random.o
+dns_rcip.o
+dns_rcrw.o
+dns_resolve.o
+dns_sortip.o
+dns_transmit.o
+dns_txt.o
+dnsresolv.a
diff --git a/dnsstub/dns_cname.c b/dnsstub/dns_cname.c
new file mode 100644
index 0000000..408949a
--- /dev/null
+++ b/dnsstub/dns_cname.c
@@ -0,0 +1,59 @@
+#include "stralloc.h"
+#include "uint_t.h"
+#include "byte.h"
+#include "ip.h"
+#include "case.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_cname.c
+ @author feh
+ @brief DNS cname lookup
+*/
+
+static char *q = 0;
+
+int dns_cname_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ uint16 numanswers;
+ uint16 datalen;
+ int ranswers = 0;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_CNAME))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (!dns_packet_getname(buf,len,pos,&q)) return DNS_ERR;
+ if (dns_domain_todot_cat(out,q) <= 0) return DNS_ERR;
+ }
+ pos += datalen;
+ ++ranswers;
+ }
+
+ return ranswers;
+}
+
+int dns_cname(stralloc *out,stralloc *fqdn)
+{
+ int rc;
+
+ if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR;
+ if (dns_resolve(q,DNS_T_CNAME) < 0) return DNS_ERR;
+ if ((rc = dns_cname_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ if (case_equals(out->s,fqdn->s)) rc = -6; // loop DNS_HARD
+
+ return rc;
+}
diff --git a/dnsstub/dns_dfd.c b/dnsstub/dns_dfd.c
new file mode 100644
index 0000000..756a1f8
--- /dev/null
+++ b/dnsstub/dns_dfd.c
@@ -0,0 +1,76 @@
+#include "error.h"
+#include "alloc.h"
+#include "byte.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_dfd.c
+ @author djb
+ @source ucspi-tcp
+ @brief domain name qualification (domain from dot)
+*/
+
+int dns_domain_fromdot(char **out,const char *buf,unsigned int n)
+{
+ char label[63];
+ unsigned int labellen = 0; /* <= sizeof label */
+ char name[255];
+ unsigned int namelen = 0; /* <= sizeof name */
+ char ch;
+ char *x;
+
+ errno = EPROTO;
+
+ for (;;) {
+ if (!n) break;
+ ch = *buf++; --n;
+ if (ch == '.') {
+ if (labellen) {
+ if (namelen + labellen + 1 > sizeof(name)) return 0;
+ name[namelen++] = labellen;
+ byte_copy(name + namelen,labellen,label);
+ namelen += labellen;
+ labellen = 0;
+ }
+ continue;
+ }
+ if (ch == '\\') { // octal -> decimal
+ if (!n) break;
+ ch = *buf++; --n;
+ if ((ch >= '0') && (ch <= '7')) {
+ ch -= '0';
+ if (n && (*buf >= '0') && (*buf <= '7')) {
+ ch <<= 3;
+ ch += *buf - '0';
+ ++buf; --n;
+ if (n && (*buf >= '0') && (*buf <= '7')) {
+ ch <<= 3;
+ ch += *buf - '0';
+ ++buf; --n;
+ }
+ }
+ }
+ }
+ if (labellen >= sizeof(label)) return 0;
+ label[labellen++] = ch;
+ }
+
+ if (labellen) {
+ if (namelen + labellen + 1 > sizeof(name)) return 0;
+ name[namelen++] = labellen;
+ byte_copy(name + namelen,labellen,label);
+ namelen += labellen;
+ labellen = 0;
+ }
+
+ if (namelen + 1 > sizeof(name)) return 0;
+ name[namelen++] = 0;
+
+ x = alloc(namelen);
+ if (!x) return DNS_MEM;
+ byte_copy(x,namelen,name);
+
+ if (*out) alloc_free(*out);
+ *out = x;
+ return 1;
+}
diff --git a/dnsstub/dns_domain.c b/dnsstub/dns_domain.c
new file mode 100644
index 0000000..654a827
--- /dev/null
+++ b/dnsstub/dns_domain.c
@@ -0,0 +1,80 @@
+#include "alloc.h"
+#include "case.h"
+#include "byte.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_domain.c
+ @author djb
+ @source ucspi-tcp
+ @brief domain qualification
+*/
+
+unsigned int dns_domain_length(const char *dn)
+{
+ const char *x;
+ unsigned char c;
+
+ x = dn;
+ while ((c = *x++))
+ x += (unsigned int) c;
+ return x - dn;
+}
+
+void dns_domain_free(char **out)
+{
+ if (*out) {
+ alloc_free(*out);
+ *out = 0;
+ }
+}
+
+int dns_domain_copy(char **out,const char *in)
+{
+ unsigned int len;
+ char *x;
+
+ len = dns_domain_length(in);
+ x = alloc(len);
+ if (!x) return 0;
+ byte_copy(x,len,in);
+ if (*out) alloc_free(*out);
+ *out = x;
+ return 1;
+}
+
+int dns_domain_equal(const char *dn1,const char *dn2)
+{
+ unsigned int len;
+
+ len = dns_domain_length(dn1);
+ if (len != dns_domain_length(dn2)) return 0;
+
+ if (case_diffb((char *)dn1,len,(char *)dn2)) return 0; /* safe since 63 < 'A' */
+ return 1;
+}
+
+int dns_domain_suffix(const char *big,const char *little)
+{
+ unsigned char c;
+
+ for (;;) {
+ if (dns_domain_equal(big,little)) return 1;
+ c = *big++;
+ if (!c) return 0;
+ big += c;
+ }
+}
+
+unsigned int dns_domain_suffixpos(const char *big,const char *little)
+{
+ const char *orig = big;
+ unsigned char c;
+
+ for (;;) {
+ if (dns_domain_equal(big,little)) return big - orig;
+ c = *big++;
+ if (!c) return 0;
+ big += c;
+ }
+}
diff --git a/dnsstub/dns_dtda.c b/dnsstub/dns_dtda.c
new file mode 100644
index 0000000..38358a2
--- /dev/null
+++ b/dnsstub/dns_dtda.c
@@ -0,0 +1,43 @@
+#include "stralloc.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_dtda.c
+ @author djb
+ @source ucspi-tcp
+ @brief domain to dot append
+*/
+
+int dns_domain_todot_cat(stralloc *out,const char *d)
+{
+ char ch;
+ char ch2;
+ unsigned char ch3;
+ char buf[4];
+
+ if (!*d)
+ return stralloc_append(out,".");
+
+ for (;;) {
+ ch = *d++;
+ while (ch--) {
+ ch2 = *d++;
+ if ((ch2 >= 'A') && (ch2 <= 'Z')) ch2 += 32; // FQDN -> lowercase
+ if (((ch2 >= 'a') && (ch2 <= 'z')) ||
+ ((ch2 >= '0') && (ch2 <= '9')) ||
+ (ch2 == '-') || (ch2 == '_')) {
+ if (!stralloc_append(out,&ch2)) return DNS_MEM;
+ }
+ else { // decimal -> octal
+ ch3 = ch2;
+ buf[3] = '0' + (ch3 & 7); ch3 >>= 3;
+ buf[2] = '0' + (ch3 & 7); ch3 >>= 3;
+ buf[1] = '0' + (ch3 & 7);
+ buf[0] = '\\';
+ if (!stralloc_catb(out,buf,4)) return DNS_MEM;
+ }
+ }
+ if (!*d) return 1;
+ if (!stralloc_append(out,".")) return DNS_MEM;
+ }
+}
diff --git a/dnsstub/dns_ip.c b/dnsstub/dns_ip.c
new file mode 100644
index 0000000..f89728c
--- /dev/null
+++ b/dnsstub/dns_ip.c
@@ -0,0 +1,198 @@
+#include "stralloc.h"
+#include "uint_t.h"
+#include "byte.h"
+#include "ip.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_ip.c
+ @author djb, fefe, feh
+ @source ucspi-tcp6
+ @brief DNS IP query
+*/
+
+static char *q = 0;
+
+int dns_ip4_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ uint16 numanswers;
+ uint16 datalen;
+ int ranswers = 0;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_A))
+ if (byte_equal(header + 2,2,DNS_C_IN))
+ if (datalen == 4) {
+ if (!dns_packet_copy(buf,len,pos,header,4)) return DNS_ERR;
+ if (!stralloc_catb(out,header,4)) return DNS_MEM;
+ }
+ pos += datalen;
+ ++ranswers;
+ }
+
+ dns_sortip4(out->s,out->len);
+ return ranswers;
+}
+
+int dns_ip4(stralloc *out,stralloc *fqdn)
+{
+ unsigned int i;
+ char code = 0;
+ int dot = 0;
+ char ch;
+ char ip[4];
+ int r;
+ int rc = 0;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+ if (!stralloc_readyplus(fqdn,1)) return DNS_MEM;
+
+ fqdn->s[fqdn->len] = 0; /* test FQDN string */
+ for (i = 1; i < fqdn->len; i++) {
+ if (fqdn->s[i] >= '_') { code = 127; break; }
+ if (fqdn->s[i] == '.') dot++;
+ }
+
+ if (code != 127 && dot == 3) /* if FQDN is just IPv4 */
+ if (ip4_scan(fqdn->s,ip) || ip4_scanbracket(fqdn->s,ip)) {
+ if (!stralloc_copyb(out,ip,4)) return DNS_MEM;
+ return 1;
+ }
+
+ code = 0;
+ for (i = 0; i <= fqdn->len; ++i) {
+ if (i < fqdn->len)
+ ch = fqdn->s[i];
+ else
+ ch = '.';
+
+ if ((ch == '[') || (ch == ']')) continue;
+ if (ch == '.') {
+ if (!stralloc_append(out,&code)) return DNS_MEM;
+ code = 0;
+ continue;
+ }
+ if ((ch >= '0') && (ch <= '9')) {
+ code *= 10;
+ code += ch - '0';
+ continue;
+ }
+
+ if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; // fdqn -> A query -> response
+ if (dns_resolve(q,DNS_T_A) >= 0) {
+ if ((r = dns_ip4_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ rc += r;
+ }
+
+ return rc;
+ }
+
+ out->len &= ~3;
+ return 0;
+}
+
+int dns_ip6_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[16];
+ uint16 numanswers;
+ uint16 datalen;
+ int ranswers = 0;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_AAAA)) {
+ if (byte_equal(header + 2,2,DNS_C_IN))
+ if (datalen == 16) {
+ if (!dns_packet_copy(buf,len,pos,header,16)) return DNS_ERR;
+ if (!stralloc_catb(out,header,16)) return DNS_MEM;
+ }
+ } else if (byte_equal(header,2,DNS_T_A))
+ if (byte_equal(header + 2,2,DNS_C_IN))
+ if (datalen == 4) {
+ byte_copy(header,12,V4mappedprefix);
+ if (!dns_packet_copy(buf,len,pos,header + 12,4)) return DNS_ERR;
+ if (!stralloc_catb(out,header,16)) return DNS_MEM;
+ }
+ pos += datalen;
+ ++ranswers;
+ }
+
+ dns_sortip6(out->s,out->len);
+ return ranswers;
+}
+
+int dns_ip6(stralloc *out,stralloc *fqdn)
+{
+ unsigned int i;
+ char code;
+ char ch;
+ char ip[16];
+ int r;
+ int rc = 0;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+ if (!stralloc_readyplus(fqdn,1)) return DNS_MEM;
+
+ fqdn->s[fqdn->len] = 0; /* if FQDN is just IPv6 */
+ if (ip6_scan(fqdn->s,ip) || ip6_scanbracket(fqdn->s,ip)) {
+ if (!stralloc_copyb(out,ip,16)) return DNS_MEM;
+ return 1;
+ }
+
+ code = 0;
+ for (i = 0; i <= fqdn->len; ++i) {
+ if (i < fqdn->len)
+ ch = fqdn->s[i];
+ else
+ ch = '.';
+
+ if ((ch == '[') || (ch == ']')) continue;
+ if (ch == '.') {
+ if (!stralloc_append(out,&code)) return DNS_MEM;
+ code = 0;
+ continue;
+ }
+ if ((ch >= '0') && (ch <= '9')) {
+ code *= 10;
+ code += ch - '0';
+ continue;
+ }
+
+ if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; // fqdn -> AAAA query -> response
+ if (dns_resolve(q,DNS_T_AAAA) >= 0) {
+ if ((r = dns_ip6_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ rc += r;
+ }
+
+ return rc;
+ }
+
+ out->len &= ~3;
+ return 0;
+}
diff --git a/dnsstub/dns_ipq.c b/dnsstub/dns_ipq.c
new file mode 100644
index 0000000..26c3818
--- /dev/null
+++ b/dnsstub/dns_ipq.c
@@ -0,0 +1,236 @@
+#include "case.h"
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+#include "dnsresolv.h"
+#include "socket_if.h"
+#include "ip.h"
+
+/**
+ @file dns_ipq.c
+ @author djb, feh
+ @source ucspi-tcp
+ @brief DNS hostname qualification for ipv4 and ipv6
+*/
+
+/**
+ @fn int doit -> @return number of added chars to name
+*/
+static int doit(stralloc *work,const char *rule)
+{
+ char ch;
+ unsigned int colon;
+ unsigned int prefixlen;
+
+ ch = *rule++;
+ if ((ch != '?') && (ch != '=') && (ch != '*') && (ch != '-')) return 1;
+ colon = str_chr((char *)rule,':');
+ if (!rule[colon]) return 1;
+
+ if (work->len < colon) return 1;
+ prefixlen = work->len - colon;
+ if ((ch == '=') && prefixlen) return 1;
+ if (case_diffb((char *)rule,colon,work->s + prefixlen)) return 1;
+ if (ch == '?') {
+ if (byte_chr(work->s,prefixlen,'.') < prefixlen) return 1;
+ if (byte_chr(work->s,prefixlen,'[') < prefixlen) return 1;
+ if (byte_chr(work->s,prefixlen,']') < prefixlen) return 1;
+ }
+
+ work->len = prefixlen;
+ if (ch == '-') work->len = 0;
+ return stralloc_cats(work,rule + colon + 1);
+}
+
+/** @fn int dns_ip4_qualify_rules -> @return number of IPv4 addresss with rules */
+
+int dns_ip4_qualify_rules(stralloc *ipout,stralloc *fqdn,const stralloc *in,const stralloc *rules)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int plus;
+ unsigned int fqdnlen;
+ int rc = 0;
+
+ if (!stralloc_copy(fqdn,(stralloc *)in)) return DNS_MEM;
+
+ for (j = i = 0; j < rules->len; ++j)
+ if (!rules->s[j]) {
+ if (!doit(fqdn,rules->s + i)) return DNS_INT;
+ i = j + 1;
+ }
+
+ fqdnlen = fqdn->len;
+ plus = byte_chr(fqdn->s,fqdnlen,'+');
+ if (plus >= fqdnlen)
+ return dns_ip4(ipout,fqdn);
+
+ i = plus + 1;
+ for (;;) {
+ j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
+ byte_copy(fqdn->s + plus,j,fqdn->s + i);
+ fqdn->len = plus + j;
+ if (rc += dns_ip4(ipout,fqdn) < 0) return DNS_ERR;
+ i += j;
+ if (i >= fqdnlen) return rc;
+ ++i;
+ }
+ return 0;
+}
+
+/** @fn int dns_ip4_qualify -> @return number of IPv4 addresss qualified */
+
+int dns_ip4_qualify(stralloc *ipout,stralloc *fqdn,const stralloc *in)
+{
+ int r;
+ static stralloc rules;
+
+ if ((r = dns_ip_qualify_localhost(ipout,fqdn,in)) > 0 ) return r;
+ if (dns_resolvconfrewrite(&rules) < 0) return DNS_INT;
+ return dns_ip4_qualify_rules(ipout,fqdn,in,&rules);
+}
+
+/** @fn int dns_ip4_qualify_rules -> @return number of IPv6 addresss with rules */
+
+int dns_ip6_qualify_rules(stralloc *ipout,stralloc *fqdn,const stralloc *in,const stralloc *rules)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int plus;
+ unsigned int fqdnlen;
+ int rc = 0;
+
+ if (!stralloc_copy(fqdn,(stralloc *)in)) return DNS_MEM;
+
+ for (j = i = 0; j < rules->len; ++j)
+ if (!rules->s[j]) {
+ if (!doit(fqdn,rules->s + i)) return DNS_INT;
+ i = j + 1;
+ }
+
+ fqdnlen = fqdn->len;
+ plus = byte_chr(fqdn->s,fqdnlen,'+');
+ if (plus >= fqdnlen)
+ return dns_ip6(ipout,fqdn);
+
+ i = plus + 1;
+ for (;;) {
+ j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
+ byte_copy(fqdn->s + plus,j,fqdn->s + i);
+ fqdn->len = plus + j;
+ if ((rc += dns_ip6(ipout,fqdn)) < 0) return DNS_ERR;
+ i += j;
+ if (i >= fqdnlen) return rc;
+ ++i;
+ }
+ return 0;
+}
+
+/** @fn int dns_ip6_qualify -> @return number of IPv6 addresss qualified */
+
+int dns_ip6_qualify(stralloc *ipout,stralloc *fqdn,const stralloc *in)
+{
+ int r;
+ static stralloc rules;
+
+ if ((r = dns_ip_qualify_localhost(ipout,fqdn,in)) > 0) return r;
+ if (dns_resolvconfrewrite(&rules) < 0) return DNS_INT;
+ return dns_ip6_qualify_rules(ipout,fqdn,in,&rules);
+}
+
+/** @fn int dns_ip_qualify_rules -> @return number of IPv6+IPv4 addresss with rules */
+
+int dns_ip_qualify_rules(stralloc *ipout,stralloc *fqdn,const stralloc *in,const stralloc *rules)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int k;
+ unsigned int plus;
+ unsigned int fqdnlen;
+ stralloc tmp = {0};
+ int rc = 0;
+
+ if (!stralloc_copy(fqdn,(stralloc *)in)) return DNS_MEM;
+ if (!stralloc_copys(ipout,"")) return DNS_MEM;
+
+ for (j = i = 0; j < rules->len; ++j)
+ if (!rules->s[j]) {
+ if (!doit(fqdn,rules->s + i)) return DNS_INT;
+ i = j + 1;
+ }
+
+ fqdnlen = fqdn->len;
+ plus = byte_chr(fqdn->s,fqdnlen,'+');
+ if (plus >= fqdnlen) {
+ rc = dns_ip6(ipout,fqdn);
+ if (dns_ip4(&tmp,fqdn) > 0) {
+ for (k = 0; k < tmp.len; k += 4) {
+ if (!stralloc_catb(ipout,(const char *) V4mappedprefix,12)) return DNS_MEM;
+ if (!stralloc_catb(ipout,tmp.s + k,4)) return DNS_MEM;
+ rc++;
+ }
+ }
+ return rc;
+ }
+
+ i = plus + 1;
+ for (;;) {
+ j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
+ byte_copy(fqdn->s + plus,j,fqdn->s + i);
+ fqdn->len = plus + j;
+ if (!stralloc_copys(ipout,"")) return DNS_MEM;
+ rc = dns_ip6(&tmp,fqdn);
+ if (rc) if (!stralloc_cat(ipout,&tmp)) return DNS_MEM;
+ if (dns_ip4(&tmp,fqdn) > 0) {
+ for (k = 0; k < tmp.len; k += 4) {
+ if (!stralloc_catb(ipout,(const char *) V4mappedprefix,12)) return DNS_MEM;
+ if (!stralloc_catb(ipout,tmp.s + k,4)) return DNS_MEM;
+ rc++;
+ }
+ }
+
+ if (rc < 0) return DNS_ERR;
+ i += j;
+ if (i >= fqdnlen) return rc;
+ ++i;
+ }
+ return 0;
+}
+
+/** @fn int dns_ip_qualify_localhost -> @return number of IP addresss */
+
+int dns_ip_qualify_localhost(stralloc *ipout,stralloc *fqdn,const stralloc *in)
+{
+ if (!stralloc_copys(ipout,"")) return DNS_MEM;
+ if (!stralloc_copys(fqdn,"")) return DNS_MEM;
+ ipout->len = 0;
+
+ if (byte_equal(in->s,9,LOCALHOST)) {
+ if (!stralloc_copyb(ipout,(const char *) V6loopback,16)) return DNS_MEM;
+ if (!stralloc_catb(ipout,(const char *) V46loopback,16)) return DNS_MEM;
+ if (!stralloc_copys(fqdn,"localhost.localhost.")) return DNS_MEM;
+ }
+ if (byte_equal(in->s,13,IP4_LOOPBACK)) {
+ if (!stralloc_copyb(ipout,(const char *) V46loopback,16)) return DNS_MEM;
+ if (!stralloc_copys(fqdn,"ip4-loopback.localhost.")) return DNS_MEM;
+ }
+ if (byte_equal(in->s,13,IP6_LOOPBACK)) {
+ if (!stralloc_copyb(ipout,(const char *) V6loopback,16)) return DNS_MEM;
+ if (!stralloc_copys(fqdn,"ip6-loopback.localhost.")) return DNS_MEM;
+ }
+// if (!stralloc_0(fqdn)) return DNS_MEM; // don't do it
+
+ return ipout->len ? ipout->len % 15 : 0;
+}
+
+/** @fn int dns_ip_qualify -> @return number of IP addresss */
+
+int dns_ip_qualify(stralloc *ipout,stralloc *fqdn,const stralloc *in)
+{
+ int r;
+ static stralloc rules;
+
+ if ((r = dns_ip_qualify_localhost(ipout,fqdn,in)) > 0 ) return r;
+ if (dns_resolvconfrewrite(&rules) < 0) return DNS_INT;
+ return dns_ip_qualify_rules(ipout,fqdn,in,&rules);
+}
diff --git a/dnsstub/dns_mx.c b/dnsstub/dns_mx.c
new file mode 100644
index 0000000..c0845ef
--- /dev/null
+++ b/dnsstub/dns_mx.c
@@ -0,0 +1,63 @@
+#include "stralloc.h"
+#include "byte.h"
+#include "uint_t.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_mx.c
+ @author djb
+ @source qmail
+ @brief dns MX query
+ @param (on output) stralloc out
+*/
+
+static char *q = 0;
+
+int dns_mx_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ char pref[2];
+ uint16 numanswers;
+ uint16 datalen;
+ int ranswers = 0;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_MX))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (!dns_packet_copy(buf,len,pos,pref,2)) return DNS_ERR;
+ if (!dns_packet_getname(buf,len,pos + 2,&q)) return DNS_ERR;
+ if (!stralloc_catb(out,pref,2)) return DNS_MEM;
+ if (dns_domain_todot_cat(out,q) <= 0) return DNS_ERR;
+ if (!stralloc_0(out)) return DNS_MEM;
+ }
+ pos += datalen;
+ ++ranswers;
+ }
+
+ return ranswers;
+}
+
+int dns_mx(stralloc *out,const stralloc *fqdn)
+{
+ int rc = 0;
+
+ if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR;
+ if (dns_resolve(q,DNS_T_MX) >= 0) {
+ if ((rc = dns_mx_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ }
+
+ return rc;
+}
diff --git a/dnsstub/dns_name.c b/dnsstub/dns_name.c
new file mode 100644
index 0000000..0723a8f
--- /dev/null
+++ b/dnsstub/dns_name.c
@@ -0,0 +1,80 @@
+#include "stralloc.h"
+#include "uint_t.h"
+#include "byte.h"
+#include "ip.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_name.c
+ @author djb, fefe, feh
+ @source ucspi-tcp
+ @brief DNS name query (ptr)
+*/
+
+static char *q = 0;
+
+int dns_name_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ uint16 numanswers;
+ uint16 datalen;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_PTR))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (!dns_packet_getname(buf,len,pos,&q)) return DNS_ERR;
+ if (dns_domain_todot_cat(out,q) <= 0) return DNS_ERR;
+ return 1;
+ }
+ pos += datalen;
+ }
+
+ return 0;
+}
+
+int dns_name4(stralloc *out,const char ip[4])
+{
+ int rc;
+ char name[DNS_NAME4_DOMAIN];
+
+ dns_name4_domain(name,ip);
+ if (dns_resolve(name,DNS_T_PTR) < 0) return DNS_ERR;
+ if ((rc = dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+
+ return rc;
+}
+
+int dns_name6(stralloc *out,const char ip[16])
+{
+ int rc;
+ char name[DNS_NAME6_DOMAIN];
+
+ dns_name6_domain(name,ip);
+ if (dns_resolve(name,DNS_T_PTR) < 0) return DNS_ERR;
+ if ((rc = dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+
+ return rc;
+}
+
+int dns_name(stralloc *out,const char ip[16])
+{
+ if (ip6_isv4mapped(ip))
+ return dns_name4(out,ip+12);
+ else
+ return dns_name6(out,ip);
+}
diff --git a/dnsstub/dns_nd.c b/dnsstub/dns_nd.c
new file mode 100644
index 0000000..6ce8ed9
--- /dev/null
+++ b/dnsstub/dns_nd.c
@@ -0,0 +1,48 @@
+#include "byte.h"
+#include "fmt.h"
+#include "ip.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_nd.c
+ @autor djb, fefe
+ @source ucspi-tcp
+ @brief DNS domain name for ip (wire format)
+*/
+
+int dns_name4_domain(char name[DNS_NAME4_DOMAIN],const char ip[4])
+{
+ unsigned int namelen;
+ unsigned int i;
+
+ namelen = 0;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[3]);
+ name[namelen++] = i;
+ namelen += i;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[2]);
+ name[namelen++] = i;
+ namelen += i;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[1]);
+ name[namelen++] = i;
+ namelen += i;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[0]);
+ name[namelen++] = i;
+ namelen += i;
+ byte_copy(name + namelen,14,"\7in-addr\4arpa\0");
+ return namelen+14;
+}
+
+int dns_name6_domain(char name[DNS_NAME6_DOMAIN],const char ip[16])
+{
+ unsigned int j;
+
+ for (j = 0; j < 16; j++) {
+ name[j * 4] = 1;
+ name[j * 4 + 1] = tohex(ip[15 - j] & 15);
+ name[j * 4 + 2] = 1;
+ name[j * 4 + 3] = tohex((unsigned char)ip[15 - j] >> 4);
+ }
+ byte_copy(name + 4 * 16,10,"\3ip6\4arpa\0");
+ return 4 * 16 + 10;
+}
+
diff --git a/dnsstub/dns_packet.c b/dnsstub/dns_packet.c
new file mode 100644
index 0000000..ce322ea
--- /dev/null
+++ b/dnsstub/dns_packet.c
@@ -0,0 +1,85 @@
+#include "error.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_packet.c
+ @author djb
+ @source ucspi-tcp
+ @brief DNS low level packet routine
+ @brief DNS should have used LZ77 instead of its own sophomoric compression algorithm.
+*/
+
+unsigned int dns_packet_copy(const char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen)
+{
+ while (outlen) {
+ if (pos >= len) { errno = EPROTO; return 0; }
+ *out = buf[pos++];
+ ++out; --outlen;
+ }
+ return pos;
+}
+
+unsigned int dns_packet_skipname(const char *buf,unsigned int len,unsigned int pos)
+{
+ unsigned char ch;
+
+ for (;;) {
+ if (pos >= len) break;
+ ch = buf[pos++];
+ if (ch >= 192) return pos + 1;
+ if (ch >= 64) break;
+ if (!ch) return pos;
+ pos += ch;
+ }
+
+ errno = EPROTO;
+ return 0;
+}
+
+unsigned int dns_packet_getname(const char *buf,unsigned int len,unsigned int pos,char **d)
+{
+ unsigned int loop = 0;
+ unsigned int state = 0;
+ unsigned int firstcompress = 0;
+ unsigned int where;
+ unsigned char ch;
+ char name[255];
+ unsigned int namelen = 0;
+
+ for (;;) {
+ if (pos >= len) goto PROTO;
+ ch = buf[pos++];
+ if (++loop >= 1000) goto PROTO;
+
+ if (state) {
+ if (namelen + 1 > sizeof(name)) goto PROTO;
+ name[namelen++] = ch;
+ --state;
+ } else {
+ while (ch >= 192) {
+ where = ch; where -= 192; where <<= 8;
+ if (pos >= len) goto PROTO;
+ ch = buf[pos++];
+ if (!firstcompress) firstcompress = pos;
+ pos = where + ch;
+ if (pos >= len) goto PROTO;
+ ch = buf[pos++];
+ if (++loop >= 1000) goto PROTO;
+ }
+ if (ch >= 64) goto PROTO;
+ if (namelen + 1 > sizeof(name)) goto PROTO;
+ name[namelen++] = ch;
+ if (!ch) break;
+ state = ch;
+ }
+ }
+
+ if (!dns_domain_copy(d,name)) return 0;
+
+ if (firstcompress) return firstcompress;
+ return pos;
+
+ PROTO:
+ errno = EPROTO;
+ return 0;
+}
diff --git a/dnsstub/dns_random.c b/dnsstub/dns_random.c
new file mode 100644
index 0000000..200cd6c
--- /dev/null
+++ b/dnsstub/dns_random.c
@@ -0,0 +1,70 @@
+#include <unistd.h>
+#include "taia.h"
+#include "uint_t.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_random.c
+ @author djb
+ @source ucspi-tcp
+ @brief random use of DNS resolvers given their IP
+*/
+
+static uint32 seed[32];
+static uint32 in[12];
+static uint32 out[8];
+static int outleft = 0;
+
+#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
+#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
+
+static void surf(void)
+{
+ uint32 t[12]; uint32 x; uint32 sum = 0;
+ int r; int i; int loop;
+
+ for (i = 0; i < 12; ++i) t[i] = in[i] ^ seed[12 + i];
+ for (i = 0; i < 8; ++i) out[i] = seed[24 + i];
+ x = t[11];
+ for (loop = 0; loop < 2; ++loop) {
+ for (r = 0; r < 16; ++r) {
+ sum += 0x9e3779b9;
+ MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13)
+ MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13)
+ MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13)
+ }
+ for (i = 0; i < 8; ++i) out[i] ^= t[i + 4];
+ }
+}
+
+void dns_random_init(const char data[128])
+{
+ int i;
+ struct taia t;
+ char tpack[16];
+
+ for (i = 0; i < 32; ++i)
+ uint32_unpack((char *)data + 4 * i,seed + i);
+
+ taia_now(&t);
+ taia_pack(tpack,&t);
+ for (i = 0; i < 4; ++i)
+ uint32_unpack(tpack + 4 * i,in + 4 + i);
+
+ in[8] = getpid();
+ in[9] = getppid();
+ /* more space in 10 and 11, but this is probably enough */
+}
+
+unsigned int dns_random(unsigned int n)
+{
+ if (!n) return 0;
+
+ if (!outleft) {
+ if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+ surf();
+ outleft = 8;
+ }
+
+ return out[--outleft] % n;
+}
diff --git a/dnsstub/dns_rcip.c b/dnsstub/dns_rcip.c
new file mode 100644
index 0000000..93b0daa
--- /dev/null
+++ b/dnsstub/dns_rcip.c
@@ -0,0 +1,114 @@
+#include "taia.h"
+#include "readclose.h"
+#include "byte.h"
+#include "ip.h"
+#include "env.h"
+#include "dnsresolv.h"
+#include "socket_if.h"
+
+/**
+ @file dns_rcip.c
+ @author djb, fefe, feh
+ @source ucspi-tcp
+ @brief DNS receive for query
+*/
+
+static stralloc data = {0};
+static stralloc ifname = {0};
+
+static int init(char ip[QUERY_MAXIPLEN],uint32 sid[QUERY_MAXNS])
+{
+ int i;
+ int j;
+ int k = 0;
+ int iplen = 0;
+ char *x;
+ char ip4[4];
+
+/* Read (compactified) IPv4|v6 addresses of resolvers
+ Store them in array IP with fixed length :
+ ip(64) -> 16 IPv4 addresses (not used anymore)
+ ip(512) -> 16*2 IPv6 addresses (we use IPv4 mapped IPv6 addresses)
+ sid(32) -> the scope for the respective IPv6 or 0
+*/
+ for (i = 0; i < QUERY_MAXNS; ++i) sid[i] = 0;
+
+ x = env_get("DNSCACHEIP");
+ if (x)
+ while (iplen <= 240 && *x != '\0') {
+ if (*x == ' ')
+ ++x;
+ else
+ if ((i = ip6_ifscan(x,ip + iplen,&ifname))) {
+ if (ifname.len > 2) sid[k] = socket_getifidx(ifname.s);
+ iplen += 16; k++;
+ if (*(x += i) == '\0') break;
+ }
+ }
+
+ if (!iplen) {
+ i = openreadclose("/etc/resolv.conf",&data,64);
+ if (i == -1) return DNS_INT;
+ if (i) {
+ if (!stralloc_append(&data,"\n")) return DNS_MEM;
+ i = 0;
+ for (j = 0; j < data.len; ++j)
+ if (data.s[j] == '\n') {
+ if (byte_equal("nameserver ",11,data.s + i) || byte_equal("nameserver\t",11,data.s + i)) {
+ i += 10;
+ while ((data.s[i] == ' ') || (data.s[i] == '\t'))
+ i++;
+ if (iplen <= 240) {
+ data.s[j] = '\0'; /* ip6_ifscan needs terminated string on input */
+ if (ip4_scan(data.s + i,ip4)) {
+ if (byte_equal(ip4,4,"\0\0\0\0"))
+ byte_copy(ip4,4,"\177\0\0\1");
+ byte_copy(ip + iplen,12,V4mappedprefix);
+ byte_copy(ip + iplen + 12,4,ip4);
+ sid[k] = 0; iplen += 16; k++;
+ } else if (ip6_ifscan(data.s + i,ip + iplen,&ifname)) {
+ if (ifname.len > 2) sid[k] = socket_getifidx(ifname.s);
+ iplen += 16; k++;
+ }
+ }
+ }
+ i = j + 1;
+ }
+ }
+ }
+
+ if (!iplen) {
+ byte_copy(ip,16,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1");
+ iplen = 16;
+ }
+ byte_zero(ip + iplen,QUERY_MAXIPLEN - iplen);
+ return 0;
+}
+
+static int ok = 0;
+static unsigned int uses;
+static struct taia deadline;
+static char ip[QUERY_MAXIPLEN]; /* defined if ok */
+static uint32 scopes[QUERY_MAXNS];
+
+int dns_resolvconfip(char s[QUERY_MAXIPLEN],uint32 scope[QUERY_MAXNS])
+{
+ struct taia now;
+
+ taia_now(&now);
+ if (taia_less(&deadline,&now)) ok = 0;
+ if (!uses) ok = 0;
+
+ if (!ok) {
+ if (init(ip,scopes) < 0) return DNS_INT;
+ taia_uint(&deadline,600);
+ taia_add(&deadline,&now,&deadline);
+ uses = 10000;
+ ok = 1;
+ }
+
+ --uses;
+ byte_copy(s,QUERY_MAXIPLEN,ip);
+ byte_copy(scope,128,scopes);
+ return 0;
+}
diff --git a/dnsstub/dns_rcrw.c b/dnsstub/dns_rcrw.c
new file mode 100644
index 0000000..4633fed
--- /dev/null
+++ b/dnsstub/dns_rcrw.c
@@ -0,0 +1,141 @@
+#include <unistd.h>
+#include "taia.h"
+#include "env.h"
+#include "byte.h"
+#include "str.h"
+#include "readclose.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_rcrw.c
+ @author djb
+ @source ucspi-tcp
+ @brief DNS receive rewrite
+*/
+
+static stralloc data = {0};
+
+static int init(stralloc *rules)
+{
+ char host[256];
+ const char *x;
+ int i;
+ int j;
+ int k;
+
+ if (!stralloc_copys(rules,"")) return DNS_MEM;
+
+ x = env_get("DNSREWRITEFILE");
+ if (!x) x = "/etc/dnsrewrite";
+
+ i = openreadclose(x,&data,64);
+ if (i == -1) return DNS_INT;
+
+ if (i) {
+ if (!stralloc_append(&data,"\n")) return DNS_MEM;
+ i = 0;
+ for (j = 0; j < data.len; ++j)
+ if (data.s[j] == '\n') {
+ if (!stralloc_catb(rules,data.s + i,j - i)) return DNS_MEM;
+ while (rules->len) {
+ if (rules->s[rules->len - 1] != ' ')
+ if (rules->s[rules->len - 1] != '\t')
+ if (rules->s[rules->len - 1] != '\r')
+ break;
+ --rules->len;
+ }
+ if (!stralloc_0(rules)) return DNS_MEM;
+ i = j + 1;
+ }
+ return 0;
+ }
+
+ x = env_get("LOCALDOMAIN");
+ if (x) {
+ if (!stralloc_copys(&data,x)) return DNS_MEM;
+ if (!stralloc_append(&data," ")) return DNS_MEM;
+ if (!stralloc_copys(rules,"?:")) return DNS_MEM;
+ i = 0;
+ for (j = 0; j < data.len; ++j)
+ if (data.s[j] == ' ') {
+ if (!stralloc_cats(rules,"+.")) return DNS_MEM;
+ if (!stralloc_catb(rules,data.s + i,j - i)) return DNS_MEM;
+ i = j + 1;
+ }
+ if (!stralloc_0(rules)) return DNS_MEM;
+ if (!stralloc_cats(rules,"*.:")) return DNS_MEM;
+ if (!stralloc_0(rules)) return DNS_MEM;
+ return 0;
+ }
+
+ i = openreadclose("/etc/resolv.conf",&data,64);
+ if (i == -1) return DNS_INT;
+
+ if (i) {
+ if (!stralloc_append(&data,"\n")) return DNS_MEM;
+ i = 0;
+ for (j = 0; j < data.len; ++j)
+ if (data.s[j] == '\n') {
+ if (byte_equal("search ",7,data.s + i) ||
+ byte_equal("search\t",7,data.s + i) ||
+ byte_equal("domain ",7,data.s + i) ||
+ byte_equal("domain\t",7,data.s + i)) {
+ if (!stralloc_copys(rules,"?:")) return DNS_MEM;
+ i += 7;
+ while (i < j) {
+ k = byte_chr(data.s + i,j - i,' ');
+ k = byte_chr(data.s + i,k,'\t');
+ if (!k) { ++i; continue; }
+ if (!stralloc_cats(rules,"+.")) return DNS_MEM;
+ if (!stralloc_catb(rules,data.s + i,k)) return DNS_MEM;
+ i += k;
+ }
+ if (!stralloc_0(rules)) return DNS_MEM;
+ if (!stralloc_cats(rules,"*.:")) return DNS_MEM;
+ if (!stralloc_0(rules)) return DNS_MEM;
+ return 0;
+ }
+ i = j + 1;
+ }
+ }
+
+ host[0] = 0;
+ if (gethostname(host,sizeof(host)) == -1) return DNS_ERR;
+ host[(sizeof(host)) - 1] = 0;
+ i = str_chr(host,'.');
+ if (host[i]) {
+ if (!stralloc_copys(rules,"?:")) return DNS_MEM;
+ if (!stralloc_cats(rules,host + i)) return DNS_MEM;
+ if (!stralloc_0(rules)) return DNS_MEM;
+ }
+ if (!stralloc_cats(rules,"*.:")) return DNS_MEM;
+ if (!stralloc_0(rules)) return DNS_MEM;
+
+ return 0;
+}
+
+static int ok = 0;
+static unsigned int uses;
+static struct taia deadline;
+static stralloc rules = {0}; /* defined if ok */
+
+int dns_resolvconfrewrite(stralloc *out)
+{
+ struct taia now;
+
+ taia_now(&now);
+ if (taia_less(&deadline,&now)) ok = 0;
+ if (!uses) ok = 0;
+
+ if (!ok) {
+ if (init(&rules) < 0) return DNS_INT;
+ taia_uint(&deadline,600);
+ taia_add(&deadline,&now,&deadline);
+ uses = 10000;
+ ok = 1;
+ }
+
+ --uses;
+ if (!stralloc_copy(out,&rules)) return DNS_MEM;
+ return 0;
+}
diff --git a/dnsstub/dns_resolve.c b/dnsstub/dns_resolve.c
new file mode 100644
index 0000000..bcc4308
--- /dev/null
+++ b/dnsstub/dns_resolve.c
@@ -0,0 +1,39 @@
+#include "iopause.h"
+#include "taia.h"
+#include "byte.h"
+#include "ip.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_resolve.c
+ @author djb, fefe, feh
+ @source ucspi-tcp
+ @brief high-level DNS resolve function
+*/
+
+struct dns_transmit dns_resolve_tx = {0};
+
+int dns_resolve(const char *q,const char qtype[2])
+{
+ struct taia stamp;
+ struct taia deadline;
+ char servers[QUERY_MAXIPLEN];
+ uint32 scopes[QUERY_MAXNS];
+ iopause_fd x[1];
+ int r;
+
+ if (dns_resolvconfip(servers,scopes) < 0) return DNS_INT;
+
+ if (dns_transmit_start6(&dns_resolve_tx,servers,1,q,qtype,(const char *)V6localnet,scopes) < 0) return DNS_COM;
+
+ for (;;) {
+ taia_now(&stamp);
+ taia_uint(&deadline,120);
+ taia_add(&deadline,&deadline,&stamp);
+ dns_transmit_io(&dns_resolve_tx,x,&deadline);
+ iopause(x,1,&deadline,&stamp);
+ r = dns_transmit_get(&dns_resolve_tx,x,&stamp);
+ if (r < 0) return DNS_COM;
+ if (r == 1) return 0;
+ }
+}
diff --git a/dnsstub/dns_sortip.c b/dnsstub/dns_sortip.c
new file mode 100644
index 0000000..56742e0
--- /dev/null
+++ b/dnsstub/dns_sortip.c
@@ -0,0 +1,45 @@
+#include "byte.h"
+#include "ip.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_sortip.c
+ @authors djb, fefe, feh
+ @source ucspi-tcp6
+ @brief random sort of DNS servers per IP
+*/
+
+/* XXX: sort servers by configurable notion of closeness? */
+/* XXX: pay attention to competence of each server? */
+/* XXX: pay attention to qualification (DNSSec, DNSCurve) of each server? */
+/* YYY: we use a randomly sorted list of NS; not depending on answer */
+
+void dns_sortip4(char *s,unsigned int n)
+{
+ unsigned int i;
+ char tmp[4];
+
+ n >>= 2; /* 4 byte per IPv4 address */
+ while (n > 1) {
+ i = dns_random(n);
+ --n;
+ byte_copy(tmp,4,s + (i << 2));
+ byte_copy(s + (i << 2),4,s + (n << 2));
+ byte_copy(s + (n << 2),4,tmp);
+ }
+}
+
+void dns_sortip6(char *s,unsigned int n)
+{
+ unsigned int i;
+ char tmp[16];
+
+ n >>= 4; /* 16 byte per IPv4 address */
+ while (n > 1) {
+ i = dns_random(n);
+ --n;
+ byte_copy(tmp,16,s + (i << 4));
+ byte_copy(s + (i << 4),16,s + (n << 4));
+ byte_copy(s + (n << 4),16,tmp);
+ }
+}
diff --git a/dnsstub/dns_transmit.c b/dnsstub/dns_transmit.c
new file mode 100644
index 0000000..2513565
--- /dev/null
+++ b/dnsstub/dns_transmit.c
@@ -0,0 +1,436 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "socket_if.h"
+#include "alloc.h"
+#include "error.h"
+#include "byte.h"
+#include "uint_t.h"
+#include "ip.h"
+#include "dnsresolv.h"
+
+/**
+ @file dns_transmit.c
+ @authors djb, fefe, feh
+ @source qlibs
+ @brief DNS query function
+ @brief scope_ids[32] -> 32 LLU root servers supported
+*/
+
+#define DNSPORT 53
+
+uint32 scope_ids[QUERY_MAXNS];
+
+static const int timeouts[5] = { 1, 2, 4, 8, 16 }; /* quadratic, not exponentially */
+
+int getscopeid(const struct dns_transmit *d,const char *ip)
+{
+ int i;
+
+ if (byte_diff(ip,2,V6linklocal)) return 0;
+ for (i = 0; i < QUERY_MAXNS; ++i)
+ if (byte_equal(d->servers + 16 * i,16,ip))
+ return scope_ids[i];
+
+ return 0;
+}
+
+int serverwantstcp(const char *buf,unsigned int len)
+{
+ char out[12];
+
+ if (!dns_packet_copy(buf,len,0,out,12)) return 1;
+ if (out[2] & 2) return 1;
+
+ return 0;
+}
+
+int serverfailed(const char *buf,unsigned int len)
+{
+ char out[12];
+ unsigned int rcode;
+
+ if (!dns_packet_copy(buf,len,0,out,12)) return 1;
+ rcode = out[3];
+ rcode &= 15;
+ if (rcode && (rcode != 3)) { errno = EAGAIN; return 1; }
+
+ return 0;
+}
+
+int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len)
+{
+ char out[12];
+ char *dn;
+ unsigned int pos;
+
+ pos = dns_packet_copy(buf,len,0,out,12); if (!pos) return 1;
+ if (byte_diff(out,2,d->query + 2)) return 1;
+ if (out[4] != 0) return 1;
+ if (out[5] != 1) return 1;
+
+ dn = 0;
+ pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1;
+ if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; }
+ alloc_free(dn);
+
+ pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1;
+ if (byte_diff(out,2,d->qtype)) return 1;
+ if (byte_diff(out + 2,2,DNS_C_IN)) return 1;
+
+ return 0;
+}
+
+void packetfree(struct dns_transmit *d)
+{
+ if (!d->packet) return;
+ alloc_free(d->packet);
+ d->packet = 0;
+}
+
+void queryfree(struct dns_transmit *d)
+{
+ if (!d->query) return;
+ alloc_free(d->query);
+ d->query = 0;
+}
+
+void socketfree(struct dns_transmit *d)
+{
+ if (!d->s1) return;
+ close(d->s1 - 1);
+ d->s1 = 0;
+}
+
+void dns_transmit_free(struct dns_transmit *d)
+{
+ queryfree(d);
+ socketfree(d);
+ packetfree(d);
+}
+
+int randombind6(struct dns_transmit *d)
+{
+ int j;
+
+ for (j = 0; j < 10; ++j) {
+ if (socket_bind6(d->s1 - 1,d->localip,1025 + dns_random(64510),d->scope_id) == 0)
+ return 0;
+ }
+ if (socket_bind6(d->s1 - 1,d->localip,0,d->scope_id) == 0)
+ return 0;
+
+ return DNS_COM;
+}
+
+int randombind4(struct dns_transmit *d)
+{
+ int j;
+
+ for (j = 0; j < 10; ++j) {
+ if (socket_bind4(d->s1 - 1,d->localip + 12,1025 + dns_random(64510)) == 0)
+ return 0;
+ }
+ if (socket_bind4(d->s1 - 1,d->localip + 12,0) == 0)
+ return 0;
+
+ return DNS_COM;
+}
+
+int thisudp(struct dns_transmit *d)
+{
+ const char *ip;
+
+ socketfree(d);
+
+ while (d->udploop < 5) {
+ for (; d->curserver < QUERY_MAXNS; ++d->curserver) {
+ ip = d->servers + 16 * d->curserver;
+ if (byte_diff(ip,16,V6localnet)) {
+ d->query[2] = dns_random(256);
+ d->query[3] = dns_random(256);
+
+ if (ip6_isv4mapped(ip)) {
+ d->s1 = 1 + socket_udp4();
+ if (!d->s1) { dns_transmit_free(d); return DNS_COM; }
+ if (randombind4(d) < 0) { dns_transmit_free(d); return DNS_COM; }
+ } else {
+ d->s1 = 1 + socket_udp6();
+ if (!d->s1) { dns_transmit_free(d); return DNS_COM; }
+ if (randombind6(d) < 0) { dns_transmit_free(d); return DNS_COM; }
+ }
+
+ if (byte_equal(ip,2,V6linklocal) && !d->scope_id)
+ d->scope_id = getscopeid(d,ip);
+ if (socket_connect(d->s1 - 1,ip,DNSPORT,d->scope_id) == 0)
+ if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) {
+ struct taia now;
+ taia_now(&now);
+ taia_uint(&d->deadline,timeouts[d->udploop]);
+ taia_add(&d->deadline,&d->deadline,&now);
+ d->tcpstate = 0;
+ return 0;
+ }
+ socketfree(d);
+ }
+ }
+ ++d->udploop;
+ d->curserver = 0;
+ }
+
+ dns_transmit_free(d); return DNS_COM;
+}
+
+int firstudp(struct dns_transmit *d)
+{
+ d->curserver = 0;
+ return thisudp(d);
+}
+
+int nextudp(struct dns_transmit *d)
+{
+ ++d->curserver;
+ return thisudp(d);
+}
+
+int thistcp(struct dns_transmit *d)
+{
+ struct taia now;
+ const char *ip;
+
+ socketfree(d);
+ packetfree(d);
+
+ for (; d->curserver < QUERY_MAXNS; ++d->curserver) {
+ ip = d->servers + 16 * d->curserver;
+ if (byte_diff(ip,16,V6localnet)) {
+ d->query[2] = dns_random(256);
+ d->query[3] = dns_random(256);
+
+ if (ip6_isv4mapped(ip)) {
+ d->s1 = 1 + socket_tcp4();
+ if (!d->s1) { dns_transmit_free(d); return DNS_COM; }
+ if (randombind4(d) < 0) { dns_transmit_free(d); return DNS_COM; }
+ } else {
+ d->s1 = 1 + socket_tcp6();
+ if (!d->s1) { dns_transmit_free(d); return DNS_COM; }
+ if (randombind6(d) < 0) { dns_transmit_free(d); return DNS_COM; }
+ }
+
+ taia_now(&now);
+ taia_uint(&d->deadline,10);
+ taia_add(&d->deadline,&d->deadline,&now);
+
+ if (byte_equal(ip,2,V6linklocal) && !d->scope_id)
+ d->scope_id = getscopeid(d,ip);
+ if (socket_connect(d->s1 - 1,ip,DNSPORT,d->scope_id) == 0) {
+ d->tcpstate = 2;
+ return 0;
+ }
+ if ((errno == EINPROGRESS) || (errno == EWOULDBLOCK)) {
+ d->tcpstate = 1;
+ return 0;
+ }
+
+ socketfree(d);
+ }
+ }
+
+ dns_transmit_free(d);
+ return DNS_COM;
+}
+
+int firsttcp(struct dns_transmit *d)
+{
+ d->curserver = 0;
+ return thistcp(d);
+}
+
+int nexttcp(struct dns_transmit *d)
+{
+ ++d->curserver;
+ return thistcp(d);
+}
+
+int dns_transmit_start(struct dns_transmit *d,const char servers[QUERY_MAXIPLEN], \
+ int flagrecursive,const char *q,const char qtype[2],const char localip[16])
+{
+ unsigned int len;
+
+ dns_transmit_free(d);
+ errno = EIO;
+
+ len = dns_domain_length(q);
+ d->querylen = len + 18;
+ d->query = alloc(d->querylen);
+ if (!d->query) return DNS_COM;
+
+ uint16_pack_big(d->query,len + 16);
+ byte_copy(d->query + 2,12,flagrecursive ? "\0\0\1\0\0\1\0\0\0\0\0\0" : \
+ "\0\0\0\0\0\1\0\0\0\0\0\0gcc-bug-workaround");
+ byte_copy(d->query + 14,len,q);
+ byte_copy(d->query + 14 + len,2,qtype);
+ byte_copy(d->query + 16 + len,2,DNS_C_IN);
+
+ byte_copy(d->qtype,2,(char *) qtype);
+ d->servers = servers;
+ byte_copy(d->localip,16,(char *) localip);
+
+ d->udploop = flagrecursive ? 1 : 0;
+
+ if (len + 16 > MSGSIZE) return firsttcp(d);
+ return firstudp(d);
+}
+
+int dns_transmit_start6(struct dns_transmit *d,const char servers[QUERY_MAXIPLEN], \
+ int flagrecursive,const char *q,const char qtype[2], \
+ const char localip[16],const uint32 scopes[QUERY_MAXNS])
+{
+ byte_copy(scope_ids,128,(char *) scopes);
+
+ return dns_transmit_start(d,servers,flagrecursive,q,qtype,localip);
+}
+
+void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline)
+{
+ x->fd = d->s1 - 1;
+
+ switch (d->tcpstate) {
+ case 0: case 3: case 4: case 5:
+ x->events = IOPAUSE_READ;
+ break;
+ case 1: case 2:
+ x->events = IOPAUSE_WRITE;
+ break;
+ }
+
+ if (taia_less(&d->deadline,deadline))
+ *deadline = d->deadline;
+}
+
+int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when)
+{
+ char udpbuf[MSGSIZE + 1];
+ unsigned char ch;
+ int r;
+ int fd;
+
+ errno = EIO;
+ fd = d->s1 - 1;
+
+ if (!x->revents) {
+ if (taia_less((struct taia *)when,&d->deadline)) return 0;
+ errno = ETIMEDOUT;
+ if (d->tcpstate == 0) return nextudp(d);
+ return nexttcp(d);
+ }
+
+/*
+have attempted to send UDP query to each server udploop times
+have sent query to curserver on UDP socket s
+*/
+ if (d->tcpstate == 0) {
+ r = recv(fd,udpbuf,sizeof(udpbuf),0);
+ if (r <= 0) {
+ if (errno == ECONNREFUSED) if (d->udploop == 2) return 0;
+ return nextudp(d);
+ }
+ if (r + 1 > sizeof(udpbuf)) return 0;
+
+ if (irrelevant(d,udpbuf,r)) return 0;
+ if (serverwantstcp(udpbuf,r)) return firsttcp(d);
+ if (serverfailed(udpbuf,r)) {
+ if (d->udploop == 2) return 0;
+ return nextudp(d);
+ }
+ socketfree(d);
+
+ d->packetlen = r;
+ d->packet = alloc(d->packetlen);
+ if (!d->packet) { dns_transmit_free(d); return DNS_COM; }
+ byte_copy(d->packet,d->packetlen,udpbuf);
+ queryfree(d);
+ return 1;
+ }
+
+/*
+have sent connection attempt to curserver on TCP socket s
+pos not defined
+*/
+ if (d->tcpstate == 1) {
+ if (!socket_connected(fd)) return nexttcp(d);
+ d->pos = 0;
+ d->tcpstate = 2;
+ return 0;
+ }
+
+/*
+have connection to curserver on TCP socket s
+have sent pos bytes of query
+*/
+ if (d->tcpstate == 2) {
+ r = write(fd,d->query + d->pos,d->querylen - d->pos);
+ if (r <= 0) return nexttcp(d);
+ d->pos += r;
+ if (d->pos == d->querylen) {
+ struct taia now;
+ taia_now(&now);
+ taia_uint(&d->deadline,10);
+ taia_add(&d->deadline,&d->deadline,&now);
+ d->tcpstate = 3;
+ }
+ return 0;
+ }
+
+/*
+have sent entire query to curserver on TCP socket s
+pos not defined
+*/
+ if (d->tcpstate == 3) {
+ r = read(fd,&ch,1);
+ if (r <= 0) return nexttcp(d);
+ d->packetlen = ch;
+ d->tcpstate = 4;
+ return 0;
+ }
+
+/*
+have sent entire query to curserver on TCP socket s
+pos not defined
+have received one byte of packet length into packetlen
+*/
+ if (d->tcpstate == 4) {
+ r = read(fd,&ch,1);
+ if (r <= 0) return nexttcp(d);
+ d->packetlen <<= 8;
+ d->packetlen += ch;
+ d->tcpstate = 5;
+ d->pos = 0;
+ d->packet = alloc(d->packetlen);
+ if (!d->packet) { dns_transmit_free(d); return DNS_COM; }
+ return 0;
+ }
+
+/*
+have sent entire query to curserver on TCP socket s
+have received entire packet length into packetlen
+packet is allocated
+have received pos bytes of packet
+*/
+ if (d->tcpstate == 5) {
+ r = read(fd,d->packet + d->pos,d->packetlen - d->pos);
+ if (r <= 0) return nexttcp(d);
+ d->pos += r;
+ if (d->pos < d->packetlen) return 0;
+
+ socketfree(d);
+ if (irrelevant(d,d->packet,d->packetlen)) return nexttcp(d);
+ if (serverwantstcp(d->packet,d->packetlen)) return nexttcp(d);
+ if (serverfailed(d->packet,d->packetlen)) return nexttcp(d);
+
+ queryfree(d);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/dnsstub/dns_txt.c b/dnsstub/dns_txt.c
new file mode 100644
index 0000000..9a1b56a
--- /dev/null
+++ b/dnsstub/dns_txt.c
@@ -0,0 +1,64 @@
+#include "stralloc.h"
+#include "uint_t.h"
+#include "byte.h"
+#include "dnsresolv.h"
+
+int dns_txt_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ uint16 numanswers;
+ uint16 datalen;
+ char ch;
+ unsigned int txtlen;
+ int i;
+ int ranswers = 0;
+
+ if (!stralloc_copys(out,"")) return DNS_MEM;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_TXT))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (pos + datalen > len) return DNS_ERR;
+ txtlen = (unsigned char) buf[pos];
+ for (i = 1; i < datalen; ++i) {
+ ch = buf[pos + i];
+ if (i == txtlen + 1) // next label
+ txtlen += (unsigned char) ch + 1;
+ else {
+ if (ch < 32) ch = '?';
+ if (ch > 126) ch = '?';
+ if (!stralloc_append(out,&ch)) return DNS_MEM;
+ }
+ }
+ }
+ pos += datalen;
+ ++ranswers;
+ if (numanswers) if (!stralloc_append(out,"\n")) return DNS_MEM;
+ }
+
+ return ranswers;
+}
+
+static char *q = 0;
+
+int dns_txt(stralloc *out,const stralloc *fqdn)
+{
+ int rc;
+
+ if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR;
+ if (dns_resolve(q,DNS_T_TXT) < 0) return DNS_ERR;
+ if ((rc = dns_txt_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+
+ return rc;
+}
diff --git a/env.c b/env.c
new file mode 100644
index 0000000..2ecaa83
--- /dev/null
+++ b/env.c
@@ -0,0 +1,136 @@
+#include "str.h"
+#include "alloc.h"
+#include "env.h"
+
+/**
+ @file env.c
+ @author djb
+ @source ucspi-tcp
+ @brief setting up environment after fork
+*/
+
+extern /*@null@*/char *env_get(char *s)
+{
+ int i;
+ unsigned int len;
+
+ if (!s) return 0;
+ len = str_len(s);
+ for (i = 0; environ[i]; ++i)
+ if (str_start(environ[i],s) && (environ[i][len] == '='))
+ return environ[i] + len + 1;
+ return 0;
+}
+
+extern char *env_findeq(char *s)
+{
+ for (; *s; ++s)
+ if (*s == '=') return s;
+ return 0;
+}
+
+int env_isinit = 0; /* if env_isinit: */
+static int ea; /* environ is a pointer to ea+1 char*'s. */
+static int en; /* the first en of those are ALLOCATED. environ[en] is 0. */
+
+static void env_del(int i) {
+ alloc_free(environ[i]);
+ environ[i] = environ[--en];
+ environ[en] = 0;
+}
+
+static void env_unsetlen(char *s,int len)
+{
+ int i;
+
+ for (i = en - 1; i >= 0; --i)
+ if (!str_diffn(s,environ[i],len))
+ if (environ[i][len] == '=')
+ env_del(i);
+}
+
+int env_unset(char *s)
+{
+ if (!env_isinit)
+ if (!env_init()) return 0;
+ env_unsetlen(s,str_len(s));
+ return 1;
+}
+
+int env_set(char *s) {
+ char *t;
+
+ t = env_findeq(s);
+ if (t) env_unsetlen(s,t - s);
+ if (en == ea) {
+ ea += 30;
+ if (!alloc_re(&environ,(en + 1) * sizeof(char *),(ea + 1) * sizeof(char *)))
+ { ea = en; return 0; }
+ }
+ environ[en++] = s;
+ environ[en] = 0;
+ return 1;
+}
+
+int env_puts(char *s) {
+ char *u;
+
+ if (!env_isinit)
+ if (!env_init()) return 0;
+ u = alloc(str_len(s) + 1);
+ if (!u) return 0;
+ str_copy(u,s);
+ if (!env_set(u)) { alloc_free(u); return 0; }
+ return 1;
+}
+
+int env_put(char *name,char *value) {
+ char *ln;
+ int len;
+
+ if (!env_isinit)
+ if (!env_init()) return 0;
+ len = str_len(name);
+ ln = alloc(len + str_len(value) + 2);
+ if (!ln) return 0;
+ str_copy(ln,name);
+ ln[len] = '=';
+ str_copy(ln + len + 1,value);
+ if (!env_set(ln)) { alloc_free(ln); return 0; }
+ return 1;
+}
+
+int env_init() {
+ char **newenviron;
+ int i;
+
+ for (en = 0; environ[en]; ++en)
+ ;
+ ea = en + 10;
+ newenviron = (char **) alloc((ea + 1) * sizeof(char *));
+ if (!newenviron) return 0;
+
+ for (en = 0; environ[en]; ++en) {
+ newenviron[en] = alloc(str_len(environ[en]) + 1);
+ if (!newenviron[en]) {
+ for (i = 0; i < en; ++i) alloc_free(newenviron[i]);
+ alloc_free(newenviron);
+ return 0;
+ }
+ str_copy(newenviron[en],environ[en]);
+ }
+
+ newenviron[en] = 0;
+ environ = newenviron;
+ env_isinit = 1;
+ return 1;
+}
+
+static char *null = 0;
+
+void env_clear() {
+ if (env_isinit)
+ while (en) env_del(0);
+ else environ = &null;
+}
+extern char *env_pick() { return environ[0]; }
diff --git a/errstr.c b/errstr.c
new file mode 100644
index 0000000..e2290fb
--- /dev/null
+++ b/errstr.c
@@ -0,0 +1,163 @@
+#include "error.h"
+
+/**
+ @file errstr.c
+ @author kp
+ @source qlibs
+ @brief error output to log for different conditions and OS
+*/
+
+#define X(e,s) if (code == e) return s;
+
+extern char *error_str(int);
+
+char *errstr(int code)
+{
+ X(0,"") // NOERROR
+ X(error_intr,"interrupted system call") // EINTR
+ X(EINTR, "interrupted system call")
+ X(error_nomem,"out of memory") // ENOMEM
+ X(ENOMEM, "out of memory")
+ X(error_noent,"file does not exist") // ENOENT
+ X(ENOENT, "file does not exist")
+ X(error_txtbsy,"text busy") // ETXTBSY
+ X(ETXTBSY, "text busy")
+ X(error_io,"input/output error") // EIO
+ X(EIO, "input/output error")
+ X(error_exist,"file already exists") // EEXISTS
+ X(EEXIST, "file already exists")
+ X(error_timeout,"timed out") // ETIMEDOUT
+ X(ETIMEDOUT, "timed out")
+ X(error_inprogress,"operation in progress") // EINPROGRESS
+ X(EINPROGRESS, "operation in progress")
+ X(error_again,"temporary failure") // EAGAIN
+ X(EAGAIN, "temporary failure")
+ X(error_wouldblock,"input/output would block") // EWOULDBLOCK (intern EAGAIN)
+ X(EWOULDBLOCK, "input/output would block")
+ X(error_pipe,"broken pipe") // EPIPE
+ X(EPIPE, "broken pipe")
+ X(error_perm,"permission denied") // EPERM
+ X(EPERM, "permission denied")
+ X(error_acces,"access denied") // EACCES
+ X(EACCES, "access denied")
+
+ X(ESRCH, "no such process")
+
+// X(error_nodevice,"device not configured") // ENXIO
+ X(ENXIO, "device not configured")
+
+ X(E2BIG, "argument list too long")
+ X(ENOEXEC, "exec format error")
+ X(EBADF, "file descriptor not open")
+ X(ECHILD, "no child processes")
+ X(EDEADLK, "operation would cause deadlock")
+ X(EFAULT, "bad address")
+ X(ENOTBLK, "not a block device")
+ X(EBUSY, "device busy")
+ X(EXDEV, "cross-device link")
+ X(ENODEV, "device does not support operation")
+// X(error_notdir,"not a directory") // ENOTDIR
+ X(ENOTDIR, "not a directory")
+ X(error_isdir,"is a directory") // EISDIR
+ X(EISDIR, "is a directory")
+ X(EINVAL, "invalid argument")
+ X(ENFILE, "system cannot open more files")
+ X(EMFILE, "process cannot open more files")
+ X(ENOTTY, "not a tty")
+ X(EFBIG, "file too big")
+ X(ENOSPC, "out of disk space")
+ X(ESPIPE, "unseekable descriptor")
+// X(error_rofs,"read-only file system") // EROFS
+ X(EROFS, "read-only file system")
+ X(EMLINK, "too many links")
+ X(EDOM, "input out of range")
+ X(ERANGE, "output out of range")
+ X(EALREADY, "operation already in progress")
+ X(ENOTSOCK, "not a socket")
+ X(EDESTADDRREQ, "destination address required")
+ X(EMSGSIZE, "message too long")
+ X(EPROTOTYPE, "incorrect protocol type")
+ X(ENOPROTOOPT, "protocol not available")
+ X(EPROTONOSUPPORT, "protocol not supported")
+ X(ESOCKTNOSUPPORT, "socket type not supported")
+ X(EOPNOTSUPP, "operation not supported")
+ X(EPFNOSUPPORT, "protocol family not supported")
+ X(EAFNOSUPPORT, "address family not supported")
+ X(EADDRINUSE, "address already used")
+ X(EADDRNOTAVAIL, "address not available")
+ X(ENETDOWN, "network down")
+ X(ENETUNREACH, "network unreachable")
+ X(ENETRESET, "network reset")
+ X(ECONNABORTED, "connection aborted")
+ X(error_connreset, "connection reset") // ECONNRESET
+ X(ECONNRESET, "connection reset")
+ X(ENOBUFS, "out of buffer space")
+ X(EISCONN, "already connected")
+ X(ENOTCONN, "not connected")
+ X(ESHUTDOWN, "socket shut down")
+ X(ETOOMANYREFS, "too many references")
+ X(error_connrefused,"connection refused") // ECONNREFUSED
+ X(ECONNREFUSED, "connection refused")
+ X(ELOOP, "symbolic link loop")
+ X(ENAMETOOLONG, "file name too long")
+ X(EHOSTDOWN, "host down")
+ X(EHOSTUNREACH, "host unreachable")
+ X(ENOTEMPTY, "directory not empty")
+ X(EUSERS, "too many users")
+ X(EDQUOT, "disk quota exceeded")
+ X(ESTALE, "stale NFS file handle")
+
+ /* BSD only (all BSD's, NOT on Linux) */
+// X(EPROCLIM, "too many processes") // -L +FB +OB +NB
+// X(EBADRPC, "RPC structure is bad") // -L +FB +OB +NB
+
+// X(ERPCMISMATCH, "RPC version mismatch") // -L +FB +OB +NB
+// X(EPROGUNAVAIL, "RPC program unavailable") // -L +FB +OB +NB
+// X(EPROGMISMATCH, "program version mismatch") // -L +FB +OB +NB
+// X(EPROCUNAVAIL, "bad procedure for program") // -L +FB +OB +NB
+// X(EFTYPE, "bad file type") // -L +FB +OB +NB
+
+ X(ENOLCK, "no locks available")
+ X(ENOSYS, "system call not available")
+ X(ENOMSG, "no message of desired type")
+ X(EIDRM, "identifier removed")
+
+// X(ERREMOTE, "object not local") // -L -FB -OB -NB
+ X(EREMOTE, "object not local") // Linux: "Object is remote"
+// X(EREMOTE, "too many levels of remote in path")
+
+ /* Linux only */
+// X(ENONET, "machine not on network") // +L -FB -OB -NB
+// X(EADV, "advertise error") // +L -FB -OB -NB
+// X(ESRMNT, "srmount error") // +L -FB -OB -NB
+// X(ECOMM, "communication error") // +L -FB -OB -NB
+// X(EREMCHG, "remote address changed") // +L -FB -OB -NB
+
+ X(error_proto,"protocol error") // EPROTO
+ /* EPROTO: see 'error.h' for OpenBSD compat */
+ X(EPROTO, "protocol error") // +L +FB -OB +NB
+
+ /* Linux and NetBSD */
+// X(ENOSTR, "not a stream device") // +L -FB -OB +NB
+// X(ETIME, "timer expired") // +L -FB -OB +NB
+// X(ENOSR, "out of stream resources") // +L -FB -OB +NB
+
+ /* FreeBSD and NetBSD */
+// X(EAUTH, "authentication error") // -L +FB -OB +NB
+// X(ENEEDAUTH, "not authenticated") // -L +FB -OB +NB
+
+ /* NOT on OpenBSD */
+// X(EBADMSG, "bad message type") // +L +FB -OB +NB
+// X(ENOLINK, "link severed") // +L +FB -OB +NB
+// X(EMULTIHOP, "multihop attempted") // +L +FB -OB +NB
+
+ /* Application/DJB specific */
+ X(EHARD, " ")
+ X(ESOFT, " ")
+ X(USAGE, "usage error") // qmail; explicit
+ X(SYNTAX, "syntax error") // djbdns, qmail; explicit
+ X(DROP, "connection dropped") // ucspi-tcp
+ X(FATAL, "unable to continue") // all
+
+ return "unknown error"; /* worst case */
+}
diff --git a/fd.c b/fd.c
new file mode 100644
index 0000000..776575c
--- /dev/null
+++ b/fd.c
@@ -0,0 +1,30 @@
+#include <fcntl.h>
+#include "fd.h"
+
+/**
+ @file fd.c
+ @autor djb
+ @source qmail
+ @brief file descriptor manipulation
+*/
+
+int close(int __fd); /* we won't use unistd.h here */
+
+int fd_copy(int to,int from)
+{
+ if (to == from) return 0;
+ if (fcntl(from,F_GETFL,0) == -1) return -1;
+ close(to);
+ if (fcntl(from,F_DUPFD,to) == -1) return -1;
+ return 0;
+}
+
+int fd_move(int to,int from)
+{
+ if (to == from) return 0;
+ if (fd_copy(to,from) == -1) return -1;
+ close(from);
+ return 0;
+}
+
+int fd_coe(int fd) {return fcntl(fd,F_SETFD,1); }
diff --git a/fmt.c b/fmt.c
new file mode 100644
index 0000000..52ab5dc
--- /dev/null
+++ b/fmt.c
@@ -0,0 +1,85 @@
+#include "fmt.h"
+
+/**
+ @file fmt.c
+ @author djb
+ @source qmail
+ @brief formating differnt inputs format for output printing
+*/
+
+unsigned int fmt_str(register char *s,register char *t)
+{
+ register unsigned int len;
+ char ch;
+ len = 0;
+ if (s) { while ((ch = t[len])) s[len++] = ch; }
+ else while (t[len]) len++;
+ return len;
+}
+
+unsigned int fmt_strn(register char *s,register char *t,register unsigned int n)
+{
+ register unsigned int len;
+ char ch;
+ len = 0;
+ if (s) { while (n-- && (ch = t[len])) s[len++] = ch; }
+ else while (n-- && t[len]) len++;
+ return len;
+}
+
+unsigned int fmt_uint(register char *s,register unsigned int u)
+{
+ register unsigned long l; l = u; return fmt_ulong(s,l);
+}
+
+unsigned int fmt_uint0(char *s,unsigned int u,unsigned int n)
+{
+ unsigned int len;
+ len = fmt_uint(FMT_LEN,u);
+ while (len < n) { if (s) *s++ = '0'; ++len; }
+ if (s) fmt_uint(s,u);
+ return len;
+}
+
+unsigned int fmt_ulong(register char *s,register unsigned long u)
+{
+ register unsigned int len; register unsigned long q;
+ len = 1; q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (s) {
+ s += len;
+ do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
+
+unsigned int fmt_xlong(register char *s,register unsigned long u)
+{
+ register unsigned int len; register unsigned long q;
+ len = 1; q = u;
+ while (q > 15) { ++len; q /= 16; }
+ if (s) {
+ s += len;
+ do { *--s = tohex(u % 16); u /= 16; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
+
+char tohex(char num) {
+ if (num < 10)
+ return num + '0';
+ else if (num < 16)
+ return num - 10 + 'a';
+ else
+ return -1;
+}
+
+int fromhex(unsigned char c) {
+ if (c >= '0' && c <= '9')
+ return c-'0';
+ else if (c >= 'A' && c <= 'F')
+ return c -'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
diff --git a/getln.c b/getln.c
new file mode 100644
index 0000000..f83d3c2
--- /dev/null
+++ b/getln.c
@@ -0,0 +1,43 @@
+#include "byte.h"
+#include "getln.h"
+
+/**
+ @file getln.c
+ @author djb
+ @source qmail
+ @brief evaluting tokenized input arguments
+*/
+
+int getln(buffer *b,stralloc *sa,int *match,int sep)
+{
+ char *cont;
+ unsigned int clen;
+
+ if (sgetln(b,sa,&cont,&clen,sep) == -1) return -1;
+ if (!clen) { *match = 0; return 0; }
+ if (!stralloc_catb(sa,cont,clen)) return -1;
+ *match = 1;
+ return 0;
+}
+
+int sgetln(buffer *b,stralloc *sa,char **cont,unsigned int *clen,int sep)
+{
+ register char *x;
+ register unsigned int i;
+ int n;
+
+ if (!stralloc_ready(sa,0)) return -1;
+ sa->len = 0;
+
+ for (;;) {
+ n = buffer_feed(b);
+ if (n < 0) return -1;
+ if (n == 0) { *clen = 0; return 0; }
+ x = buffer_PEEK(b);
+ i = byte_chr(x,n,sep);
+ if (i < n) { buffer_SEEK(b,*clen = i + 1); *cont = x; return 0; }
+ if (!stralloc_readyplus(sa,n)) return -1;
+ i = sa->len;
+ sa->len = i + buffer_get(b,sa->s + i,n);
+ }
+}
diff --git a/getoptb.c b/getoptb.c
new file mode 100644
index 0000000..edee6b8
--- /dev/null
+++ b/getoptb.c
@@ -0,0 +1,100 @@
+#include "buffer.h"
+#include "getoptb.h"
+
+/**
+ @file getoptb.c
+ @author djb
+ @source ucspi-tcp
+ @brief 'getopt' version w/o stdlib
+*/
+
+#define optind subgetoptind
+#define optproblem subgetoptproblem
+
+int opterr = 1;
+char *optprogname = 0;
+
+int getopt(int argc,char **argv,char *opts)
+{
+ int c;
+ char *s;
+
+ if (!optprogname) {
+ optprogname = *argv;
+ if (!optprogname) optprogname = "";
+ for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1;
+ }
+ c = subgetopt(argc,argv,opts);
+ if (opterr)
+ if (c == '?') {
+ char chp[2]; chp[0] = optproblem; chp[1] = '\n';
+ buffer_puts(buffer_2,optprogname);
+ if (argv[optind] && (optind < argc))
+ buffer_puts(buffer_2,": illegal option -- ");
+ else
+ buffer_puts(buffer_2,": option requires an argument -- ");
+ buffer_put(buffer_2,chp,2);
+ buffer_flush(buffer_2);
+ }
+ return c;
+}
+
+#define optpos subgetoptpos
+#define optarg subgetoptarg
+#define optdone subgetoptdone
+
+int optind = 1;
+int optpos = 0;
+char *optarg = 0;
+int optproblem = 0;
+int optdone = SUBGETOPTDONE;
+
+int subgetopt(int argc,char **argv,char *opts)
+{
+ int c;
+ char *s;
+
+ optarg = 0;
+ if (!argv || (optind >= argc) || !argv[optind]) return optdone;
+ if (optpos && !argv[optind][optpos]) {
+ ++optind;
+ optpos = 0;
+ if ((optind >= argc) || !argv[optind]) return optdone;
+ }
+ if (!optpos) {
+ if (argv[optind][0] != '-') return optdone;
+ ++optpos;
+ c = argv[optind][1];
+ if ((c == '-') || (c == 0)) {
+ if (c) ++optind;
+ optpos = 0;
+ return optdone;
+ }
+ /* otherwise c is reassigned below */
+ }
+ c = argv[optind][optpos];
+ ++optpos;
+ s = opts;
+ while (*s) {
+ if (c == *s) {
+ if (s[1] == ':') {
+ optarg = argv[optind] + optpos;
+ ++optind;
+ optpos = 0;
+ if (!*optarg) {
+ optarg = argv[optind];
+ if ((optind >= argc) || !optarg) { /* argument past end */
+ optproblem = c;
+ return '?';
+ }
+ ++optind;
+ }
+ }
+ return c;
+ }
+ ++s;
+ if (*s == ':') ++s;
+ }
+ optproblem = c;
+ return '?';
+}
diff --git a/include/alloc.h b/include/alloc.h
new file mode 100644
index 0000000..56a4a52
--- /dev/null
+++ b/include/alloc.h
@@ -0,0 +1,12 @@
+#ifndef ALLOC_H
+#define ALLOC_H
+
+extern /*@null@*//*@out@*/char *alloc();
+extern void alloc_free();
+extern int alloc_re();
+
+/* use these names in the future */
+#define qfree alloc_free
+#define qrealloc alloc_re
+
+#endif
diff --git a/include/base64.h b/include/base64.h
new file mode 100644
index 0000000..a9164c0
--- /dev/null
+++ b/include/base64.h
@@ -0,0 +1,7 @@
+#ifndef BASE64_H
+#define BASE64_H
+
+extern int b64decode();
+extern int b64encode();
+
+#endif
diff --git a/include/buffer.h b/include/buffer.h
new file mode 100644
index 0000000..57cd960
--- /dev/null
+++ b/include/buffer.h
@@ -0,0 +1,63 @@
+#ifndef BUFFER_H
+#define BUFFER_H
+#include <sys/types.h> /* need type: ssize_t */
+
+typedef struct buffer {
+ char *x;
+ unsigned int p;
+ size_t n;
+ int fd;
+ ssize_t (*op)();
+} buffer;
+
+#define BUFFER_INIT(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
+#define BUFFER_SMALL 256
+#define BUFFER_INSIZE 8192
+#define BUFFER_OUTSIZE 8192
+#define BUFFER_MTUSIZE 1450
+
+extern void buffer_init(buffer *,ssize_t (*op)(),int,char *,size_t);
+
+extern int buffer_flush(buffer *);
+extern int buffer_put(buffer *,const char *,size_t);
+extern int buffer_putalign(buffer *,const char *,size_t);
+extern int buffer_putflush(buffer *,const char *,size_t);
+extern int buffer_puts(buffer *,const char *);
+extern int buffer_putsalign(buffer *,const char *);
+extern int buffer_putsflush(buffer *,const char *);
+
+#define buffer_PUTC(s,c) \
+ ( ((s)->n != (s)->p) \
+ ? ( (s)->x[(s)->p++] = (c), 0 ) \
+ : buffer_put((s),&(c),1) \
+ )
+
+extern int buffer_get(buffer *,char *,size_t);
+extern int buffer_bget(buffer *,char *,size_t);
+extern int buffer_feed(buffer *);
+
+extern char *buffer_peek(buffer *);
+extern void buffer_seek(buffer *,size_t);
+
+#define buffer_PEEK(s) ( (s)->x + (s)->n )
+#define buffer_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
+
+#define buffer_GETC(s,c) \
+ ( ((s)->p > 0) \
+ ? ( *(c) = (s)->x[(s)->n], buffer_SEEK((s),1), 1 ) \
+ : buffer_get((s),(c),1) \
+ )
+
+extern int buffer_copy(buffer *,buffer *);
+
+extern ssize_t buffer_unixread(int,char *,size_t);
+extern ssize_t buffer_unixwrite(int,char *,size_t);
+
+extern buffer *buffer_0;
+extern buffer *buffer_1;
+extern buffer *buffer_2;
+extern buffer *buffer_0small;
+extern buffer *buffer_1small;
+extern buffer *buffer_2small;
+
+#endif
diff --git a/include/byte.h b/include/byte.h
new file mode 100644
index 0000000..f437341
--- /dev/null
+++ b/include/byte.h
@@ -0,0 +1,21 @@
+#ifndef BYTE_H
+#define BYTE_H
+
+/**
+ @file byte.h
+ @author djb, feh
+ @source s/qmail
+ @comment no declaration of argument types; too many compiler errors
+*/
+
+extern unsigned int byte_chr();
+extern unsigned int byte_rchr();
+extern void byte_copy();
+extern void byte_copyr();
+extern int byte_diff();
+extern void byte_zero();
+extern void byte_fill();
+
+#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
+
+#endif
diff --git a/include/case.h b/include/case.h
new file mode 100644
index 0000000..d4cead2
--- /dev/null
+++ b/include/case.h
@@ -0,0 +1,17 @@
+#ifndef CASE_H
+#define CASE_H
+
+extern void case_lowers(char *);
+extern void case_lowerb(char *,unsigned int);
+extern void case_uppers(char *);
+extern void case_upperb(char *,unsigned int);
+extern int case_diffs(char *,char *);
+extern int case_diffrs(char *,char *);
+extern int case_diffb(char *,unsigned int,char *);
+extern int case_starts(char *,char *);
+extern int case_startb(char *,unsigned int,char *);
+
+#define case_equals(s,t) (!case_diffs((s),(t)))
+#define case_equalrs(s,t) (!case_diffrs((s),(t)))
+
+#endif
diff --git a/include/cdbmake.h b/include/cdbmake.h
new file mode 100644
index 0000000..9c20d2d
--- /dev/null
+++ b/include/cdbmake.h
@@ -0,0 +1,39 @@
+/* Public domain. */
+
+#ifndef CDB_MAKE_H
+#define CDB_MAKE_H
+
+#include "buffer.h"
+#include "uint_t.h"
+
+#define CDB_HPLIST 1000
+
+struct cdb_hp { uint32 h; uint32 p; } ;
+
+struct cdb_hplist {
+ struct cdb_hp hp[CDB_HPLIST];
+ struct cdb_hplist *next;
+ int num;
+} ;
+
+struct cdb_make {
+ char bspace[8192];
+ char final[2048];
+ uint32 count[256];
+ uint32 start[256];
+ struct cdb_hplist *head;
+ struct cdb_hp *split; /* includes space for hash */
+ struct cdb_hp *hash;
+ uint32 numentries;
+ buffer b;
+ uint32 pos;
+ int fd;
+} ;
+
+extern int cdb_make_start(struct cdb_make *,int);
+extern int cdb_make_addbegin(struct cdb_make *,unsigned int,unsigned int);
+extern int cdb_make_addend(struct cdb_make *,unsigned int,unsigned int,uint32);
+extern int cdb_make_add(struct cdb_make *,char *,unsigned int,char *,unsigned int);
+extern int cdb_make_finish(struct cdb_make *);
+
+#endif
diff --git a/include/cdbread.h b/include/cdbread.h
new file mode 100644
index 0000000..bee4fd4
--- /dev/null
+++ b/include/cdbread.h
@@ -0,0 +1,38 @@
+/* Public domain. */
+
+#ifndef CDB_H
+#define CDB_H
+
+#include "uint_t.h"
+
+#define CDB_HASHSTART 5381ULL
+extern uint32 cdb_hashadd(uint32,unsigned char);
+extern uint32 cdb_hash(char *,unsigned int);
+extern uint32 cdb_unpack(unsigned char *);
+
+struct cdb {
+ char *map; /* 0 if no map is available */
+ int fd;
+ uint32 size; /* initialized if map is nonzero */
+ uint32 loop; /* number of hash slots searched under this key */
+ uint32 khash; /* initialized if loop is nonzero */
+ uint32 kpos; /* initialized if loop is nonzero */
+ uint32 hpos; /* initialized if loop is nonzero */
+ uint32 hslots; /* initialized if loop is nonzero */
+ uint32 dpos; /* initialized if cdb_findnext() returns 1 */
+ uint32 dlen; /* initialized if cdb_findnext() returns 1 */
+} ;
+
+extern void cdb_free(struct cdb *);
+extern void cdb_init(struct cdb *,int fd);
+
+extern int cdb_read(struct cdb *,char *,unsigned int,uint32);
+
+extern void cdb_findstart(struct cdb *);
+extern int cdb_findnext(struct cdb *,char *,unsigned int);
+extern int cdb_find(struct cdb *,char *,unsigned int);
+
+#define cdb_datapos(c) ((c)->dpos)
+#define cdb_datalen(c) ((c)->dlen)
+
+#endif
diff --git a/include/close.h b/include/close.h
new file mode 100644
index 0000000..fc5b7bb
--- /dev/null
+++ b/include/close.h
@@ -0,0 +1,12 @@
+#ifndef CLOSE_H
+#define CLOSE_H
+
+/*
+ * Revision 20160713, Kai Peter
+ *
+ * (POSIX: 'close' is defined in <unistd.h>)
+*/
+
+extern int close(int __fd);
+
+#endif
diff --git a/include/constmap.h b/include/constmap.h
new file mode 100644
index 0000000..750702e
--- /dev/null
+++ b/include/constmap.h
@@ -0,0 +1,21 @@
+#ifndef CONSTMAP_H
+#define CONSTMAP_H
+
+typedef unsigned long constmap_hash;
+
+struct constmap {
+ int num;
+ constmap_hash mask;
+ constmap_hash *hash;
+ int *first;
+ int *next;
+ char **input;
+ int *inputlen;
+} ;
+
+int constmap_init(struct constmap *,char *,int,int);
+int constmap_init_char(struct constmap *,char *,int,int,char);
+void constmap_free();
+char *constmap();
+
+#endif
diff --git a/include/direntry.h b/include/direntry.h
new file mode 100644
index 0000000..d1628a9
--- /dev/null
+++ b/include/direntry.h
@@ -0,0 +1,10 @@
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+/* sysdep: +dirent */
+
+#include <sys/types.h>
+#include <dirent.h>
+#define direntry struct dirent
+
+#endif
diff --git a/include/dnsresolv.h b/include/dnsresolv.h
new file mode 100644
index 0000000..406af50
--- /dev/null
+++ b/include/dnsresolv.h
@@ -0,0 +1,202 @@
+#ifndef DNSRESOLV_H
+#define DNSRESOLV_H
+
+/*
+ * Revision 20230613, Erwin Hoffmann
+ * - DNS_NXD (return code 0) for NXDOMAIN and NODATA added
+ * - DNS_SOFT as shortcut for DNS_ERR or DNS_COM
+ * - DNS_HARD indicates DNS loop problems
+ * Revision 20221101, Erwin Hoffmann
+ * - DNS_COM has now return code -3 (as documented; tx. Franz S.)
+ * Revision 20210922, Erwin Hoffmann
+ * - Added constants MAXMSGSIZE and MAXSEGMENT - not used yet
+ * Revision 20210401, Erwin Hoffmann
+ * - removed obsolete dns_sortip(); not going to work with GCC-10 anyway
+ * Revision 20200719, Erwin Hoffmann
+ * - added dns_qualify_localhost function including fqdn retrun
+ * Revision 20190730, Erwin Hoffmann
+ * - revised DNS_* return codes to make them compliant with ucspi-*
+ * Revision 20190430, Erwin Hoffmann
+ * - added DNS_SOFT/HARD/MEM complient to s/qmail
+ * - code changes in all decendent modules
+ * Revision 20180222, Erwin Hoffmann
+ * - we consider in total 32 NS IPs (IPv4 + IPv6)
+ * - added dns_transmit_start6
+ * - added uint32 scope_ids[32],
+ * the initial NS scopes read from /etc/resolv.conf et al.
+ * Revision 20180118, Erwin Hoffmann
+ * - included MSGSIZE for DNS messages (instead of MTUSIZE)
+ * Revision 20171231, Erwin Hoffmann
+ * - renamed to dnsresolv.h and removed *qmail declarations
+ * Revision 20170902, Erwin Hoffmann
+ * - added old definitions from *qmail for (temp) backwards compatibility
+ * - added more DNS RR definitions
+*/
+
+#include "stralloc.h"
+#include "iopause.h"
+#include "taia.h"
+
+/* Note: The conventions are subject of change in forthcoming versions */
+
+#define DNS_NXD 0 /* NXDOMAIN, NODATA */
+#define DNS_MEM -1 /* out of memory; fatal */
+#define DNS_ERR -2 /* parsing errors and others */
+#define DNS_COM -3 /* (socket) communication errors: SERVFAIL */
+#define DNS_INT -4 /* internal errors */
+#define DNS_SOFT -5 /* DNS_ERR or DNS_COM */
+#define DNS_HARD -6 /* DNS loop problem */
+
+#define MSGSIZE MTUSIZE /* todays default */
+// #define MSGSIZE 512 /* RFC 1035 */
+#define MAXMSGSIZE 4096 /* 4069 seen with EDNS0 */
+#define MAXSEGMENT 65535 /* Max TCP buffer size */
+
+#define QUERY_MAXNS 32 /* 16 IPv4 + 16 IPv6 NS */
+#define QUERY_MAXIPLEN 512 /* QUERY_MAXNS * 16 */
+
+/* Note: These following definitions are subject of change */
+
+#define DNS_C_IN "\0\1"
+#define DNS_C_ANY "\0\377"
+
+#define DNS_T_A "\0\1"
+#define DNS_T_NS "\0\2"
+#define DNS_T_CNAME "\0\5"
+#define DNS_T_SOA "\0\6"
+#define DNS_T_PTR "\0\14"
+#define DNS_T_HINFO "\0\15"
+#define DNS_T_MX "\0\17"
+#define DNS_T_TXT "\0\20"
+#define DNS_T_RP "\0\21"
+#define DNS_T_SIG "\0\30"
+#define DNS_T_KEY "\0\31"
+#define DNS_T_AAAA "\0\34"
+#define DNS_T_SRV "\0\41"
+#define DNS_T_NAPTR "\0\43"
+#define DNS_T_CERT "\0\45"
+#define DNS_T_OPT "\0\51"
+#define DNS_T_DS "\0\53"
+#define DNS_T_SSHFP "\0\54"
+#define DNS_T_IPSECKEY "\0\55"
+#define DNS_T_RRSIG "\0\56"
+#define DNS_T_NSEC "\0\57"
+#define DNS_T_DNSKEY "\0\60"
+#define DNS_T_NSEC3 "\0\62"
+#define DNS_T_NSEC3PARAM "\0\63"
+#define DNS_T_TLSA "\0\64"
+#define DNS_T_HIP "\0\67"
+#define DNS_T_OPENPGPKEY "\0\75"
+#define DNS_T_SPF "\0\143"
+#define DNS_T_AXFR "\0\374"
+#define DNS_T_ANY "\0\377"
+#define DNS_T_CAA "\1\1"
+
+#define LOCALHOST "localhost" /* no clear distinction IPv4/IPv6 */
+#define IP4_LOOPBACK "ip4-loopback"
+#define IP6_LOOPBACK "ip6-loopback"
+
+struct dns_transmit {
+ char *query; /* 0, or dynamically allocated */
+ unsigned int querylen;
+ char *packet; /* 0, or dynamically allocated */
+ unsigned int packetlen;
+ int s1; /* 0, or 1 + an open file descriptor */
+ int tcpstate;
+ unsigned int udploop;
+ unsigned int curserver;
+ struct taia deadline;
+ unsigned int pos;
+ const char *servers;
+ uint32 scope_id;
+ char localip[16];
+ char qtype[2];
+} ;
+
+/* General */
+
+extern void dns_random_init(const char *);
+extern unsigned int dns_random(unsigned int);
+
+extern void dns_domain_free(char **);
+extern int dns_domain_copy(char **,const char *);
+extern unsigned int dns_domain_length(const char *);
+extern int dns_domain_equal(const char *,const char *);
+extern int dns_domain_suffix(const char *,const char *);
+extern unsigned int dns_domain_suffixpos(const char *,const char *);
+extern int dns_domain_fromdot(char **,const char *,unsigned int);
+extern int dns_domain_todot_cat(stralloc *,const char *);
+extern int dns_ip_qualify(stralloc *,stralloc *,const stralloc *);
+extern int dns_ip_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
+extern int dns_ip_qualify_localhost(stralloc *,stralloc *,const stralloc *);
+
+extern unsigned int dns_packet_copy(const char *,unsigned int,unsigned int,char *,unsigned int);
+extern unsigned int dns_packet_getname(const char *,unsigned int,unsigned int,char **);
+extern unsigned int dns_packet_skipname(const char *,unsigned int,unsigned int);
+
+extern struct dns_transmit dns_resolve_tx;
+extern int dns_transmit_start(struct dns_transmit *,const char *,int,const char *,const char *,const char *);
+extern void dns_transmit_free(struct dns_transmit *);
+extern void dns_transmit_io(struct dns_transmit *,iopause_fd *,struct taia *);
+extern int dns_transmit_get(struct dns_transmit *,const iopause_fd *,const struct taia *);
+
+/* Common IPv4 + IPv6 */
+
+extern int dns_resolvconfip(char *,uint32 *);
+extern int dns_resolvconfrewrite(stralloc *);
+extern int dns_resolve(const char *,const char *);
+
+extern int dns_name(stralloc *,const char *);
+extern int dns_name_packet(stralloc *,const char *,unsigned int);
+extern int dns_txt_packet(stralloc *,const char *,unsigned int);
+extern int dns_txt(stralloc *,const stralloc *);
+extern int dns_mx_packet(stralloc *,const char *,unsigned int);
+extern int dns_mx(stralloc *,const stralloc *);
+
+/* IPv4 specific */
+
+extern int dns_ip4_packet(stralloc *,const char *,unsigned int);
+extern int dns_ip4(stralloc *,stralloc *);
+extern void dns_sortip4(char *,unsigned int);
+
+extern int dns_ip4_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
+extern int dns_ip4_qualify(stralloc *,stralloc *,const stralloc *);
+
+#define DNS_NAME4_DOMAIN 31
+extern int dns_name4_domain(char *,const char *);
+extern int dns_name4(stralloc *,const char *);
+extern int randombind4(struct dns_transmit *);
+
+/* IPv6 specific */
+
+extern int dns_ip6_packet(stralloc *,const char *,unsigned int);
+extern int dns_ip6(stralloc *,stralloc *);
+extern void dns_sortip6(char *,unsigned int);
+
+extern int dns_ip6_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
+extern int dns_ip6_qualify(stralloc *,stralloc *,const stralloc *);
+
+#define DNS_NAME6_DOMAIN (4*16+11)
+extern int dns_name6_domain(char *,const char *);
+extern int dns_name6(stralloc *,const char *);
+
+extern int dns_cname_packet(stralloc *,const char *,unsigned int);
+extern int dns_cname(stralloc *,stralloc *);
+
+extern int dns_transmit_start6(struct dns_transmit *,const char *,int,const char *,const char *,const char *,const uint32 *);
+extern int randombind6(struct dns_transmit *);
+
+/* General */
+
+extern void socketfree(struct dns_transmit *);
+extern void queryfree(struct dns_transmit *);
+extern void packetfree(struct dns_transmit *);
+extern int serverwantstcp(const char *,unsigned int);
+extern int serverfailed(const char *,unsigned int);
+extern int getscopeid(const struct dns_transmit *,const char *);
+extern int firstudp(struct dns_transmit *);
+extern int nextudp(struct dns_transmit *);
+extern int firsttcp(struct dns_transmit *);
+extern int nexttcp(struct dns_transmit *);
+
+#endif
diff --git a/include/env.h b/include/env.h
new file mode 100644
index 0000000..8f6ea87
--- /dev/null
+++ b/include/env.h
@@ -0,0 +1,29 @@
+#ifndef ENV_H
+#define ENV_H
+
+/*
+ * Revision 20171220, Erwin Hoffmann
+ * - removed variable names
+ * Revision 20160628, Kai Peter
+ * - updated code (*env_get) like in ucspi-tcp-0.88
+ * - commented out 'env_pick' and 'env_clear'
+*/
+
+extern char **environ;
+
+extern int env_isinit;
+extern int env_init();
+
+extern /*@null@*/char *env_get(char *);
+extern int env_put(char *,char *);
+extern int env_puts(char *);
+extern int env_set(char *);
+extern int env_unset(char *);
+extern char *env_pick();
+extern void env_clear();
+
+extern char *env_findeq(char *);
+
+#define env_put2 env_put /* backwards compatibility */
+
+#endif
diff --git a/include/error.h b/include/error.h
new file mode 100644
index 0000000..7685c9e
--- /dev/null
+++ b/include/error.h
@@ -0,0 +1,57 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <errno.h>
+
+#ifndef EPROTO /* OpenBSD compat */
+#define EPROTO EINTR
+#endif
+
+#define error_str(i) errstr(i)
+extern char *error_str(int);
+
+/* Exception handling notes:
+ (1) system errors RECEIVED according to <errno.h> during operation and handed over
+ (2) application errors DEFINED internally and ennumerated alongside with <errno.h>
+*/
+
+/* djb backwards compatibility - deprecated form of system errors */
+ /* Comparison of error codes and constants:
+ intern Linux FreeBSD OmniOS */
+#define error_intr EINTR /* -1 4 4 4 */
+#define error_nomem ENOMEM /* -2 12 12 12 */
+#define error_noent ENOENT /* -3 2 2 2 */
+#define error_txtbsy ETXTBSY /* -4 26 26 26 */
+#define error_io EIO /* -5 5 5 5 */
+#define error_exist EEXIST /* -6 17 17 17 */
+#define error_timeout ETIMEDOUT /* -7 110 60 145 */
+#define error_inprogress EINPROGRESS /* -8 115 36 160 */
+#define error_wouldblock EWOULDBLOCK /* -9 EAGAIN EAGAIN EAGAIN */
+#define error_again EAGAIN /* -10 11 35 11 */
+#define error_pipe EPIPE /* -11 32 32 32 */
+#define error_perm EPERM /* -12 1 1 1 */
+#define error_acces EACCES /* -13 13 13 13 */
+#define error_nodevice ENODEV /* -14 (6) (6) 19 */
+#define error_proto EPROTO /* -15 71 92 71 */
+#define error_isdir EISDIR /* -16 21 21 21 */
+#define error_connrefused ECONNREFUSED /* -17 111 61 146 */
+//extern int error_notdir; /* -18 20 20 20 */
+#define error_rofs EROFS /* -19 30 30 30 */
+#define error_connreset ECONNRESET /* -20 104 54 131 */
+
+/* djb uses some internal application error and class definitions -- revised (feh) */
+#define CAT -10 /* raw message w/o terminating \n */
+#define LOG -90 /* generic logging */
+#define INFO -91 /* named logging */
+#define TEMP -97 /* (triggered) temporay alert condition */
+#define ALERT -98 /* (triggered) alert condition */
+#define WARN -99 /* exception condition */
+#define ESOFT -100 /* soft error, reversed negative */
+#define EHARD -111 /* hard error, reversed negative */
+#define USAGE 100 /* usage error on call -- explicit usage() */
+#define SYNTAX 101 /* usage/syntax error on call -- explicit syntaxerror() */
+#define DROP 110 /* connection dropped -- explicit dropped() */
+#define FATAL 111 /* internal error -- all */
+#define ERROR 112 /* application error */
+
+#endif
diff --git a/include/exit.h b/include/exit.h
new file mode 100644
index 0000000..f74b741
--- /dev/null
+++ b/include/exit.h
@@ -0,0 +1,13 @@
+#ifndef EXIT_H
+#define EXIT_H
+
+/**
+ @file exit.h
+ @author djb, feh
+ @source qmail
+ @brief convenience header
+*/
+
+extern void _exit(int);
+
+#endif
diff --git a/include/fd.h b/include/fd.h
new file mode 100644
index 0000000..21047c9
--- /dev/null
+++ b/include/fd.h
@@ -0,0 +1,8 @@
+#ifndef FD_H
+#define FD_H
+
+extern int fd_copy(int,int);
+extern int fd_move(int,int);
+extern int fd_coe(int fd);
+
+#endif
diff --git a/include/fifo.h b/include/fifo.h
new file mode 100644
index 0000000..3c1bf68
--- /dev/null
+++ b/include/fifo.h
@@ -0,0 +1,12 @@
+#ifndef FIFO_H
+#define FIFO_H
+
+/**
+ @file fifo.h
+ @author djb, feh
+ @source s/qmail
+*/
+
+int fifo_make(char *,int);
+
+#endif
diff --git a/include/fmt.h b/include/fmt.h
new file mode 100644
index 0000000..6e68d60
--- /dev/null
+++ b/include/fmt.h
@@ -0,0 +1,36 @@
+#ifndef FMT_H
+#define FMT_H
+
+/**
+ @file fmt.h
+ @author djb, kp, feh
+ @source qmail
+ @brief conversion function declarations
+ */
+
+#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
+#define FMT_LEN ((char *) 0) /* convenient abbreviation */
+
+extern unsigned int fmt_str(char *,char *);
+extern unsigned int fmt_strn(char *,char *,unsigned int);
+extern unsigned int fmt_uint(char *,unsigned int);
+extern unsigned int fmt_uint0(char *,unsigned int,unsigned int);
+extern unsigned int fmt_ulong(char *,unsigned long);
+extern unsigned int fmt_xlong(char *,unsigned long);
+
+extern int fromhex(unsigned char);
+extern char tohex(char);
+
+/* for future releases */
+// extern unsigned int fmt_xint(char *,unsigned int);
+// extern unsigned int fmt_nbbint(char *,unsigned int,unsigned int,unsigned int,unsigned int);
+// extern unsigned int fmt_ushort(char *,unsigned short);
+// extern unsigned int fmt_xshort(char *,unsigned short);
+// extern unsigned int fmt_nbbshort(char *,unsigned int,unsigned int,unsigned int,unsigned short);
+// extern unsigned int fmt_nbblong(char *,unsigned int,unsigned int,unsigned int,unsigned long);
+// extern unsigned int fmt_plusminus(char *,int);
+// extern unsigned int fmt_minus(char *,int);
+// extern unsigned int fmt_0x(char *,int);
+/* to be done */
+
+#endif
diff --git a/include/genalloc.h b/include/genalloc.h
new file mode 100644
index 0000000..ee489af
--- /dev/null
+++ b/include/genalloc.h
@@ -0,0 +1,74 @@
+#ifndef GENALLOC_H
+#define GENALLOC_H
+
+/*
+ * Revision 20210307, Erwin Hoffmann
+ * -
+*/
+
+#include <sys/types.h>
+
+/* GEN_ALLOC demystified:
+
+ GEN_ALLOC generates a list of self-defined types (structs) in an
+ allocated contiguous heap chunk while copying the content of the
+ entire field members or appending the existing field.
+ GEN_ALLOC types care of currently used and/or allocated bytes of field.
+
+Macros:
+ GEN_ALLOC_ready (ta,type,field,len,a,i,n,x,base,ta_ready)
+ GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus)
+ GEN_ALLOC_append (ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append)
+
+ 0. ta: 'type alloc' - typedef'ed struct name (aka ipalloc et al.)
+ 1. type: defined struct (used for size information)
+ 2. field: declared public name of struct
+ 2. len: used length of string
+ 4. a: allocated size
+ 5. i: current allocated size for member x
+ 6. n: bytes to allocate; in 'ready' mode: +size of one entry;
+ in 'readyplus' mode: +size of +used size of one entry
+ 7. x: local name (alias to field name)
+ 8. base: size of single entry
+ 9. ta_ready/ta_readyplus (operation)
+ 10. ta_append (operation)
+
+*/
+
+/* file: gen_alloc.h */
+#define GEN_ALLOC_typedef(ta,type,field,len,a) \
+ typedef struct ta { type *field; unsigned int len; unsigned int a; } ta;
+
+/* file: gen_allocdefs.h (deprecated) */
+// used in: ipalloc, prioq, qmail-remote, qmail-inject, token822
+#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
+int ta_ready(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
+int ta_rplus(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; n += x->len; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
+int ta_append(x,i) register ta *x; register type *i; \
+{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
+
+#endif
diff --git a/include/getln.h b/include/getln.h
new file mode 100644
index 0000000..23eb58b
--- /dev/null
+++ b/include/getln.h
@@ -0,0 +1,10 @@
+#ifndef GETLN_H
+#define GETLN_H
+
+#include "buffer.h"
+#include "stralloc.h"
+
+extern int getln(buffer *,stralloc *,int *,int);
+extern int sgetln(buffer *,stralloc *,char **,unsigned int *,int);
+
+#endif
diff --git a/include/getoptb.h b/include/getoptb.h
new file mode 100644
index 0000000..0338a15
--- /dev/null
+++ b/include/getoptb.h
@@ -0,0 +1,28 @@
+#ifndef BGETOPT_H
+#define BGETOPT_H
+
+/*
+ * Revision 20160714, Kai Peter
+ * - consolidated 'sgetopt.h' and 'subgetopt.h' into '(b)getopt.h'
+*/
+
+#define optarg subgetoptarg
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define optproblem subgetoptproblem
+#define optprogname subgetoptprogname
+#define opteof subgetoptdone
+
+#define SUBGETOPTDONE -1
+
+extern int subgetopt(int,char **,char *);
+extern char *subgetoptarg;
+extern int subgetoptind;
+extern int subgetoptpos;
+extern int subgetoptproblem;
+extern char *subgetoptprogname;
+extern int subgetoptdone;
+
+extern int opterr;
+
+#endif
diff --git a/include/iopause.h b/include/iopause.h
new file mode 100644
index 0000000..e3cfa01
--- /dev/null
+++ b/include/iopause.h
@@ -0,0 +1,28 @@
+#ifndef IOPAUSE_H
+#define IOPAUSE_H
+
+#define IOPAUSE_POLL
+
+#include <sys/types.h>
+#ifdef HAS_POLL_H
+#include <poll.h>
+
+typedef struct pollfd iopause_fd;
+#define IOPAUSE_READ POLLIN
+#define IOPAUSE_WRITE POLLOUT
+#else
+typedef struct {
+ int fd;
+ short events;
+ short revents;
+} iopause_fd;
+#define IOPAUSE_READ 1
+#define IOPAUSE_WRITE 4
+#endif
+
+#include "taia.h"
+
+extern int iopause(iopause_fd *,unsigned int,struct taia *,struct taia *);
+extern unsigned long pollmax;
+
+#endif
diff --git a/include/ip.h b/include/ip.h
new file mode 100644
index 0000000..3f0c299
--- /dev/null
+++ b/include/ip.h
@@ -0,0 +1,107 @@
+#ifndef IP_H
+#define IP_H
+
+/*
+ * Revision 20200603, Erwin Hoffmann
+ * - added V46loopback address
+ * Revision 20190414, Erwin Hoffmann
+ * - removed ip_scan and ip_scanbracket (comp. versions).
+ * - added ia6_fmt and ia4_fmt (for qmail)
+ * Revision 20180314, Erwin Hoffmann
+ * - ip4_cidr added and argument list changed (also for ip6_cidr)
+ * Revision 20180213, Erwin Hoffmann
+ * - ip4_scan/ip6_scan/ip4_scanbracket/ip6_fmt_flat revised
+ * Revision 20180206, Erwin Hoffmann
+ * - added V6linklocal address
+ * Revision 20170319, Kai Peter
+ * - rewrite (consolidation and compatibility)
+ * Revision 20170210, Kai Peter
+ * - added definition 'V4loopback' and redefinition 'ip4loopback'
+ * Revision 20170908, Erwin Hoffmann
+ * - added some definitions from s/qmail + required for djbdns
+*/
+
+/* Consolidated header files ip.h from *qmail (with IPv6) and ip4.h/ip6.h
+ from libowfat. Thus it could be used with 'older' and 'newer' code.
+*/
+
+#include "byte.h"
+#include "stralloc.h"
+
+#define V4MAPPREFIX "::ffff:"
+#define HOSTNAMELEN 1025
+#define IP4MLMTU 512
+#define IP6MLMTU 1280 /* RFC 8200 */
+#define MTUSIZE IP6MLMTU
+
+#define IP4_FMT 20 /* backwards compatibility */
+#define IP6_FMT 40 /* " */
+#define IPFMT 72 /* used in qmail-remote only (temp?) */
+/* may be better: */
+//#define IP4_FMT IPFMT /* backwards compatibility */
+//#define IP6_FMT IPFMT /* " */
+/* deprecated: */
+#define FMT_IP4 IP4_FMT /* more backwards compatibility */
+#define FMT_IP6 IP6_FMT /* " */
+
+/* these structs are going deprecated (should replaced by socket lib) */
+struct ip4_address { unsigned char d[4]; }; /* 4 decimal pieces */
+struct ip6_address { unsigned char d[16]; }; /* 16 hex pieces */
+#define ip_address ip4_address /* backward compatibility */
+
+unsigned int ip4_bytestring(stralloc *,char [4],int);
+unsigned int ip4_cidr(char *,char [4],unsigned long *);
+unsigned int ip4_fmt(char *,char [4]);
+unsigned int ip4_scan(const char *,char [4]);
+unsigned int ip4_scanbracket(const char *,char [4]);
+unsigned int ia4_fmt(char *,char [4]);
+
+const static char V4loopback[4] = {127,0,0,1};
+const static char V4localnet[4] = {0,0,0,0};
+const static char V4broadcast[4] = {-1,-1,-1,-1}; // all bits 1
+
+/***
+ * Compactified IPv6 addresses are really ugly to parse.
+ * Syntax: (h = hex digit) [RFC 5952]
+ * 1. hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh
+ * 2. leading 0s in any octet can be suppressed
+ * 3. any number of 0000 may be abbreviated as "::"
+ * a) but only once;
+ * b) the longest piece or on equal match the first piece,
+ * c) a single instance of 0000 has to displayed as 0
+ * 4. The last two words may be written as IPv4 address
+ *
+ * Flat ip6 address syntax:
+ * hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh (32 hex digits)
+ *
+ * struct ip6_address format:
+ * cccccccccccccccc (16 chars; d[16]) -- each char swallows two hex values
+ *
+ * Bitstring representation with length prefix:
+ * bbbbbbb.........bbbb (max 128 bits); stralloc(ip6string); b = 0 || 1
+ *
+ */
+
+unsigned int ip6_bytestring(stralloc *,char *,int);
+unsigned int ip6_cidr(char *,char [16],unsigned long *);
+unsigned int ip6_fmt(char *,char [16]);
+unsigned int ip6_fmt_flat(char *,char [16]);
+unsigned int ip6_ifscan(char *,char [16],stralloc *);
+unsigned int ip6_scan(const char *,char [16]);
+unsigned int ip6_scanbracket(const char *,char [16]);
+unsigned int ip6_scan_flat(const char *,char [16]);
+unsigned int ia6_fmt(char *,char [16]);
+
+static const unsigned char V4mappedprefix[12] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff};
+static const unsigned char V46loopback[16] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 127,0,0,1};
+static const unsigned char V6loopback[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1};
+static const unsigned char V6localnet[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
+static const unsigned char V6linklocal[2] = {0xfe,0x80};
+
+int fromhex(unsigned char);
+char tohex(char);
+
+#define V6any V6localnet /* backward compatibility */
+#define ip6_isv4mapped(ip) (byte_equal(ip,12,V4mappedprefix))
+
+#endif
diff --git a/include/lock.h b/include/lock.h
new file mode 100644
index 0000000..74df4c9
--- /dev/null
+++ b/include/lock.h
@@ -0,0 +1,14 @@
+#ifndef LOCK_H
+#define LOCK_H
+
+/**
+ @file lock.h
+ @author djb, feh
+ @source s/qmail
+*/
+
+int lock_ex(int);
+int lock_un(int);
+int lock_exnb(int);
+
+#endif
diff --git a/include/logmsg.h b/include/logmsg.h
new file mode 100644
index 0000000..c480c8b
--- /dev/null
+++ b/include/logmsg.h
@@ -0,0 +1,31 @@
+#ifndef LOGMSG_H
+#define LOGMSG_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include "error.h"
+
+extern void logmsg(const char *who, int ecode, unsigned int class,
+ const char *msg);
+
+/* useful combinations of params */
+/* class: FATAL (hard) - system error */
+#define err_sys(w,e) logmsg(w,e,FATAL,"")
+#define err_sys_plus(w,e,m) logmsg(w,e,FATAL,m)
+/* class: WARN (application) - temporary error */
+#define err_tmp(w,e,m) logmsg(w,e,WARN,m)
+#define err_tmp_plus(w,e,m) logmsg(w,e,WARN,m)
+/* class: facultative ('int'ernal definition */
+#define err_int(w,e,c) logmsg(w,e,c,"")
+#define err_int_plus(w,e,c,m) logmsg(w,e,c,m)
+/* log messages */
+/* #define log(w,m) logmsg(w,0,LOG,m) // obsoleted by */
+#define log_who(w,m) logmsg(w,0,LOG,m)
+#define log_anon(m) logmsg("",0,LOG,m)
+#define log_cat(n) logmsg("",0,CAT,m)
+
+/* build log message from multiple partial strings */
+extern char *build_log_msg(const char *[]);
+#define B(...) build_log_msg((const char *[]){__VA_ARGS__,NULL}) // K/R sect. 7.3
+
+#endif
diff --git a/include/ndelay.h b/include/ndelay.h
new file mode 100644
index 0000000..7a03a8d
--- /dev/null
+++ b/include/ndelay.h
@@ -0,0 +1,13 @@
+#ifndef NDELAY_H
+#define NDELAY_H
+
+/**
+ @brief ndelay.h
+ @author djb, feh
+ @source s/qmail
+*/
+
+int ndelay_on(int);
+int ndelay_off(int);
+
+#endif
diff --git a/include/open.h b/include/open.h
new file mode 100644
index 0000000..428ebfb
--- /dev/null
+++ b/include/open.h
@@ -0,0 +1,16 @@
+#ifndef OPEN_H
+#define OPEN_H
+
+/**
+ @file open.h
+ @author djb, feh
+ @source s/qmail
+*/
+
+int open_read(const char *);
+int open_excl(const char *);
+int open_append(const char *);
+int open_trunc(const char *);
+int open_write(const char *);
+
+#endif
diff --git a/include/pathexec.h b/include/pathexec.h
new file mode 100644
index 0000000..9db6fb4
--- /dev/null
+++ b/include/pathexec.h
@@ -0,0 +1,10 @@
+#ifndef PATHEXEC_H
+#define PATHEXEC_H
+#include "stralloc.h"
+
+extern void pathexec_run(const char *,char *const *,char *const *);
+extern int pathexec_env(const char *,const char *);
+extern int pathexec_multienv(stralloc *);
+extern void pathexec(char *const *);
+
+#endif
diff --git a/include/prot.h b/include/prot.h
new file mode 100644
index 0000000..7dd0503
--- /dev/null
+++ b/include/prot.h
@@ -0,0 +1,7 @@
+#ifndef PROT_H
+#define PROT_H
+
+extern int prot_gid(int);
+extern int prot_uid(int);
+
+#endif
diff --git a/include/readclose.h b/include/readclose.h
new file mode 100644
index 0000000..a95f4f5
--- /dev/null
+++ b/include/readclose.h
@@ -0,0 +1,14 @@
+#ifndef READCLOSE_H
+#define READCLOSE_H
+
+#include "stralloc.h"
+#include "close.h"
+
+extern int readclose_append(int,stralloc *,unsigned int);
+extern int readclose(int,stralloc *,unsigned int);
+
+#define slurpclose readclose /* backwards compatibility */
+
+extern int openreadclose(const char *,stralloc *,unsigned int);
+
+#endif
diff --git a/include/readwrite.h b/include/readwrite.h
new file mode 100644
index 0000000..40b427f
--- /dev/null
+++ b/include/readwrite.h
@@ -0,0 +1,9 @@
+#ifndef READWRITE_H
+#define READWRITE_H
+
+#include <unistd.h>
+
+// extern int read();
+// extern int write();
+
+#endif
diff --git a/include/rename.h b/include/rename.h
new file mode 100644
index 0000000..641f68a
--- /dev/null
+++ b/include/rename.h
@@ -0,0 +1,6 @@
+#ifndef RENAME_H
+#define RENAME_H
+
+extern int rename(const char *, const char *);
+
+#endif
diff --git a/include/scan.h b/include/scan.h
new file mode 100644
index 0000000..f1d4030
--- /dev/null
+++ b/include/scan.h
@@ -0,0 +1,12 @@
+#ifndef SCAN_H
+#define SCAN_H
+
+extern unsigned int scan_0x(const char *,unsigned int *);
+extern unsigned int scan_xint(const char *,unsigned int *);
+extern unsigned int scan_8long(const char *,unsigned long *);
+extern unsigned int scan_uint(const char *,unsigned int *);
+extern unsigned int scan_long(const char *,long *);
+extern unsigned int scan_ulong(const char *,unsigned long *);
+extern unsigned int scan_xlong(const char *,unsigned long *);
+
+#endif
diff --git a/include/seek.h b/include/seek.h
new file mode 100644
index 0000000..06aad97
--- /dev/null
+++ b/include/seek.h
@@ -0,0 +1,15 @@
+#ifndef SEEK_H
+#define SEEK_H
+
+typedef unsigned long seek_pos;
+
+extern seek_pos seek_cur(int);
+
+extern int seek_set(int,seek_pos);
+extern int seek_end(int);
+
+extern int seek_trunc(int,seek_pos);
+
+#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
+
+#endif
diff --git a/include/select.h b/include/select.h
new file mode 100644
index 0000000..646dd50
--- /dev/null
+++ b/include/select.h
@@ -0,0 +1,13 @@
+#ifndef SELECT_H
+#define SELECT_H
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef HAS_SELECT_H
+#include <sys/select.h>
+#endif
+
+extern int select();
+
+#endif
diff --git a/include/sig.h b/include/sig.h
new file mode 100644
index 0000000..3efdd32
--- /dev/null
+++ b/include/sig.h
@@ -0,0 +1,63 @@
+#ifndef SIG_H
+#define SIG_H
+
+/*
+ * Revision 20160714, Kai Peter
+ * - updated some declarations no new(er) one's from ucspi-tcp-0.88
+*/
+
+/* new(er) declarations from ucspi-tcp-0.88: */
+extern int sig_alarm;
+extern int sig_child;
+extern int sig_cont;
+extern int sig_hangup;
+extern int sig_pipe;
+extern int sig_term;
+
+extern void (*sig_defaulthandler)();
+extern void (*sig_ignorehandler)();
+
+extern void sig_catch(int,void (*)());
+#define sig_ignore(s) (sig_catch((s),sig_ignorehandler))
+#define sig_uncatch(s) (sig_catch((s),sig_defaulthandler))
+
+extern void sig_block(int);
+extern void sig_unblock(int);
+extern void sig_blocknone(void);
+extern void sig_pause(void);
+
+extern void sig_dfl(int);
+
+/* declaration of (net)qmail package (untouched) */
+extern void sig_miscignore();
+extern void sig_bugcatch();
+
+extern void sig_pipeignore();
+extern void sig_pipedefault();
+
+extern void sig_contblock();
+extern void sig_contunblock();
+extern void sig_contcatch();
+extern void sig_contdefault();
+
+extern void sig_termblock();
+extern void sig_termunblock();
+extern void sig_termcatch();
+extern void sig_termdefault();
+
+extern void sig_alarmblock();
+extern void sig_alarmunblock();
+extern void sig_alarmcatch();
+extern void sig_alarmdefault();
+
+extern void sig_childblock();
+extern void sig_childunblock();
+extern void sig_childcatch();
+extern void sig_childdefault();
+
+extern void sig_hangupblock();
+extern void sig_hangupunblock();
+extern void sig_hangupcatch();
+extern void sig_hangupdefault();
+
+#endif
diff --git a/include/socket_if.h b/include/socket_if.h
new file mode 100644
index 0000000..54cfa5f
--- /dev/null
+++ b/include/socket_if.h
@@ -0,0 +1,97 @@
+#ifndef SOCKETIF_H
+#define SOCKETIF_H
+
+/* Revsision 20220608
+ * - removed obsolete socket_local4() and socket_remote4()
+ * Revision 20210828, Erwin Hoffmann
+ * - added socket_accept4()
+ * Revision 20210226, Erwin Hoffmann
+ * - removed dependency on ipv4socket (loose coupling)
+ * - only single socket_tcp() and
+ * - single socket_udp() function serving both IPv4 + IPv6
+ * Revision 20181125, Erwin Hoffmann
+ * - switched to 'uint_t.h'
+ * - changed 'socket_tcp' --> 'socket_tcp4', 'socket_udp' --> 'socket_udp4'
+ * - added 'socket_ip6optionskill'
+ * - reordered and included backword compatible IPv6 calls
+ * - commented multicast socket declarations
+ * - enlarged usage for ipv4socket -> dual bind IPv4/IPv6
+*/
+
+#define __APPLE_USE_RFC_3542 /* MacOS Anycast support */
+
+#include "uint_t.h"
+
+/* IPv4 only */
+extern int socket_accept4(int,char [4],uint16 *);
+extern int socket_bind4(int,const char [4],uint16);
+extern int socket_bind4_reuse(int,const char [4],uint16);
+extern int socket_connect4(int,const char [4],uint16);
+extern int socket_send4(int,const char *,unsigned int,const char [4],uint16);
+extern int socket_broadcast(int,const char *,unsigned int,uint16);
+
+/* Backward compatibility */
+#define socket_local4 socket_local
+#define socket_remote4 socket_remote
+
+/* IPv6 only */
+extern int socket_bind6(int,const char [16],uint16,uint32);
+extern int socket_bind6_reuse(int,const char [16],uint16,uint32);
+extern int socket_connect6(int,const char [16],uint16,uint32);
+extern int socket_send6(int,const char *,unsigned int,const char [16],uint16,uint32);
+extern const char* socket_getifname(uint32);
+extern uint32 socket_getifidx(const char *);
+extern int socket_ip6optionskill(int);
+extern int socket_ip6anycast(int);
+
+/* Common IPv4 & IPv6 */
+extern int socket_accept(int,char [16],uint16 *,uint32 *);
+extern int socket_bind(int,const char [16],uint16,uint32);
+extern int socket_bind_reuse(int,const char [16],uint16,uint32);
+extern int socket_connect(int,const char [16],uint16,uint32);
+extern int socket_connected(int);
+extern int socket_listen(int,int);
+extern int socket_local(int,char [16],uint16 *,uint32 *);
+extern int socket_recv(int,char *,unsigned int,char [16],uint16 *,uint32 *);
+extern int socket_remote(int,char [16],uint16 *,uint32 *);
+extern int socket_send(int,const char *,unsigned int,const char [16],uint16,uint32);
+extern void socket_tryreservein(int,int);
+extern int socket_ipoptionskill(int);
+extern int socket_dualstack(int);
+extern int socket_nodualstack(int);
+
+/* Backward compatibility */
+#define socket_accept6 socket_accept
+#define socket_local6 socket_local
+#define socket_recv6 socket_recv
+#define socket_remote6 socket_remote
+
+/* TCP */
+extern int socket_tcp4(void);
+extern int socket_tcp6(void);
+extern int socket_tcp(void);
+extern int socket_tcpnodelay(int);
+
+/* UDP */
+extern int socket_udp4(void);
+extern int socket_udp6(void);
+extern int socket_udp(void);
+
+/*********** For future use ***********************************/
+/* enable sending udp packets to the broadcast address */
+// extern int socket_broadcast(int);
+/* join a multicast group on the given interface */
+// extern int socket_mcjoin4(int,char *,char *);
+// extern int socket_mcjoin6(int,char *,int);
+/* leave a multicast group on the given interface */
+// extern int socket_mcleave4(int,char *);
+// extern int socket_mcleave6(int,char *);
+/* set multicast TTL/hop count for outgoing packets */
+// extern int socket_mcttl4(int,char);
+// extern int socket_mcttl6(int,char);
+/* enable multicast loopback */
+// extern int socket_mcloop4(int,char);
+// extern int socket_mcloop6(int,char);
+/**************************************************************/
+
+#endif
diff --git a/include/str.h b/include/str.h
new file mode 100644
index 0000000..fe36d0b
--- /dev/null
+++ b/include/str.h
@@ -0,0 +1,25 @@
+#ifndef STR_H
+#define STR_H
+
+/*
+ * Revision 20170918, Kai Peter
+ * - added 'str_copyb()', thanks Erwin Hoffmann
+ * Revision 20170501, Kai Peter
+ * - added '*str_append' and 'str_cat'
+*/
+
+extern unsigned int str_copy(char *,const char *);
+extern unsigned int str_copyb(char *,const char *,unsigned int);
+extern int str_diff(const char *,const char *);
+extern int str_diffn(const char *,const char *,unsigned int);
+//extern unsigned int str_len(char *); // --> this produces lot of warnings !!!
+extern unsigned int str_len();
+extern unsigned int str_chr(const char *,int);
+extern unsigned int str_rchr(const char *,int);
+extern int str_start(const char *,const char *);
+extern char *str_append(char *out,const char *s);
+
+#define str_equal(s,t) (!str_diff((s),(t)))
+#define str_cat(s,t) str_append(s,t)
+
+#endif
diff --git a/include/stralloc.h b/include/stralloc.h
new file mode 100644
index 0000000..0f4a7d2
--- /dev/null
+++ b/include/stralloc.h
@@ -0,0 +1,49 @@
+#ifndef STRALLOC_H
+#define STRALLOC_H
+
+/*
+ * Revision 20210307, Erwin Hoffmann
+ * -
+*/
+
+#include <sys/types.h>
+
+/* stralloc is the internal data structure all functions are working on.
+ * s is the string.
+ * len is the used length of the string.
+ * a is the allocated length of the string.
+ */
+
+typedef struct stralloc {
+ char* s;
+ size_t len;
+ size_t a;
+} stralloc;
+
+//extern int stralloc_ready(stralloc *,unsigned int);
+extern int stralloc_ready(stralloc *sa,size_t len);
+//extern int stralloc_readyplus(stralloc *,unsigned int);
+extern int stralloc_readyplus(stralloc *sa,size_t len);
+extern int stralloc_copy(stralloc *,stralloc *);
+extern int stralloc_cat(stralloc *,stralloc *);
+extern int stralloc_copys(stralloc *,const char *);
+extern int stralloc_cats(stralloc *,const char *);
+extern int stralloc_copyb(stralloc *,const char *,unsigned int);
+extern int stralloc_catb(stralloc *,const char *,unsigned int);
+//extern int stralloc_append(stralloc *,char *); /* beware: this takes a pointer to 1 char */
+extern int stralloc_append(stralloc *sa,const char *in); /* beware: this takes a pointer to 1 char */
+extern int stralloc_starts(stralloc *,const char *);
+
+#define stralloc_0(sa) stralloc_append(sa,"")
+
+extern int stralloc_catulong0(stralloc *,unsigned long,unsigned int);
+extern int stralloc_catlong0(stralloc *,long,unsigned int);
+
+extern void stralloc_free(stralloc *);
+
+#define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0))
+#define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n)))
+#define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n)))
+#define stralloc_catint(sa,i) (stralloc_catlong0((sa),(i),0))
+
+#endif
diff --git a/include/tai.h b/include/tai.h
new file mode 100644
index 0000000..37d24e0
--- /dev/null
+++ b/include/tai.h
@@ -0,0 +1,74 @@
+#ifndef TAI_H
+#define TAI_H
+
+/*
+ * Revision 20160728, Kai Peter
+ * - switched to 'uint_t.h'
+*/
+/* this header file comes from libowfat, http://www.fefe.de/libowfat/ */
+
+/* Times with 1 second precision */
+
+#include "uint_t.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A struct tai value is an integer between 0 inclusive and 2^64
+ * exclusive. The format of struct tai is designed to speed up common
+ * operations; applications should not look inside struct tai.
+ *
+ * A struct tai variable is commonly used to store a TAI64 label. Each
+ * TAI64 label refers to one second of real time. TAI64 labels span a
+ * range of hundreds of billions of years.
+ *
+ * A struct tai variable may also be used to store the numerical
+ * difference between two TAI64 labels.
+ * See http://cr.yp.to/libtai/tai64.html */
+
+typedef struct tai {
+ uint64 x;
+} tai64;
+
+
+#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u)))
+
+/* tai_now puts the current time into t. More precisely: tai_now puts
+ * into t its best guess as to the TAI64 label for the 1-second interval
+ * that contains the current time.
+ *
+ * This implementation of tai_now assumes that the time_t returned from
+ * the time function represents the number of TAI seconds since
+ * 1970-01-01 00:00:10 TAI. This matches the convention used by the
+ * Olson tz library in ``right'' mode. */
+void tai_now(struct tai *);
+
+/* tai_approx returns a double-precision approximation to t. The result
+ * of tai_approx is always nonnegative. */
+#define tai_approx(t) ((double) ((t)->x))
+
+/* tai_add adds a to b modulo 2^64 and puts the result into t. The
+ * inputs and output may overlap. */
+void tai_add(struct tai *,const struct tai *,const struct tai *);
+/* tai_sub subtracts b from a modulo 2^64 and puts the result into t.
+ * The inputs and output may overlap. */
+void tai_sub(struct tai *,const struct tai *,const struct tai *);
+/* tai_less returns 1 if a is less than b, 0 otherwise. */
+#define tai_less(t,u) ((t)->x < (u)->x)
+
+#define TAI_PACK 8
+/* tai_pack converts a TAI64 label from internal format in t to external
+ * TAI64 format in buf. */
+void tai_pack(char *,const struct tai *);
+/* tai_unpack converts a TAI64 label from external TAI64 format in buf
+ * to internal format in t. */
+void tai_unpack(const char *,struct tai *);
+
+void tai_uint(struct tai *,unsigned int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/taia.h b/include/taia.h
new file mode 100644
index 0000000..03f455c
--- /dev/null
+++ b/include/taia.h
@@ -0,0 +1,40 @@
+#ifndef TAIA_H
+#define TAIA_H
+
+/*
+ * Revision 20170329, Kai Peter
+ * - changed type of 'taia_now()' from void to int
+*/
+
+/* time with precision of 1 attosecond */
+
+#include "tai.h"
+
+struct taia {
+ struct tai sec;
+ unsigned long nano; /* 0...999999999 */
+ unsigned long atto; /* 0...999999999 */
+} ;
+
+extern void taia_tai(struct taia *,struct tai *);
+
+extern int taia_now(struct taia *);
+
+extern double taia_approx(struct taia *);
+extern double taia_frac(struct taia *);
+
+extern void taia_add(struct taia *,struct taia *,struct taia *);
+extern void taia_sub(struct taia *,struct taia *,struct taia *);
+extern void taia_half(struct taia *,struct taia *);
+extern int taia_less(struct taia *,struct taia *);
+
+#define TAIA_PACK 16
+extern void taia_pack(char *,struct taia *);
+extern void taia_unpack(char *,struct taia *);
+
+#define TAIA_FMTFRAC 19
+extern unsigned int taia_fmtfrac(char *,struct taia *);
+
+extern void taia_uint(struct taia *,unsigned int);
+
+#endif
diff --git a/include/timeout.h b/include/timeout.h
new file mode 100644
index 0000000..1b45e9f
--- /dev/null
+++ b/include/timeout.h
@@ -0,0 +1,7 @@
+#ifndef TIMEOUT_H
+#define TIMEOUT_H
+
+extern int timeoutread(int,int,char *,int);
+extern int timeoutwrite(int,int,char *,int);
+
+#endif
diff --git a/include/timeoutconn.h b/include/timeoutconn.h
new file mode 100644
index 0000000..308ec5a
--- /dev/null
+++ b/include/timeoutconn.h
@@ -0,0 +1,10 @@
+#ifndef TIMEOUTCONN_H
+#define TIMEOUTCONN_H
+
+#include "uint_t.h"
+
+extern int timeoutconn4(int,char *,uint16,unsigned int);
+extern int timeoutconn6(int,char *,uint16,unsigned int,uint32);
+extern int timeoutconn(int,char *,uint16,unsigned int,uint32);
+
+#endif
diff --git a/include/uint_t.h b/include/uint_t.h
new file mode 100644
index 0000000..84eb06e
--- /dev/null
+++ b/include/uint_t.h
@@ -0,0 +1,76 @@
+#include <stdint.h>
+
+/**
+ @file uint_t.h
+ @author djb, kp, feh
+ @source qmail, djbdns6
+ @brief additional types and pack routines
+ @brief define basic integer types and size through <stdint.h>
+*/
+
+#ifndef UINT8_H
+#define UINT8_H
+
+#ifdef HAS_UINT8_H
+typedef uint8_t uint8;
+#else
+typedef unsigned char uint8;
+#endif
+
+#endif
+
+#ifndef UINT16_H
+#define UINT16_H
+
+typedef uint16_t uint16;
+
+extern void uint16_pack(char *,uint16);
+extern void uint16_pack_big(char *,uint16);
+extern void uint16_unpack(char *,uint16 *);
+extern void uint16_unpack_big(char *,uint16 *);
+#endif
+
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef uint32_t uint32;
+
+extern void uint32_pack(char *,uint32);
+extern void uint32_pack_big(char *,uint32);
+extern void uint32_unpack(char *,uint32 *);
+extern void uint32_unpack_big(char *,uint32 *);
+#endif
+
+#ifndef UINT64_H
+#define UINT64_H
+
+#ifdef HAS_UINT64_H
+typedef uint64_t uint64;
+#else
+typedef unsigned long long uint64;
+#endif
+
+extern void uint64_pack(char *,uint64);
+extern void uint64_pack_big(char *,uint64);
+extern void uint64_unpack(char *,uint64 *);
+extern void uint64_unpack_big(char *,uint64 *);
+#endif
+
+#ifndef UINT128_H
+#define UINT128_H
+
+/* uint128 used for native IPv6 address presentation */
+
+struct uint128_t
+{
+ uint64_t hi; /* routing area */
+ uint64_t lo; /* local area */
+};
+
+typedef struct uint128_t uint128;
+
+extern void uint128_pack(char *,uint128);
+extern void uint128_pack_big(char *,uint128);
+extern void uint128_unpack(char *,uint128 *);
+extern void uint128_unpack_big(char *,uint128 *);
+#endif
diff --git a/include/wait.h b/include/wait.h
new file mode 100644
index 0000000..f7cfc60
--- /dev/null
+++ b/include/wait.h
@@ -0,0 +1,20 @@
+#ifndef WAIT_H
+#define WAIT_H
+
+/**
+ @file wait.h
+ @author djb
+ @source s/qmail
+*/
+
+int wait_pid(int *,int);
+int wait_nohang(int *);
+int wait_stop();
+int wait_stopnohang();
+
+#define wait_crashed(w) ((w) & 127)
+#define wait_exitcode(w) ((w) >> 8)
+#define wait_stopsig(w) ((w) >> 8)
+#define wait_stopped(w) (((w) & 127) == 127)
+
+#endif
diff --git a/install b/install
new file mode 100755
index 0000000..ef58eaa
--- /dev/null
+++ b/install
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+. ./conf-build
+
+install_libs() { cp *.a $OFILES "$LIBDIR" ; }
+install_hdrs() { cp include/*.h "$HDRDIR" ; }
+
+echo -n "Installing qlibs ..."
+mkdir -p "$LIBDIR" "$HDRDIR" 2>/dev/null
+
+[ "$LIBDIR" ] && install_libs
+[ "$HDRDIR" ] && install_hdrs
+
+echo " done!"
diff --git a/iopause.c b/iopause.c
new file mode 100644
index 0000000..94a6fea
--- /dev/null
+++ b/iopause.c
@@ -0,0 +1,84 @@
+#include <poll.h>
+#include "taia.h"
+#include "select.h"
+#include "iopause.h"
+
+/**
+ @file iopause.c
+ @author djb
+ @source qmail
+ @brief stateful reading from net
+ @return > 0 if successful
+*/
+
+int iopause(iopause_fd *x,unsigned int len,struct taia *deadline,struct taia *stamp)
+{
+ struct taia t;
+ int millisecs;
+ double d;
+ int i, r;
+
+ if (taia_less(deadline,stamp))
+ millisecs = 0;
+ else {
+ t = *stamp;
+ taia_sub(&t,deadline,&t);
+ d = taia_approx(&t);
+ if (d > 1000.0) d = 1000.0;
+ millisecs = d * 1000.0 + 20.0;
+ if (millisecs < 0) millisecs = 20.0;
+ }
+
+ for (i = 0; i < len; ++i)
+ x[i].revents = 0;
+
+#ifdef IOPAUSE_POLL
+ r = poll(x,len,millisecs);
+
+ /* XXX: some kernels apparently need x[0] even if len is 0 */
+ /* XXX: how to handle EAGAIN? are kernels really this dumb? */
+ /* XXX: how to handle EINVAL? when exactly can this happen? */
+
+#else
+ struct timeval tv;
+ fd_set rfds;
+ fd_set wfds;
+ int nfds;
+ int fd;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ nfds = 1;
+ for (i = 0; i < len; ++i) {
+ fd = x[i].fd;
+ if (fd < 0) continue;
+ if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/
+
+ if (fd >= nfds) nfds = fd + 1;
+ if (x[i].events & IOPAUSE_READ) FD_SET(fd,&rfds);
+ if (x[i].events & IOPAUSE_WRITE) FD_SET(fd,&wfds);
+ }
+
+ tv.tv_sec = millisecs / 1000;
+ tv.tv_usec = 1000 * (millisecs % 1000);
+
+ r = select(nfds,&rfds,&wfds,(fd_set *) 0,&tv);
+ if (r <= 0) return r;
+
+ /* XXX: for EBADF, could seek out and destroy the bad descriptor */
+
+ for (i = 0; i < len; ++i) {
+ fd = x[i].fd;
+ if (fd < 0) continue;
+ if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/
+
+ if (x[i].events & IOPAUSE_READ)
+ if (FD_ISSET(fd,&rfds)) x[i].revents |= IOPAUSE_READ;
+ if (x[i].events & IOPAUSE_WRITE)
+ if (FD_ISSET(fd,&wfds)) x[i].revents |= IOPAUSE_WRITE;
+ }
+
+#endif
+ return r;
+}
diff --git a/ip4.c b/ip4.c
new file mode 100644
index 0000000..e77cfb6
--- /dev/null
+++ b/ip4.c
@@ -0,0 +1,166 @@
+#include "fmt.h"
+#include "scan.h"
+#include "str.h"
+#include "ip.h"
+
+/**
+ @file ip4.c
+ @author djb, fefe, feh, kp
+ @source ucspi-tcp, ucspi-tcp6
+ @brief handling of IPv4 addresses
+*/
+
+/**
+ @brief ip4_fmt
+ converts IPv4 address to dotted decimal string format
+ @param input: IPv4 char array
+ output: IPv4 address string
+ @return int length of address (ok > 0)
+ */
+
+unsigned int ip4_fmt(char *s,char ip[4])
+{
+ unsigned int len;
+ unsigned int i;
+
+ len = 0;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[0]); len += i; if (s) s += i;
+ if (s) { *s++ = '.'; } ++len;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[1]); len += i; if (s) s += i;
+ if (s) { *s++ = '.'; } ++len;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[2]); len += i; if (s) s += i;
+ if (s) { *s++ = '.'; } ++len;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[3]); len += i; if (s) s += i;
+ return len;
+}
+
+/**
+ @brief ia4_fmt
+ converts IPv4 address into DNS inverse nibble format
+ @param input: IPv4 char array
+ output: IPv4 address string
+ @return int length of address (ok > 0)
+ */
+
+unsigned int ia4_fmt(char *s,char ip[4])
+{
+ unsigned int i;
+ unsigned int len;
+
+ len = 0;
+ i = fmt_ulong(s,(unsigned long) ip[3]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip[2]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip[1]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_ulong(s,(unsigned long) ip[0]); len += i; if (s) s += i;
+ i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i;
+ return len;
+}
+
+/**
+ @brief ip4_scan
+ parse IPv4 address string and convert to IP address array
+ @param input: IPv4 address string
+ output: IPv4 char array
+ @return int length of ip_address (ok > 0)
+ */
+
+unsigned int ip4_scan(const char *s,char ip[4])
+{
+ unsigned int i;
+ unsigned int len;
+ unsigned long u;
+
+ byte_zero(ip,4);
+ len = 0;
+ i = scan_ulong((char *)s,&u); if (!i) { return 0; } ip[0] = u; s += i; len += i;
+ if (*s != '.') { return 0; } ++s; ++len;
+ i = scan_ulong((char *)s,&u); if (!i) { return 0; } ip[1] = u; s += i; len += i;
+ if (*s != '.') { return 0; } ++s; ++len;
+ i = scan_ulong((char *)s,&u); if (!i) { return 0; } ip[2] = u; s += i; len += i;
+ if (*s != '.') { return 0; } ++s; ++len;
+ i = scan_ulong((char *)s,&u); if (!i) { return 0; } ip[3] = u; s += i; len += i;
+ return len;
+}
+
+/**
+ @brief ip4_scanbracket
+ parse IPv4 address string enclosed in brackets and convert to IP address array
+ @param input: IPv4 char array
+ output: IPv4 char array
+ @return int length of ip_address (ok > 0)
+ */
+
+unsigned int ip4_scanbracket(const char *s,char ip[4])
+{
+ unsigned int len;
+
+ if (*s != '[') return 0;
+ len = ip4_scan(s + 1,ip);
+ if (!len) return 0;
+ if (s[len + 1] != ']') return 0;
+ return len + 2;
+}
+
+/**
+ @brief ip4_cidr
+ parse IPv4 address string
+ concatinated with the prefix length: 192.168.1/24
+ @param input: IPv6 char array
+ output: IPv6 char array, long plen
+ @return int length of ip6_address/ip
+ */
+
+unsigned int ip4_cidr(char *s,char ip[4],unsigned long *plen)
+{
+ unsigned int j = 0;
+ *plen = 32UL;
+
+ j = str_chr(s,'/');
+ if (s[j] == '/') {
+ s[j] = 0;
+ j = scan_ulong(s + j + 1,plen);
+ }
+ return ip4_scan((const char *)s,ip);
+}
+
+/**
+ @brief ip4_bytestring
+ parse IPv4 address and represent as char string with length prefix
+ @param input: IPv4 char array, prefix length
+ output: pointer to stralloc bytestring
+ @return n: number of bytes, if ok; -1: memory shortage; -2: input error
+ */
+
+unsigned int ip4_bytestring(stralloc *ipstring,char ip[4],int prefix)
+{
+ int i, j, n = 0;
+ unsigned char number;
+
+ if (!stralloc_readyplus(ipstring,32)) return -1;
+ if (!stralloc_copys(ipstring,"")) return -1;
+
+ for (i = 0; i < 4; i++) {
+ number = (unsigned char) ip[i];
+ if (number > 255) return -2;
+
+ for (j = 7; j >= 0; j--) {
+ if (number & (1 << j)) {
+ n++;
+ if (!stralloc_cats(ipstring,"1")) return -1;
+ } else {
+ n++;
+ if (!stralloc_cats(ipstring,"0")) return -1;
+ }
+ prefix--;
+ if (!prefix) goto DONE;
+ }
+ }
+
+DONE:
+ if (!stralloc_0(ipstring)) return 1;
+
+ return n;
+}
diff --git a/ip6.c b/ip6.c
new file mode 100644
index 0000000..cf8d85b
--- /dev/null
+++ b/ip6.c
@@ -0,0 +1,360 @@
+#include "fmt.h"
+#include "byte.h"
+#include "scan.h"
+#include "ip.h"
+#include "str.h"
+
+/**
+ @file ip6.c
+ @author djb, fefe, feh
+ @source ucspi-tcp, ucspi-tcp6
+ @brief handling of IPv6 addresses
+*/
+
+/***
+ @brief ip6_fmt
+ convert IPv6 address to compactified IPv6 address string
+ @param input: IPv6 char array
+ output: pointer to IPv6 address string
+ @return int length of address (ok > 0)
+ */
+
+unsigned int ip6_fmt(char *s,char ip[16])
+{
+ unsigned int len;
+ unsigned int i;
+ unsigned int temp, temp0;
+ unsigned int compressing;
+ unsigned int compressed;
+ int j;
+
+ len = 0;
+ compressing = 0;
+ compressed = 0;
+
+ for (j = 0; j < 16; j += 2) {
+ if (j == 12 && ip6_isv4mapped(ip)) {
+ len += ip4_fmt(s,ip+12);
+ break;
+ }
+
+ temp = ((unsigned long) (unsigned char) ip[j] << 8) +
+ (unsigned long) (unsigned char) ip[j+1];
+
+ temp0 = 0;
+ if (!compressing && j < 16)
+ temp0 = ((unsigned long) (unsigned char) ip[j+2] << 8) +
+ (unsigned long) (unsigned char) ip[j+3];
+
+ if (temp == 0 && temp0 == 0 && !compressed) {
+ if (!compressing) {
+ compressing = 1;
+ if (j == 0) {
+ if (s) *s++ = ':';
+ ++len;
+ }
+ }
+ } else {
+ if (compressing) {
+ compressing = 0;
+ ++compressed;
+ if (s) *s++ = ':';
+ ++len;
+ }
+ i = fmt_xlong(s,temp);
+ len += i;
+ if (s) s += i;
+ if (j < 14) {
+ if (s) *s++ = ':';
+ ++len;
+ }
+ }
+ }
+ if (compressing) { *s++ = ':'; ++len; }
+
+ return len;
+}
+
+/***
+ @brief ip6_fmt_flat
+ convert IPv6 address to IPv6 address string
+ @param input: IPv6 char array
+ output: pointer to IPv6 address string
+ @return int length of address (ok > 0)
+ */
+
+unsigned int ip6_fmt_flat(char *s,char ip[16])
+{
+ int i;
+ for (i = 0; i < 16; i++) {
+ *s++ = tohex((unsigned char)ip[i] >> 4);
+ *s++ = tohex((unsigned char)ip[i] & 15);
+ }
+ return 32;
+}
+
+/***
+ @brief ia6_fmt
+ convert IPv6 address to inverse DNS nibble format
+ 1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.arpa
+ @param input: IPv6 char array
+ output: pointer to IPv6 address string
+ @return int length of address
+ */
+
+unsigned int ia6_fmt(char *s,char ip[16])
+{
+ unsigned int i;
+ unsigned int len;
+ int j;
+
+ static char data[] = "0123456789abcdef";
+ len = 0;
+
+ for (j = 15; j >= 0; j--) {
+ i = fmt_str(s,&data[ip[j] & 0x0f]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ i = fmt_str(s,&data[ip[j] >> 4 & 0x0f]); len += i; if (s) s += i;
+ i = fmt_str(s,"."); len += i; if (s) s += i;
+ }
+ i = fmt_str(s,"ip6.arpa."); len += i; if (s) s += i;
+
+ return len;
+}
+
+
+/***
+ @brief ip6_scan_flat
+ convert IPv6 address string to IPv6 address array
+ @param input: pointer to IPv6 address string
+ output: IPv6 char array
+ @return int length of address (ok > 0)
+ */
+
+unsigned int ip6_scan_flat(const char *s,char ip[16])
+{
+ int i, tmp;
+
+ for (i = 0; i < 16; i++) {
+ tmp = fromhex(*s++);
+ if (tmp < 0) return 0;
+ ip[i] = tmp << 4;
+ tmp = fromhex(*s++);
+ if (tmp < 0) return 0;
+ ip[i] += tmp;
+ }
+ return 32;
+}
+
+/***
+ @brief ip6_scan
+ parse compactified IPv6 address string and convert to IPv6 address array
+ @param input: pointer to IPv6 address string
+ output: IPv6 char array
+ @return int length of ip6_address/ip
+ */
+
+unsigned int ip6_scan(const char *s,char ip[16])
+{
+ unsigned int i, j;
+ unsigned int len = 0;
+ unsigned long u;
+
+ char suffix[16];
+ int prefixlen = 0;
+ int suffixlen = 0;
+
+ /* Always return IPv4 as IPv4-mapped IPv6 address */
+ if ((i = ip4_scan(s,ip+12))) {
+ for (len = 0; len < 12; ++len)
+ ip[len] = V4mappedprefix[len];
+ return i;
+ if (byte_equal(ip+12,4,V4localnet)) {
+ byte_copy(ip,16,V6localnet);
+ return 16;
+ }
+ }
+ byte_zero(ip,16);
+
+ for (;;) {
+ if (*s == ':') {
+ len++;
+ if (s[1] == ':') { /* Found "::", skip to part 2 */
+ s += 2; len++;
+ break;
+ }
+ s++;
+ }
+ i = scan_xlong((char *)s,&u);
+ if (!i) return 0;
+
+ if (prefixlen == 12 && s[i] == '.') {
+ /* the last 4 bytes may be written as IPv4 address */
+ i = ip4_scan(s,ip+12);
+ if (i)
+ return i+len;
+ else
+ return 0;
+ }
+
+ ip[prefixlen++] = (u >> 8);
+ ip[prefixlen++] = (u & 255);
+ s += i; len += i;
+ if (prefixlen == 16) return len;
+ }
+
+/* part 2, after "::" */
+ for (;;) {
+ if (*s == ':') {
+ if (suffixlen == 0) break;
+ s++;
+ len++;
+ } else if (suffixlen != 0) break;
+
+ i = scan_xlong((char *)s,&u);
+ if (!i) {
+ len--;
+ break;
+ }
+
+ if (suffixlen + prefixlen <= 12 && s[i] == '.') {
+ j = ip4_scan(s,suffix+suffixlen);
+ if (j) {
+ suffixlen += 4;
+ len += j;
+ break;
+ } else
+ prefixlen = 12 - suffixlen; /* make end-of-loop test true */
+ }
+
+ suffix[suffixlen++] = (u >> 8);
+ suffix[suffixlen++] = (u & 255);
+ s += i; len += i;
+ if (prefixlen + suffixlen == 16) break;
+ }
+
+ for (i = 0; i < suffixlen; i++)
+ ip[16 - suffixlen + i] = suffix[i];
+
+ return len;
+}
+
+/***
+ @brief ip6_scanbracket
+ parse IPv6 string address enclosed in brackets
+ @param input: pointer to IPv6 address string
+ output: IPv6 char array
+ @return int length of ip_address (ok > 0)
+ */
+
+unsigned int ip6_scanbracket(const char *s,char ip[16])
+{
+ unsigned int len;
+
+ if (*s != '[') return 0;
+ len = ip6_scan(s + 1,ip);
+ if (!len) return 0;
+ if (s[len + 1] != ']') return 0;
+ return len + 2;
+}
+
+/**
+ @brief ip6_ifscan
+ parse compactified IPv6 address string
+ concatinated with the interface name: fe80::1%eth0
+ @param input: pointer to IPv6 address string
+ output: IPv6 char array, stralloc interface_name
+ @return int length of ip6_address/ip
+ */
+
+unsigned int ip6_ifscan(char *s,char ip[16],stralloc *ifname)
+{
+ int i;
+ int j = 0;
+ int k = 0;
+ if (!stralloc_copys(ifname,"0")) return 0;
+
+ if ((j = str_chr(s,'%'))) {
+ if ((i = str_chr(s+j+1,' '))) k = i;
+ else if ((i = str_chr(s+j+1,'\n'))) k = i;
+ else if ((i = str_chr(s+j+1,'\t'))) k = i;
+ if (k) s[j+k+1] = '\0'; /* input might contain trailing chars */
+ if (!stralloc_copys(ifname,s+j+1)) return 0;
+ s[j] = 0;
+ }
+ if (!stralloc_0(ifname)) return 0;
+
+ return ip6_scan(s,ip);
+}
+
+/**
+ @brief ip6_cidr
+ parse compactified IPv6 address string
+ concatinated with the prefix length: fe80::1/64
+ @param input: pointer to IPv6 address string
+ output: IPv6 char array, long plen
+ @return int length of ip6_address/ip
+ */
+
+unsigned int ip6_cidr(char *s,char ip[16],unsigned long *plen)
+{
+ unsigned int j = 0;
+ *plen = 128UL;
+
+ j = str_chr(s,'/');
+ if (s[j] == '/') {
+ s[j] = 0;
+ j = scan_ulong(s+j+1,plen);
+ }
+ return ip6_scan((const char *)s,ip);
+}
+
+/**
+ @brief ip6_bytestring
+ parse IPv6 address and represent as char string with length prefix
+ @param input: IPv6 char array, prefix length
+ output: pointer to stralloc bit string;
+ @return n: number of bytes, if ok; -1: memory shortage
+ */
+
+unsigned int ip6_bytestring(stralloc *ipstring,char ip[16],int prefix)
+{
+ int i, j, n = 0;
+ unsigned char lowbyte, highbyte;
+
+ if (!stralloc_readyplus(ipstring,128)) return -1;
+ if (!stralloc_copys(ipstring,"")) return -1;
+
+ for (i = 0; i < 16; i++) {
+ lowbyte = (unsigned char) (ip[i]) & 0x0f;
+ highbyte = (unsigned char) (ip[i] >> 4) & 0x0f;
+
+ for (j = 3; j >= 0; j--) {
+ if (highbyte & (1 << j)) {
+ n++;
+ if (!stralloc_cats(ipstring,"1")) return -1;
+ } else {
+ n++;
+ if (!stralloc_cats(ipstring,"0")) return -1;
+ }
+ prefix--;
+ if (!prefix) goto DONE;
+ }
+ for (j = 3; j >= 0; j--) {
+ if (lowbyte & (1 << j)) {
+ n++;
+ if (!stralloc_cats(ipstring,"1")) return -1;
+ } else {
+ n++;
+ if (!stralloc_cats(ipstring,"0")) return -1;
+ }
+ prefix--;
+ if (!prefix) goto DONE;
+ }
+ }
+
+DONE:
+ if (!stralloc_0(ipstring)) return -1;
+
+ return n;
+}
diff --git a/lock.c b/lock.c
new file mode 100644
index 0000000..4ac6b40
--- /dev/null
+++ b/lock.c
@@ -0,0 +1,22 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include "lock.h"
+
+/**
+ @file lock.c
+ @author djb
+ @source qmail
+ @brief locking of resources
+*/
+
+#ifdef HASFLOCK
+int lock_ex(int fd) { return flock(fd,LOCK_EX); }
+int lock_exnb(int fd) { return flock(fd,LOCK_EX | LOCK_NB); }
+int lock_un(int fd) { return flock(fd,LOCK_UN); }
+#else
+int lock_ex(int fd) { return lockf(fd,1,0); }
+int lock_exnb(int fd) { return lockf(fd,2,0); }
+int lock_un(int fd) { return lockf(fd,0,0); }
+#endif
diff --git a/logmsg.c b/logmsg.c
new file mode 100644
index 0000000..3f079f4
--- /dev/null
+++ b/logmsg.c
@@ -0,0 +1,97 @@
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include "buffer.h"
+#include "fmt.h"
+#include "str.h"
+#include "stralloc.h"
+#include "logmsg.h"
+
+/**
+ @file logmsg.c
+ @author kp, feh
+ @source qlibs
+ @brief unified system and error message handling
+*/
+
+#define WHO "logmsg"
+
+char *build_log_msg(const char *x[])
+{
+ stralloc sa = {0};
+ stralloc_copys(&sa,"");
+
+ while(*x) { if (!stralloc_cats(&sa,*x++)) err_sys(WHO,errno); } /* concatenate *x */
+
+ if (!stralloc_0(&sa)) err_sys(WHO,errno);
+ return(sa.s);
+}
+
+void logmsg(const char *who,int ecode,unsigned int class,const char *msg)
+{
+ char strnum[FMT_ULONG];
+ char *codestr = "";
+ char *classstr = "";
+ char *errmsg = "";
+
+ errno = 0; /* re-initialize errno, value is in 'code' now */
+
+/* Part 1: obtain the (error) code -- perhaps received from OS */
+
+ if (ecode != 0) {
+ codestr = "";
+ if (ecode < 0) { // check for negative error codes
+ ecode = (ecode^-1) + 1;
+ codestr = "-";
+ }
+ strnum[fmt_ulong(strnum,ecode)] = 0; /* format for output */
+ char *temp = strnum;
+ codestr = str_cat(codestr,temp);
+ }
+
+/* Part 2: behavioral on error */
+
+ switch (class) {
+ case ERROR: classstr = "error: "; break; // info + exit
+ case FATAL: classstr = "fatal: "; break; // info + exit
+ case DROP: classstr = "drop: "; break; // info + next call/iteration
+ case ALERT: classstr = "alert: "; break; // info + next statement
+ case WARN: classstr = "warning: "; break; // info + next statement
+ case INFO: classstr = "info: "; break; // info + continue
+ case SYNTAX: classstr = "syntax: "; break; // info + exit
+ case USAGE: classstr = "usage: "; break; // info + exit
+ case TEMP: classstr = "temp: "; break; // info + exit
+ case CAT: classstr = ""; break; // info w/o \n
+ default:
+ class = LOG; classstr = ""; break; // custom info + continue
+ }
+
+/* Part 3: get system error message */
+
+ if (class == FATAL || class == DROP)
+ errmsg = error_str(errno);
+
+/* Part 4: construct log message: Source: Class (Ecode) Message: Errmsg */
+
+ buffer_puts(buffer_2,who);
+ buffer_puts(buffer_2,": ");
+ buffer_puts(buffer_2,classstr);
+ if (class == FATAL || class == DROP || class == ERROR) {
+ buffer_puts(buffer_2,"(");
+ buffer_puts(buffer_2,codestr);
+ buffer_puts(buffer_2,") ");
+ }
+ buffer_puts(buffer_2,msg);
+ if (errno) {
+ buffer_puts(buffer_2,": ");
+ buffer_puts(buffer_2,errmsg);
+ }
+ if (class != CAT) {
+ buffer_puts(buffer_2,"\n");
+ buffer_flush(buffer_2);
+ }
+
+ if (class == USAGE) _exit(USAGE);
+ if (class == SYNTAX) _exit(SYNTAX);
+ if (class == FATAL || class == DROP || class == ERROR) _exit(ecode);
+}
diff --git a/man/INSTALL b/man/INSTALL
new file mode 100644
index 0000000..7281ed9
--- /dev/null
+++ b/man/INSTALL
@@ -0,0 +1,12 @@
+Imstallation of fehQlibs man pages
+==================================
+
+a) It is expected that the fehQlibs are installed at /usr/local.
+b) Check your manpath settings: $ manpath
+c) You might want to compress the manpages: gzip *.3
+d) If available, the compressed or raw man pages can be simply copied to
+ - /usr/local/man/man3 or
+ - /usr/local/share/man/man3
+ 'root' is required to complete this step.
+
+That's it!
diff --git a/man/alloc.3 b/man/alloc.3
new file mode 100644
index 0000000..5bd40d2
--- /dev/null
+++ b/man/alloc.3
@@ -0,0 +1,68 @@
+.TH qlibs: alloc 3
+.SH NAME
+alloc \- allocate memory
+.SH SYNTAX
+.B #include \(dqalloc.h\(dq
+
+char *\fBalloc\fP(\fInew\fR);
+
+void \fBalloc_free\fP(\fIx\fR);
+
+void \fBalloc_re\fP(&\fIx\fR,\fIold\fR,\fInew\fR);
+
+char *\fIx\fR;
+.br
+unsigned int \fIold\fR;
+.br
+unsigned int \fInew\fR;
+.SH DESCRIPTION
+.B alloc
+allocates enough space from the heap for
+.I new
+bytes of data,
+adequately aligned for any data type.
+.I new
+may be 0.
+.B alloc
+returns a pointer to the space.
+If space is not available,
+.B alloc
+returns 0,
+setting
+.B errno
+appropriately.
+
+.B alloc_free
+returns space to the heap.
+
+.B alloc_re
+expands the space allocated to
+.I x
+from
+.I old
+bytes to
+.I new
+bytes.
+It allocates new space,
+copies
+.I old
+bytes from the old space to the new space,
+returns the old space to the heap,
+and changes
+.I x
+to point to the new space.
+It then returns 1.
+If space is not available,
+.B alloc_re
+returns 0,
+leaving the old space alone.
+.SH "CVE-2005-1513"
+This version of
+.B alloc
+respects
+.I limits.h
+to avoid memory resource exhaustion.
+.SH "SEE ALSO"
+sbrk(2),
+malloc(3),
+error(3)
diff --git a/man/buffer.3 b/man/buffer.3
new file mode 100644
index 0000000..2d4d0d2
--- /dev/null
+++ b/man/buffer.3
@@ -0,0 +1,185 @@
+.TH buffer 3
+.SH NAME
+buffer \- generic read/write buffering
+.SH SYNTAX
+.B #include \(dqbuffer.h\(dq
+
+buffer* buffer_0; /* like stdio's stdin */
+.br
+buffer* buffer_1; /* like stdio's stdout */
+.br
+buffer* buffer_2; /* like stdio's stderr */
+
+void \fBbuffer_init\fR(buffer &\fIb\fR,ssize_t (*\fIop\fR)(int,char *,size_t),
+ int \fIfd\fR, char *\fIy\fR, size_t \fIylen\fR);
+.br
+ssize_t \fBbuffer_get\fP(buffer *\fIb\fR,char *\fIx\fR,size_t \fIlen\fR);
+
+int \fBbuffer_put\fP(buffer *\fIb\fR,const char *\fIx\fR,size_t \fIlen\fR);
+.br
+int \fBbuffer_puts\fP(buffer *\fIb\fR,const char *\fIx\fR);
+.br
+int \fBbuffer_putalign\fP(buffer *\fIb\fR,char *\fIx\fR,unsigned int \fIlen\fR);
+.br
+int \fBbuffer_putsalign\fP(buffer *\fIb\fR,char *\fIx\fR);
+
+int \fBbuffer_putflush\fP(buffer *\fIb\fR,char *\fIx\fR,unsigned int \fIlen\fR);
+.br
+int \fBbuffer_putsflush\fP(buffer *\fIb\fR,char *\fIx\fR);
+
+int \fBbuffer_flush\fP(buffer *\fIb\fR);
+.br
+int \fBbuffer_copy\fP(buffer *\fIbo\fR,buffer *\fIbi\fR);
+
+int \fBbuffer_unixread\fP(int \fIfd\fR,char *\fIbuf\fR,size_t \fIlen\fR);
+.br
+int \fBbuffer_unixwrite\fP(int \fIfd\fR,char *\fIbuf\fR,size_t \fIlen\fR);
+.SH DESCRIPTION
+.B buffer.h
+describes a generic buffer interface that can be used for
+read and write buffering. Buffers must be initialized with
+\fBbuffer_init\fR.
+
+A buffer can only be used for reading or writing at the same time, not
+both.
+
+Unlike
+.BR stdio ,
+these write buffers are not flushed automatically at
+program termination; you must manually call \fBbuffer_flush\fR,
+\fBbuffer_putflush\fR, or \fBbuffer_putsflush\fR.
+
+.B buffer_init
+prepares \fIb\fR to store a string in \fIy\fR[0], \fIy\fR[1], ...,
+\fIy\fR[\fIylen\fR-1]. Initially the string is empty.
+
+.B buffer_init
+also prepares \fIb\fR to use the read/write operation specified by
+\fIop\fR and \fIfd\fR.
+
+You can use
+
+ buffer \fIb\fR = BUFFER_INIT(\fIop\fR,\fIfd\fR,\fIy\fR,\fIylen\fR);
+
+to initialize \fIb\fR statically if \fIop\fR, \fIfd\fR, \fIy\fR, and \fIylen\fR
+are compile-time constants.
+
+You can call
+.B buffer_init
+again at any time. Note that this discards the currently buffered string.
+
+.B buffer_get
+copies data to \fIx\fR[0], \fIx\fR[1], ...,
+\fIx\fR[\fIlen\fR-1] from the beginning of a string stored in
+preallocated space; removes these \fIlen\fR bytes from the string; and
+returns \fIlen\fR.
+
+If, however, the string has fewer than \fIlen\fR (but more than 0)
+bytes,
+.I buffer_get
+copies only that many bytes, and returns that number.
+
+If the string is empty,
+.B buffer_get
+first uses a \fBread operation\fR to
+feed data into the string. The \fBread operation\fR may indicate end of
+input.
+
+The preallocated space and the \fBread operation\fR are specified by
+\fIb\fR. You must initialize \fBb\fR using
+.B buffer_init
+before calling
+.B buffer_get
+(or use the pre-initialized \fIbuffer_0\fR).
+
+.B buffer_put
+writes \fIlen\fR bytes from \fIx\fR to \fIb\fR.
+
+The difference to
+.B buffer_putalign
+is that, when there isn't enough space
+for new data,
+.B buffer_put
+calls
+.B buffer_flush
+before copying any data, while
+.B buffer_putalign
+fills all available space with data before calling
+.B buffer_flush.
+
+.B buffer_copy
+copies one buffer to other one.
+The output buffer needs to have at least the
+preallocated size of the input buffer.
+.B buffer_unixread
+and
+.B buffer_unixwrite
+perform the same operation like standard Unix
+.B read
+or
+.BR write.
+.SH MACROS
+Apart from this basic usage, some helpful macro
+definitions are provided:
+.B BUFFER_INIT(op,fd,buf,len)
+uses
+.I op
+function to operate (read/write) on
+.I buf
+with
+.I len
+to file descriptor
+.IR fd .
+.B buffer_GETC(buf,c)
+returns the upmost position of character
+.I c
+within buffer
+.I buf
+or 0.
+.B buffer_PUTC(buf,c)
+adds character
+.I c
+to buffer
+.IR buf.
+.SH EXAMPLE
+ #include <buffer.h>
+ #include <open.h>
+
+ char buf[BUFFER_INSIZE]; // defined in buffer.h
+ int fd = open_read("/etc/services");
+ buffer input;
+
+ if (fd >= 0) {
+ char x;
+ buffer_init(&input,read,fd,buf,sizeof buf);
+ while (buffer_get(&input,&x,1) == 1) {
+ buffer_put(buffer_1,&x,1);
+ if (x == '\\n') break;
+ }
+ buffer_flush(buffer_1);
+ }
+.SH "RETURN CODES"
+.B buffer_put
+and
+.B buffer_get
+return
+.I 0
+if everything was fine,
+.IR -1 ,
+on error (setting \fIerrno\fR).
+.B buffer_copy
+returns
+.IR -2 ,
+if the input buffer can't be read, and
+.IR -3 ,
+if the data can't successfully copied
+to the output buffer. On success
+.B buffer_copy
+returns
+.IR 0 .
+.SH "REFERENCES"
+https://cr.yp.to/lib/buffer_get.html
+
+https://cr.yp.to/lib/buffer_put.html
+.SH "SEE ALSO"
+stdio(3)
diff --git a/man/byte.3 b/man/byte.3
new file mode 100644
index 0000000..277e479
--- /dev/null
+++ b/man/byte.3
@@ -0,0 +1,82 @@
+.TH qlibs byte 3
+.SH NAME
+byte \- byte manipulation/evaluation
+.SH SYNTAX
+.B #include \(dqbyte.h\(dq
+
+unsigned int \fBbyte_chr\fP(const char *\fIhaystack\fR,unsigned int \fIlen\fR,char \fIneedle\fR);
+.br
+unsigned int \fBbyte_rchr\fP(const char *\fIhaystack\fR,unsigned int \fIlen\fR,char \fIneedle\fR);
+
+void \fBbyte_copy\fP(char *\fIout\fR,unsigned int \fIlen\fR,const char *\fIin\fR);
+.br
+void \fBbyte_copyr\fP(char *\fIout\fR,unsigned int \fIlen\fR,const char *\fIin\fR);
+
+int \fBbyte_diff\fP(const char *\fIone\fR,unsigned int \fIlen\fR,const char *\fItwo\fR);
+
+int \fBbyte_equal\fP(const char *\fIone\fR,unsigned int \fIlen\fR,const char *\fItwo\fR);
+
+void \fBbyte_fill\fP(char *\fIout\fR,unsigned int \fIlen\fR,const char \fIc\fR);
+.br
+void \fBbyte_zero\fP(char *\fIout\fR,unsigned int \fIlen\fR);
+.SH DESCRIPTION
+.B byte_chr
+returns the smallest integer \fIi\fR between 0 and
+\fIlen\fR-1 inclusive such that \fIone\fR[\fIi\fR] equals \fIneedle\fR.
+If no such integer exists,
+.B byte_chr
+returns \fIlen\fR.
+.B byte_chr
+may read all bytes \fIone\fR[0], \fIone\fR[1], ...,
+\fIone\fR[\fIlen\fR-1], even if not all the bytes are relevant to the
+answer.
+
+.B byte_rchr
+returns the largest integer \fIi\fR between 0 and
+\fIlen\fR-1 inclusive such that \fIone\fR[\fIi\fR] equals \fIneedle\fR.
+If no such integer exists,
+.B byte_rchr
+returns \fIlen\fR.
+.B byte_rchr
+may read all bytes \fIone\fR[0], \fIone\fR[1], ...,
+\fIone\fR[\fIlen\fR-1], even if not all the bytes are relevant to the
+answer.
+
+.B byte_copy
+copies \fIin\fR[0] to \fIout\fR[0], \fIin\fR[1] to
+\fIout\fR[1], etc., and finally \fIin\fR[\fIlen\fR-1] to
+\fIout\fR[\fIlen\fR-1].
+
+.B byte_copyr
+copies \fIin\fR[\fIlen\fR-1] to \fIout\fR[\fIlen\fR-1],
+\fIin\fR[\fIlen\fR-2] to \fIout\fR[\fIlen\fR-2], etc., and
+\fIin\fR[0] to \fIout\fR[0].
+
+.B byte_diff
+returns negative, 0, or positive, depending on whether
+the string \fIone\fR[0], \fIone\fR[1], ..., \fIone\fR[\fIlen\fR-1] is
+lexicographically smaller than, equal to, or greater than the string
+\fIone\fR[0], \fIone\fR[1], ..., \fIone\fR[\fIlen\fR-1].
+When the strings are different,
+.B byte_diff
+does not read bytes past the first difference.
+
+.B byte_equal
+returns
+.I 1
+if the strings are equal,
+.I 0
+otherwise.
+When the strings are different,
+.B byte_equal
+does not read bytes past the first difference.
+
+.B byte_fill
+fills \fIout\fR[0], \fIout\fR[1], ..., \fIout\fR[\fIlen\fR-1]
+with a single byte \fIc\fR.
+
+.B byte_zero
+sets \fIout\fR[0], \fIout\fR[1], ..., \fIout\fR[\fIlen\fR-1] to 0.
+.SH "SEE ALSO"
+case(3),
+stralloc(3)
diff --git a/man/case.3 b/man/case.3
new file mode 100644
index 0000000..c9a879a
--- /dev/null
+++ b/man/case.3
@@ -0,0 +1,124 @@
+.TH qlibs: case 3
+.SH NAME
+case \- case independent string manipulation/evaluation
+.SH SYNTAX
+.B #include \(dqcase.h\(dq
+
+void \fBcase_lowers\fP(\fIs\fR);
+.br
+void \fBcase_lowerb\fP(\fIs\fR,\fIlen\fR);
+.br
+void \fBcase_uppers\fP(\fIs\fR);
+.br
+void \fBcase_upperb\fP(\fIs\fR,\fIlen\fR);
+.br
+int \fBcase_diffs\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_equals\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_diffrs\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_equalrs\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_starts\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_diffb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
+.br
+int \fBcase_startb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
+
+char *\fIs\fR;
+.br
+char *\fIt\fR;
+.br
+unsigned int \fIlen\fR;
+.SH DESCRIPTION
+.B case_lowers
+converts each uppercase byte in the string
+.I s
+to lowercase.
+.I s
+must be 0-terminated.
+
+.B case_lowerb
+converts each uppercase byte in the buffer
+.IR s ,
+of length
+.IR len ,
+to lowercase.
+
+.B case_uppers
+converts each lowercase byte in the string
+.I s
+to uppercase.
+.I s
+must be 0-terminated.
+
+.B case_upperb
+converts each lowercase byte in the buffer
+.IR s ,
+of length
+.IR len ,
+to uppercase.
+
+.B case_diffs
+lexicographically compares lowercase versions of the strings
+.I s
+and
+.IR t .
+It returns something positive, negative, or zero
+when the first is larger than, smaller than, or equal to the second.
+.I s
+and
+.I t
+must be 0-terminated.
+
+.B case_equals
+means
+.BR !case_diffs .
+
+.B case_diffrs
+and
+.B case_equalrs
+compare strings from right to left instead from left to right.
+
+.B case_starts
+returns 1 if a lowercase version of
+.I s
+starts with a lowercase version of
+.IR t .
+.I s
+and
+.I t
+must be 0-terminated.
+
+.B case_diffb
+lexicographically compares lowercase versions of the buffers
+.I s
+and
+.IR t ,
+each of length
+.IR len .
+It returns something positive, negative, or zero
+when the first is larger than, smaller than, or equal to the second.
+
+.B case_startb
+returns 1 if a lowercase version of the buffer
+.IR s ,
+of length
+.IR len ,
+starts with a lowercase version of the string
+.IR t .
+.I t
+must be 0-terminated.
+
+The
+.B case
+routines
+are ASCII-specific.
+They are suitable for programs that handle
+case-independent networking protocols.
+
+All comparisons are performed on unsigned bytes.
+.SH "SEE ALSO"
+byte(3),
+stralloc(3)
diff --git a/man/cdbmake.3 b/man/cdbmake.3
new file mode 100644
index 0000000..7e7fd3e
--- /dev/null
+++ b/man/cdbmake.3
@@ -0,0 +1,61 @@
+.TH qlibs:cdbmake 3
+.SH NAME
+cdmake \- generate and fill a constant database
+.SH SYNTAX
+.B #include \(dqcdmake.h\(dq
+
+int \fBcdb_make_start\fP(struct cdb *\fIc\fR,int \fIfd\fR);
+.br
+int \fBcdb_make_add\fP(struct cdb *\fIc\fR,char *\fIkey\fR,unsigned int \fIkeylen\fR,
+ char *\fIdata\fR,unsigend int \fIdatalen\fR);
+.br
+int \fBcdb_make_finish\fP(struct cdb *\fIc\fR);
+.SH DESCRIPTION
+.B cdb_make_start
+generates and intitialises a new cdb named
+.I c
+and makes it available via file descriptor
+.IR fd .
+
+.B cdb_make_add
+adds entries using the file descriptor
+.I fd
+given as
+.I key
+with length
+.I keylen
+into the cdb returning the hashed values
+.I data
+with length
+.IR datalen .
+
+.B cdb_make_finish
+finalises the data structure provided as
+.IR fd .
+
+.SH "RETURN CODES"
+Usually, the
+.B cdb_make_*
+routines provide a return code of
+.I 0
+for successful operations and
+.I -1
+if anything is going wrong.
+
+.SH EXAMPLE
+#include <cdbmake.h>
+
+ int fd;
+ stralloc data = {0};
+ stralloc key = {0};
+
+ struct cdb_make c;
+
+ if (cdb_make_start(&c,fd) == -1) die_write();
+ if (cdb_make_add(&c,key.s,key.len,data.s,data.len) == -1)
+ die_write();
+ if (cdb_make_finish(&c) == -1) die_write();
+ if (fsync(fd) == -1) die_write();
+
+.SH "SEE ALSO"
+cdbread(3)
diff --git a/man/cdbread.3 b/man/cdbread.3
new file mode 100644
index 0000000..6d4641f
--- /dev/null
+++ b/man/cdbread.3
@@ -0,0 +1,177 @@
+.TH qlibs:cdbread 3
+.SH NAME
+cdbread \- fetch information from a constant database
+.SH SYNTAX
+.B #include \(dqcdbread.h\(dq
+
+void \fBcdb_init\fP(struct cdb *\fIc\fR,int \fIfd\fR);
+.br
+int \fBcdb_read\fP(struct cdb *\fIc\fR,char *\fIdata\fR,unsigned int \fIdlen\fR,uint32 \fIpos\fR);
+.br
+int \fBcdb_findstart\fP(int \fIfd\fR,char *\fIkey\fR,unsigned int \fIlen\fR);
+.br
+int \fBcdb_findnext\fP(int \fIfd\fR,char *\fIkey\fR,unsigned int \fIlen\fR);
+.br
+int \fBcdb_find\fP(int \fIfd\fR,char *\fIney\fR,unsigned int \fIlen\fR);
+
+.br
+int \fBcdb_findnext\fP(int \fIfd\fR,char *\fIkey\fR,unsigned int \fIlen\fR);
+.br
+void \fBcdb_free\fP(struct cdb *\fIc\fR):
+.SH DESCRIPTION
+.B cdb_free
+unallocates
+.I c
+if
+.I c
+is allocated.
+Otherwise it leaves
+.I c
+alone.
+.B cdb_free
+does not close
+.IR fd .
+
+.B cdb_init
+allocates
+.B c
+to hold information about a constant database read by descriptor
+.IR fd .
+You may call
+.B cdb_init
+repeatedly; if
+.I c
+is already allocated,
+.B cdb_init
+unallocates it first.
+
+.B cdb_read
+reads
+.I dlen
+bytes into
+.I d
+from byte position
+.I dpos
+in the database. You must allocate
+.I c
+before calling
+.BR cdb_read .
+Normally
+.B cdb_read
+returns
+.IR 0 .
+If the database file is shorter than
+.I dpos+dlen
+bytes, or if there is a disk read error,
+.B cdb_read
+returns
+.IR -1 ,
+setting
+.I errno
+appropriately.
+
+.B cdb_findstart
+prepares
+.I c
+to search for the first record under a new
+.IR key .
+You must allocate
+.I c
+before calling
+.BR cdb_findstart ,
+and you must call
+.B cdb_findstart
+before calling
+.BR cdb_findnext .
+
+.B cdb_findnext
+looks for the nth record under
+.I key
+in the database, where
+.I n
+is the number of calls to
+.B cdb_findnext
+after the most recent call to
+.BR cdb_findstart .
+If it finds the record,
+.B cdb_findnext
+returns
+.IR 1 ;
+if there are exactly n-1 such records,
+.B cdb_findnext
+returns
+.IR 0 ;
+if there are fewer than n-1 such records, the behavior of
+.B cdb_findnext
+is undefined; if there is a database format error or disk error,
+.B cdb_findnext
+returns
+.IR -1 , setting
+.I errno
+appropriately. Each call to
+.B cdb_findnext
+(before another call to
+.BR cdb_findstart )
+must use the same
+.I k
+and
+.IR klen .
+
+If
+.B cdb_findnext
+returns
+.IR 1 ,
+it arranges for
+.B cdb_datapos
+to return the starting byte position of the data in the record, and for
+.B cdb_datalen
+to return the number of bytes of data in the record.
+Otherwise the results of
+.B cdb_datapos
+and
+.B cdb_datalen
+are undefined.
+
+.B cdb_find
+is the same as
+.B cdb_findstart
+followed by
+.BR cdb_findnext :
+it finds the first record under
+.IR key.
+
+.B cdb_datapos
+and
+.B cdb_datalen
+are macros pointing to the found information following
+.I key
+in the cdb and returning their length.
+.SH EXAMPLE
+#include <cdbread.h>
+
+ int fd;
+ char *data;
+ unsigned int len;
+ stralloc key = {0};
+
+ static struct cdb c;
+
+ cdb_init(&c,fd);
+
+ switch (cdb_find(&c,key.s,key.len)) {
+ case -1: return -1;
+ case 0: return 0;
+ }
+
+ len = cdb_datalen(&c);
+ data = alloc(len);
+ if (!data) return -1;
+
+ if (cdb_read(&c,data,datalen,cdb_datapos(&c)) == -1) {
+ alloc_free(data);
+ return -1;
+ }
+
+ cdb_free(&c);
+.SH "SEE ALSO"
+cdbmake(3)
diff --git a/man/constmap.3 b/man/constmap.3
new file mode 100644
index 0000000..e28f5f4
--- /dev/null
+++ b/man/constmap.3
@@ -0,0 +1,56 @@
+.TH qlibs:constmap 3
+.SH NAME
+constmap \- fetch matching strings from a hashed data structure in constant time
+.SH SYNTAX
+.B #include \(dqconstmap.h\(dq
+.SH SYNOPSIS
+int \fBconstmap_init\fP(struct constmap \fI*cm\fR,char \fI*string\fR,int \fIlen\fR,int \fIflagcolon\fR);
+.br
+int \fBconstmap_init_char\fP(struct constmap \fI*cm\fR,char \fI*string\fR,int \fIlen\fR,int \fIflagcolon\fR,char \fIflagchar\fR);
+.br
+char *\fBconstmap\fP(struct constmap \fI*cm\fR,char \fI*string\fR,int \fIlen\fR);
+.br
+void \fBconstmap_free\fP(\fIstruct constmap *cm\fR);
+.SH DESCRIPTION
+Reading a file perhaps with
+.I control_readfile(&cmap,"path/filename",0)
+.B constmap_init
+can be used to convert its content into a constant time search map:
+.IR constmap .
+Here, you can specify whether the entries in
+.I constmap
+are plain or key/value structured setting
+.I flagcolon
+to one and assuming the delimiter equals to a colon:
+.IR : .
+If the delimiter needs to be particulary tailored, use
+.BR constmap_init_char .
+
+Given the search
+.I string
+and providing its length
+.I len
+(without the trailing \\0)
+.B constmap
+will now retrieve the information in constant time
+returning a pointer to the search result or
+.IR 0 .
+
+The datastructure can be freed by means of
+.BR constmap_free .
+.SH EXAMPLE
+#include <constmap.h>
+
+ struct constmap cmap;
+ stralloc result = {0};
+ char *info;
+ char *search;
+
+ if (control_readfile(&cmap,"control/conf",0))
+ constmap_init(&cmap,result.s,result.len,1));
+
+ info = constmap(&cmap,search,str_len (search));
+ if (!info) return 0;
+
+.SH "SEE ALSO"
+cdbread(3), cdbmake(3)
diff --git a/man/dns.3 b/man/dns.3
new file mode 100644
index 0000000..837450c
--- /dev/null
+++ b/man/dns.3
@@ -0,0 +1,249 @@
+.TH qlibs: dnsresolv
+.SH NAME
+dns \- dns resolver routines
+.SH SYNTAX
+.B #include \(dqdnsresolv.h\(dq
+
+int \fBdns_ip4_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR);
+.br
+int \fBdns_ip4\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR);
+
+int \fBdns_ip6_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR);
+.br
+int \fBdns_ip6\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR);
+
+int \fBdns_mx\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR);
+.br
+int \fBdns_mx_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR);
+
+void \fBdns_name4_domain\fP(char \fIq\fR[DNS_NAME4_DOMAIN],const char *\fIip\fR[4]);
+.br
+int \fBdns_name4\fP(stralloc *\fIout\fR,const char *\fIip\fR[4]);
+
+void \fBdns_name6_domain\fP(char \fIq\fR[DNS_NAME6_DOMAIN],const char *\fIip\fR[16]);
+.br
+int \fBdns_name6\fP(stralloc *\fIout\fR,const char *\fIip\fR[16]);
+
+int \fBdns_name\fP(stralloc *\fIout\fR,const char *\fIip\fR[16]);
+.br
+int \fBdns_name_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR);
+
+int \fBdns_txt\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR);
+.br
+int \fBdns_txt_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR);
+
+int \fBdns_cname\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR);
+.br
+int \fBdns_cname_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR);
+
+int \fBdns_ip4_qualify\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR,const stralloc *\fIudn\fR);
+.br
+int \fBdns_ip6_qualify\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR,const stralloc *\fIudn\fR);
+.br
+int \fBdns_ip_qualify\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR,const stralloc *\fIudn\fR);
+.SH DESCRIPTION
+.B dns_ip[4|6]_packet
+is a low-level component of
+.BR dns_ip[4|6] ,
+designed to support asynchronous DNS lookups.
+It reads a DNS packet of length \fIlen\fR from
+\fIbuf\fR, extracts the IP(4|6) addresses from the answer section of the packet and
+puts the addresses into \fIout\fR.
+
+.B dns_ip[4|6]
+looks up 4/16-byte IPv6 addresses for the fully qualified domain name of
+\fIfqdn\fR. It puts the concatenation of the IPv[4|6] addresses into \fIout\fR and
+returns the number of received answers for \fIfqdn\fR or \fI0\fR if none are replied.
+If the domain does not exist in DNS, or has no IP addresses,
+\fIout\fR will be empty. More generally, if \fIfqdn\fR is considered by
+.B dns_ip4
+to be a dotted-decimal IPv4 address, it is provissioned as \fIout\fR
+without checking the DNS while returning \fI1\fR.
+Brackets may enclose the dotted-decimal IP address; they are ignored.
+
+.B dns_name[4|6]_packet
+is a low-level component of
+.B dns_name[4|6]
+designed to support asynchronous DNS lookups.
+It reads a DNS packet of length \fIlen\fR from \fIbuf\fR,
+
+.B dns_name[4|6]_domain
+is a low-level component of
+.BR dns_name[4|6] .
+It converts an IP address such as
+.I 1.2.3.4
+or
+.I 4321:0:1:2:3:4:567:89ab
+into a domain name such as
+.I 4.3.2.1.in-addr.arpa
+or
+.I b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa
+and places the packet-encoded domain name into \fIq\fR.
+.I q
+is zero terminated.
+.I q
+must have space for DNS_NAME[4|6]_DOMAIN bytes.
+
+.B dns_mx_packet
+is a low-level component of
+.BR dns_mx ,
+designed to support asynchronous DNS lookups.
+It reads a DNS packet of length \fIlen\fR from \fIbuf\fR,
+extracts the MX records from the answer section of the packet, puts the
+result into \fIout\fR, and returns the number of replies received
+or \fI-1\fR the same way as
+.BR dns_mx .
+
+.B dns_mx
+looks up MX records for the fully-qualified domain name in
+\fIfqdn\fR. It puts the MX records into \fIout\fR and returns the number of
+received records or \fI0\fR.
+Each MX record is a two-byte MX distance (big endian) followed by a
+\\0-terminated dot-encoded domain name. If the domain does not exist in
+DNS, or has no MX records, \fIout\fR will be empty.
+
+.B dns_txt_packet
+is a low-level component of
+.BR dns_txt ,
+designed to support
+asynchronous DNS lookups. It reads a DNS packet of length \fIlen\fR from \fIbuf\fR,
+extracts the TXT records from the answer section of the packet, puts the
+result into \fIout\fR, and returns the number of replies
+or \fI-1\fR the same way as \fBdns_txt\fR.
+
+.B dns_txt
+looks up TXT records for the fully-qualified domain name in
+\fIfqdn\fR. It puts the concatenation of the TXT records into \fIout\fR
+and returns the number of replies received.
+If the domain does not exist in DNS, or has no TXT records, \fIout\fR will be empty.
+
+.B dns_cname_packet
+is a low-level component of
+.BR dns_cname ,
+designed to support
+asynchronous DNS lookups. It reads a DNS packet of length \fIlen\fR from \fIbuf\fR,
+extracts the TXT records from the answer section of the packet, puts the
+result into \fIout\fR, and returns the number of replies received or
+\fI-1\fR the same way as \fBdns_cname\fR.
+
+.B dns_cname
+looks up the canonical name for a given
+.IR fqdn .
+
+.B dns_ip[4|6]_qualify
+feeds the name \fIudn\fR through qualification and looks up their
+4-byte/16-byte IP addresses. It puts the fully qualified domain name
+into \fIfqdn\fR and the concatenation of the IP addresses into \fIout\fR,
+while returning the number of encountered IP addresses.
+.B dns_ip[4|6]_qualify
+evaluates the environment variables
+.I $DNSREWRITEFILE
+pointing to
+.I /etc/dnsrewrite
+and
+.I $LOCALDOMAIN
+and finally reading
+.I /etc/resolv.conf
+to build the
+.I Full Qualified Domain Name (FQDN)
+using the
+.I domain suffix
+for
+.IR hostname .
+.B dns_ip_qualify
+returns
+.I out
+as
+.I IP[4|6]
+and
+.IR FQDN .
+If the domain does not exist in DNS, or has no IP addresses,
+\fIout\fR will be empty and the return code is \fI0\fR.
+
+In case
+.B dns_ip[4|6]_qualify
+is fed with an
+.I IP[4|6]
+addresses instead of domain names,
+it recognizes those not to be subject of
+.IR qualification .
+The particular names
+.IR localhost ,
+.I ip4-loopback
+and
+.I ip6-loopback
+are treated locally and mapped to
+the respective
+.I IP[4|6]
+addresses (and vice versa)
+without facilitating a DNS lookup.
+.SH "INPUT"
+For the high-level routines
+.BR dns_ip[4|6] ,
+.BR dns_ip_qualify[4|6] ,
+.BR dns_name[4|6] ,
+.BR dns_mx ,
+.BR dns_name ,
+.BR dns_txt
+and
+.BR dns_cname
+the provided input variable
+.I stralloc \*\fqdn
+needs to be un-terminated, thus the given
+string length is identitical to the number of
+.I fqdn
+characters.
+.SH "OUTPUT"
+The returned IP addresses are given as character (byte) strings
+with 4 or 16 bytes:
+.B dns_ip4
+and
+.B dns_ip4_qualifiy
+return 4 byte IPv4 addresses, where as
+.B dns_ip6
+and
+.B dns_ip
+as well as
+.B dns_ip6_qualify
+and
+.B dns_ip_qualify
+return sets of 16 byte IPv6 addresses, where
+the potential IPv4 addresses are given in their
+IPv6-mapped-IPv4 representation.
+.SH "RETURN CODES"
+The dns routines
+.BR dns_cname* ,
+.BR dns_ip* ,
+.BR dns_mx* ,
+.B dns_name*
+and
+.BR dns_txt*
+return the number of answers received, which may be
+.I 0
+and potentially
+.I -1
+in case of a memory failure. The full set of return codes:
+
+.EX
+Value | Macro | Explaination
+------+----------+-------------------------------------
+ n>0 | | n = number of answers reeceived
+ 0 | DNS_NXD | either NXDOMAIN or NODATA
+ -1 | DNS_MEM | out of memory (fatal)
+ -2 | DNS_ERR | parsing errors
+ -3 | DNS_COM | socket communicaton errror; SERVFAIL
+ -4 | DNS_INT | internal error
+ -5 | DNS_SOFT | DNS_ERR or DNS_COM
+ -6 | DNS_HARD | DNS loop problem (CNAME)
+.EE
+
+.I errno
+is set appropriately.
+In case of a failure, the respective output variables like
+\fIout\fR and \fIfqdn\fR may or may not change.
+Since a received reply may be empty, always check the length of the
+received output additionally.
+.SH "SEE ALSO"
+ip4(3),
+ip6(3),
+dnsstub(3)
diff --git a/man/dnsstub.3 b/man/dnsstub.3
new file mode 100644
index 0000000..6bce8a3
--- /dev/null
+++ b/man/dnsstub.3
@@ -0,0 +1,109 @@
+.TH qlibs: dnsstub
+.SH NAME
+dns \- stub resolver
+.SH SYNTAX
+.B #include \(dqdnsresolv.h\(dq
+
+int \fBdns_resolvconfip\fP(char \fIservers\fR[512],uint32 \fIscope\fR[32]);
+
+int \fBdns_transmit_start\fP(struct dns_transmit *\fIq\fR,const char \fIservers\fR[512],
+ int \fIflagrecursive\fR,const char *\fIq\fR,const char \fIqtype\fR[2],
+ const char \fIlocalip\fR[16]);
+.br
+int \fBdns_transmit_start6\fP(struct dns_transmit *\fIq\fR,const char \fIservers\fR[512],
+ int \fIflagrecursive\fR,const char *\fIq\fR,const char \fIqtype\fR[2],
+ const char \fIlocalip\fR[16],const uint32 \fIscopes\fR[32]);
+
+int \fBdns_sortip4\fP(char *\fIservers\fR,unsigned int \fIn\fR);
+.br
+int \fBdns_sortip6\fP(char *\fIservers\fR,unsigned int \fIn\fR);
+.SH DESCRIPTION
+.B dns_resolvconfip
+reads the name servers defined in
+.I /etc/resolv.conf
+and uses
+.B ip6_ifscan
+to fetch their IPv4/IPv6 addresses together with the scope for the LLU address.
+Up to
+.I 32
+name servers can be specified.
+
+Apart from the system-wide
+.I /etc/resolv.conf
+the IP addresses of name servers can preferrably be provided by means of the
+environment variable
+.I $DNSCACHEIP
+for each user application. The variable
+.I $DNSCACHEIP
+may include up to 32 name server IP addresses separated by white spaces:
+
+.EX
+ DNSCACHEIP="10.0.1.53 fe80::1%lo0 ::1"
+.EE
+
+.B dns_transmit_start
+and
+.B dns_transmit_start6
+use the list of name server IP's for a recursive or none-recursive
+query
+.I q
+of type
+.I qtype
+starting from IP address
+.I localip
+for which the
+.I struct dns_transmit
+provides the required book keeping information.
+.B dns_transmit_start6
+additionally is able to evaluate the given
+.I scope_id
+as information for the local interface in order
+to bind to the remote IPv6 LLU addresses. Usually
+.I scope_id
+defaults to
+.IR 0 .
+
+.B dns_sortip4
+and
+.B dns_sortip6
+randomize the list of name server IPs upon call and as result the first address
+is used to facilitate the name lookup.
+.IR FQDN .
+.SH "RETURN CODES"
+Different from the original djb implemtation, the
+following return code scheme is used:
+.TP 2
+rc > 0
+A 'positive' answer is given, thus results returned.
+.TP 2
+rc = 0
+A 'neutral' answer was provided, thus typically the
+operation succeeded, but without replies.
+.TP 2
+rc < 0
+An operational error was encountered.
+.TP 0
+Potentially, this needs to be treated specifically:
+.TP 2
+DNS_INT (rc = -4)
+Internal DNS operational error; ie. resources were not
+available.
+.TP 2
+DNS_COM (rc = -3)
+DNS communication error.
+.TP 2
+DNS_ERR (rc = -2)
+Parsing errors and others.
+.TP 2
+DNS_MEM (rc = -1)
+Memory error occured.
+.TP 0
+A 'safe' usage of return codes thus checks not only the presence
+of a return code, but rather additionally its sign.
+Negative return codes point to errors.
+.SH "SEE ALSO"
+http://cr.yp.to/djbdns/qualify.html,
+dns(3),
+ip4(3),
+ip6(3),
+socket_if(3)
diff --git a/man/env.3 b/man/env.3
new file mode 100644
index 0000000..c8d0bf2
--- /dev/null
+++ b/man/env.3
@@ -0,0 +1,68 @@
+.TH qlibs: env 3
+.SH NAME
+env \- manage variables in the environment
+.SH SYNTAX
+.B #include \(dqenv.h\(dq
+
+char **\fBenviron\fP;
+
+char *\fBenv_put\fP(char *\fINAME\fR,char *\fIvalue\fR);
+.br
+char *\fBenv_puts\fP(char *\fINAME=value\fR);
+.br
+char *\fBenv_set\fP(char *\fINAME=value\fR);
+.br
+char *\fBenv_unset\fP(char *\fINAME\fR);
+.br
+char *\fBenv_get\fP(char *\fINAME\fR);
+.br
+char *\fBenv_pick\fP();
+.br
+char *\fBenv_clear\fP();
+.SH DESCRIPTION
+The environment,
+.BR environ ,
+is a 0-terminated array of 0-terminated strings,
+called environment variables.
+Each environment variable is of the form
+.IR NAME\fB=\fIvalue .
+
+.B env_puts
+puts the string
+.I \'NAME=value\'
+into the environment.
+
+.B env_put
+assigns the new enviromment variable
+.I NAME
+with
+.IR value .
+
+.B env_set
+assigns an existing environment variable
+.I NAME
+with
+.IR value :
+.IR \'NAME=value\' .
+
+.B env_unset
+unsets an existing environment variable given as
+.IR NAME .
+
+.B env_get
+returns the assigned value of the first variable whose name is
+.IR NAME ,
+or
+.I 0
+if there is no such variable.
+
+.B env_pick
+returns any variable in the environment,
+or
+.I 0
+if the environment is empty.
+
+.B env_clear
+clears the whole envionment.
+.SH "SEE ALSO"
+environ(7)
diff --git a/man/error.3 b/man/error.3
new file mode 100644
index 0000000..b96be1f
--- /dev/null
+++ b/man/error.3
@@ -0,0 +1,75 @@
+.TH qlibs: error 3
+.SH NAME
+error \- syscall error codes and information
+.SH SYNTAX
+.B #include \(dqlogmsg.h\(dq
+
+extern int \fBerrno\fP;
+
+extern int \fBerror_intr\fP;
+.br
+extern int \fBerror_nomem\fP;
+.br
+extern int \fBerror_noent\fP;
+.br
+extern int \fBerror_txtbsy\fP;
+.br
+extern int \fBerror_io\fP;
+.br
+extern int \fBerror_exist\fP;
+.br
+extern int \fBerror_timeout\fP;
+.br
+extern int \fBerror_inprogress\fP;
+.br
+extern int \fBerror_wouldblock\fP;
+.br
+extern int \fBerror_again\fP;
+.br
+extern int \fBerror_pipe\fP;
+.br
+extern int \fBerror_perm\fP;
+.br
+extern int \fBerror_access\fP;
+
+char *\fBerror_str\fP(int \fIe\fR);
+
+int \fBerror_temp\fP(int \fIe\fR);
+UNIX syscalls provide detailed error codes in the
+.B errno
+variable.
+The
+.B error
+library provides portable names for a variety of possible
+.B errno
+values.
+
+.B error_str
+returns a printable string describing syscall error code
+.IR e .
+Normally
+.I e
+is
+.BR errno .
+
+.B error_temp
+returns
+.I 1
+if syscall error code
+.I e
+is a soft error,
+.I 0
+if it is a hard error. Normally
+.I e
+is
+.BR errno .
+
+A hard error is persistent:
+file not found, read-only file system, symbolic link loop, etc.
+
+A soft error is usually transient:
+out of memory, out of disk space, I/O error, disk quota exceeded,
+connection refused, host unreachable, etc.
+.SH "SEE ALSO"
+sysmsg(3),
+errno(2)
diff --git a/man/fd.3 b/man/fd.3
new file mode 100644
index 0000000..b22338d
--- /dev/null
+++ b/man/fd.3
@@ -0,0 +1,79 @@
+.TH qlibs: fd 3
+.SH NAME
+fd \- duplicate, move or close a file descriptor
+.SH SYNTAX
+.B #include \(dqfd.h\(dq
+
+int \fBfd_copy\fP(int \fIto\fR,int \fIfrom\fR);
+.br
+int \fBfd_move\fP(int \fIto\fR,int \fIfrom\fR);
+.br
+int \fBfd_coe\fP(int \fIfd\fR);
+.SH DESCRIPTION
+.B fd_copy
+copies
+descriptor
+.I from
+to descriptor
+.IR to .
+If
+.I to
+was already open,
+.B fd_copy
+closes it.
+.B fd_copy
+always leaves
+.I from
+intact;
+if
+.I to
+and
+.I from
+are the same number,
+.B fd_copy
+does nothing.
+.B fd_copy
+does not guarantee that
+.I to
+will remain open, if it was open, in case of error.
+
+.B fd_move
+moves
+descriptor
+.I from
+to descriptor
+.IR to .
+If
+.I to
+was already open,
+.B fd_move
+closes it.
+If the move is successful,
+.B fd_move
+closes
+.IR from .
+Exception:
+if
+.I to
+and
+.I from
+are the same number,
+.B fd_move
+does nothing.
+
+.B fd_coe
+closes
+.IR fd .
+.SH "RETURN CODES"
+.BR fd_copy ,
+.BR fd_move ,
+and
+.B fd_coe
+return
+.I 0
+on success,
+and
+.I -1
+on error.
+.SH "SEE ALSO"
+dup(2), fcntl(2)
diff --git a/man/fmt.3 b/man/fmt.3
new file mode 100644
index 0000000..ac9c985
--- /dev/null
+++ b/man/fmt.3
@@ -0,0 +1,90 @@
+.TH qlibs: fmt 3
+.SH NAME
+fmt \- ASCII representation of strings and integers
+.SH SYNTAX
+.B #include \(dqfmt.h\(dq
+
+unsigned int \fBfmt_str\fP(char *\fIdest\fR,const char *\fIsource\fR);
+.br
+unsigned int \fBfmt_strn\fP(char *\fIdest\fR,const char *\fIsource\fR,unsigned int \fImaxlen\fR);
+
+unsigned int \fBfmt_uint\fP(char *\fIdest\fR,unsigned int \fIsource\fR);
+.br
+unsigned int \fBfmt_uint0\fP(char *\fIdest\fR,unsigned int \fIsource\fR,unsigned int \fIn\fR);
+.br
+unsigned int \fBfmt_ulong\fP(char *\fIdest\fR,unsigned long \fIsource\fR);
+.br
+unsigned int \fBfmt_xlong\fP(char *\fIdest\fR,unsigned long \fIsource\fR);
+.br
+char \fBtohex\fP(char \fInum\fR);
+.br
+int \fBfromhex\fP(unsigned char \fIc\fR);
+.SH DESCRIPTION
+.B fmt_str
+copies all leading nonzero bytes from \fIsource\fR to \fIdest\fR
+and returns the number of bytes it copied.
+.B fmt_str
+does not append \\0.
+
+.B fmt_strn
+copies at most \fImaxlen\fR leading nonzero bytes from
+\fIsource\fR to \fIdest\fR and returns the number of bytes it copied.
+.B fmt_strn
+does not append \\0.
+
+.B fmt_uint
+writes an ASCII representation ('0' to '9', base 10) of
+\fIsource\fR to \fIdest\fR and returns the number of bytes written.
+.B fmt_uint
+does not append \\0.
+
+.B fmt_uint0
+writes an ASCII representation ('0' to '9', base 10) of
+\fIsource\fR to \fIdest\fR and returns the number of bytes written.
+The output is padded with '0'-bytes until it encompasses at least
+\fIn\fR bytes, but it will not be truncated if it does not fit.
+.B fmt_uint0
+does not append \\0.
+
+.B fmt_ulong
+writes an ASCII representation ('0' to '9', base 10) of
+\fIsource\fR to \fIdest\fR and returns the number of bytes written
+perhaps including a trailing \\0.
+.B fmt_ulong
+does not append \\0.
+
+.B fmt_xlong
+writes an ASCII representation ('0' to '9' and 'a' to 'f', base 16)
+of \fIsource\fR to \fIdest\fR and returns the number of bytes
+written.
+.B fmt_xlong
+does not append \\0.
+
+.B tohex
+reads the ASCII representation of a decimal \fInum\fR and returns its
+hexadecimal ASCII value; thus \'0\' -> \'0\' ... \'9\' -> \'9\',
+\'10\' -> \'a\' and finally \'15\' -> f'.
+
+.B fromhex
+reads the ACSII representation of a hexadecimal number \'0\' to \'f\'
+irrelevant of its case and returns its integer value.
+
+For convenience,
+.B fmt.h
+defines the integers
+.I FMT_LEN
+and
+.I FMT_ULONG
+to be big enough to the number of bytes it would have written.
+
+
+.SH "RETURN CODES"
+If \fIdest\fR equals FMT_LEN (i.e. is zero), all
+.B fmt_*
+routins return the number of bytes it would have written
+i.e. the number of leading nonzero bytes of \fIsource\fR
+perhaps including a \\0.
+.SH "SEE ALSO"
+byte(3),
+case(3),
+scan(3)
diff --git a/man/getln.3 b/man/getln.3
new file mode 100644
index 0000000..bb4ae7c
--- /dev/null
+++ b/man/getln.3
@@ -0,0 +1,102 @@
+.TH glibs: getln 3
+.SH NAME
+getln \ - read one line of data
+.SH SYNTAX
+.B #include \(dqgetln.h\(dq
+
+int \fBgetln\fP(&buffer_0,&sa,&match,sep);
+.br
+int \fBgetln2\fP(&buffer_0,&sa,&cont,&clen,sep);
+
+buffer \fIbuffer_0\fR;
+.br
+stralloc \fIsa\fR;
+.br
+int \fImatch\fR;
+.br
+int \fIsep\fR;
+.br
+char *\fIcont\fR;
+.br
+unsigned int \fIclen\fR;
+.SH DESCRIPTION
+.B getln
+reads a line of characters, terminated by a sep character, from
+.IR buffer_0 .
+It returns the line in
+.I sa
+and sets match to
+.IR 1 .
+If
+.B getln
+sees end-of-input before it sees
+.IR sep ,
+it returns the partial line in
+.I sa
+and sets match to
+.IR 0 .
+
+.B getln2
+reads a line of characters, terminated by a
+.I sep
+character, from
+.IR buffer_0 .
+The line is returned in two pieces. The first piece is stored in
+.IR sa .
+The second piece is
+.IR cont ,
+a pointer to
+.I clen
+characters inside the
+.I buffer_0
+buffer. The second piece must be copied somewhere else before
+.I ss
+is used again.
+If
+.B getln2
+sees end-of-input before it sees
+.IR sep ,
+it sets
+.I clen
+to
+.I 0
+and does not set
+.IR cont .
+It puts the partial line into
+.IR sa.
+.SH "RETURN CODES"
+.B getln
+normally returns
+.IR 0 .
+If it runs out of memory, or encounters an error from
+.IR ss ,
+it returns
+.IR -1 ,
+setting
+.I errno
+appropriately.
+
+.B getln2
+normally returns
+.IR 0 .
+If it runs out of memory, or encounters an error from
+.IR ss ,
+it returns
+.IR -1 ,
+setting
+.I errno
+appropriately.
+.SH NOTE
+The input buffer
+.I buffer_0
+is already pre-allocated.
+It can be used without initialization as synonym for STDIN.
+.SH CREDITS
+The
+.B getln
+and
+.B getln2
+man page were taken from Bruce Guenther and
+originally published by Dan Bernstein for qmail-1.03.
+.SH SEE ALSO
+stralloc(3)
diff --git a/man/getoptb.3 b/man/getoptb.3
new file mode 100644
index 0000000..f3e329b
--- /dev/null
+++ b/man/getoptb.3
@@ -0,0 +1,22 @@
+.TH qlibs: getoptb 3
+.SH NAME
+getoptb \- get option character from command line
+.SH SYNTAX
+.B #include \(dqgetoptb.h\(dq
+.SH DESCRIPTION
+Qlib's
+.B getopt
+is a replacement for the standard Unix
+.B getopt
+library, based on
+.B sgetopt
+and printing errors by means of
+.B buffer
+rather than
+.BR stdio .
+
+See
+.B getopt(3)
+for interface details.
+.SH "SEE ALSO"
+buffer(3)
diff --git a/man/iopause.3 b/man/iopause.3
new file mode 100644
index 0000000..c7ab9fb
--- /dev/null
+++ b/man/iopause.3
@@ -0,0 +1,87 @@
+.TH qlibs: iopause 3
+.SH NAME
+iopause \- stateful handling of events based on the poll interface or file descriptors
+.SH SYNTAX
+.B #include \(dqioause.h\(dq
+
+int \fBiopause\fP(iopause_fd *\fIx\fR,unsigned int \fIlen\fR,struct taia *\fIdeadline\f$,struct taia *\fIstamp\fR);
+.SH DESCRIPTION
+.B iopause
+checks for file descriptor readability or writeability as specified
+by \fIx\fR[0].fd, \fIx\fR[0].events, \fIx\fR[1].fd, \fIx\fR[1].events, ..., \fIx\fR[\fIlen\fR-1].fd,
+\fIx\fR[\fIlen\fR-1].events. If \fIx\fR[i].events includes the bit IOPAUSE_READ,
+.B iopause
+checks for readability of the descriptor \fIx\fR[i].fd;
+if \fIx\fR[i].events includes the bit IOPAUSE_WRITE,
+.B iopause
+checks for writability of the descriptor
+\fIx\fR[i].fd; other bits in \fIx\fR[i].events have undefined effects.
+
+.B iopause
+sets the IOPAUSE_READ bit in \fIx\fR[i].revents if it finds that \fIx\fR[i].fd
+is readabled and it sets the IOPAUSE_WRITE bit in \fIx\fR[i].revents if it finds
+that \fIx\fR[i].fd is writable.
+Beware that readability and writeability may be destroyed at any moment
+by other processes with access to the same file \fIx\fR[i].fd refers to.
+
+If there is no readability or writeability to report,
+.B iopause
+waits until \fIdeadline\fR for something to happen.
+.B iopause
+will return before \fIdeadline\fR if a
+descriptor becomes readable or writeable, or an interrupting signal
+arrives, or some system-defined amount of time passes.
+.B iopause
+sets
+.I revents
+in any case.
+
+You must put a current timestamp into \fIstamp\fR before calling
+.BR iopause .
+In case the the current timestamp is older than the previous one (ie. due to daylight-saving)
+negative values are omitted.
+
+.SH "IMPLEMENTATION NOTES"
+The current implementation of
+.B iopause
+uses the \fBpoll\fR interface if that is available.
+On some systems, \fBpoll\fR needs to dynamically allocate kernel
+memory. In case not enough memory is available,
+.B iopause
+will return immediately and will report (often incorrectly) that no descriptors are
+readable or writeable.
+
+If the \fBpoll\fR interface is not available,
+.B iopause
+uses the \fBselect\fR function. This function
+cannot see descriptor numbers past a system-defined limit, typically 256
+or 1024;
+.I iopause
+will artificially pretend that those descriptors are never readable or writeable.
+
+Future implementations of
+.B iopause
+may work around these problems on some
+systems, at the expense of chewing up all available CPU time.
+
+Both \fBpoll\fR and \fBselect\fR use relative timeouts rather than absolute deadlines.
+Some kernels round the timeout down to a multiple of 10 milliseconds; this
+can burn quite a bit of CPU time as the deadline approaches.
+.B iopause
+compensates for this by adding 20 milliseconds to the timeout.
+In case the current timestamp is older than the previous one (ie. due to daylight-saving)
+the timeout is set to 20 milliseconds.
+.SH "RETURN CODES"
+.B iopause
+reads and deploys the return codes of the
+.I poll
+and
+.I select
+call. Only positive return values shall be considered by the calling routines for a succcessful
+invocation.
+.SH CREDITS
+Parts of the description are taken from the 'libowfat' man page.
+.SH "SEE ALSO"
+select(2),
+poll(3),
+taia(3)
diff --git a/man/ip4.3 b/man/ip4.3
new file mode 100644
index 0000000..d6b5928
--- /dev/null
+++ b/man/ip4.3
@@ -0,0 +1,73 @@
+.TH qlibs: ipv4
+.SH NAME
+ipv4 \- IPv4 address evaluation and conversion
+.SH SYNTAX
+.B #include \(dqip.h\(dq
+
+unsigned int \fBip4_fmt\fP(char *\fIs\fR,char \fIip[4]\fR);
+.br
+unsigned int \fBip4_scan\fP(const char *\fIs\fR,char \fIip[4]\fR);
+.br
+unsigned int \fBip4_scanbracket\fP(char *\fIs\fR,char \fIip[4]\fR);
+.br
+unsigned int \fBip_scanbracket\fP(char *\fIip_str\fR,char *\fIs\fR);
+.br
+unsigned int \fBip4_cidr\fP(char *\fIs\fR,char \fIip[4]\fR,unsigned long *\fIplen\fR);
+.br
+unsigned int \fBip4_bytestring\fP(stralloc *\fIip4string\fR,char \fIip[4]\fR,int \fIplen\fR);
+.SH DESCRIPTION
+.B ip4_fmt
+reads the
+.I char[4]
+IPv4 address and returns a dotted-decimal IPv4 address string
+.IR 1.2.3.4 .
+
+.B ip4_scan
+reads an IPv4 address string
+.I 1.2.3.4
+and converts it to the
+.I char[4]
+IPv4 address.
+
+.B ip4_scanbracket
+reads an IPv4 address string enclosed in brackets
+.I [1.2.3.4]
+removes the brackets and calls
+.B ip4_scan
+on the result.
+
+.B ip_scanbracket
+reads an IP address string enclosed in brackets
+.I [1.2.3.4]
+or
+.IR [fe80::1] ,
+removes the brackets and calls
+.B ip4_scan
+or
+.B ip6_scan
+upon detecting an IPv6 address on the result.
+
+.B ip4_cidr
+reads the CIDR IPv4 address string
+.I 1.2.3.4/15
+determines the prefix as integer
+.I plen
+and calls
+.BR ip4_scan .
+If no prefix is identified, it returns 32.
+
+.B ip4_bytestring
+reads the IPv4 address given as
+.I char[4]
+while returning a 0-terminated 'bytestring' representation
+.I 1001001....
+up to the given prefix length
+.IR plen .
+.SH "RETURN CODES"
+The
+.B ip(4)*
+programs return the number of bytes processed.
+.B ip4_bytestring
+returns regative numbers on failure.
+.SH "SEE ALSO"
+ip6(3), socket_if(3)
diff --git a/man/ip6.3 b/man/ip6.3
new file mode 100644
index 0000000..f3f5cb1
--- /dev/null
+++ b/man/ip6.3
@@ -0,0 +1,98 @@
+.TH qlibs: ipv6
+.SH NAME
+ipv6 \- IPv6 address evaluation and conversion
+.SH SYNOPSIS
+.B #include \(dqip.h\(dq
+
+unsigned int \fBip6_fmt\fP(char *\fIs\fR,char \fIip[16]\fR);
+.br
+unsigned int \fBip6_fmt_flat\fP(char *\fIs\fR,char \fIip[16]\fR);
+.br
+unsigned int \fBip6_scan_flat\fP(const char *\fIs\fR,char \fIip[16]\fR);
+.br
+unsigned int \fBip6_scan\fP(const char *\fIs\fR,char \fIip[16]\fR);
+.br
+unsigned int \fBip6_scanbracket\fP(const char *\fIs\fR,char \fIip[16]\fR);
+.br
+unsigned int \fBip6_ifscan\fP(char *\fIs\fR,char \fIip[16]\fR,stralloc *\fIifname\fR);
+.br
+unsigned int \fBip6_cidr\fP(char *\fIs\fR,char \fIip[16]\fR,unsigned long *\fIplen\fR);
+.br
+unsigned int \fBip6_bytestring\fP(stralloc *\fIip6string\fR,char \fIip[16]\fR,int \fIplen\fR);
+.SH DESCRIPTION
+.B ip6_fmt
+reads the
+.I char[16]
+IPv6 address and returns a compactified hexadecimal IPv6 address string
+.IR fe80::fefe .
+
+.B ip6_fmt_flat
+reads the
+.I char[16]
+IPv6 address and returns all hexadecimal IPv6 address labels as string
+.IR fe80:0000:....:fefe .
+
+.B ip6_scan
+reads a compactified IPv6 address string
+.I fe80::fefe
+and converts it to the
+.I char[16]
+IPv6 address.
+
+.B ip6_scan_flat
+reads an uncompressed IPv6 address als hexadecimal string
+and returns it's
+.I char[16]
+IPv6 address.
+
+.B ip6_scanbracket
+reads a compactified IPv6 address string enclosed in brackets
+.I [fe80::fefe]
+removes the brackets and calls
+.B ip6_scan
+on the result.
+
+.B ip6_ifscan
+reads the compactified IPv6 address string appended with the
+interface_name
+.I fe80::fefe%eth0
+returns
+.I ifname
+and calls
+.B ip6_scan
+for the (stripped) IPv6 address.
+
+.B ip6_cidr
+reads the compactified CIDR IPv6 address string
+.I fe80::fefe/64
+determines the prefix as integer
+.I plen
+and calls
+.BR ip6_scan .
+If no prefix is identfied, it returns 128.
+
+.B ip6_bytestring
+reads the IPv6 address given as
+.I char[16]
+while returning a 0-terminated 'bytestring' representation
+.I 1001001....
+up to the given prefix length
+.IR plen .
+
+The macro
+.B ipv6_v4mapped
+reads the IPv6 addresses given as
+.I char[16]
+and returns
+.I 0
+in case the given IPv6 address is not
+a IPv4 mapped address.
+.SH "RETURN CODES"
+The
+.B ip(6)*
+programs return the number of bytes processed.
+.B ip6_bytestring
+returns negative numbers on failure.
+.SH "SEE ALSO"
+ip4(3),
+socket_if(3)
diff --git a/man/logmsg.3 b/man/logmsg.3
new file mode 100644
index 0000000..f819a18
--- /dev/null
+++ b/man/logmsg.3
@@ -0,0 +1,109 @@
+.TH qlibs: logmsg 3
+.SH NAME
+logmsg \- handle system errors and application log messages
+.SH SYNTAX
+.B #include \(dqlogmsg.h\(dq
+
+int logmsg(const char *who, int ecode, unsigned int classs, char *msg)
+
+\fBerr_sys\fR(w,e) logmsg(w,e,FATAL,"")
+.br
+\fBerr_sys_plus\fR(w,e,m) logmsg(w,e,FATAL,m)
+.br
+\fBerr_tmp\fR(w,e) logmsg(w,e,WARN,"")
+.br
+\fBerr_tmp_plus\fR(w,e,m) logmsg(w,e,WARN,m)
+.br
+\fBerr_int\fR(w,e,c) logmsg(w,e,c,"")
+.br
+\fBerr_int_plus\fR(w,e,c,m) logmsg(w,e,c,m)
+.br
+\fBlog_who\fR(w,m) logmsg(w,0,LOG,m)
+.br
+\fBlog_anon\fR(m) logmsg("",0,LOG,m)
+.SH DESCRIPTION
+\fBlogmsg\fR prints error, warning, or info/logging messages to stderr
+and potentially terminates the calling program, depending on the \fIclass\fR given.
+\fIwho\fR is the name of the program, \fIecode\fR is an error code,
+\fIclass\fR determines the behavior upon call and \fImsg\fR is the logging message.
+Read "error.h" to learn more about related constants.
+.SH ECODE
+\fIecode\fR is the error code and subject to be displayed in the log file and
+potentially used upon exit if the \fIclass\fR equals \fBERROR\fR, \fBFATAL\fR, or \fBDROP\fR.
+
+To avoid conflicts with syscall error codes, appplication defined error codes should be negative.
+The values \fI-15\fR, \fI-100\fR and \fI-111\fR are reserved for backward compatibility.
+.SH CLASS
+The \fIclass\fr parameter indicates how the application handles exceptions and displays the
+log message.
+.TP 4
+o
+\fBLOG\fR, \fBINFO\fR, \fBALERT\fR, \fBWARN\fR - display message and continue operation
+.TP 4
+o
+\fBDROP\fR - display warning message and continue while returning to the calling program
+.TP 4
+o
+\fBUSAGE\fR, \fBSYNTAX\fR, \fBFATAL\fR, \fBERROR\fR
+- display error message and exit application with error code
+.RE
+
+\fBINFO\fR, \fBALERT\fR, \fBWARN\fR, \fBDROP\fR, \fBUSAGE\fR, and \fBFATAL\fR as well
+as \fBERROR\fR display the respective class string like \fIwarning:\fR in the log message,
+while \fBLOG\fR shows the log message only.
+
+The class \fBFATAL\fR should be used for system error codes only, rather \fBERROR\fR
+and \fBWARN\fR shall be set in conjunction with an application error/warning.
+.SH MESSAGE
+If the custom message \fImsg\fR is given, it will be printed additionally.
+.SH FORMAT
+The log message format consists of the tokens
+\fIwho\fR: (\fIecode\fR) \fImsg\fR : \fImsg\fR.
+.I ecode
+is displayed only for classes \fBFATAL\fR, \fBERROR\fR, or \fBDROP\fR.
+.I msg
+is the system's explanation according to the variable
+.I errno
+if provided.
+.SH NOTES
+.I logmsg.c
+uses
+.I errstr.c
+routines.
+Error codes and classes are defined in
+.I error.h
+and included by
+.IR logmsg.h .
+.SH "EXIT CODE"
+\fBlogmsg\fR exits \fIecode\fR for classes \fBERROR\fR, \fBFATAL\fR, \fBSYNTAX\fR,
+and \fBUSAGE\fR terminating the application.
+.SH HISTORY
+Dan Bernstein used sets of \fIstrerr_dieY*()\fR and \fIstrerr_warnY()\fR messages
+which explicitely determine the message and behavior class.
+Other classes were occasionally defined on demand, such als \fIusage()\fR.
+
+Kai Peter introduced the \fIerrmsg\fR facility in his \fBqlibs\fR
+including a \fBsyslog\fR compliant \fIseverity\fR as second parameter.
+.SH EXAMPLES
+The macro definitions uses \fBw\fR for the calling program,
+\fBe\fR for error code, \fBc\fR for class, and \fBm\fR for message.
+
+ #include "logmsg.h"
+ #define WHO "my_prog"
+
+ err_sys(WHO,errno);
+ err_sys_plus(WHO,-111,"additional message");
+
+ err_tmp("",-100);
+ err_tmp_plus("",errno,"additional message");
+
+ log_who(WHO,"message");
+
+log_anon() is like log_who() but doesn't print the caller name.
+
+An user defined message \fBs\fR can be build from multiple arguments by using the \fIB\fR
+(build) macro:
+
+ err_sys_plus((errno),B("unable to run: ",*argv));
+.SH "SEE ALSO"
+syslog(3)
diff --git a/man/pathexec.3 b/man/pathexec.3
new file mode 100644
index 0000000..41dc978
--- /dev/null
+++ b/man/pathexec.3
@@ -0,0 +1,124 @@
+.TH qlibs: pathexec 3
+.SH NAME
+pathexec \- run a program within a given environment
+.SH SYNTAX
+.B #include \(dqpathexec.h\(dq
+
+\fBpathexec_run\fP(const char *\fIp\fR,char **\fIa\fR,char **\fIe\fR);
+.br
+\fBpathexec\fP(char *const *\fIa\fR);
+.br
+\fBpathexec_env\fP(const char *\fIs\fR,char *\fIt\fR);
+.SH DESCRIPTION
+.B pathexec_run
+searches for a program named
+.IR p .
+It replaces the current process with a copy of that program.
+The main function in that program will be given arguments
+.I a
+and environment
+.IR e .
+.B pathexec_run
+looks for
+.I p
+as specified by the
+.I $PATH
+environment variable.
+.I $PATH
+is a colon-separated list of directories
+.IR d ;
+.B pathexec_run
+tries
+.B execve
+on files named
+.IR d/p ,
+in the order that the directories appear inside
+.IR $PATH .
+An empty directory name is treated as a single dot.
+
+If
+.I $PATH
+is not set,
+.B pathexec_run
+uses the path
+.IR /bin:/usr/bin ;
+i.e., it tries
+.B execve
+on
+.IR /bin/p ,
+then
+.IR /usr/bin/p .
+
+If
+.I p
+contains a slash,
+.B pathexec_run
+ignores
+.I $PATH
+and simply runs
+.B execve
+on a file named
+.IR p .
+
+.B pathexec
+calls
+.B pathexec_run
+with program name
+.IR a[0] ,
+arguments
+.IR a ,
+and the same environment as the current process,
+modified as described below.
+.B pathexec
+has the same return behavior as
+.BR pathexec_run .
+.B pathexec_env
+modifies the environment used by
+.BR pathexec .
+It removes a variable named
+.IR s,
+if one exists. It then adds a variable named
+.I s
+with value
+.IR t,
+if the pointer
+.I t
+is nonzero. The name
+.I s
+must not contain
+.IR = .
+.SH "RETURN CODES"
+Normally
+.B pathexec_run
+does not return, because the process has been replaced.
+However, if all the
+.B execve
+attempts fail,
+.B pathexec_run
+returns, setting
+.I errno
+to the most interesting error returned by
+.BR execve .
+Furthermore,
+.B pathexec_run
+returns immediately if an
+.B execve
+attempt fails with an error other than
+.IR error_noent ,
+.IR error_acces ,
+.IR error_perm ,
+or
+.IR error_isdir .
+This list is subject to change.
+
+Normally
+.B pathexec_env
+returns
+.IR 1 .
+If it is unable to allocate memory, it returns
+.IR 0 ,
+leaving the
+.B pathexec
+environment alone.
+.SH "SEE ALSO"
+error(3)
diff --git a/man/scan.3 b/man/scan.3
new file mode 100644
index 0000000..a466daa
--- /dev/null
+++ b/man/scan.3
@@ -0,0 +1,81 @@
+.TH qlibs: scan 3
+.SH NAME
+scan \- string to integer conversion
+.SH SYNTAX
+.B #include \(dqscan.h\(dq
+
+unsigned int \fBscan_8long\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR);
+.br
+unsigned int \fBscan_uint\fP(const char *\fIsrc\fR,int *\fIdest\fR);
+.br
+unsigned int \fBscan_long\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR);
+.br
+unsigned int \fBscan_ulong\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR);
+.br
+unsigned int \fBscan_xint\fP(const char *\fIsrc\fR,int *\fIdest\fR);
+.br
+unsigned int \fBscan_xlong\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR);
+.SH DESCRIPTION
+.B scan_8long
+parses an unsigned long integer in octal ASCII representation
+from \fIsrc\fR and writes the result into \fIdest\fR. It returns the
+number of bytes read from \fIsrc\fR.
+
+.B scan_uint
+parses an unsigned integer in decimal ASCII representation
+from \fIsrc\fR and writes the result into \fIdest\fR. It returns the
+number of bytes read from \fIsrc\fR.
+
+.B scan_ulong
+parses an unsigned long integer in decimal ASCII representation
+from \fIsrc\fR and writes the result into \fIdest\fR. It returns the
+number of bytes read from \fIsrc\fR.
+Leading + or - or space (or anything outside of 0-9) is not accepted.
+The libc conventions of "023" for octal or "0x23" for hexadecimal are
+not supported.
+.B scan_ulong
+will abort the scan if the next character is not a digit, or
+if it is a digit but adding it to the number would lead to an integer
+overflow.
+.B scan_ulong
+returns the number of characters successfully scanned and
+processed from src.
+
+.B scan_long
+includes the same logic as
+.B scan_ulong
+but now on a signed long integer in decimal ASCII format while
+recognizing the leading '+' or '-' sign.
+
+.B scan_xint
+parses an unsigned integer in hexadecimal ASCII representation
+from \fIsrc\fR and writes the result into \fIdest\fR. It returns the
+number of bytes read from \fIsrc\fR.
+
+.B scan_xlong
+parses an unsigned long integer in hexadecimal ASCII
+representation from \fIsrc\fR and writes the result into \fIdest\fR. It
+returns the number of bytes read from \fIsrc\fR.
+.B scan_xlong
+understands both upper and lower case letters.
+.B scan_xlong
+does not expect or understand a "0x" prefix.
+.SH EXAMPLES
+scan_ulong("23",&i) -> i=23, return 2
+
+scan_ulong("+23",&i) -> return 0
+
+scan_ulong("-23",&i) -> return 0
+
+scan_ulong(" 23",&i) -> return 0
+
+scan_ulong("23,42",&i) -> i=23, return 2
+
+scan_ulong("023",&i) -> i=23, return 3
+
+scan_ulong("0x23",&i) -> i=0, return 1
+
+scan_ulong("4294967296",&i) -> i=429496729, return 9 // 32-bit system
+.SH "SEE ALSO"
+case(3),
+byte(3)
diff --git a/man/socket_bind.3 b/man/socket_bind.3
new file mode 100644
index 0000000..434e45c
--- /dev/null
+++ b/man/socket_bind.3
@@ -0,0 +1,117 @@
+.TH qlibs: socket_bind 3
+.SH NAME
+socket_bind \- binding a TCP/UDP socket to a local IP address, port,
+and perhaps scope_id
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_bind4\fP(int \fIs\fR,char \fIip\fR[4],uint16 \fIport\fR);
+.br
+int \fBsocket_bind4_reuse\fP(int \fIs\fR,char \fIip\fR[4],uint16 \fIport\fR);
+
+int \fBsocket_bind6\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR);
+.br
+int \fBsocket_bind6_reuse\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR);
+
+int \fBsocket_bind\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR);
+.br
+int \fBsocket_bind_reuse\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR);
+.SH DESCRIPTION
+.B socket_bind4
+sets the local IP address and TCP/UDP port of a TCP/UDP
+socket \fIs\fR to \fIip\fR and \fIport\fR respectively.
+
+.B socket_bind4_reuse
+sets the local IP address and TCP/UDP port of a
+TCP/UDP socket \fIs\fR to \fIip\fR and \fIport\fR respectively.
+Unlike
+.BR socket_bind4 ,
+this function will also tell the operating system
+that the address is to be reused soon, which turns off the normal pause
+before this IP and port can be bound again.
+
+.B socket_bind6
+sets the local IP address and TCP/UDP port of a TCP/UDP
+socket \fIs\fR to \fIip\fR, \fIport\fR and \fIscope_id\fR respectively.
+
+.B socket_bind6_reuse
+sets the local IP address and TCP/UDP port of a TCP/UDP socket \fIs\fR
+to \fIip\fR, \fIport\fR and \fIscope_id\fR respectively.
+Unlike
+.BR socket_bind6 ,
+this function will also tell the operating system
+that the address is to be reused soon, which turns off the normal pause
+before this IP and port can be bound again.
+
+.B socket_bind
+sets the local IPv4/IPv6 address and TCP/UDP port of a TCP/UDP
+socket \fIs\fR to \fIip\fR, \fIport\fR, and \fIscope_id\fR respectively.
+
+For IPv4 and IPv4-mapped IPv6 addresses
+.B socket_bind
+will use
+.B socket_bind4
+or otherwise
+.BR socket_bind6 .
+
+.B socket_bind_reuse
+sets the local IPv4/IPv6 address and TCP/UDP port of a TCP/UDP socket \fIs\fR
+to \fIip\fR, \fIport\fR, and \fIscope_id\fR respectively.
+Unlike
+.BR socket_bind ,
+this function will also tell the operating system
+that the address is to be reused soon, which turns off the normal pause
+before this IP and port can be bound again.
+
+For IPv4 and IPv4-mapped IPv6 addresses
+.B socket_bind_reuse
+will use
+.B socket_bind4_reuse
+or otherwise
+.BR socket_bind6_reuse .
+.SH "AUTOMATIC BINDING"
+If the IPv4 address is 0 or the IPv6 address is ::, the operating system
+chooses a local IP address.
+If \fIport\fR is 0, the operating system chooses a port.
+\fIscope_id\fR is usually 0, except for IPv6 LLU addresses where
+.B socket_getifidx
+can be used to determine
+.I scope_id
+from the interface name.
+.SH "RETURN CODES"
+Normally, all
+.I socket_bind*
+routines return
+.IR 0 .
+If anything goes wrong, the return code is
+.I -1
+and setting
+.I errno
+appropriately.
+.SH EXAMPLE
+ #include <socket_if.h>
+ #include <ip.h>
+
+ int \fIs\fR;
+ char \fIlcoalip\fR[16];
+ char \fIremoteip\fR[16];
+ uint16 \fIp\fR = 0;
+ uint32 \fIscope_id\fR = 0;
+
+ if (ip6_isv4mapped(ip))
+ s = socket_tcp4();
+ else
+ s = socket_tcp6();
+ if (s == -1)
+ err_tmp(111,"unable to create TCP socket: ");
+
+ socket_connect(s,remoteip,p,scope_id);
+.SH "SEE ALSO"
+socket_if(3),
+socket_connect(3),
+socket_info(3),
+socket_recv(3),
+socket_send(3),
+socket_setup(3),
+socket_tcp(3),
+socket_udp(3)
diff --git a/man/socket_connect.3 b/man/socket_connect.3
new file mode 100644
index 0000000..e289393
--- /dev/null
+++ b/man/socket_connect.3
@@ -0,0 +1,110 @@
+.TH qlibs: socket_connect 3
+.SH NAME
+socket_connect \- initiate or test a socket connection to a remote IPv4/IPv6 address
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_connect4\fP(int \fIs\fR,const char \fIip\fR[4],uint16 \fIport\fR);
+.br
+int \fBsocket_connect6\fP(int \fIs\fR,const char \fIip\fR[16],uint16 \fIport\fR,
+ uint32 \fIscope_id\fR);
+.br
+int \fBsocket_connect\fP(int \fIs\fR,const char \fIip\fR[16],uint16 \fIport\fR,
+ uint32 \fIscope_id\fR);
+
+int \fBsocket_connected\fP(int \fIs\fR);
+.SH DESCRIPTION
+.B socket_connect4
+attempts to make a connection from TCP or UDP socket \fIs\fR to
+TCP port \fIport\fR on IP address \fIip\fR.
+You can call
+.B socket_connect4
+without calling
+.BR socket_bind4 .
+This has the effect as first calling
+.B socket_bind4
+with IP address 0.0.0.0 and port 0.
+
+.B socket_connect6
+attempts to make a connection from TCP or UDP socket \fIs\fR to
+TCP port \fIport\fR on IP address \fIip\fR and \fIscope_id\fR.
+The meaning of \fIscope_id\fR is dependent on the implementation and
+IPv6 IP. For link-local IPv6 addresses it specifies the outgoing
+interface index. From a given interface name (e.g. "eth0")
+it's index can be retrieved with
+.BR socket_getifidx .
+\fIscope_id\fR should normally be set to 0.
+You can call
+.B socket_connect6
+without calling
+.BR socket_bind6 .
+This has the effect as first calling
+.B socket_bind6
+with IP address :: and port 0.
+
+.B socket_connect
+attempts to make a connection from TCP socket \fIs\fR to
+TCP port \fIport\fR on IP address \fIip\fR and \fIscope_id\fR
+calling
+.BR socket_connect6 .
+If however, \fIip\fR is an IPv4 or IPv4-mapped IPv6 address
+.B socket_connect4
+is called instead.
+
+Once a socket is connected, you can use the read and write
+system calls to transmit data.
+
+.B socket_connected
+can be used to verify, whether a background connection failed or
+succeeded, thus \fIs\fR became writable or not.
+.SH EXAMPLE
+ #include <socket_if.h>
+
+ int \fIs\fR;
+ char \fIlocalip\fR[16];
+ char \fIremoteip\fR[16];
+ uint16 \fIp\fR = 0;
+
+ s = socket_tcp();
+ socket_bind(s,localip,p,0);
+ socket_connect(s,remoteip,p,0);
+
+ if (socket_connected(s) != 1)
+ err_tmp(""111,fatal,"unable to setup TCP connection: ");
+.SH "RETURN CODES"
+.BR socket_connect4 ,
+.BR socket_connect6
+and
+.BR socket_connect
+may return
+.IR 0 ,
+to indicate that the connection succeeded (and succeeded immediately,
+if the socket is non-blocking)
+.IR -1 ,
+setting
+.I errno
+to error_inprogress or error_wouldblock, to indicate
+that the socket is non-blocking
+.IR -1 ,
+setting
+.I errno
+to something else, to indicate that the connection
+failed (and failed immediately, if the socket is non-blocking).
+
+.B socket_connected
+returns
+.I 1
+if \fIs\fR is a socket and a connection is established,
+.I 0
+otherwise and setting
+.I errno
+appropriately.
+.SH "SEE ALSO"
+socket_if(3),
+socket_info(3),
+socket_bind(3),
+socket_recv(3),
+socket_send(3),
+socket_setup(3),
+socket_tcp(3),
+socket_udp(3)
diff --git a/man/socket_if.3 b/man/socket_if.3
new file mode 100644
index 0000000..1c1ef70
--- /dev/null
+++ b/man/socket_if.3
@@ -0,0 +1,97 @@
+.TH qlibs: socket_if 3
+.SH NAME
+socket_if \- retrieve scope_id for interface name and vice versa
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+uint32 \fBsocket_getifidx\fP(const char *\fIifname\fR);
+
+const char *\fBsocket_getifname\fP(uint32 \fIscope_id\fR);
+.SH DESCRIPTION
+.B socket_getifidx
+returns the \fIscope_id\fR of an interface named as
+.I ifname
+typically ''eth0''.
+
+.B socket_getifname
+retrieves from the operating system's assigned
+.I scope_id
+the interface name
+.IR ifname .
+.SH INTERFACE_NAME VERSUS SCOPE_ID
+For IPv6 LLU addresses an additional
+.I ifname
+(interface name) has to be provided accompanying the IPv6 address:
+.IR fe80::1%eth0 .
+The operating systems rather uses
+.I scope_id
+as index for
+.IR ifname .
+
+For global IPv6 and ULA addresses
+.I ifname
+can be set to
+.IR 0 .
+Since IPv4 addresses on any interface are always unique,
+simply use
+.I 0
+for all cases.
+.SH BACKGROUND
+Qlib's socket routines provide an easy API to setup
+TCP or UDP connections over IPv4 or IPv6 networks. Together with
+Qlib's IP address parsing capabilities, a set of high-level
+socket routines allow a common IPv4/IPv6 handling.
+.SH SOCKET FILES
+.TP 5
+.B socket_bind.c
+bind to or reuse the local IPv4/IPv6 address and port
+for a socket connection
+.TP 5
+.B socket_connect.c
+attempts to setup a TCP or UDP client connection
+.TP 5
+.B socket_info.c
+get local/remote IPv4/IPv6 address of socket
+.TP 5
+.B socket_recv.c
+set up a receiving IPv4/IPv6 connection
+.TP 5
+.B socket_send.c
+send UDP datagram over a IPv4 or IPv6 connection
+.TP 5
+.B socket_setup.c
+listen to and accept an IPv4/IPv6 TCP socket connection
+.TP 5
+.B socket_tcp.c
+create a non-blocking TCP stream socket
+.TP 5
+.B socket_udp.c
+create a non-blocking UDP datagram socket
+.SH USAGE
+Most of the above files include their IPv4 and
+IPv6 counterparts together with a combined usage
+requiring in addition a
+.I scope_id
+or simply
+.IR 0 .
+IPv4 addresses are usually converted upon reading to
+IPv4-mapped IPv6 addresses using Qlib's IP address
+parsing functions.
+IPv4 and IPv6 socket calls - if required -
+need to be distinguished
+by the calling routines testing
+
+.EX
+ ip6_isv4mapped(ip)
+.EE
+
+Otherwise, the unified IPv6/IPv4 versions will be used.
+.SH "SEE ALSO"
+socket_bind(3),
+socket_connect(3),
+socket_info(3),
+socket_recv(3),
+socket_send(3),
+socket_setup(3),
+socket_tcp(3),
+socket_udp(3)
diff --git a/man/socket_info.3 b/man/socket_info.3
new file mode 100644
index 0000000..899a7cb
--- /dev/null
+++ b/man/socket_info.3
@@ -0,0 +1,47 @@
+.TH socket_info 3
+.SH NAME
+socket_info \- retrieving IP connection information for an existing socket
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_local\fP(int \fIs\fR,char \fIip\fR[16],
+ uint16 *\fIport\fR,uint32 *\fIscope_id\fR);
+
+int \fBsocket_remote\fP(int \fIs\fR,char \fIip\fR[16],
+ uint16 *\fIport\fR,uint32 *\fIscope_id\fR);
+.SH DESCRIPTION
+.B socket_local
+returns the local IPv6 or IPv4-mapped IPv6 address as \fIip\fR,
+port as \fIport\fR and perhaps the scope_id as \fIscope_id\fR for the UDP or TCP socket.
+
+.B socket_remote
+returns the remote IPv6 or IPv4-mapped IPv6 address as \fIip\fR,
+port as \fIport\fR and perhaps the scope_id as \fIscope_id\fR for the UDP or TCP socket.
+
+.SH "RETURN CODES"
+If something goes wrong,
+.B socket_local
+and
+.B socket_remote
+return
+.IR -1 ,
+setting
+.I errno
+appropriately.
+.SH EXAMPLE
+#include <socket_if.h>
+ int \fIs\fR;
+ char \fIip\fR[16];
+ uint16 \fIport\fR;
+
+ if (socket_remote(s,ip,&port,0) == -1)
+ err_tmp("",111,"unable to get remote address: ");
+.SH "SEE ALSO"
+socket_if(3),
+socket_bind(3),
+socket_connect(3),
+socket_recv(3),
+socket_send(3),
+socket_setup(3),
+socket_tcp(3),
+socket_udp(3)
diff --git a/man/socket_recv.3 b/man/socket_recv.3
new file mode 100644
index 0000000..bebef08
--- /dev/null
+++ b/man/socket_recv.3
@@ -0,0 +1,56 @@
+.TH qlibs: socket_recv 3
+.SH NAME
+socket_recv \- receive UDP datagrams over IPv4/IPv6 connections
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_recv\fP(int \fIs\fR, const char *\fIbuf\fR, unsigned int \fIlen\fR,
+ const char \fIip\fR[16], uint16 *\fIport\fR, uint32 *\fIscope_id\fR);
+.SH DESCRIPTION
+.B socket_recv
+reads \fIlen\fR bytes starting at \fIbuf\fR in a UDP datagram
+over the socket \fIs\fR while providing information about the
+remote IP address \fIip\fR and the UDP \fIport\fR and
+the perhaps \fIscope_id\fR of the receiving interface.
+
+You can call
+.B socket_recv
+without calling
+.BR socket_bind .
+This has the effect as first calling
+.B socket_bind
+with IP address :: and port 0.
+.SH RETURN VALUE
+.B socket_recv
+returns
+.IR 0 ,
+otherwise
+.I -1
+and sets
+.I errno
+appropriately.
+.SH EXAMPLE
+ #include <socket_if.h>
+ #include <ip.h>
+
+ int \fIs\fR;
+ char \fIlocalip\fR[16];
+ char \fIremoteip\fR[16];
+ uint16 \fIp\fR, \fIport\fR;
+ uint32 \fIscope_id\fR;
+ unsigned int \fIlen\fR;
+ int \fIr\fR;
+ char buf[MTUSIZE+1];
+
+ s = socket_udp();
+ socket_bind_reuse(s,localip,p,0);
+ r = socket_recv(s,buf,len,remoteip,&port,&scope_id);
+.SH SEE ALSO
+socket_if(3),
+socket_info(3),
+socket_bind(3),
+socket_connect(3)
+socket_send(3),
+socket_setup(3),
+socket_tcp(3),
+socket_udp(3)
diff --git a/man/socket_send.3 b/man/socket_send.3
new file mode 100644
index 0000000..cf74300
--- /dev/null
+++ b/man/socket_send.3
@@ -0,0 +1,76 @@
+.TH qlibs: socket_send 3
+.SH ROUTINES
+socket_send \- sending data over a UDP socket
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_send4\fP(int \fIs\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR,
+ const char \fIip\fR[4],uint16 \fIport\fR);
+.br
+int \fBsocket_send6\fP(int \fIs\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR,
+ const char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR);
+.br
+int \fBsocket_send\fP(int \fIs\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR,
+ const char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR);
+.SH DESCRIPTION
+.B socket_send4
+sends \fIlen\fR bytes starting at \fIbuf\fR in a UDP
+datagram over the socket \fIs\fR to UDP port \fIport\fR on IPv4 address
+\fIip\fR.
+
+.B socket_send6
+sends \fIlen\fR bytes starting at \fIbuf\fR in a UDP datagram
+over the socket \fIs\fR to UDP port \fIport\fR on IPv6 address \fIip\fR and perhaps
+using \fIscope_id\fR as outging interface.
+
+For link-local IPv6 (LLU) addresses \fIscope_id\fR specifies the outgoing
+interface index.
+.I socket_id
+can be queried for the given name of the interface (e.g. "eth0") by means of
+.BR socket_getifidx .
+\fIscope_id\fR should normally be set to 0 except for link local IPv6 addresses
+
+.B socket_send
+sends \fIlen\fR bytes starting at \fIbuf\fR in a UDP datagram
+over the socket \fIs\fR to UDP port \fIport\fR on IP address \fIip\fR and perhaps
+using \fIscope_id\fR as outging interface.
+
+You can call
+.B socket_send*
+without calling
+.BR socket_bind* .
+This has the effect as first calling
+.B socket_bind4
+with IP address 0.0.0.0 and port 0
+or
+.B socket_bind6
+with IP address :: and port 0.
+.SH RETURN VALUE
+.B socket_send*
+returns
+.I 0
+if the datagram was sent successfully. If not,
+it returns
+.I -1
+and sets
+.I errno
+appropriately.
+.SH EXAMPLE
+ #include <socket_if.h>
+
+ int \fIs\fR;
+ char \fIip\fR[4];
+ uint16 \fIp\fR;
+
+ s = socket_udp();
+ socket_bind(s,ip,p);
+ socket_send(s,"hello, world",12,ip,p,0);
+.SH "SEE ALSO"
+socket_if(3),
+socket_info(3),
+socket_bind(3),
+socket_connect(3),
+socket_recv(3),
+socket_setup(3),
+socket_tcp(3),
+socket_udp(3)
diff --git a/man/socket_setup.3 b/man/socket_setup.3
new file mode 100644
index 0000000..639b28b
--- /dev/null
+++ b/man/socket_setup.3
@@ -0,0 +1,105 @@
+.TH qlibs: socket_setup 3
+.SH NAME
+socket_setup \- listen to or accept socket for incoming TCP connections
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_listen\fP(int \fIs\fR,int \fIn\fR);
+
+int \fBsocket_accept\fP(int \fIs\fR,char \fIip\fR[16],
+ uint16 *\fIport\fR,uint32 *\fIscope_id\fR);
+
+int \fBsocket_ipoptionskill\fR(int \fIs\fR);
+
+int \fBsocket_ip6anycast(\fR(int \fIs\fR);
+
+int \fBsocket_dualstack\fR(int \fIs\fR);
+
+int \fBsocket_nodualstack\fR(int \fIs\fR);
+.SH DESCRIPTION
+.B socket_listen
+prepares TCP socket \fIs\fR to accept TCP connections.
+It allows a backlog of approximately \fIn\fR TCP SYNs.
+(On systems supporting SYN cookies, the backlog is irrelevant.)
+
+.B socket_accept
+accepts the connection. It creates a new socket
+for the connection and returns a file descriptor pointing to the new
+socket; you can use the read and write system calls to transmit data
+through that file descriptor.
+Further, it provides information about client's
+\fIip\fR address and TCP \fIport\fR number
+perhaps together with local receiving interface \fIscope_id\fR.
+
+.B socket_ipoptionskill
+is used to disable previously defined options in IPv4 or IPv6 packets
+like Source Routing prior of using this socket for data exchange.
+.B socket_ipoptionskill
+uses the
+.BR setsockopt .
+
+.B socket_ip6anycast
+enables unspecified reversed anycasting on the listening socket
+.IR s
+with IPv6 address
+.IR :: .
+Upon receiving IPv6 packets, the socket records the
+incoming IPv6 address and the receiving \fIscope_id\fR
+in order provide additional routing information.
+
+.B socket_dualstack
+and
+.B socket_nodualstack
+can be used to force or forbid dual-stack behavior
+setting the
+.B setsockopt
+variable
+.I IPV6_V6ONLY
+appropriately. In the last case, a potential servers
+needs two instances to accept incoming IPv6 and IPv6 packets.
+.SH "RETURN CODES"
+Normally
+.BR socket_listen ,
+.B socket_accept
+and
+.B socket_ipotionskill
+as well as
+.B socket_dualstack
+and
+.B socket_nodualstack
+return
+.I 0
+and if anything goes wrong it returns
+.IR -1 ,
+setting
+.I errno
+appropriately.
+.SH EXAMPLE
+ #include <socket_if.h>
+
+ int \fIs\fR, \fIt\fR;
+ int \fIr\fR;
+ char \fIip\fR[16];
+ uint16 \fIp\fR;
+
+ if ((s = socket_tcp()) == -1)
+ err_tmp("",111,"unable to create TCP socket: ");
+ r = socket_ipoptionskill(s);
+ if (socket_bind_reuse(s,(char *)V6localnet,8002,0) == -1)
+ err_tmp("",111,"unable to bind: ");
+ if (socket_listen(s,1) == -1)
+ err_tmp("",111,"unable to listen: ");
+
+ t = socket_tcp();
+ socket_bind(t,ip,p,0);
+ socket_listen(s,16);
+ socket_accept(t,ip,&p,&scope_id);
+.SH "SEE ALSO"
+socket_if(3),
+socket_bind(3),
+socket_connect(3),
+socket_info(3),
+socket_recv(3),
+socket_send(3),
+socket_tcp(3),
+socket_udp(3)
diff --git a/man/socket_tcp.3 b/man/socket_tcp.3
new file mode 100644
index 0000000..3616ed7
--- /dev/null
+++ b/man/socket_tcp.3
@@ -0,0 +1,58 @@
+.TH qlibs: socket_tcp 3
+.SH NAME
+socket_tcp \- setting up TCP sockets
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_tcp4\fP();
+.br
+int \fBsocket_tcp6\fP();
+.br
+int \fBsocket_tcp\fP();
+.SH DESCRIPTION
+.B socket_tcp4
+creates a non-blocking TCP/IPv4 stream socket and
+providing a file descriptor pointing to that socket.
+
+.B socket_tcp6
+creates a non-blocking TCP/IPv6 stream socket and
+providing a file descriptor pointing to that socket.
+
+.B socket_tcp
+creates a non-blocking IPv6 TCP/IP socket calling
+.B socket_tcp6
+unless it can't bind to IPv6 and now facilitating
+.BR socket_tcp4 .
+.SH "REMOTE CODES"
+All these routines return
+.IR 0
+except in case of failures, returning
+.I -1
+and setting
+.I errno
+appropriately, without allocating any resources.
+.SH EXAMPLE
+ #include <socket_if.h>
+
+ int \fIt\fR;
+ char \fIlocalip\fR[16];
+ char \fIremoteip\fR[16];
+ uint16 \fIp\fR;
+
+ if (ip6_isv4mapped(remoteip)) {
+ t = socket_tcp4();
+ socket_bind4(t,localip + 12,0);
+ } else {
+ t = socket_tcp6();
+ socket_bind6(t,localip,0,0);
+ }
+ socket_connect(s,remoteip,p,0);
+.SH "SEE ALSO"
+socket_if(3),
+socket_bind(3),
+socket_connect(3),
+socket_info(3),
+socket_recv(3),
+socket_send(3),
+socket_setup(3),
+socket_udp(3)
diff --git a/man/socket_udp.3 b/man/socket_udp.3
new file mode 100644
index 0000000..eff0290
--- /dev/null
+++ b/man/socket_udp.3
@@ -0,0 +1,58 @@
+.TH qlibs: socket_udp 3
+.SH NAME
+socket_udp \- setting up UDP datagram sockets
+.SH SYNTAX
+.B #include \(dqsocket_if.h\(dq
+
+int \fBsocket_udp4\fP();
+.br
+int \fBsocket_udp6\fP();
+.br
+int \fBsocket_udp\fP();
+.SH DESCRIPTION
+.B socket_udp4
+creates a non-blocking UDP/IPv4 datagram socket and
+providing a file descriptor pointing to that socket.
+
+.B socket_udp6
+creates a non-blocking UDP/IPv6 datagram socket and
+providing a file descriptor pointing to that socket.
+
+.B socket_udp
+creates a non-blocking IPv6 UDP socket calling
+.B socket_udp6
+unless it can't bind to IPv6 and now facilitating
+.BR socket_tcp4 .
+
+.SH "REMOTE CODES"
+All these routines return
+.IR 0
+except in case of failures, returning
+.I -1
+and setting
+.I errno
+appropriately, without allocating any resources.
+.SH EXAMPLE
+ #include <socket_if.h>
+ int \fIu\fR;
+ char \fIlocalip\fR[16];
+ char \fIremoteip\fR[16];
+ uint16 \fIp\fR; // port
+
+ if (ip6_isv4mapped(remoteip)) {
+ u = socket_udp4();
+ socket_bind4(u,localip + 12,p);
+ } else
+ u = socket_udp6();
+ socket_bind6(u,localip,p,0);
+ }
+ socket_connect(u,remoteip,p,0);
+.SH "SEE ALSO"
+socket_if(3),
+socket_bind(3),
+socket_connect(3),
+socket_info(3),
+socket_recv(3),
+socket_send(3),
+socket_setup(3),
+socket_tcp(3)
diff --git a/man/str.3 b/man/str.3
new file mode 100644
index 0000000..c8643ed
--- /dev/null
+++ b/man/str.3
@@ -0,0 +1,61 @@
+.TH qlibs: str 3
+.SH NAME
+str \- string evaluation and comparision (for allocated strings)
+.SH SYNTAX
+.B #include \(dqstr.h\(dq
+
+unsigned int \fBstr_copy\fR(char *\fIdst\fR, const char *\fIsrc\fR);
+.br
+unsigned int \fBstr_copyb\fR(char *\fIdst\fR, const char *\fIsrc\fR, unsigned int \fIlen\fR);
+.br
+unsigned int \fBstr_chr\fR(const char *\fIs\fR, int \fIc\fR);
+.br
+unsigned int \fBstr_rchr\fR(const char *\fIs\fR, int \fIc\fR);
+.br
+unsigned int \fBstr_diff\fR(const char *\fIs\fR, const char *\fIt\fR);
+.br
+unsigned int \fBstr_diffn\fR(const char *\fIs\fR, const char *\fIt\fR, unsigned int \fIlen\fR);
+.br
+unsigned int \fBstr_equal\fR(const char *\fIs\fR, const char *\fIt\fR);
+.br
+unsigned int \fBstr_len\fP(const char *\fIs\fR);
+.br
+unsigned int \fBstr_starts\fP(const char *\fIdst\fR, const char *\fIsrc\fR);
+.br
+char *\fBstr_append\fP(char *\fIdst\fR, const char *\fIs\fR);
+.SH DESCRIPTION
+\fBstr_copy\fR copies \fIsrc\fR into \fIdst\fR up to the first occurance of \\0 while including it.
+It returns the number of bytes written.
+If \fIdst\fR is smaller than \fIsrc\fR, \fBstr_copy\fR fills up \fIdst\fR
+to its allocated size and returns the number of bytes it would have be written.
+\fBstr_copyb\fR copies \fIlen\fR bytes from \fIsrc\fR into \fIdst\fR.
+
+\fBstr_chr\fR and \fBstr_rchr\fR searches for a single character in a string
+(from left-to-right or from right-to-left) and returns the position of
+the \fIfirst\fR occurrance of \fIc\fR or the length \fIlen\fR of \fIs\fR.
+
+\fBstr_len\fR determines the length of a string. It returns the position
+of the first occurance of \\0 in \fIs\fR.
+
+\fBstr_diff\fR, \fBstr_diffn\fR, and \fBstr_equal\fR compare two strings.
+\fBstr_diff\fR and \fBstr_diffn\fR returns negative, zero or positive, depending
+whether the string \fIs\fR[0], \fIs\fR[1], ..., \fIs\fR[n]=='\\0' is
+ASCII lexicographically smaller than, equal to, or greater than the string
+\fIt\fR[0], \fIt\fR[1], ..., \fIt\fR[m-1]=='\\0'. If the strings are different,
+both functions do not read bytes past the first difference.
+\fBstr_diffn\fR considers the strings equal if the first \fIn\fR characters match.
+
+\fBstr_equal\fR returns 1, if \fIs\fR and \fIt\fR match up to and including the
+first occurance of \\0 or returns 0 otherwise. It is the opposite of \fBstr_diff\fR.
+
+\fBstr_start\fR compares the two strings from the begining.
+It returns 1 if \fIt\fR is a prefix of \fIs\fR or 0 otherwise.
+
+\fB*str_append\fR appends a single byte \fIs\fR to \fIout\fR given \fIout\fR
+is allocated with enough space.
+.SH "NOTE"
+For the \fBstr_copy*\fR and \fB*str_append\fP commands \fIdst\fR
+has to have a preallocated space.
+Otherwise the result may be unexpected.
+.SH "SEE ALSO"
+byte(3), stralloc(3)
diff --git a/man/stralloc.3 b/man/stralloc.3
new file mode 100644
index 0000000..55da25d
--- /dev/null
+++ b/man/stralloc.3
@@ -0,0 +1,160 @@
+.TH qlibs stralloc 3
+.SH NAME
+stralloc \- dynamically allocated strings
+.SH SYNTAX
+.B #include \(dqstralloc.h\(dq
+
+int \fBstralloc_ready\fP(&\fIsa\fR,\fIlen\fR);
+.br
+int \fBstralloc_readyplus\fP(&\fIsa\fR,\fIlen\fR);
+
+int \fBstralloc_copy\fP(&\fIsa\fR,&\fIsa2\fR);
+.br
+int \fBstralloc_copys\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_copyb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fBstralloc_cat\fP(&\fIsa\fR,&\fIsa2\fR);
+.br
+int \fBstralloc_cats\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_catb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fBstralloc_append\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_0\fP(&\fIsa\fR);
+
+int \fBstralloc_starts\fP(&\fIsa\fR,\fIbuf\fR);
+
+stralloc \fIsa\fR = {0};
+.br
+stralloc \fIsa2\fR = {0};
+.br
+unsigned int \fIlen\fR;
+.br
+char *\fIbuf\fR;
+.SH DESCRIPTION
+A
+.B stralloc
+variable holds a string in dynamically allocated space.
+String length is limited only by memory.
+String contents are unrestricted.
+
+The
+.B stralloc
+structure has three components:
+.I sa\fB.s
+is a pointer to the string, or 0 if it is not allocated;
+.I sa\fB.len
+is the number of bytes in the string, if it is allocated;
+.I sa\fB.a
+is the number of bytes allocated for the string, if it is allocated.
+A
+.B stralloc
+variable should be initialized to {0},
+meaning unallocated.
+
+.B stralloc_ready
+makes sure that
+.I sa
+has enough space allocated for
+.I len
+characters.
+It allocates extra space if necessary.
+
+.B stralloc_readyplus
+makes sure that
+.I sa
+has enough space allocated for
+.I len
+characters more than its current length.
+If
+.I sa
+is unallocated,
+.B stralloc_readyplus
+is the same as
+.BR stralloc_ready .
+
+.B stralloc_copy
+copies
+.I sa2
+to
+.IR sa ,
+allocating space if necessary.
+Here
+.I sa2
+is an allocated
+.B stralloc
+variable.
+
+.B stralloc_copys
+copies a 0-terminated string,
+.IR buf ,
+to
+.IR sa ,
+without the 0.
+
+.B stralloc_copyb
+copies
+.I len
+characters from
+.I buf
+to
+.IR sa .
+
+.B stralloc_cat
+appends
+.I sa2
+to
+.IR sa ,
+allocating space if necessary.
+If
+.I sa
+is unallocated,
+.B stralloc_cat
+is the same as
+.BR stralloc_copy .
+
+.B stralloc_cats
+and
+.B stralloc_catb
+are analogous to
+.B stralloc_copys
+and
+.BR stralloc_copyb .
+
+.B stralloc_append
+adds a single character,
+.IR *buf ,
+to
+.IR sa ,
+allocating space if necessary.
+
+.B stralloc_0
+adds a single 0 character
+to
+.IR sa .
+
+.B stralloc_starts
+returns 1 if the 0-terminated string
+.IR buf ,
+without the 0,
+is a prefix of
+.IR sa .
+.SH "ERROR HANDLING"
+If a
+.B stralloc
+routine runs out of memory,
+it leaves
+.I sa
+alone and returns 0,
+setting
+.B errno
+appropriately.
+On success it returns 1;
+this guarantees that
+.I sa
+is allocated.
+.SH "SEE ALSO"
+alloc(3),
+error(3)
diff --git a/man/taia.3 b/man/taia.3
new file mode 100644
index 0000000..0aeb4e0
--- /dev/null
+++ b/man/taia.3
@@ -0,0 +1,91 @@
+.TH taia 3
+.SH NAME
+taia \- Temps Atomique International (Attosecond) time routines
+.SH SYNTAX
+.B #include \(dqtaia.h\(dq
+
+extern int \fBtaia_add\fP(struct taia *\fIt\fR,const struct taia *\fIa\fR,const struct taia *\fIb\fR);
+.br
+extern int \fBtaia_addsec\fP(struct taia *\fIt\fR,const struct taia *\fIs\fR,int \fIsecs\fR);
+.br
+extern int \fBtaia_approx\fP(const struct taia *\fIt\fR);
+.br
+int \fBtaia_frac\fP(const struct taia *\fIt\fR);
+.br
+extern int \fBtaia_less\fP(const struct tai *\fIa\fR,const struct tai *\fIb\fR);
+.br
+extern int \fBtaia_now\fP(struct taia *\fIt\fR);
+.br
+extern int \fBtaia_pack\fP(char *buf,const struct taia *\fIt\fR);
+.br
+extern int \fBtaia_sub\fP(struct taia *\fIt\fR,const struct taia *\fIa\fR,const struct taia *\fIb\fR);
+.br
+extern int \fBtaia_tai\fP(const struct taia *\fIt\fR,struct tai *\fIsec\fR);
+.br
+extern int \fBtaia_uint\fP(struct taia *\fIt\fR,unsigned int \fIsecs\fR);
+.br
+extern int \fBtaia_unpack\fP(const char *buf,struct taia *\fIt\fR);
+.SH DESCRIPTION
+.B taia_add
+adds \fIa\fR to \fIb\fR and writes the result to \fIt\fR.
+The inputs and output may overlap.
+
+.B taia_addsec
+adds \fIsecs\fR seconds to \fIs\fR and writes the result to \fIt\fR.
+The inputs and output may overlap.
+
+.B taia_approx
+returns a double-precision approximation of \fIt\fR.
+The result of
+.B taia_approx
+is always nonnegative.
+
+.B taia_frac
+returns a double-precision approximation to the fraction part
+of \fIt\fR. The result of
+.B taia_frac
+is always nonnegative.
+
+.B taia_less
+returns 1 if \fIa\fR is less than \fIb\fR, 0 otherwise.
+
+.B taia_now
+puts the current time into \fIt\fR. More precisely:
+.B tai_now
+puts into \fIt\fR its best guess as to the TAI64NA label
+for the 1-attosecond interval that contains the current time.
+
+This implementation of taia_now assumes that the
+.I time_t
+returned from the time function represents the number of TAI seconds since 1970-01-01
+00:00:10 TAI. This matches the convention used by the Olson tz library
+in ``right'' mode.
+
+Beware that many clocks are not set accurately, and even the best
+scientific clocks are nowhere near 1-attosecond accuracy; however, an
+inaccurate clock may still produce reasonably accurate time differences.
+
+.B taia_pack
+converts a TAI64NA label from internal format in \fIt\fR to external
+TAI64NA format in \fIbuf\fR.
+
+.B taia_sub
+subtracts \fIb\fR from \fIa\fR and writes the result to \fIt\fR.
+The inputs and output may overlap.
+
+.B taia_tai
+places into \fIsec\fR the integer part of \fIt\fR. If \fIt\fR
+contains a TAI64NA label then \fIsec\fR will contain the corresponding
+TAI64 label.
+
+.B taia_uint
+converts \fIsecs\fR into a struct taia (setting the fractional part to zero).
+
+.B taia_unpack
+converts a TAI64NA label from external TAI64NA format in
+\fIbuf\fR to internal format in \fIt\fR.
+.SH "SEE ALSO"
+time(1),
+utime(3),
+ctime(3),
+locale(1)
diff --git a/man/timeout.3 b/man/timeout.3
new file mode 100644
index 0000000..6113f27
--- /dev/null
+++ b/man/timeout.3
@@ -0,0 +1,36 @@
+.TH qlibs: timeout 3
+.SH NAME
+timeout \- reading from and writing to a file descriptor
+.SH SYNTAX
+.B #include \(dqtimeout.h\(dq
+
+int \fBtimeoutread\fP(int \fIt\fR,int \fIfd\fR,char *\fIbuf\fR,int \fIlen\fR);
+.br
+int \fBtimeoutwrite\fP(int,int,char *,int);
+.SH DESCRIPTION
+.B timeoutread
+reads from file descriptor
+.I fd
+.I len
+bytes into
+.I buf
+returning
+.I 0
+on success and
+.I -1
+in case a timeout has occured.
+.B timeoutwrite
+writes to file descriptor
+.I fd
+.I len
+bytes from
+.I buf
+returning
+.I 0
+on success and
+.I -1
+in case a timeout has occured.
+.SH "SEE ALSO"
+wait(3),
+timeoutconn(3)
+taia(3).
diff --git a/man/timeoutconn.3 b/man/timeoutconn.3
new file mode 100644
index 0000000..dad9e95
--- /dev/null
+++ b/man/timeoutconn.3
@@ -0,0 +1,52 @@
+.TH qlibs: timeoutconn 3
+.SH NAME
+timeoutconn \- set up an outoing stream socket for IPv4/IPv6
+.SH SYNTAX
+.B #include \(dqtimeoutconn.h\(dq
+
+int \fBtimeoutconn4\fP(int \fIs\fR,char \fIip[4]\fR,uint16 \fIp\fR,unsigned int \fIt\fR);
+.br
+int \fBtimeoutconn6\fP(int \fIs\fR,char \fIip[16]\fR,uint16 \fIp\fR,unsigned int \fIt\fR,uint32 \fInetif\fR);
+.br
+int \fBtimeoutconn\fP(int \fIs\fR,char \fIip[16]\fR,uint16 \fIp\fR,unsigned int \fIt\fR,uint32 \fInetif\fR);
+.SH DESCRITPION
+.B timeoutconn
+tries to establish a TCP stream socket to address
+.I ip
+on remoteport
+.I p
+and waiting for
+.I t
+seconds and on success returns the
+.I s
+socket identifier.
+
+In case
+.I addresss
+is an IPv6 LLU address, one needs to define the
+.I netif
+interface index, otherwise
+.I 0
+is the default.
+.SH INTERNALS
+.B timeoutconn
+depends on
+.B taia
+and
+.B iopuase
+to determine the timespan and perhaps
+.B ndelay
+to succeed.
+.SH RETURN CODES
+.B timeoutconn
+returns
+.I 0
+if the connection is established
+and
+.I -1
+if an error has occured.
+.SH SEE ALSO
+socket_if(3),
+timeout(3)
+taia(3),
+wait(3)
diff --git a/man/wait.3 b/man/wait.3
new file mode 100644
index 0000000..c3b6c63
--- /dev/null
+++ b/man/wait.3
@@ -0,0 +1,96 @@
+.TH qlibs: wait 3
+.SH NAME
+wait \- check child process status
+.SH SYNTAX
+.B #include \(dqwait.h\(dq
+
+int \fBwait_nohang\fP(&\fIwstat\fR);
+.br
+int \fBwait_stop\fP(&\fIwstat\fR);
+.br
+int \fBwait_stopnohang\fP(&\fIwstat\fR);
+.br
+int \fBwait_pid\fP(&\fIwstat\fR,\fIpid\fR);
+
+int \fBwait_exitcode\fP(\fIwstat\fR);
+.br
+int \fBwait_crashed\fP(\fIwstat\fR);
+.br
+int \fBwait_stopped\fP(\fIwstat\fR);
+.br
+int \fBwait_stopsig\fP(\fIwstat\fR);
+
+int \fIpid\fR;
+.br
+int \fIwstat\fR;
+.SH DESCRIPTION
+.B wait_nohang
+looks for zombies (child processes that have exited).
+If it sees a zombie,
+it eliminates the zombie,
+puts the zombie's exit status into
+.IR wstat ,
+and returns the zombie's process ID.
+If there are several zombies,
+.B wait_nohang
+picks one.
+If there are children but no zombies,
+.B wait_nohang
+returns
+.IR 0 .
+If there are no children,
+.B wait_nohang
+returns
+.IR -1 ,
+setting
+.B errno
+appropriately.
+
+.B wait_stopnohang
+is similar to
+.BR wait_nohang ,
+but it also looks for children that have stopped.
+
+.B wait_stop
+is similar to
+.BR wait_stopnohang ,
+but if there are children it will pause waiting for one of them
+to stop or exit.
+
+.B wait_pid
+waits for child process
+.I pid
+to exit.
+It eliminates any zombie that shows up in the meantime,
+discarding the exit status.
+
+.B wait_stop
+and
+.B wait_pid
+retry upon
+.BR error_intr .
+.SH "STATUS PARSING"
+If the child stopped,
+.B wait_stopped
+is nonzero;
+.B wait_stopsig
+is the signal that caused the child to stop.
+
+If the child exited by crashing,
+.B wait_stopped
+is zero;
+.B wait_crashed
+is nonzero.
+
+If the child exited normally,
+.B wait_stopped
+is zero;
+.B wait_crashed
+is zero;
+and
+.B wait_exitcode
+is the child's exit code.
+.SH "SEE ALSO"
+pathexec(3),
+wait(2),
+error(3)
diff --git a/man/x.html b/man/x.html
new file mode 100644
index 0000000..224edc1
--- /dev/null
+++ b/man/x.html
@@ -0,0 +1,99 @@
+<HTML>
+<BODY>
+<PRE>
+<!-- Manpage converted by man2html 3.0.1 -->
+SYNTAX
+ #include "logmsg.h"
+
+ int logmsg(const char *who, int ecode, unsigned int classs, char *msg)
+
+ err_sys(w,e) logmsg(w,e,FATAL,"")
+ err_sys_plus(w,e,m) logmsg(w,e,FATAL,m)
+ err_tmp(w,e) logmsg(w,e,WARN,"")
+ err_tmp_plus(w,e,m) logmsg(w,e,WARN,m)
+ err_int(w,e,c) logmsg(w,e,c,"")
+ err_int_plus(w,e,c,m) logmsg(w,e,c,m)
+ log_who(w,m) logmsg(w,0,LOG,m)
+ log_anon(m) logmsg("",0,LOG,m)
+
+DESCRIPTION
+ logmsg prints error, warning, or info/logging messages to stderr and
+ potentially terminates the calling program, depending on the class
+ given. who is the name of the program, ecode is an error code, class
+ determines the behavior upon call and msg is the logging message. Read
+ "error.h" to learn more about related constants.
+
+ECODE
+ ecode is the error code and subject to be displayed in the log file and
+ potentially used upon exit if the class equals ERROR, FATAL, or DROP.
+
+ To avoid conflicts with syscall error codes, appplication defined error
+ codes should be negative. The values -15, -100 and -111 are reserved
+ for backward compatibility.
+
+CLASS
+ The class parameter indicates how the application handles exceptions
+ and displays the log message.
+
+ o LOG, INFO, ALERT, WARN - display message and continue operation
+
+ o DROP - display warning message and continue while returning to the
+ calling program
+
+ o USAGE, SYNTAX, FATAL, ERROR - display error message and exit appli‐
+ cation with error code
+
+ INFO, ALERT, WARN, DROP, USAGE, and FATAL as well as ERROR display the
+ respective class string like warning: in the log message, while LOG
+ shows the log message only.
+
+ The class FATAL should be used for system error codes only, rather ER‐
+ ROR and WARN shall be set in conjunction with an application er‐
+ ror/warning.
+
+MESSAGE
+ If the custom message msg is given, it will be printed additionally.
+
+ Dan Bernstein used sets of strerr_dieY*() and strerr_warnY() messages
+ which explicitely determine the message and behavior class. Other
+ classes were occasionally defined on demand, such als usage().
+
+ Kai Peter introduced the errmsg facility in his qlibs including a sys‐
+ log compliant severity as second parameter.
+
+EXAMPLES
+ The macro definitions uses w for the calling program, e for error
+ code, c for class, and m for message.
+
+ #include "logmsg.h"
+ #define WHO "my_prog"
+
+ err_sys(WHO,errno);
+ err_sys_plus(WHO,-111,"additional message");
+
+ err_tmp("",-100);
+ err_tmp_plus("",errno,"additional message");
+
+ log_who(WHO,"message");
+
+ log_anon() is like log_who() but doesn't print the caller name.
+
+ An user defined message s can be build from multiple arguments by using
+ the B (build) macro:
+
+ err_sys_plus((errno),B("unable to run: ",*argv));
+
+SEE ALSO
+ <B>syslog(3)</B>
+
+
+
+ 3 qlibs:(logmsg)
+</PRE>
+<HR>
+<ADDRESS>
+Man(1) output converted with
+<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
+</ADDRESS>
+</BODY>
+</HTML>
diff --git a/ndelay.c b/ndelay.c
new file mode 100644
index 0000000..f4b5eb8
--- /dev/null
+++ b/ndelay.c
@@ -0,0 +1,24 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "ndelay.h"
+
+/**
+ @file ndelay.c
+ @author djb
+ @soure qmail
+ @brief delaying of IO operations
+*/
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+int ndelay_on(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
+}
+
+int ndelay_off(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+}
diff --git a/open.c b/open.c
new file mode 100644
index 0000000..c430698
--- /dev/null
+++ b/open.c
@@ -0,0 +1,25 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+/**
+ @file open.c
+ @author djb
+ @source qmail
+ @brief open a file
+*/
+
+int open_append(const char *fn)
+{ return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); }
+
+int open_excl(const char *fn)
+{ return open(fn,O_WRONLY | O_EXCL | O_CREAT,0644); }
+
+int open_read(const char *fn)
+{ return open(fn,O_RDONLY | O_NDELAY); }
+
+int open_trunc(const char *fn)
+{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); }
+
+int open_write(const char *fn)
+{ return open(fn,O_WRONLY | O_NDELAY); }
diff --git a/pathexec.c b/pathexec.c
new file mode 100644
index 0000000..2c1e7d1
--- /dev/null
+++ b/pathexec.c
@@ -0,0 +1,121 @@
+#include <unistd.h>
+#include "alloc.h"
+#include "error.h"
+#include "stralloc.h"
+#include "str.h"
+#include "byte.h"
+#include "env.h"
+#include "pathexec.h"
+
+/**
+ @file pathexec.c
+ @author djb
+ @source ucspi-tcp, ucspi-ssl
+ @brief populate environment after fork
+*/
+
+static stralloc plus;
+static stralloc tmp;
+
+int pathexec_env(const char *s,const char *t)
+{
+ if (!s) return 1;
+ if (!stralloc_copys(&tmp,s)) return 0;
+ if (t) {
+ if (!stralloc_cats(&tmp,"=")) return 0;
+ if (!stralloc_cats(&tmp,t)) return 0;
+ }
+ if (!stralloc_0(&tmp)) return 0;
+ return stralloc_cat(&plus,&tmp);
+}
+
+int pathexec_multienv(stralloc *sa)
+{
+ if (!sa) return 1;
+ return stralloc_cat(&plus,sa);
+}
+
+void pathexec(char * const *argv)
+{
+ char **e;
+ unsigned int elen;
+ unsigned int i;
+ unsigned int j;
+ unsigned int split;
+ unsigned int t;
+
+ if (!stralloc_cats(&plus,"")) return;
+
+ elen = 0;
+ for (i = 0; environ[i]; ++i)
+ ++elen;
+ for (i = 0; i < plus.len; ++i)
+ if (!plus.s[i])
+ ++elen;
+
+ e = (char **) alloc((elen + 1) * sizeof(char *));
+ if (!e) return;
+
+ elen = 0;
+ for (i = 0; environ[i]; ++i)
+ e[elen++] = environ[i];
+
+ j = 0;
+ for (i = 0; i < plus.len; ++i)
+ if (!plus.s[i]) {
+ split = str_chr(plus.s + j,'=');
+ for (t = 0; t < elen; ++t)
+ if (byte_equal(plus.s + j,split,e[t]))
+ if (e[t][split] == '=') {
+ --elen;
+ e[t] = e[elen];
+ break;
+ }
+ if (plus.s[j + split])
+ e[elen++] = plus.s + j;
+ j = i + 1;
+ }
+ e[elen] = 0;
+
+ pathexec_run(*argv,argv,e);
+ alloc_free(e);
+}
+
+void pathexec_run(const char *file,char *const *argv,char *const *envp)
+{
+ char *path;
+ unsigned int split;
+ int savederrno;
+
+ if (file[str_chr(file,'/')]) {
+ execve(file,argv,envp);
+ return;
+ }
+
+ path = env_get("PATH");
+ if (!path) path = "/bin:/usr/bin";
+
+ savederrno = 0;
+ for (;;) {
+ split = str_chr(path,':');
+ if (!stralloc_copyb(&tmp,path,split)) return;
+ if (!split)
+ if (!stralloc_cats(&tmp,".")) return;
+ if (!stralloc_cats(&tmp,"/")) return;
+ if (!stralloc_cats(&tmp,file)) return;
+ if (!stralloc_0(&tmp)) return;
+
+ execve(tmp.s,argv,envp);
+ if (errno != ENOENT) {
+ savederrno = errno;
+ if ((errno != EACCES) && (errno != EPERM) && (errno != EISDIR)) return;
+ }
+
+ if (!path[split]) {
+ if (savederrno) errno = savederrno;
+ return;
+ }
+ path += split;
+ path += 1;
+ }
+}
diff --git a/prot.c b/prot.c
new file mode 100644
index 0000000..907de52
--- /dev/null
+++ b/prot.c
@@ -0,0 +1,31 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+//#include "hasshsgr.h"
+#include "prot.h"
+
+/**
+ @file prot.c
+ @author djb
+ @source qmail
+ @brief setting up uid an gid for OS (short group was for ancient solaris)
+*/
+
+/* XXX: there are more portability problems here waiting to leap out at me */
+
+int prot_gid(int gid)
+{
+//#ifdef HASSHORTSETGROUPS
+// short x[2];
+// x[0] = gid; x[1] = 73; /* catch errors */
+// if (setgroups(1,x) == -1) return -1;
+//#else
+ if (setgroups(1,(gid_t *)&gid) == -1) return -1;
+//#endif
+ return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
+}
+
+int prot_uid(int uid)
+{
+ return setuid(uid);
+}
diff --git a/readclose.c b/readclose.c
new file mode 100644
index 0000000..b0bce7e
--- /dev/null
+++ b/readclose.c
@@ -0,0 +1,43 @@
+#include <unistd.h>
+#include "open.h"
+#include "error.h"
+#include "readclose.h"
+
+/**
+ @file readclose.c
+ @author kp
+ @source qlibs
+ @brief This is the successor of the older 'slurpclose.c' file. The function
+ 'slurpclose' is now called 'readclose_append'. The other function
+ 'readclose' was introduced here initial.
+*/
+
+int readclose_append(int fd,stralloc *sa,unsigned int bufsize)
+{
+ int r;
+ for (;;) {
+ if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+ r = read(fd,sa->s + sa->len,bufsize);
+ if (r == -1) if (errno == EINTR) continue;
+ if (r <= 0) { close(fd); return r; }
+ sa->len += r;
+ }
+}
+
+int readclose(int fd,stralloc *sa,unsigned int bufsize)
+{
+ if (!stralloc_copys(sa,"")) { close(fd); return -1; }
+ return readclose_append(fd,sa,bufsize);
+}
+
+int openreadclose(const char *fn,stralloc *sa,unsigned int bufsize)
+{
+ int fd;
+ fd = open_read((char *) fn);
+ if (fd == -1) {
+ if (errno == ENOENT) return 0;
+ return -1;
+ }
+ if (readclose(fd,sa,bufsize) == -1) return -1;
+ return 1;
+}
diff --git a/scan.c b/scan.c
new file mode 100644
index 0000000..da3d8ad
--- /dev/null
+++ b/scan.c
@@ -0,0 +1,120 @@
+#include "scan.h"
+
+/**
+ @file scan.c
+ @author djb
+ @source qmail, ucspi-tcp
+ @brief scanning/conversion of strings to different variable types
+*/
+
+static long int fromhex(unsigned char c)
+{
+ if (c>='0' && c<='9')
+ return c-'0';
+ else if (c>='A' && c<='F')
+ return c-'A'+10;
+ else if (c>='a' && c<='f')
+ return c-'a'+10;
+ return -1;
+}
+
+unsigned int scan_0x(register const char *s,register unsigned int *u)
+{
+ register unsigned int pos = 0;
+ register unsigned long result = 0;
+ register long int c;
+
+ while ((c = fromhex((unsigned char) (s[pos]))) >= 0) {
+ result = (result << 4) + c;
+ ++pos;
+ }
+ *u = result;
+ return pos;
+}
+
+unsigned int scan_8long(register const char *s,register unsigned long *u)
+{
+ register unsigned int pos = 0;
+ register unsigned long result = 0;
+ register unsigned long c;
+
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 8) {
+ result = result * 8 + c;
+ ++pos;
+ }
+ *u = result;
+ return pos;
+}
+
+unsigned int scan_uint(register const char *s,register unsigned int *u)
+{
+ register unsigned int pos;
+ unsigned long result;
+
+ pos = scan_ulong(s,&result);
+ *u = result;
+ return pos;
+}
+
+unsigned int scan_plusminus(register const char *s,register int *sign)
+{
+ if (*s == '+') { *sign = 1; return 1; }
+ if (*s == '-') { *sign = -1; return 1; }
+ *sign = 1; return 0;
+}
+
+unsigned int scan_long(register const char *s,register long *i)
+{
+ int sign;
+ unsigned long u;
+ register unsigned int len;
+
+ len = scan_plusminus(s,&sign); s += len;
+ len += scan_ulong(s,&u);
+ if (sign < 0) *i = -u; else *i = u;
+ return len;
+}
+
+
+unsigned int scan_ulong(register const char *s,register unsigned long *u)
+{
+ register unsigned int pos = 0;
+ register unsigned long result = 0;
+ register unsigned long c;
+
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
+ result = result * 10 + c;
+ ++pos;
+ }
+ *u = result;
+ return pos;
+}
+
+unsigned int scan_xlong(const char *s,unsigned long *u)
+{
+ register const char *t = s;
+ register int l = 0;
+ register unsigned char c;
+
+ while ((c = fromhex(*t)) < 16) {
+ l = (l<<4)+c;
+ ++t;
+ }
+ *u=l;
+ return t-s;
+}
+
+unsigned int scan_xint(const char *s,unsigned int *i)
+{
+ register const char *t = s;
+ register unsigned int l = 0;
+ register unsigned char c;
+
+ while ((l >> (sizeof(l)*8 - 4)) == 0
+ && (c = (unsigned char)fromhex((unsigned char)*t))<16) {
+ l= (l << 4) + c;
+ ++t;
+ }
+ *i = l;
+ return (unsigned int)(t-s);
+}
diff --git a/seek.c b/seek.c
new file mode 100644
index 0000000..ac761a9
--- /dev/null
+++ b/seek.c
@@ -0,0 +1,30 @@
+#include <sys/types.h>
+#include "seek.h"
+
+/**
+ @file seek.c
+ @author djb
+ @source qmail
+ @brief seek in an open file descritor
+*/
+
+off_t lseek(int fd,off_t offset,int whence);
+int ftruncate(int fd, off_t length);
+
+#define CUR 1 /* sigh */
+
+seek_pos seek_cur(int fd)
+{ return lseek(fd,(off_t) 0,CUR); }
+
+#define END 2 /* sigh */
+
+int seek_end(int fd)
+{ if (lseek(fd,(off_t) 0,END) == -1) return -1; return 0; }
+
+#define SET 0 /* sigh */
+
+int seek_set(int fd,seek_pos pos)
+{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; }
+
+int seek_trunc(int fd,seek_pos pos)
+{ return ftruncate(fd,(off_t) pos); }
diff --git a/sharedlib b/sharedlib
new file mode 100755
index 0000000..3bf4f26
--- /dev/null
+++ b/sharedlib
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec cc -shared ${1+"$@"}
diff --git a/sig.c b/sig.c
new file mode 100644
index 0000000..0963948
--- /dev/null
+++ b/sig.c
@@ -0,0 +1,110 @@
+#include <signal.h>
+#include "sig.h"
+
+/**
+ @file sig.c
+ @author djb
+ @source qmail
+ @brief signal handling functions
+*/
+
+void sig_alarmblock() { sig_block(SIGALRM); }
+void sig_alarmunblock() { sig_unblock(SIGALRM); }
+void sig_alarmcatch(f) void (*f)(); { sig_catch(SIGALRM,f); }
+void sig_alarmdefault() { sig_catch(SIGALRM,SIG_DFL); }
+int sig_alarm = SIGALRM;
+
+void sig_block(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
+}
+
+void sig_unblock(int sig)
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigaddset(&ss,sig);
+ sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
+}
+
+void sig_blocknone()
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+}
+
+void sig_catch(int sig,void (*f)())
+{
+ struct sigaction sa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig,&sa,(struct sigaction *) 0);
+}
+
+void sig_pause()
+{
+ sigset_t ss;
+ sigemptyset(&ss);
+ sigsuspend(&ss);
+}
+
+void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); }
+void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); }
+int sig_pipe = SIGPIPE;
+
+void sig_childblock() { sig_block(SIGCHLD); }
+void sig_childunblock() { sig_unblock(SIGCHLD); }
+void sig_childcatch(f) void (*f)(); { sig_catch(SIGCHLD,f); }
+void sig_childdefault() { sig_catch(SIGCHLD,SIG_DFL); }
+int sig_child = SIGCHLD;
+
+void sig_hangupblock() { sig_block(SIGHUP); }
+void sig_hangupunblock() { sig_unblock(SIGHUP); }
+void sig_hangupcatch(f) void (*f)(); { sig_catch(SIGHUP,f); }
+void sig_hangupdefault() { sig_catch(SIGHUP,SIG_DFL); }
+int sig_hangup = SIGHUP;
+
+void sig_termblock() { sig_block(SIGTERM); }
+void sig_termunblock() { sig_unblock(SIGTERM); }
+void sig_termcatch(f) void (*f)(); { sig_catch(SIGTERM,f); }
+void sig_termdefault() { sig_catch(SIGTERM,SIG_DFL); }
+int sig_term = SIGTERM;
+
+void sig_bugcatch(void (*f)())
+{
+ sig_catch(SIGILL,f);
+ sig_catch(SIGABRT,f);
+ sig_catch(SIGFPE,f);
+ sig_catch(SIGBUS,f);
+ sig_catch(SIGSEGV,f);
+#ifdef SIGSYS
+ sig_catch(SIGSYS,f);
+#endif
+#ifdef SIGEMT
+ sig_catch(SIGEMT,f);
+#endif
+}
+void (*sig_defaulthandler)() = SIG_DFL;
+
+void sig_miscignore()
+{
+ sig_catch(SIGVTALRM,SIG_IGN);
+ sig_catch(SIGPROF,SIG_IGN);
+ sig_catch(SIGQUIT,SIG_IGN);
+ sig_catch(SIGINT,SIG_IGN);
+ sig_catch(SIGHUP,SIG_IGN);
+#ifdef SIGXCPU
+ sig_catch(SIGXCPU,SIG_IGN);
+#endif
+#ifdef SIGXFSZ
+ sig_catch(SIGXFSZ,SIG_IGN);
+#endif
+}
+void (*sig_ignorehandler)() = SIG_IGN;
+
+int sig_cont = SIGCONT;
diff --git a/socket_bind.c b/socket_bind.c
new file mode 100644
index 0000000..b942e20
--- /dev/null
+++ b/socket_bind.c
@@ -0,0 +1,79 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket_if.h"
+#include "ip.h"
+
+/**
+ @file socket_bind.c
+ @author djb, fefe, feh
+ @source qmail, djbdns, ucspi-tcp6
+ @brief binding a socket to a local resource
+*/
+
+int socket_bind4(int s,const char ip[4],uint16 port)
+{
+ struct sockaddr_in sa;
+
+ byte_zero(&sa,sizeof(sa));
+ sa.sin_family = AF_INET;
+ uint16_pack_big((char *)&sa.sin_port,port);
+ byte_copy((char *)&sa.sin_addr,4,ip);
+
+ return bind(s,(struct sockaddr *)&sa,sizeof(sa));
+}
+
+int socket_bind4_reuse(int s,const char ip[4],uint16 port)
+{
+ int opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+ return socket_bind4(s,ip,port);
+}
+
+/* seems not to be used here -- djbdns requires it */
+void socket_tryreservein(int s,int size)
+{
+ while (size >= 1024) {
+ if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size)) == 0) return;
+ size -= (size >> 5);
+ }
+}
+
+int socket_bind6(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+ struct sockaddr_in6 sa;
+
+ byte_zero(&sa,sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ uint16_pack_big((char *)&sa.sin6_port,port);
+/* implicit: sa.sin6_flowinfo = 0; */
+ byte_copy((char *)&sa.sin6_addr,16,ip);
+ sa.sin6_scope_id = scope_id;
+
+ return bind(s,(struct sockaddr *)&sa,sizeof(sa));
+}
+
+int socket_bind6_reuse(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+ int opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+ return socket_bind6(s,ip,port,scope_id);
+}
+
+int socket_bind(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+ if (ip6_isv4mapped(ip))
+ return socket_bind4(s,ip + 12,port);
+
+ return socket_bind6(s,ip,port,scope_id);
+}
+
+int socket_bind_reuse(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+ if (ip6_isv4mapped(ip))
+ return socket_bind4_reuse(s,ip + 12,port);
+
+ return socket_bind6_reuse(s,ip,port,scope_id);
+}
diff --git a/socket_connect.c b/socket_connect.c
new file mode 100644
index 0000000..7b20659
--- /dev/null
+++ b/socket_connect.c
@@ -0,0 +1,66 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include "byte.h"
+#include "socket_if.h"
+#include "ip.h"
+
+/**
+ @file socket_connect.c
+ @author djb, fefe, feh, kp
+ @source qmail, ucscpi-tcp6
+ @brief connection to remote IP host
+*/
+
+int socket_connect6(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+ struct sockaddr_in6 sa;
+
+ byte_zero(&sa,sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ uint16_pack_big((char *)&sa.sin6_port,port);
+ sa.sin6_flowinfo = 0;
+ sa.sin6_scope_id = scope_id;
+ byte_copy((char *)&sa.sin6_addr,16,ip);
+
+ return connect(s,(struct sockaddr *)&sa,sizeof(sa));
+}
+
+/* this explizit declaration is needed to prevent compiler warnings */
+int read(int _str, void *_buf, int _b);
+
+int socket_connect4(int s,const char ip[4],uint16 port)
+{
+ struct sockaddr_in sa;
+
+ byte_zero(&sa,sizeof(sa));
+ sa.sin_family = AF_INET;
+ uint16_pack_big((char *)&sa.sin_port,port);
+ byte_copy((char *)&sa.sin_addr,4,ip);
+
+ return connect(s,(struct sockaddr *)&sa,sizeof(sa));
+}
+
+int socket_connect(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+ if (ip6_isv4mapped(ip))
+ return socket_connect4(s,ip + 12,port);
+
+ return socket_connect6(s,ip,port,scope_id);
+}
+
+int socket_connected(int s)
+{
+ struct sockaddr_in6 sa;
+ int dummy;
+ char ch;
+
+ dummy = sizeof(sa);
+ if (getpeername(s,(struct sockaddr *)&sa,(socklen_t *)&dummy) == -1) {
+ read(s,&ch,1); /* sets errno */
+ return 0;
+ }
+ return 1;
+}
diff --git a/socket_if.c b/socket_if.c
new file mode 100644
index 0000000..f55af4e
--- /dev/null
+++ b/socket_if.c
@@ -0,0 +1,36 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include "socket_if.h"
+
+/**
+ @file socket_if.c
+ @author fefe, feh
+ @source ucspi-tcp6
+ @brief interface handling for LLU
+*/
+
+const unsigned char V4loopback[4] = {127,0,0,1};
+const unsigned char V4localnet[4] = {0,0,0,0};
+/* the 'V4mappedprefix' constant is needed by 'ip.a' too */
+const unsigned char V4mappedprefix[12] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff};
+const unsigned char V6localnet[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
+const unsigned char V6loopback[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1};
+
+uint32 socket_getifidx(const char *ifname)
+{
+ return if_nametoindex(ifname);
+}
+
+static char ifname[IFNAMSIZ];
+
+const char* socket_getifname(uint32 scope_id)
+{
+ char *tmp = if_indextoname(scope_id,ifname);
+ if (tmp)
+ return tmp;
+ else
+ return "[unknown]";
+}
diff --git a/socket_info.c b/socket_info.c
new file mode 100644
index 0000000..e644798
--- /dev/null
+++ b/socket_info.c
@@ -0,0 +1,58 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket_if.h"
+#include "ip.h"
+
+/**
+ @file socket_info.c
+ @author djb, fefe, feh
+ @source ucspi-tcp6
+ @brief querying local and remote info for socket
+*/
+
+int socket_local(int s,char ip[16],uint16 *port,uint32 *scope_id)
+{
+ struct sockaddr_in6 sa;
+ unsigned int dummy = sizeof(sa);
+
+ if (getsockname(s,(struct sockaddr *)&sa,&dummy) == -1) return -1;
+
+ if (sa.sin6_family == AF_INET) {
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)&sa;
+ byte_copy(ip,12,V4mappedprefix);
+ byte_copy(ip+12,4,(char *)&sa4->sin_addr);
+ uint16_unpack_big((char *)&sa4->sin_port,port);
+ if (scope_id) *scope_id = 0;
+ } else {
+ byte_copy(ip,16,(char *)&sa.sin6_addr);
+ uint16_unpack_big((char *)&sa.sin6_port,port);
+ if (scope_id) *scope_id = sa.sin6_scope_id;
+ }
+
+ return 0;
+}
+
+int socket_remote(int s,char ip[16],uint16 *port,uint32 *scope_id)
+{
+ struct sockaddr_in6 sa;
+ unsigned int dummy = sizeof(sa);
+
+ if (getpeername(s,(struct sockaddr *)&sa,&dummy) == -1) return -1;
+
+ if (sa.sin6_family == AF_INET) {
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)&sa;
+ byte_copy(ip,12,V4mappedprefix);
+ byte_copy(ip+12,4,(char *)&sa4->sin_addr);
+ uint16_unpack_big((char *)&sa4->sin_port,port);
+ *scope_id = 0;
+ } else {
+ byte_copy(ip,16,(char *)&sa.sin6_addr);
+ uint16_unpack_big((char *)&sa.sin6_port,port);
+ *scope_id = sa.sin6_scope_id;
+ }
+
+ return 0;
+}
diff --git a/socket_recv.c b/socket_recv.c
new file mode 100644
index 0000000..de8c856
--- /dev/null
+++ b/socket_recv.c
@@ -0,0 +1,39 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "ip.h"
+#include "socket_if.h"
+
+/**
+ @file socket_recv.c
+ @author djb, fefe
+ @source ucspi-tcp6
+ @brief setup receiving socket
+*/
+
+int socket_recv(int s,char *buf,unsigned int len,char ip[16],uint16 *port,uint32 *scope_id)
+{
+ struct sockaddr_in6 sa;
+ unsigned int dummy = sizeof(sa);
+ int r;
+
+ byte_zero(&sa,dummy);
+ r = recvfrom(s,buf,len,0,(struct sockaddr *)&sa,&dummy);
+ if (r == -1) return -1;
+
+ if (sa.sin6_family == AF_INET) {
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa;
+ byte_copy(ip,12,V4mappedprefix);
+ byte_copy(ip+12,4,(char *)&sa4->sin_addr);
+ uint16_unpack_big((char *)&sa4->sin_port,port);
+ if (scope_id) *scope_id = 0;
+ } else {
+ byte_copy(ip,16,(char *)&sa.sin6_addr);
+ uint16_unpack_big((char *)&sa.sin6_port,port);
+ if (scope_id) *scope_id = sa.sin6_scope_id;
+ }
+
+ return r;
+}
diff --git a/socket_send.c b/socket_send.c
new file mode 100644
index 0000000..9f09520
--- /dev/null
+++ b/socket_send.c
@@ -0,0 +1,62 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "ip.h"
+#include "socket_if.h"
+
+/**
+ @file socket_send.c
+ @author djb, fefe, feh
+ @source ucspi-tcp6
+ @brief setup sending socket
+*/
+
+int socket_send4(int s,const char *buf,unsigned int len,const char ip[4],uint16 port)
+{
+ struct sockaddr_in sa;
+
+ byte_zero(&sa,sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ uint16_pack_big((char *)&sa.sin_port,port);
+ byte_copy((char *)&sa.sin_addr,4,ip);
+
+ return sendto(s,buf,len,0,(struct sockaddr *)&sa,sizeof(sa));
+}
+
+int socket_send6(int s,const char *buf,unsigned int len,const char ip[16],uint16 port,uint32 scope_id)
+{
+ struct sockaddr_in6 sa;
+
+ byte_zero(&sa,sizeof(sa));
+
+ sa.sin6_family = AF_INET6;
+ sa.sin6_scope_id = scope_id;
+ uint16_pack_big((char *)&sa.sin6_port,port);
+ byte_copy((char *)&sa.sin6_addr,16,ip);
+
+ return sendto(s,buf,len,0,(struct sockaddr *)&sa,sizeof(sa));
+}
+
+int socket_send(int s,const char *buf,unsigned int len,const char ip[16],uint16 port,uint32 scope_id)
+{
+ if (ip6_isv4mapped(ip))
+ return socket_send4(s,buf,len,ip + 12,port);
+ else
+ return socket_send6(s,buf,len,ip,port,scope_id);
+}
+
+int socket_broadcast4(int s,const char *buf,unsigned int len,uint16 port)
+{
+ struct sockaddr_in sa;
+
+ byte_zero(&sa,sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ uint16_pack_big((char *)&sa.sin_port,port);
+ byte_copy((char *)&sa.sin_addr,4,V4broadcast);
+
+ return sendto(s,buf,len,0,(struct sockaddr *)&sa,sizeof(sa));
+}
diff --git a/socket_setup.c b/socket_setup.c
new file mode 100644
index 0000000..fb65fa2
--- /dev/null
+++ b/socket_setup.c
@@ -0,0 +1,99 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket_if.h"
+#include "ip.h"
+
+/**
+ @file socket_setup.c
+ @author djb, feh
+ @source ucspi-tcp6
+ @brief setup listening socket
+*/
+
+int socket_accept(int s,char ip[16],uint16 *port,uint32 *scope_id)
+{
+ struct sockaddr_in6 sa;
+ unsigned int dummy = sizeof(sa);
+ int fd;
+
+ fd = accept(s,(struct sockaddr *)&sa,&dummy);
+ if (fd == -1) return -1;
+
+ if (sa.sin6_family == AF_INET) {
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)&sa;
+ byte_copy(ip,12,V4mappedprefix);
+ byte_copy(ip+12,4,(char *)&sa4->sin_addr);
+ uint16_unpack_big((char *)&sa4->sin_port,port);
+ if (scope_id) *scope_id = 0;
+ } else {
+ byte_copy(ip,16,(char *)&sa.sin6_addr);
+ uint16_unpack_big((char *)&sa.sin6_port,port);
+ if (scope_id) *scope_id = sa.sin6_scope_id;
+ }
+
+ return fd;
+}
+
+int socket_accept4(int s,char ip[4],uint16 *port)
+{
+ struct sockaddr_in sa;
+ unsigned int dummy = sizeof(sa);
+ int fd;
+
+ fd = accept(s,(struct sockaddr *) &sa,&dummy);
+ if (fd == -1) return -1;
+
+ byte_copy(ip,4,(char *) &sa.sin_addr);
+ uint16_unpack_big((char *) &sa.sin_port,port);
+
+ return fd;
+}
+
+int socket_listen(int s,int backlog)
+{
+ return listen(s,backlog);
+}
+
+int socket_ipoptionskill(int s)
+{
+ int r;
+
+ r = setsockopt(s,IPPROTO_IP,1,(char *) 0,0); /* 1 == IP_OPTIONS */
+ r = setsockopt(s,IPPROTO_IPV6,1,(char *) 0,0);
+
+ return r;
+}
+
+int socket_ip6anycast(int s)
+{
+ int opt = 1;
+ int r;
+
+#ifdef GEN_IP_PKTINFO /* Linux */
+ r = setsockopt(s,IPPROTO_IP,GEN_IP_PKTINFO,&opt,sizeof(opt));
+#elif IP_PKTINFO /* Solaris */
+ r = setsockopt(s,IPPROTO_IP,IP_PKTINFO,&opt,sizeof(opt));
+#elif IP_RECVDSTADDR /* BSD */
+ r = setsockopt(s,IPPROTO_IP,IP_RECVDSTADDR,&opt,sizeof(opt));
+#elif IPV6_RECVDSTADDR
+ r = setsockopt(s,IPPROTO_IPV6,IP_RECVDSTADDR,&opt,sizeof(opt));
+#endif
+ return r;
+}
+
+int socket_dualstack(int s)
+{
+ int opt = 0;
+
+ return setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&opt,sizeof(opt));
+}
+
+int socket_nodualstack(int s)
+{
+ int opt = 1;
+
+ return setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&opt,sizeof(opt));
+}
diff --git a/socket_tcp.c b/socket_tcp.c
new file mode 100644
index 0000000..1ff050e
--- /dev/null
+++ b/socket_tcp.c
@@ -0,0 +1,63 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include "close.h" /* better use unistd.h ? */
+#include "ndelay.h"
+#include "socket_if.h"
+#include "error.h"
+
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT EINVAL
+#endif
+
+/**
+ @file socket_tcp.c
+ @author djb, fefe, feh
+ @source ucspi-tcp6
+ @brief setup TCP stream socket
+*/
+
+int socket_tcp4(void)
+{
+ int s;
+
+ s = socket(AF_INET,SOCK_STREAM,0);
+ if (s != -1)
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+
+ return s;
+}
+
+int socket_tcp6(void)
+{
+ int s;
+
+ s = socket(AF_INET6,SOCK_STREAM,0);
+ if (s != -1)
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+
+ return s;
+}
+
+int socket_tcp(void)
+{
+ int s;
+
+ s = socket(AF_INET6,SOCK_STREAM,0);
+ if (s == -1)
+ if (errno == EINVAL || errno == EAFNOSUPPORT || errno == EPROTO || errno == EPROTONOSUPPORT)
+ s = socket(AF_INET,SOCK_STREAM,0);
+
+ if (s != -1)
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+
+ return s;
+}
+
+int socket_tcpnodelay(int s)
+{
+ int opt = 1;
+ return setsockopt(s,IPPROTO_TCP,1,&opt,sizeof(opt)); /* 1 == TCP_NODELAY */
+}
diff --git a/socket_udp.c b/socket_udp.c
new file mode 100644
index 0000000..743cdf1
--- /dev/null
+++ b/socket_udp.c
@@ -0,0 +1,57 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include "close.h" /* better use unistd.h ? */
+#include "ndelay.h"
+#include "socket_if.h"
+#include "error.h"
+
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT EINVAL
+#endif
+
+/**
+ @file socket_udp.c
+ @author djb, fefe, feh
+ @source ucspi-tcp6
+ @brief setup a UDP message socket
+*/
+
+int socket_udp6(void)
+{
+ int s;
+
+ s = socket(AF_INET6,SOCK_DGRAM,0);
+ if (s != -1)
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+
+ return s;
+}
+
+int socket_udp4(void)
+{
+ int s;
+
+ s = socket(AF_INET,SOCK_DGRAM,0);
+ if (s != -1)
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+
+ return s;
+}
+
+int socket_udp(void)
+{
+ int s;
+
+ s = socket(AF_INET6,SOCK_DGRAM,0);
+ if (s == -1)
+ if (errno == EINVAL || errno == EAFNOSUPPORT || errno == EPROTO || errno == EPROTONOSUPPORT)
+ s = socket(AF_INET,SOCK_DGRAM,0);
+
+ if (s != -1)
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+
+ return s;
+}
diff --git a/str.c b/str.c
new file mode 100644
index 0000000..7d5ab5e
--- /dev/null
+++ b/str.c
@@ -0,0 +1,135 @@
+#include "str.h"
+#include "stralloc.h"
+
+/**
+ @file str.c
+ @author djb
+ @source qmail
+ @brief string handling functions
+*/
+
+unsigned int str_copy(register char *s,register const char *t)
+{
+ register int len;
+
+ len = 0;
+ for (;;) {
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ }
+}
+
+unsigned int str_copyb(register char *s,register const char *t,unsigned int max)
+{
+ register int len;
+
+ len = 0;
+
+ while (max-- > 0) {
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ if (!(*s = *t)) { return len; } ++s; ++t; ++len;
+ }
+
+ return len;
+}
+
+int str_diff(register const char *s,register const char *t)
+{
+ register char x;
+
+ for (;;) {
+ x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ }
+ return ((int)(unsigned int)(unsigned char) x)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
+
+int str_diffn(register const char *s,register const char *t,unsigned int len)
+{
+ register char x;
+
+ for (;;) {
+ if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t;
+ }
+ return ((int)(unsigned int)(unsigned char) x)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
+
+unsigned int str_len(register const char *s)
+{
+ register const char *t;
+
+ t = s;
+ for (;;) {
+ if (!*t) { return t - s; } ++t;
+ if (!*t) { return t - s; } ++t;
+ if (!*t) { return t - s; } ++t;
+ if (!*t) { return t - s; } ++t;
+ }
+}
+
+unsigned int str_chr(register const char *s,int c)
+{
+ register char ch;
+ register const char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!*t) { break; } if (*t == ch) { break; } ++t;
+ if (!*t) { break; } if (*t == ch) { break; } ++t;
+ if (!*t) { break; } if (*t == ch) { break; } ++t;
+ if (!*t) { break; } if (*t == ch) { break; } ++t;
+ }
+ return t - s;
+}
+
+unsigned int str_rchr(register const char *s,int c)
+{
+ register char ch;
+ register const char *t;
+ register const char *u;
+
+ ch = c;
+ t = s;
+ u = 0;
+ for (;;) {
+ if (!*t) { break; } if (*t == ch) { u = t; } ++t;
+ if (!*t) { break; } if (*t == ch) { u = t; } ++t;
+ if (!*t) { break; } if (*t == ch) { u = t; } ++t;
+ if (!*t) { break; } if (*t == ch) { u = t; } ++t;
+ }
+ if (!u) u = t;
+ return u - s;
+}
+
+int str_start(register const char *s,register const char *t)
+{
+ register char x;
+
+ for (;;) {
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ }
+}
+
+char *str_append(char *dest, char const *s)
+{
+ static stralloc sa = {0};
+
+ stralloc_copys(&sa,dest);
+ stralloc_catb(&sa,s,sizeof(s));
+ return sa.s;
+}
diff --git a/stralloc.c b/stralloc.c
new file mode 100644
index 0000000..8c0335e
--- /dev/null
+++ b/stralloc.c
@@ -0,0 +1,126 @@
+#include <stdlib.h>
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+#include "alloc.h"
+
+/**
+ @file stralloc.c
+ @author djb
+ @source qmail, ucspi-tcp
+ @brief genious dynamic string handling
+*/
+
+int stralloc_starts(stralloc *sa,const char *s)
+{
+ int len;
+ len = str_len(s);
+ return (sa->len >= len) && byte_equal(s,len,sa->s);
+}
+
+int stralloc_cat(stralloc *sato,stralloc *safrom)
+{
+ return stralloc_catb(sato,safrom->s,safrom->len);
+}
+
+int stralloc_catb(stralloc *sa,const char *s,unsigned int n)
+{
+ if (!sa->s) return stralloc_copyb(sa,s,n);
+ if (!stralloc_readyplus(sa,n + 1)) return 0;
+ byte_copy(sa->s + sa->len,n,s);
+ sa->len += n;
+ sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
+
+int stralloc_cats(stralloc *sa,const char *s)
+{
+ return stralloc_catb(sa,s,str_len(s));
+}
+
+int stralloc_copy(stralloc *sato,stralloc *safrom)
+{
+ return stralloc_copyb(sato,safrom->s,safrom->len);
+}
+
+
+int stralloc_ready(stralloc *sa,size_t len)
+{
+ register size_t wanted = len+(len>>3)+30; /* heuristic from djb */
+ if (wanted<len) wanted = len;
+ if (!sa->s || sa->a<len) {
+ register char* tmp;
+ if (!(tmp = realloc(sa->s,wanted))) // !!! needs stdlib (realloc)
+ return 0;
+ sa->a = wanted;
+ sa->s = tmp;
+ }
+ return 1;
+}
+
+int stralloc_readyplus(stralloc *sa,size_t len)
+{
+ if (sa->s) {
+ if (sa->len + len < len) return 0; /* catch integer overflow */
+ return stralloc_ready(sa,sa->len+len);
+ } else
+ return stralloc_ready(sa,len);
+}
+
+int stralloc_copyb(stralloc *sa,const char *s,unsigned int n)
+{
+ if (!stralloc_ready(sa,n + 1)) return 0;
+ byte_copy(sa->s,n,s);
+ sa->len = n;
+ sa->s[n] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
+
+int stralloc_copys(stralloc *sa,const char *s)
+{
+ return stralloc_copyb(sa,s,str_len(s));
+}
+
+int stralloc_catulong0(stralloc *sa,unsigned long u,unsigned int n)
+{
+ unsigned int len;
+ unsigned long q;
+ char *s;
+
+ len = 1;
+ q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (len < n) len = n;
+
+ if (!stralloc_readyplus(sa,len)) return 0;
+ s = sa->s + sa->len;
+ sa->len += len;
+ while (len) { s[--len] = '0' + (u % 10); u /= 10; }
+
+ return 1;
+}
+
+int stralloc_catlong0(stralloc *sa,long l,unsigned int n)
+{
+ if (l < 0) {
+ if (!stralloc_append(sa,"-")) return 0;
+ l = -l;
+ }
+ return stralloc_catulong0(sa,l,n);
+}
+
+int stralloc_append(stralloc *sa,const char *in)
+{
+ if (stralloc_readyplus(sa,1)) {
+ sa->s[sa->len] = *in;
+ ++sa->len;
+ return 1;
+ }
+ return 0;
+}
+
+void stralloc_free(stralloc *sa) {
+ if (sa->s) free(sa->s);
+ sa->s = 0;
+ sa->a = sa->len = 0;
+}
diff --git a/tai.c b/tai.c
new file mode 100644
index 0000000..65a8225
--- /dev/null
+++ b/tai.c
@@ -0,0 +1,59 @@
+#include <time.h>
+#include "tai.h"
+
+/**
+ @file tai.c
+ @author djb
+ @source qmail
+ @brief 'temps atomic' time handling
+*/
+
+void tai_add(struct tai *t,const struct tai *u,const struct tai *v)
+{
+ t->x = u->x + v->x;
+}
+
+void tai_now(struct tai *t)
+{
+ tai_unix(t,time((time_t *) 0));
+}
+
+void tai_pack(char *s,const struct tai *t)
+{
+ uint64 x;
+
+ x = t->x;
+ s[7] = (char)x; x >>= 8;
+ s[6] = (char)x; x >>= 8;
+ s[5] = (char)x; x >>= 8;
+ s[4] = (char)x; x >>= 8;
+ s[3] = (char)x; x >>= 8;
+ s[2] = (char)x; x >>= 8;
+ s[1] = (char)x; x >>= 8;
+ s[0] = (char)x;
+}
+
+void tai_sub(struct tai *t,const struct tai *u,const struct tai *v)
+{
+ t->x = u->x - v->x;
+}
+
+void tai_uint(struct tai *t,unsigned int u)
+{
+ t->x = u;
+}
+
+void tai_unpack(const char *s,struct tai *t)
+{
+ uint64 x;
+
+ x = (unsigned char) s[0];
+ x <<= 8; x += (unsigned char) s[1];
+ x <<= 8; x += (unsigned char) s[2];
+ x <<= 8; x += (unsigned char) s[3];
+ x <<= 8; x += (unsigned char) s[4];
+ x <<= 8; x += (unsigned char) s[5];
+ x <<= 8; x += (unsigned char) s[6];
+ x <<= 8; x += (unsigned char) s[7];
+ t->x = x;
+}
diff --git a/taia.c b/taia.c
new file mode 100644
index 0000000..22bd816
--- /dev/null
+++ b/taia.c
@@ -0,0 +1,104 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include "taia.h"
+
+/**
+ @file taia.c
+ @author djb
+ @source qmail
+ @brief 'tai' attosecond time handling
+*/
+
+/* XXX: breaks tai encapsulation */
+
+void taia_add(struct taia *t,struct taia *u,struct taia *v)
+{
+ t->sec.x = u->sec.x + v->sec.x;
+ t->nano = u->nano + v->nano;
+ t->atto = u->atto + v->atto;
+ if (t->atto > 999999999UL) {
+ t->atto -= 1000000000UL;
+ ++t->nano;
+ }
+ if (t->nano > 999999999UL) {
+ t->nano -= 1000000000UL;
+ ++t->sec.x;
+ }
+}
+
+double taia_approx(struct taia *t)
+{
+ return tai_approx(&t->sec) + taia_frac(t);
+}
+
+double taia_frac(struct taia *t)
+{
+ return (t->atto * 0.000000001 + t->nano) * 0.000000001;
+}
+
+int taia_less(struct taia *t,struct taia *u)
+{
+ if (t->sec.x < u->sec.x) return 1;
+ if (t->sec.x > u->sec.x) return 0;
+ if (t->nano < u->nano) return 1;
+ if (t->nano > u->nano) return 0;
+ return t->atto < u->atto;
+}
+
+int taia_now(struct taia *t)
+{
+ struct timeval now;
+ if (gettimeofday(&now,(struct timezone *) 0) == 0) {
+ tai_unix(&t->sec,now.tv_sec);
+ t->nano = 1000 * now.tv_usec + 500;
+ t->atto = 0;
+ return 0;
+ }
+ t->nano = 0;
+ t->atto = 0;
+ return -1;
+}
+
+void taia_pack(char *s,struct taia *t)
+{
+ unsigned long x;
+
+ tai_pack(s,&t->sec);
+ s += 8;
+
+ x = t->atto;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x;
+ x = t->nano;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
+
+void taia_sub(struct taia *t,struct taia *u,struct taia *v)
+{
+ unsigned long unano = u->nano;
+ unsigned long uatto = u->atto;
+
+ t->sec.x = u->sec.x - v->sec.x;
+ t->nano = unano - v->nano;
+ t->atto = uatto - v->atto;
+ if (t->atto > uatto) {
+ t->atto += 1000000000UL;
+ --t->nano;
+ }
+ if (t->nano > unano) {
+ t->nano += 1000000000UL;
+ --t->sec.x;
+ }
+}
+
+void taia_uint(struct taia *t,unsigned int s)
+{
+ t->sec.x = s;
+ t->nano = 0;
+ t->atto = 0;
+}
diff --git a/timeout.c b/timeout.c
new file mode 100644
index 0000000..e721b66
--- /dev/null
+++ b/timeout.c
@@ -0,0 +1,59 @@
+#include <unistd.h>
+#include "error.h"
+#include "iopause.h"
+#include "timeout.h"
+
+/**
+ @file timeout.c
+ @author djb
+ @source qmail
+ @brief read/write timeout handling
+*/
+
+int timeoutread(int t,int fd,char *buf,int len)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+
+ taia_now(&now);
+ taia_uint(&deadline,t);
+ taia_add(&deadline,&now,&deadline);
+
+ x.fd = fd;
+ x.events = IOPAUSE_READ;
+ for (;;) {
+ taia_now(&now);
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ return read(fd,buf,len);
+}
+
+int timeoutwrite(int t,int fd,char *buf,int len)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+
+ taia_now(&now);
+ taia_uint(&deadline,t);
+ taia_add(&deadline,&now,&deadline);
+
+ x.fd = fd;
+ x.events = IOPAUSE_WRITE;
+ for (;;) {
+ taia_now(&now);
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ return write(fd,buf,len);
+}
diff --git a/timeoutconn.c b/timeoutconn.c
new file mode 100644
index 0000000..c94f600
--- /dev/null
+++ b/timeoutconn.c
@@ -0,0 +1,112 @@
+#include "ndelay.h"
+#include "socket_if.h"
+#include "iopause.h"
+#include "error.h"
+#include "timeoutconn.h"
+#include "ip.h"
+
+/**
+ @file timeoutconn.c
+ @author djb, fefe, feh
+ @source qmail
+ @brief socket read/write timeout handling; return code of iopause considered
+*/
+
+int timeoutconn4(int s,char ip[4],uint16 port,unsigned int timeout)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+ unsigned int p = 0;
+
+ if (socket_connect4(s,ip,port) == -1) {
+ if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) return -1;
+ x.fd = s;
+ x.events = IOPAUSE_WRITE;
+ taia_now(&now);
+ taia_uint(&deadline,timeout);
+ taia_add(&deadline,&now,&deadline);
+ for (;;) {
+ taia_now(&now);
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break; /* 's' available */
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT; /* note that connect attempt is continuing */
+ return -1;
+ }
+ p++;
+ }
+ if (!socket_connected(s)) return -1;
+ }
+
+ if (ndelay_off(s) == -1) return -1;
+ return 0;
+}
+
+int timeoutconn6(int s,char ip[16],uint16 port,unsigned int timeout,uint32 netif)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+ unsigned int p = 0;
+
+ if (socket_connect6(s,ip,port,netif) == -1) {
+ if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) return -1;
+ x.fd = s;
+ x.events = IOPAUSE_WRITE;
+ taia_now(&now);
+ taia_uint(&deadline,timeout);
+ taia_add(&deadline,&now,&deadline);
+ for (;;) {
+ taia_now(&now);
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break; /* 's' available */
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT; /* note that connect attempt is continuing */
+ return -1;
+ }
+ p++;
+ }
+ if (!socket_connected(s)) return -1;
+ }
+
+ if (ndelay_off(s) == -1) return -1;
+ return 0;
+}
+
+int timeoutconn(int s,char ip[16],uint16 port,unsigned int timeout,uint32 netif)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+ unsigned int p = 0;
+ int r;
+
+ if (ip6_isv4mapped(ip))
+ r = socket_connect4(s,ip + 12,port);
+ else
+ r = socket_connect6(s,ip,port,netif);
+
+ if (r == -1) {
+ if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) return -1;
+ x.fd = s;
+ x.events = IOPAUSE_WRITE;
+ taia_now(&now);
+ taia_uint(&deadline,timeout);
+ taia_add(&deadline,&now,&deadline);
+ for (;;) {
+ taia_now(&now);
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break; /* 's' available */
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT; /* note that connect attempt is continuing */
+ return -1;
+ }
+ p++;
+ }
+ if (!socket_connected(s)) return -1;
+ }
+
+ if (ndelay_off(s) == -1) return -1;
+ return 0;
+}
diff --git a/uint128p.c b/uint128p.c
new file mode 100644
index 0000000..57c713e
--- /dev/null
+++ b/uint128p.c
@@ -0,0 +1,100 @@
+#include "uint_t.h"
+
+/**
+ @file uint128p.c
+ @author feh, jannis
+ @source djbdns6
+ @brief packing/unpacking 128 bit integer to/from char string
+*/
+
+void uint128_pack(char s[16],uint128 u)
+{
+ s[0] = u.lo & 255; u.lo >>= 8;
+ s[1] = u.lo & 255; u.lo >>= 8;
+ s[2] = u.lo & 255; u.lo >>= 8;
+ s[3] = u.lo & 255; u.lo >>= 8;
+ s[4] = u.lo & 255; u.lo >>= 8;
+ s[5] = u.lo & 255; u.lo >>= 8;
+ s[6] = u.lo & 255; u.lo >>= 8;
+ s[7] = u.lo & 255; u.lo >>= 8;
+
+ s[8] = u.hi & 255; u.hi >>= 8;
+ s[9] = u.hi & 255; u.hi >>= 8;
+ s[10] = u.hi & 255; u.hi >>= 8;
+ s[11] = u.hi & 255; u.hi >>= 8;
+ s[12] = u.hi & 255; u.hi >>= 8;
+ s[13] = u.hi & 255; u.hi >>= 8;
+ s[14] = u.hi & 255; u.hi >>= 8;
+ s[15] = u.hi & 255;
+}
+void uint128_pack_big(char s[16],uint128 u)
+{
+ s[15] = u.lo & 255; u.lo >>= 8;
+ s[14] = u.lo & 255; u.lo >>= 8;
+ s[13] = u.lo & 255; u.lo >>= 8;
+ s[12] = u.lo & 255; u.lo >>= 8;
+ s[11] = u.lo & 255; u.lo >>= 8;
+ s[10] = u.lo & 255; u.lo >>= 8;
+ s[9] = u.lo & 255; u.lo >>= 8;
+ s[8] = u.lo & 255; u.lo >>= 8;
+
+ s[7] = u.hi & 255; u.hi >>= 8;
+ s[6] = u.hi & 255; u.hi >>= 8;
+ s[5] = u.hi & 255; u.hi >>= 8;
+ s[4] = u.hi & 255; u.hi >>= 8;
+ s[3] = u.hi & 255; u.hi >>= 8;
+ s[2] = u.hi & 255; u.hi >>= 8;
+ s[1] = u.hi & 255; u.hi >>= 8;
+ s[0] = u.hi & 255;
+}
+
+void uint128_unpack(char s[16],uint128 *u)
+{
+ uint128 result;
+ result.hi = result.lo = 0ULL;
+
+ result.hi = (unsigned char) s[15]; result.hi <<= 8;
+ result.hi += (unsigned char) s[14]; result.hi <<= 8;
+ result.hi += (unsigned char) s[13]; result.hi <<= 8;
+ result.hi += (unsigned char) s[12]; result.hi <<= 8;
+ result.hi += (unsigned char) s[11]; result.hi <<= 8;
+ result.hi += (unsigned char) s[10]; result.hi <<= 8;
+ result.hi += (unsigned char) s[9]; result.hi <<= 8;
+ result.hi += (unsigned char) s[8]; //correct
+
+ result.lo += (unsigned char) s[7]; result.lo <<= 8;
+ result.lo += (unsigned char) s[6]; result.lo <<= 8;
+ result.lo += (unsigned char) s[5]; result.lo <<= 8;
+ result.lo += (unsigned char) s[4]; result.lo <<= 8;
+ result.lo += (unsigned char) s[3]; result.lo <<= 8;
+ result.lo += (unsigned char) s[2]; result.lo <<= 8;
+ result.lo += (unsigned char) s[1]; result.lo <<= 8;
+ result.lo += (unsigned char) s[0];
+
+ *u = result;
+}
+void uint128_unpack_big(char s[16],uint128 *u)
+{
+ uint128 result;
+ result.hi = result.lo = 0ULL;
+
+ result.hi = (unsigned char) s[0]; result.hi <<= 8;
+ result.hi += (unsigned char) s[1]; result.hi <<= 8;
+ result.hi += (unsigned char) s[2]; result.hi <<= 8;
+ result.hi += (unsigned char) s[3]; result.hi <<= 8;
+ result.hi += (unsigned char) s[4]; result.hi <<= 8;
+ result.hi += (unsigned char) s[5]; result.hi <<= 8;
+ result.hi += (unsigned char) s[6]; result.hi <<= 8;
+ result.hi += (unsigned char) s[7];
+
+ result.lo += (unsigned char) s[8]; result.lo <<= 8;
+ result.lo += (unsigned char) s[9]; result.lo <<= 8;
+ result.lo += (unsigned char) s[10]; result.lo <<= 8;
+ result.lo += (unsigned char) s[11]; result.lo <<= 8;
+ result.lo += (unsigned char) s[12]; result.lo <<= 8;
+ result.lo += (unsigned char) s[13]; result.lo <<= 8;
+ result.lo += (unsigned char) s[14]; result.lo <<= 8;
+ result.lo += (unsigned char) s[15];
+
+ *u = result;
+}
diff --git a/uint16p.c b/uint16p.c
new file mode 100644
index 0000000..5dddf21
--- /dev/null
+++ b/uint16p.c
@@ -0,0 +1,40 @@
+#include "uint_t.h"
+
+/**
+ @file uint16p.c
+ @author djb
+ @source qmail
+ @brief packing/unpacking 16 bit integer to/from char string
+*/
+
+void uint16_pack(char s[2],uint16 u)
+{
+ s[0] = u & 255;
+ s[1] = u >> 8;
+}
+
+void uint16_pack_big(char s[2],uint16 u)
+{
+ s[1] = u & 255;
+ s[0] = u >> 8;
+}
+
+void uint16_unpack(char s[2],uint16 *u)
+{
+ uint16 result;
+
+ result = (unsigned char) s[1]; result <<= 8;
+ result += (unsigned char) s[0];
+
+ *u = result;
+}
+
+void uint16_unpack_big(char s[2],uint16 *u)
+{
+ uint16 result;
+
+ result = (unsigned char) s[0]; result <<= 8;
+ result += (unsigned char) s[1];
+
+ *u = result;
+}
diff --git a/uint32p.c b/uint32p.c
new file mode 100644
index 0000000..f3f04ea
--- /dev/null
+++ b/uint32p.c
@@ -0,0 +1,47 @@
+#include "uint_t.h"
+
+/**
+ @file uint32p.c
+ @author djb
+ @source qmail
+ @brief packing/unpacking 32 bit integer to/from char string
+*/
+
+void uint32_pack(char s[4],uint32 u)
+{
+ s[0] = u & 255; u >>= 8;
+ s[1] = u & 255; u >>= 8;
+ s[2] = u & 255;
+ s[3] = u >> 8;
+}
+void uint32_pack_big(char s[4],uint32 u)
+{
+ s[3] = u & 255; u >>= 8;
+ s[2] = u & 255; u >>= 8;
+ s[1] = u & 255;
+ s[0] = u >> 8;
+}
+
+void uint32_unpack(char s[4],uint32 *u)
+{
+ uint32 result;
+
+ result = (unsigned char) s[3]; result <<= 8;
+ result += (unsigned char) s[2]; result <<= 8;
+ result += (unsigned char) s[1]; result <<= 8;
+ result += (unsigned char) s[0];
+
+ *u = result;
+}
+
+void uint32_unpack_big(char s[4],uint32 *u)
+{
+ uint32 result;
+
+ result = (unsigned char) s[0]; result <<= 8;
+ result += (unsigned char) s[1]; result <<= 8;
+ result += (unsigned char) s[2]; result <<= 8;
+ result += (unsigned char) s[3];
+
+ *u = result;
+}
diff --git a/uint64p.c b/uint64p.c
new file mode 100644
index 0000000..41b8ceb
--- /dev/null
+++ b/uint64p.c
@@ -0,0 +1,62 @@
+#include "uint_t.h"
+
+/**
+ @file uint64p.c
+ @author feh, jannis
+ @source djbdns6
+ @brief packing/unpacking 64 bit integer to/from char string
+*/
+
+void uint64_pack(char s[8],uint64 u)
+{
+ s[0] = u & 255; u >>= 8;
+ s[1] = u & 255; u >>= 8;
+ s[2] = u & 255; u >>= 8;
+ s[3] = u & 255; u >>= 8;
+ s[4] = u & 255; u >>= 8;
+ s[5] = u & 255; u >>= 8;
+ s[6] = u & 255; u >>= 8;
+ s[7] = u & 255;
+}
+void uint64_pack_big(char s[8],uint64 u)
+{
+ s[7] = u & 255; u >>= 8;
+ s[6] = u & 255; u >>= 8;
+ s[5] = u & 255; u >>= 8;
+ s[4] = u & 255; u >>= 8;
+ s[3] = u & 255; u >>= 8;
+ s[2] = u & 255; u >>= 8;
+ s[1] = u & 255; u >>= 8;
+ s[0] = u & 255;
+}
+
+void uint64_unpack(char s[8],uint64 *u)
+{
+ uint64 result;
+
+ result = (unsigned char) s[7]; result <<= 8;
+ result += (unsigned char) s[6]; result <<= 8;
+ result += (unsigned char) s[5]; result <<= 8;
+ result += (unsigned char) s[4]; result <<= 8;
+ result += (unsigned char) s[3]; result <<= 8;
+ result += (unsigned char) s[2]; result <<= 8;
+ result += (unsigned char) s[1]; result <<= 8;
+ result += (unsigned char) s[0];
+
+ *u = result;
+}
+void uint64_unpack_big(char s[8],uint64 *u)
+{
+ uint64 result;
+
+ result = (unsigned char) s[0]; result <<= 8;
+ result += (unsigned char) s[1]; result <<= 8;
+ result += (unsigned char) s[2]; result <<= 8;
+ result += (unsigned char) s[3]; result <<= 8;
+ result += (unsigned char) s[4]; result <<= 8;
+ result += (unsigned char) s[5]; result <<= 8;
+ result += (unsigned char) s[6]; result <<= 8;
+ result += (unsigned char) s[7];
+
+ *u = result;
+}
diff --git a/uint8p.c b/uint8p.c
new file mode 100644
index 0000000..a7a1460
--- /dev/null
+++ b/uint8p.c
@@ -0,0 +1,39 @@
+#include "uint_t.h"
+
+/**
+ @file uint8x.c
+ @author feh
+ @brief packing/unpacking 8 bit int to/from char string
+*/
+
+void uint8_pack(char s[2],uint8 u)
+{
+ s[0] = u & 255;
+ s[1] = u >> 4;
+}
+
+void uint8_pack_big(char s[2],uint8 u)
+{
+ s[1] = u & 255;
+ s[0] = u >> 4;
+}
+
+void uint8_unpack(char s[2],uint8 *u)
+{
+ uint8 result;
+
+ result = (unsigned char) s[1]; result <<= 4;
+ result += (unsigned char) s[0];
+
+ *u = result;
+}
+
+void uint8_unpack_big(char s[2],uint8 *u)
+{
+ uint8 result;
+
+ result = (unsigned char) s[0]; result <<= 4;
+ result += (unsigned char) s[1];
+
+ *u = result;
+}
diff --git a/wait.c b/wait.c
new file mode 100644
index 0000000..869a120
--- /dev/null
+++ b/wait.c
@@ -0,0 +1,25 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "logmsg.h"
+
+/**
+ @file wait.c
+ @author djb
+ @source qmail
+ @brief wait for forked processes
+*/
+
+int wait_nohang(int *wstat)
+{
+ return waitpid(-1,wstat,WNOHANG);
+}
+
+int wait_pid(int *wstat,int pid)
+{
+ int r;
+
+ do
+ r = waitpid(pid,wstat,0);
+ while ((r == -1) && (errno == EINTR));
+ return r;
+}