From 96cf8dffe4f7b0b910f790066ae622dc429eb522 Mon Sep 17 00:00:00 2001 From: Jannis Hoffmann Date: Tue, 9 Jul 2024 13:02:45 +0200 Subject: initial commit of version 23 --- BUILD | 1 + CHANGELOG | 69 ++++++++ CONTENT | 63 +++++++ FILES | 162 ++++++++++++++++++ LICENSE | 42 +++++ Makefile | 123 ++++++++++++++ README.md | 132 +++++++++++++++ TARGETS | 79 +++++++++ VERSION | 1 + alloc.c | 50 ++++++ base64.c | 124 ++++++++++++++ buffer.c | 226 +++++++++++++++++++++++++ byte.c | 99 +++++++++++ case.c | 134 +++++++++++++++ cdbmake.c | 158 ++++++++++++++++++ cdbread.c | 169 +++++++++++++++++++ conf-build | 30 ++++ configure | 69 ++++++++ constmap.c | 169 +++++++++++++++++++ dnsstub/Makefile | 23 +++ dnsstub/README.md | 171 +++++++++++++++++++ dnsstub/TARGETS | 18 ++ dnsstub/dns_cname.c | 59 +++++++ dnsstub/dns_dfd.c | 76 +++++++++ dnsstub/dns_domain.c | 80 +++++++++ dnsstub/dns_dtda.c | 43 +++++ dnsstub/dns_ip.c | 198 ++++++++++++++++++++++ dnsstub/dns_ipq.c | 236 ++++++++++++++++++++++++++ dnsstub/dns_mx.c | 63 +++++++ dnsstub/dns_name.c | 80 +++++++++ dnsstub/dns_nd.c | 48 ++++++ dnsstub/dns_packet.c | 85 ++++++++++ dnsstub/dns_random.c | 70 ++++++++ dnsstub/dns_rcip.c | 114 +++++++++++++ dnsstub/dns_rcrw.c | 141 ++++++++++++++++ dnsstub/dns_resolve.c | 39 +++++ dnsstub/dns_sortip.c | 45 +++++ dnsstub/dns_transmit.c | 436 +++++++++++++++++++++++++++++++++++++++++++++++++ dnsstub/dns_txt.c | 64 ++++++++ env.c | 136 +++++++++++++++ errstr.c | 163 ++++++++++++++++++ fd.c | 30 ++++ fmt.c | 85 ++++++++++ getln.c | 43 +++++ getoptb.c | 100 ++++++++++++ include/alloc.h | 12 ++ include/base64.h | 7 + include/buffer.h | 63 +++++++ include/byte.h | 21 +++ include/case.h | 17 ++ include/cdbmake.h | 39 +++++ include/cdbread.h | 38 +++++ include/close.h | 12 ++ include/constmap.h | 21 +++ include/direntry.h | 10 ++ include/dnsresolv.h | 202 +++++++++++++++++++++++ include/env.h | 29 ++++ include/error.h | 57 +++++++ include/exit.h | 13 ++ include/fd.h | 8 + include/fifo.h | 12 ++ include/fmt.h | 36 ++++ include/genalloc.h | 74 +++++++++ include/getln.h | 10 ++ include/getoptb.h | 28 ++++ include/iopause.h | 28 ++++ include/ip.h | 107 ++++++++++++ include/lock.h | 14 ++ include/logmsg.h | 31 ++++ include/ndelay.h | 13 ++ include/open.h | 16 ++ include/pathexec.h | 10 ++ include/prot.h | 7 + include/readclose.h | 14 ++ include/readwrite.h | 9 + include/rename.h | 6 + include/scan.h | 12 ++ include/seek.h | 15 ++ include/select.h | 13 ++ include/sig.h | 63 +++++++ include/socket_if.h | 97 +++++++++++ include/str.h | 25 +++ include/stralloc.h | 49 ++++++ include/tai.h | 74 +++++++++ include/taia.h | 40 +++++ include/timeout.h | 7 + include/timeoutconn.h | 10 ++ include/uint_t.h | 76 +++++++++ include/wait.h | 20 +++ install | 14 ++ iopause.c | 84 ++++++++++ ip4.c | 166 +++++++++++++++++++ ip6.c | 360 ++++++++++++++++++++++++++++++++++++++++ lock.c | 22 +++ logmsg.c | 97 +++++++++++ man/INSTALL | 12 ++ man/alloc.3 | 68 ++++++++ man/buffer.3 | 185 +++++++++++++++++++++ man/byte.3 | 82 ++++++++++ man/case.3 | 124 ++++++++++++++ man/cdbmake.3 | 61 +++++++ man/cdbread.3 | 177 ++++++++++++++++++++ man/constmap.3 | 56 +++++++ man/dns.3 | 249 ++++++++++++++++++++++++++++ man/dnsstub.3 | 109 +++++++++++++ man/env.3 | 68 ++++++++ man/error.3 | 75 +++++++++ man/fd.3 | 79 +++++++++ man/fmt.3 | 90 ++++++++++ man/getln.3 | 102 ++++++++++++ man/getoptb.3 | 22 +++ man/iopause.3 | 87 ++++++++++ man/ip4.3 | 73 +++++++++ man/ip6.3 | 98 +++++++++++ man/logmsg.3 | 109 +++++++++++++ man/pathexec.3 | 124 ++++++++++++++ man/scan.3 | 81 +++++++++ man/socket_bind.3 | 117 +++++++++++++ man/socket_connect.3 | 110 +++++++++++++ man/socket_if.3 | 97 +++++++++++ man/socket_info.3 | 47 ++++++ man/socket_recv.3 | 56 +++++++ man/socket_send.3 | 76 +++++++++ man/socket_setup.3 | 105 ++++++++++++ man/socket_tcp.3 | 58 +++++++ man/socket_udp.3 | 58 +++++++ man/str.3 | 61 +++++++ man/stralloc.3 | 160 ++++++++++++++++++ man/taia.3 | 91 +++++++++++ man/timeout.3 | 36 ++++ man/timeoutconn.3 | 52 ++++++ man/wait.3 | 96 +++++++++++ man/x.html | 99 +++++++++++ ndelay.c | 24 +++ open.c | 25 +++ pathexec.c | 121 ++++++++++++++ prot.c | 31 ++++ readclose.c | 43 +++++ scan.c | 120 ++++++++++++++ seek.c | 30 ++++ sharedlib | 2 + sig.c | 110 +++++++++++++ socket_bind.c | 79 +++++++++ socket_connect.c | 66 ++++++++ socket_if.c | 36 ++++ socket_info.c | 58 +++++++ socket_recv.c | 39 +++++ socket_send.c | 62 +++++++ socket_setup.c | 99 +++++++++++ socket_tcp.c | 63 +++++++ socket_udp.c | 57 +++++++ str.c | 135 +++++++++++++++ stralloc.c | 126 ++++++++++++++ tai.c | 59 +++++++ taia.c | 104 ++++++++++++ timeout.c | 59 +++++++ timeoutconn.c | 112 +++++++++++++ uint128p.c | 100 ++++++++++++ uint16p.c | 40 +++++ uint32p.c | 47 ++++++ uint64p.c | 62 +++++++ uint8p.c | 39 +++++ wait.c | 25 +++ 163 files changed, 12177 insertions(+) create mode 100644 BUILD create mode 100644 CHANGELOG create mode 100755 CONTENT create mode 100644 FILES create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 TARGETS create mode 100644 VERSION create mode 100644 alloc.c create mode 100644 base64.c create mode 100644 buffer.c create mode 100644 byte.c create mode 100644 case.c create mode 100644 cdbmake.c create mode 100644 cdbread.c create mode 100644 conf-build create mode 100755 configure create mode 100644 constmap.c create mode 100644 dnsstub/Makefile create mode 100644 dnsstub/README.md create mode 100644 dnsstub/TARGETS create mode 100644 dnsstub/dns_cname.c create mode 100644 dnsstub/dns_dfd.c create mode 100644 dnsstub/dns_domain.c create mode 100644 dnsstub/dns_dtda.c create mode 100644 dnsstub/dns_ip.c create mode 100644 dnsstub/dns_ipq.c create mode 100644 dnsstub/dns_mx.c create mode 100644 dnsstub/dns_name.c create mode 100644 dnsstub/dns_nd.c create mode 100644 dnsstub/dns_packet.c create mode 100644 dnsstub/dns_random.c create mode 100644 dnsstub/dns_rcip.c create mode 100644 dnsstub/dns_rcrw.c create mode 100644 dnsstub/dns_resolve.c create mode 100644 dnsstub/dns_sortip.c create mode 100644 dnsstub/dns_transmit.c create mode 100644 dnsstub/dns_txt.c create mode 100644 env.c create mode 100644 errstr.c create mode 100644 fd.c create mode 100644 fmt.c create mode 100644 getln.c create mode 100644 getoptb.c create mode 100644 include/alloc.h create mode 100644 include/base64.h create mode 100644 include/buffer.h create mode 100644 include/byte.h create mode 100644 include/case.h create mode 100644 include/cdbmake.h create mode 100644 include/cdbread.h create mode 100644 include/close.h create mode 100644 include/constmap.h create mode 100644 include/direntry.h create mode 100644 include/dnsresolv.h create mode 100644 include/env.h create mode 100644 include/error.h create mode 100644 include/exit.h create mode 100644 include/fd.h create mode 100644 include/fifo.h create mode 100644 include/fmt.h create mode 100644 include/genalloc.h create mode 100644 include/getln.h create mode 100644 include/getoptb.h create mode 100644 include/iopause.h create mode 100644 include/ip.h create mode 100644 include/lock.h create mode 100644 include/logmsg.h create mode 100644 include/ndelay.h create mode 100644 include/open.h create mode 100644 include/pathexec.h create mode 100644 include/prot.h create mode 100644 include/readclose.h create mode 100644 include/readwrite.h create mode 100644 include/rename.h create mode 100644 include/scan.h create mode 100644 include/seek.h create mode 100644 include/select.h create mode 100644 include/sig.h create mode 100644 include/socket_if.h create mode 100644 include/str.h create mode 100644 include/stralloc.h create mode 100644 include/tai.h create mode 100644 include/taia.h create mode 100644 include/timeout.h create mode 100644 include/timeoutconn.h create mode 100644 include/uint_t.h create mode 100644 include/wait.h create mode 100755 install create mode 100644 iopause.c create mode 100644 ip4.c create mode 100644 ip6.c create mode 100644 lock.c create mode 100644 logmsg.c create mode 100644 man/INSTALL create mode 100644 man/alloc.3 create mode 100644 man/buffer.3 create mode 100644 man/byte.3 create mode 100644 man/case.3 create mode 100644 man/cdbmake.3 create mode 100644 man/cdbread.3 create mode 100644 man/constmap.3 create mode 100644 man/dns.3 create mode 100644 man/dnsstub.3 create mode 100644 man/env.3 create mode 100644 man/error.3 create mode 100644 man/fd.3 create mode 100644 man/fmt.3 create mode 100644 man/getln.3 create mode 100644 man/getoptb.3 create mode 100644 man/iopause.3 create mode 100644 man/ip4.3 create mode 100644 man/ip6.3 create mode 100644 man/logmsg.3 create mode 100644 man/pathexec.3 create mode 100644 man/scan.3 create mode 100644 man/socket_bind.3 create mode 100644 man/socket_connect.3 create mode 100644 man/socket_if.3 create mode 100644 man/socket_info.3 create mode 100644 man/socket_recv.3 create mode 100644 man/socket_send.3 create mode 100644 man/socket_setup.3 create mode 100644 man/socket_tcp.3 create mode 100644 man/socket_udp.3 create mode 100644 man/str.3 create mode 100644 man/stralloc.3 create mode 100644 man/taia.3 create mode 100644 man/timeout.3 create mode 100644 man/timeoutconn.3 create mode 100644 man/wait.3 create mode 100644 man/x.html create mode 100644 ndelay.c create mode 100644 open.c create mode 100644 pathexec.c create mode 100644 prot.c create mode 100644 readclose.c create mode 100644 scan.c create mode 100644 seek.c create mode 100755 sharedlib create mode 100644 sig.c create mode 100644 socket_bind.c create mode 100644 socket_connect.c create mode 100644 socket_if.c create mode 100644 socket_info.c create mode 100644 socket_recv.c create mode 100644 socket_send.c create mode 100644 socket_setup.c create mode 100644 socket_tcp.c create mode 100644 socket_udp.c create mode 100644 str.c create mode 100644 stralloc.c create mode 100644 tai.c create mode 100644 taia.c create mode 100644 timeout.c create mode 100644 timeoutconn.c create mode 100644 uint128p.c create mode 100644 uint16p.c create mode 100644 uint32p.c create mode 100644 uint64p.c create mode 100644 uint8p.c create mode 100644 wait.c 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 +#include +#include +#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 +#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 +#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 +#include +#include +#include +#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 +#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 +#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 +#include +#include +#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 +#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 /* 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 ) +*/ + +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 +#include +#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 + +#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 during operation and handed over + (2) application errors DEFINED internally and ennumerated alongside with +*/ + +/* 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 + +/* 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 +#ifdef HAS_POLL_H +#include + +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 +#include +#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 + +// 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 +#include + +#ifdef HAS_SELECT_H +#include +#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 + +/* 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 + +/** + @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 +*/ + +#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 +#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 +#include +#include +#include +#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 +#include +#include +#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 + #include + + 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 + + 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 + + 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 + + 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 + #include + + 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 + + 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 + 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 + #include + + 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 + + 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 + + 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 + + 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 + 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 @@ + + +
+
+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
+       syslog(3)
+
+
+
+                                       3                        qlibs:(logmsg)
+
+
+
+Man(1) output converted with +man2html +
+ + 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 +#include +#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 +#include +#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 +#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 +#include +#include +//#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 +#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 +#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 +#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 +#include +#include +#include +#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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#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 +#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 (wanteds || sa->as,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 +#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 +#include +#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 +#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 +#include +#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; +} -- cgit v1.2.3