diff options
-rw-r--r-- | BUILD | 1 | ||||
-rw-r--r-- | CHANGELOG | 69 | ||||
-rwxr-xr-x | CONTENT | 63 | ||||
-rw-r--r-- | FILES | 162 | ||||
-rw-r--r-- | LICENSE | 42 | ||||
-rw-r--r-- | Makefile | 123 | ||||
-rw-r--r-- | README.md | 132 | ||||
-rw-r--r-- | TARGETS | 79 | ||||
-rw-r--r-- | VERSION | 1 | ||||
-rw-r--r-- | alloc.c | 50 | ||||
-rw-r--r-- | base64.c | 124 | ||||
-rw-r--r-- | buffer.c | 226 | ||||
-rw-r--r-- | byte.c | 99 | ||||
-rw-r--r-- | case.c | 134 | ||||
-rw-r--r-- | cdbmake.c | 158 | ||||
-rw-r--r-- | cdbread.c | 169 | ||||
-rw-r--r-- | conf-build | 30 | ||||
-rwxr-xr-x | configure | 69 | ||||
-rw-r--r-- | constmap.c | 169 | ||||
-rw-r--r-- | dnsstub/Makefile | 23 | ||||
-rw-r--r-- | dnsstub/README.md | 171 | ||||
-rw-r--r-- | dnsstub/TARGETS | 18 | ||||
-rw-r--r-- | dnsstub/dns_cname.c | 59 | ||||
-rw-r--r-- | dnsstub/dns_dfd.c | 76 | ||||
-rw-r--r-- | dnsstub/dns_domain.c | 80 | ||||
-rw-r--r-- | dnsstub/dns_dtda.c | 43 | ||||
-rw-r--r-- | dnsstub/dns_ip.c | 198 | ||||
-rw-r--r-- | dnsstub/dns_ipq.c | 236 | ||||
-rw-r--r-- | dnsstub/dns_mx.c | 63 | ||||
-rw-r--r-- | dnsstub/dns_name.c | 80 | ||||
-rw-r--r-- | dnsstub/dns_nd.c | 48 | ||||
-rw-r--r-- | dnsstub/dns_packet.c | 85 | ||||
-rw-r--r-- | dnsstub/dns_random.c | 70 | ||||
-rw-r--r-- | dnsstub/dns_rcip.c | 114 | ||||
-rw-r--r-- | dnsstub/dns_rcrw.c | 141 | ||||
-rw-r--r-- | dnsstub/dns_resolve.c | 39 | ||||
-rw-r--r-- | dnsstub/dns_sortip.c | 45 | ||||
-rw-r--r-- | dnsstub/dns_transmit.c | 436 | ||||
-rw-r--r-- | dnsstub/dns_txt.c | 64 | ||||
-rw-r--r-- | env.c | 136 | ||||
-rw-r--r-- | errstr.c | 163 | ||||
-rw-r--r-- | fd.c | 30 | ||||
-rw-r--r-- | fmt.c | 85 | ||||
-rw-r--r-- | getln.c | 43 | ||||
-rw-r--r-- | getoptb.c | 100 | ||||
-rw-r--r-- | include/alloc.h | 12 | ||||
-rw-r--r-- | include/base64.h | 7 | ||||
-rw-r--r-- | include/buffer.h | 63 | ||||
-rw-r--r-- | include/byte.h | 21 | ||||
-rw-r--r-- | include/case.h | 17 | ||||
-rw-r--r-- | include/cdbmake.h | 39 | ||||
-rw-r--r-- | include/cdbread.h | 38 | ||||
-rw-r--r-- | include/close.h | 12 | ||||
-rw-r--r-- | include/constmap.h | 21 | ||||
-rw-r--r-- | include/direntry.h | 10 | ||||
-rw-r--r-- | include/dnsresolv.h | 202 | ||||
-rw-r--r-- | include/env.h | 29 | ||||
-rw-r--r-- | include/error.h | 57 | ||||
-rw-r--r-- | include/exit.h | 13 | ||||
-rw-r--r-- | include/fd.h | 8 | ||||
-rw-r--r-- | include/fifo.h | 12 | ||||
-rw-r--r-- | include/fmt.h | 36 | ||||
-rw-r--r-- | include/genalloc.h | 74 | ||||
-rw-r--r-- | include/getln.h | 10 | ||||
-rw-r--r-- | include/getoptb.h | 28 | ||||
-rw-r--r-- | include/iopause.h | 28 | ||||
-rw-r--r-- | include/ip.h | 107 | ||||
-rw-r--r-- | include/lock.h | 14 | ||||
-rw-r--r-- | include/logmsg.h | 31 | ||||
-rw-r--r-- | include/ndelay.h | 13 | ||||
-rw-r--r-- | include/open.h | 16 | ||||
-rw-r--r-- | include/pathexec.h | 10 | ||||
-rw-r--r-- | include/prot.h | 7 | ||||
-rw-r--r-- | include/readclose.h | 14 | ||||
-rw-r--r-- | include/readwrite.h | 9 | ||||
-rw-r--r-- | include/rename.h | 6 | ||||
-rw-r--r-- | include/scan.h | 12 | ||||
-rw-r--r-- | include/seek.h | 15 | ||||
-rw-r--r-- | include/select.h | 13 | ||||
-rw-r--r-- | include/sig.h | 63 | ||||
-rw-r--r-- | include/socket_if.h | 97 | ||||
-rw-r--r-- | include/str.h | 25 | ||||
-rw-r--r-- | include/stralloc.h | 49 | ||||
-rw-r--r-- | include/tai.h | 74 | ||||
-rw-r--r-- | include/taia.h | 40 | ||||
-rw-r--r-- | include/timeout.h | 7 | ||||
-rw-r--r-- | include/timeoutconn.h | 10 | ||||
-rw-r--r-- | include/uint_t.h | 76 | ||||
-rw-r--r-- | include/wait.h | 20 | ||||
-rwxr-xr-x | install | 14 | ||||
-rw-r--r-- | iopause.c | 84 | ||||
-rw-r--r-- | ip4.c | 166 | ||||
-rw-r--r-- | ip6.c | 360 | ||||
-rw-r--r-- | lock.c | 22 | ||||
-rw-r--r-- | logmsg.c | 97 | ||||
-rw-r--r-- | man/INSTALL | 12 | ||||
-rw-r--r-- | man/alloc.3 | 68 | ||||
-rw-r--r-- | man/buffer.3 | 185 | ||||
-rw-r--r-- | man/byte.3 | 82 | ||||
-rw-r--r-- | man/case.3 | 124 | ||||
-rw-r--r-- | man/cdbmake.3 | 61 | ||||
-rw-r--r-- | man/cdbread.3 | 177 | ||||
-rw-r--r-- | man/constmap.3 | 56 | ||||
-rw-r--r-- | man/dns.3 | 249 | ||||
-rw-r--r-- | man/dnsstub.3 | 109 | ||||
-rw-r--r-- | man/env.3 | 68 | ||||
-rw-r--r-- | man/error.3 | 75 | ||||
-rw-r--r-- | man/fd.3 | 79 | ||||
-rw-r--r-- | man/fmt.3 | 90 | ||||
-rw-r--r-- | man/getln.3 | 102 | ||||
-rw-r--r-- | man/getoptb.3 | 22 | ||||
-rw-r--r-- | man/iopause.3 | 87 | ||||
-rw-r--r-- | man/ip4.3 | 73 | ||||
-rw-r--r-- | man/ip6.3 | 98 | ||||
-rw-r--r-- | man/logmsg.3 | 109 | ||||
-rw-r--r-- | man/pathexec.3 | 124 | ||||
-rw-r--r-- | man/scan.3 | 81 | ||||
-rw-r--r-- | man/socket_bind.3 | 117 | ||||
-rw-r--r-- | man/socket_connect.3 | 110 | ||||
-rw-r--r-- | man/socket_if.3 | 97 | ||||
-rw-r--r-- | man/socket_info.3 | 47 | ||||
-rw-r--r-- | man/socket_recv.3 | 56 | ||||
-rw-r--r-- | man/socket_send.3 | 76 | ||||
-rw-r--r-- | man/socket_setup.3 | 105 | ||||
-rw-r--r-- | man/socket_tcp.3 | 58 | ||||
-rw-r--r-- | man/socket_udp.3 | 58 | ||||
-rw-r--r-- | man/str.3 | 61 | ||||
-rw-r--r-- | man/stralloc.3 | 160 | ||||
-rw-r--r-- | man/taia.3 | 91 | ||||
-rw-r--r-- | man/timeout.3 | 36 | ||||
-rw-r--r-- | man/timeoutconn.3 | 52 | ||||
-rw-r--r-- | man/wait.3 | 96 | ||||
-rw-r--r-- | man/x.html | 99 | ||||
-rw-r--r-- | ndelay.c | 24 | ||||
-rw-r--r-- | open.c | 25 | ||||
-rw-r--r-- | pathexec.c | 121 | ||||
-rw-r--r-- | prot.c | 31 | ||||
-rw-r--r-- | readclose.c | 43 | ||||
-rw-r--r-- | scan.c | 120 | ||||
-rw-r--r-- | seek.c | 30 | ||||
-rwxr-xr-x | sharedlib | 2 | ||||
-rw-r--r-- | sig.c | 110 | ||||
-rw-r--r-- | socket_bind.c | 79 | ||||
-rw-r--r-- | socket_connect.c | 66 | ||||
-rw-r--r-- | socket_if.c | 36 | ||||
-rw-r--r-- | socket_info.c | 58 | ||||
-rw-r--r-- | socket_recv.c | 39 | ||||
-rw-r--r-- | socket_send.c | 62 | ||||
-rw-r--r-- | socket_setup.c | 99 | ||||
-rw-r--r-- | socket_tcp.c | 63 | ||||
-rw-r--r-- | socket_udp.c | 57 | ||||
-rw-r--r-- | str.c | 135 | ||||
-rw-r--r-- | stralloc.c | 126 | ||||
-rw-r--r-- | tai.c | 59 | ||||
-rw-r--r-- | taia.c | 104 | ||||
-rw-r--r-- | timeout.c | 59 | ||||
-rw-r--r-- | timeoutconn.c | 112 | ||||
-rw-r--r-- | uint128p.c | 100 | ||||
-rw-r--r-- | uint16p.c | 40 | ||||
-rw-r--r-- | uint32p.c | 47 | ||||
-rw-r--r-- | uint64p.c | 62 | ||||
-rw-r--r-- | uint8p.c | 39 | ||||
-rw-r--r-- | wait.c | 25 |
163 files changed, 12177 insertions, 0 deletions
@@ -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. @@ -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. + @@ -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 @@ -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 @@ -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 @@ -0,0 +1 @@ +fehQlibs-21 @@ -0,0 +1,50 @@ +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include "byte.h" +#include "alloc.h" + +#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */ +#define SPACE 4096 /* must be multiple of ALIGNMENT */ + +typedef union { char irrelevant[ALIGNMENT]; double d; } aligned; +static aligned realspace[SPACE / ALIGNMENT]; +#define space ((char *) realspace) +static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */ + +/*@null@*//*@out@*/char *alloc(unsigned int n) { + char *x; + +/* Guninski exploit + patch from Qualys (CVE-2005-1513) */ + + if (n >= (INT_MAX >> 3)) { + errno = ENOMEM; + return 0; + } + + n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */ + if (n <= avail) { avail -= n; return space + avail; } + x = malloc(n); + if (!x) errno = ENOMEM; + return x; +} + +void alloc_free(char *x) +{ + if (x >= space) + if (x < space + SPACE) + return; /* XXX: assuming that pointers are flat */ + free(x); +} + +int alloc_re(char **x,unsigned int m,unsigned int n) +{ + char *y; + + y = alloc(n); + if (!y) return 0; + byte_copy(y,m,*x); + qfree(*x); + *x = y; + return 1; +} diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..986201e --- /dev/null +++ b/base64.c @@ -0,0 +1,124 @@ +#include "base64.h" +#include "stralloc.h" +#include "str.h" + +/** + @file base64.c + @author unkown + @source unknown + @brief base64 en+decoding of strings +*/ + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(const unsigned char *in,int l,stralloc *out) /* not null terminated */ +{ + int p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + while (in[l-1] == B64PAD) { + p ++; + l--; + } + + n = (l + p) / 4; + i = (n * 3) - p; + if (!stralloc_ready(out,i)) return -1; + out->len = i; + s = out->s; + + for (i = 0; i < n - 1 ; i++) { + x = 0; + for (j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + s[2] = (unsigned char)(x & 255); x >>= 8; + s[1] = (unsigned char)(x & 255); x >>= 8; + s[0] = (unsigned char)(x & 255); x >>= 8; + s += 3; in += 4; + } + + x = 0; + for (j = 0; j < 4; j++) { + if(in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if(in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if(in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if(in[j] == '+') + x = (x << 6) + 62; + else if(in[j] == '/') + x = (x << 6) + 63; + else if(in[j] == '=') + x = (x << 6); + } + + b[2] = (unsigned char)(x & 255); x >>= 8; + b[1] = (unsigned char)(x & 255); x >>= 8; + b[0] = (unsigned char)(x & 255); x >>= 8; + + for (i = 0; i < 3 - p; i++) + s[i] = b[i]; + + return 0; +} + +int b64encode(stralloc *in,stralloc *out) /* not null terminated */ +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + i = in->len / 3 * 4 + 4; + if (!stralloc_ready(out,i)) return -1; + s = out->s; + + for (i = 0; i < in->len; i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 63]; + } + out->len = s - out->s; + + return 0; +} diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..92d72b1 --- /dev/null +++ b/buffer.c @@ -0,0 +1,226 @@ +#include <unistd.h> +#include "buffer.h" +#include "str.h" +#include "byte.h" +#include "error.h" + +/** + @file buffer.c + @author djb + @brief input/output routines +*/ + +void buffer_init(buffer *s,ssize_t (*op)(),int fd,char *buf,size_t len) +{ + s->x = buf; + s->fd = fd; + s->op = op; + s->p = 0; + s->n = len; +} + +ssize_t buffer_0_read(int fd,char *buf,size_t len) +{ + if (buffer_flush(buffer_1) == -1) return -1; + return read(fd,buf,len); +} + +char buffer_0_space[BUFFER_INSIZE]; +static buffer it0 = BUFFER_INIT(buffer_0_read,0,buffer_0_space,sizeof(buffer_0_space)); +buffer *buffer_0 = &it0; + +char buffer_1_space[BUFFER_OUTSIZE]; +static buffer it1 = BUFFER_INIT(write,1,buffer_1_space,sizeof(buffer_1_space)); +buffer *buffer_1 = &it1; + +char buffer_2_space[BUFFER_OUTSIZE]; +static buffer it2 = BUFFER_INIT(write,2,buffer_2_space,sizeof(buffer_2_space)); +buffer *buffer_2 = &it2; + +char buffer_0_small[BUFFER_SMALL]; +static buffer is0 = BUFFER_INIT(buffer_0_read,0,buffer_0_small,sizeof(buffer_0_small)); +buffer *buffer_0small = &is0; + +char buffer_1_small[BUFFER_SMALL]; +static buffer is1 = BUFFER_INIT(write,1,buffer_1_small,sizeof(buffer_1_small)); +buffer *buffer_1small = &is1; + +char buffer_2_small[BUFFER_SMALL]; +static buffer is2 = BUFFER_INIT(write,2,buffer_2_small,sizeof(buffer_2_small)); +buffer *buffer_2small = &is2; + +ssize_t buffer_unixread(int fd,char *buf,size_t len) +{ + return read(fd,buf,len); +} + +ssize_t buffer_unixwrite(int fd,char *buf,size_t len) +{ + return write(fd,buf,len); +} + +int buffer_copy(buffer *bout,buffer *bin) +{ + int n; + char *x; + + for (;;) { + n = buffer_feed(bin); + if (n < 0) return -2; + if (!n) return 0; + x = buffer_PEEK(bin); + if (buffer_put(bout,x,n) == -1) return -3; + buffer_SEEK(bin,n); + } +} + +static int oneread(ssize_t (*op)(),int fd,char *buf,size_t len) +{ + int r; + + for (;;) { + r = op(fd,buf,len); + if (r == -1) if (errno == EINTR) continue; + return r; + } +} + +static int getthis(buffer *s,char *buf,size_t len) +{ + if (len > s->p) len = s->p; + s->p -= len; + byte_copy(buf,len,s->x + s->n); + s->n += len; + return len; +} + +int buffer_feed(buffer *s) +{ + int r; + + if (s->p) return s->p; + r = oneread(s->op,s->fd,s->x,s->n); + if (r <= 0) return r; + s->p = r; + s->n -= r; + if (s->n > 0) byte_copyr(s->x + s->n,r,s->x); + return r; +} + +int buffer_bget(buffer *s,char *buf,size_t len) +{ + int r; + + if (s->p > 0) return getthis(s,buf,len); + if (s->n <= len) return oneread(s->op,s->fd,buf,s->n); + r = buffer_feed(s); + if (r <= 0) return r; + return getthis(s,buf,len); +} + +int buffer_get(buffer *s,char *buf,size_t len) +{ + int r; + + if (s->p > 0) return getthis(s,buf,len); + if (s->n <= len) return oneread(s->op,s->fd,buf,len); + r = buffer_feed(s); + if (r <= 0) return r; + return getthis(s,buf,len); +} + +char *buffer_peek(buffer *s) +{ + return s->x + s->n; +} + +void buffer_seek(buffer *s,size_t len) +{ + s->n += len; + s->p -= len; +} + +static int allwrite(ssize_t (*op)(),int fd,const char *buf,size_t len) +{ + int w; + + while (len) { + w = op(fd,buf,len); + if (w == -1) { + if (errno == EINTR) continue; + return -1; /* note that some data may have been written */ + } + if (w == 0) /* luser's fault */ + ; + buf += w; + len -= w; + } + return 0; +} + +int buffer_flush(buffer *s) +{ + int p; + + p = s->p; + if (!p) return 0; + s->p = 0; + return allwrite(s->op,s->fd,s->x,p); +} + +int buffer_putalign(buffer *s,const char *buf,size_t len) +{ + unsigned int n; + + while (len > (n = s->n - s->p)) { + byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n; + if (buffer_flush(s) == -1) return -1; + } + /* now len <= s->n - s->p */ + byte_copy(s->x + s->p,len,buf); + s->p += len; + return 0; +} + +int buffer_put(buffer *s,const char *buf,size_t len) +{ + unsigned int n; + + n = s->n; + if (len > n - s->p) { + if (buffer_flush(s) == -1) return -1; + /* now s->p == 0 */ + if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE; + while (len > s->n) { + if (n > len) n = len; + if (allwrite(s->op,s->fd,buf,n) == -1) return -1; + buf += n; + len -= n; + } + } + /* now len <= s->n - s->p */ + byte_copy(s->x + s->p,len,buf); + s->p += len; + return 0; +} + +int buffer_putflush(buffer *s,const char *buf,size_t len) +{ + if (buffer_flush(s) == -1) return -1; + return allwrite(s->op,s->fd,buf,len); +} + +int buffer_putsalign(buffer *s,const char *buf) +{ + return buffer_putalign(s,buf,str_len(buf)); +} + +int buffer_puts(buffer *s,const char *buf) +{ + return buffer_put(s,buf,str_len(buf)); +} + +int buffer_putsflush(buffer *s,const char *buf) +{ + return buffer_putflush(s,buf,str_len(buf)); +} @@ -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; + } +} @@ -0,0 +1,134 @@ +#include "case.h" +#include "str.h" + +/** + @file case.c + @author djb + @brief string comparison and helper functions; case insensitive +*/ + +int case_diffb(register char *s,unsigned int len,register char *t) +{ + register unsigned char x; + register unsigned char y; + + while (len > 0) { + --len; + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (x != y) + return ((int)(unsigned int) x) - ((int)(unsigned int) y); + } + return 0; +} + +int case_diffs(register char *s,register char *t) +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (x != y) break; + if (!x) break; + } + return ((int)(unsigned int) x) - ((int)(unsigned int) y); +} + +int case_diffrs(register char *s,register char *t) +{ + register unsigned char x = 0; + register unsigned char y = 0; + unsigned int lens = str_len(s); + unsigned int lent = str_len(t); + + while (lens > 0 && lent > 0) { + x = s[--lens] - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + y = t[--lent] - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (x != y) break; + if (!x) break; + if (!y) break; + } + return ((int)(unsigned int) x) - ((int)(unsigned int) y); +} + +void case_lowerb(char *s,unsigned int len) +{ + unsigned char x; + while (len > 0) { + --len; + x = *s - 'A'; + if (x <= 'Z' - 'A') *s = x + 'a'; + ++s; + } +} + +void case_lowers(char *s) +{ + unsigned char x; + while ((x = *s)) { + x -= 'A'; + if (x <= 'Z' - 'A') *s = x + 'a'; + ++s; + } +} + +void case_upperb(char *s,unsigned int len) +{ + unsigned char x; + while (len > 0) { + --len; + x = *s - 'a'; + if (x <= 'z' - 'a') *s = x + 'A'; + ++s; + } +} + +void case_uppers(char *s) +{ + unsigned char x; + while ((x = *s)) { + x -= 'a'; + if (x <= 'z' - 'a') *s = x + 'A'; + ++s; + } +} + +int case_startb(register char *s,unsigned int len,register char *t) +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (!y) return 1; + if (!len) return 0; + --len; + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + if (x != y) return 0; + } +} + +int case_starts(register char *s,register char *t) +{ + register unsigned char x; + register unsigned char y; + + for (;;) { + x = *s++ - 'A'; + if (x <= 'Z' - 'A') x += 'a'; else x += 'A'; + y = *t++ - 'A'; + if (y <= 'Z' - 'A') y += 'a'; else y += 'A'; + if (!y) return 1; + if (x != y) return 0; + } +} diff --git a/cdbmake.c b/cdbmake.c new file mode 100644 index 0000000..28e170c --- /dev/null +++ b/cdbmake.c @@ -0,0 +1,158 @@ +#include <unistd.h> +#include "seek.h" +#include "error.h" +#include "alloc.h" +#include "cdbread.h" +#include "cdbmake.h" + +/** + @file cdbmake.c + @author djb + @source ucspi-tcp + @brief constant data base (cdb) generation +*/ + +int cdb_make_start(struct cdb_make *c,int fd) +{ + c->head = 0; + c->split = 0; + c->hash = 0; + c->numentries = 0; + c->fd = fd; + c->pos = sizeof(c->final); + buffer_init(&c->b,write,fd,c->bspace,sizeof(c->bspace)); + return seek_set(fd,c->pos); +} + +static int posplus(struct cdb_make *c,uint32 len) +{ + uint32 newpos = c->pos + len; + if (newpos < len) { errno = ENOMEM; return -1; } + c->pos = newpos; + return 0; +} + +int cdb_make_addend(struct cdb_make *c,unsigned int keylen,unsigned int datalen,uint32 h) +{ + struct cdb_hplist *head; + + head = c->head; + if (!head || (head->num >= CDB_HPLIST)) { + head = (struct cdb_hplist *) alloc(sizeof(struct cdb_hplist)); + if (!head) return -1; + head->num = 0; + head->next = c->head; + c->head = head; + } + head->hp[head->num].h = h; + head->hp[head->num].p = c->pos; + ++head->num; + ++c->numentries; + if (posplus(c,8) == -1) return -1; + if (posplus(c,keylen) == -1) return -1; + if (posplus(c,datalen) == -1) return -1; + return 0; +} + +int cdb_make_addbegin(struct cdb_make *c,unsigned int keylen,unsigned int datalen) +{ + char buf[8]; + + if (keylen > 0xffffffff) { errno = ENOMEM; return -1; } + if (datalen > 0xffffffff) { errno = ENOMEM; return -1; } + + uint32_pack(buf,keylen); + uint32_pack(buf + 4,datalen); + if (buffer_putalign(&c->b,buf,8) == -1) return -1; + return 0; +} + +int cdb_make_add(struct cdb_make *c,char *key,unsigned int keylen,char *data,unsigned int datalen) +{ + if (cdb_make_addbegin(c,keylen,datalen) == -1) return -1; + if (buffer_putalign(&c->b,key,keylen) == -1) return -1; + if (buffer_putalign(&c->b,data,datalen) == -1) return -1; + return cdb_make_addend(c,keylen,datalen,cdb_hash(key,keylen)); +} + +int cdb_make_finish(struct cdb_make *c) +{ + char buf[8]; + int i; + uint32 len; + uint32 u; + uint32 memsize; + uint32 count; + uint32 where; + struct cdb_hplist *x; + struct cdb_hp *hp; + + for (i = 0; i < 256; ++i) + c->count[i] = 0; + + for (x = c->head; x; x = x->next) { + i = x->num; + while (i--) + ++c->count[255 & x->hp[i].h]; + } + + memsize = 1; + for (i = 0; i < 256; ++i) { + u = c->count[i] * 2; + if (u > memsize) + memsize = u; + } + + memsize += c->numentries; /* no overflow possible up to now */ + u = (uint32) 0 - (uint32) 1; + u /= sizeof(struct cdb_hp); + if (memsize > u) { errno = ENOMEM; return -1; } + + c->split = (struct cdb_hp *) alloc(memsize * sizeof(struct cdb_hp)); + if (!c->split) return -1; + + c->hash = c->split + c->numentries; + + u = 0; + for (i = 0; i < 256; ++i) { + u += c->count[i]; /* bounded by numentries, so no overflow */ + c->start[i] = u; + } + + for (x = c->head; x; x = x->next) { + i = x->num; + while (i--) + c->split[--c->start[255 & x->hp[i].h]] = x->hp[i]; + } + + for (i = 0; i < 256; ++i) { + count = c->count[i]; + + len = count + count; /* no overflow possible */ + uint32_pack(c->final + 8 * i,c->pos); + uint32_pack(c->final + 8 * i + 4,len); + + for (u = 0; u < len; ++u) + c->hash[u].h = c->hash[u].p = 0; + + hp = c->split + c->start[i]; + for (u = 0; u < count; ++u) { + where = (hp->h >> 8) % len; + while (c->hash[where].p) + if (++where == len) + where = 0; + c->hash[where] = *hp++; + } + + for (u = 0; u < len; ++u) { + uint32_pack(buf,c->hash[u].h); + uint32_pack(buf + 4,c->hash[u].p); + if (buffer_putalign(&c->b,buf,8) == -1) return -1; + if (posplus(c,8) == -1) return -1; + } + } + + if (buffer_flush(&c->b) == -1) return -1; + if (seek_begin(c->fd) == -1) return -1; + return buffer_putflush(&c->b,c->final,sizeof(c->final)); +} diff --git a/cdbread.c b/cdbread.c new file mode 100644 index 0000000..c8ffa42 --- /dev/null +++ b/cdbread.c @@ -0,0 +1,169 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> +#include "error.h" +#include "seek.h" +#include "byte.h" +#include "cdbread.h" + +/** + @file cdbread.c + @author djb + @source ucspi-tcp, fastforward + @brief read entries from a cdb +*/ + +uint32 cdb_unpack(unsigned char *buf) +{ + uint32 num; + num = buf[3]; num <<= 8; + num += buf[2]; num <<= 8; + num += buf[1]; num <<= 8; + num += buf[0]; + return num; +} + +void cdb_free(struct cdb *c) +{ + if (c->map) { + munmap(c->map,c->size); + c->map = 0; + } +} + +void cdb_findstart(struct cdb *c) +{ + c->loop = 0; +} + +void cdb_init(struct cdb *c,int fd) +{ + struct stat st; + char *x; + + cdb_free(c); + cdb_findstart(c); + c->fd = fd; + + if (fstat(fd,&st) == 0) + if (st.st_size <= 0xffffffff) { + x = mmap(0,st.st_size,PROT_READ,MAP_SHARED,fd,0); + if (x + 1) { + c->size = st.st_size; + c->map = x; + } + } +} + +int cdb_read(struct cdb *c,char *buf,unsigned int len,uint32 pos) +{ + if (c->map) { + if ((pos > c->size) || (c->size - pos < len)) goto FORMAT; + byte_copy(buf,len,c->map + pos); + } + else { + if (seek_set(c->fd,pos) == -1) return -1; + while (len > 0) { + int r; + do + r = read(c->fd,buf,len); + while ((r == -1) && (errno == EINTR)); + if (r == -1) return -1; + if (r == 0) goto FORMAT; + buf += r; + len -= r; + } + } + return 0; + + FORMAT: + errno = EPROTO; + return -1; +} + +static int match(struct cdb *c,char *key,unsigned int len,uint32 pos) +{ + char buf[32]; + int n; + + while (len > 0) { + n = sizeof(buf); + if (n > len) n = len; + if (cdb_read(c,buf,n,pos) == -1) return -1; + if (byte_diff(buf,n,key)) return 0; + pos += n; + key += n; + len -= n; + } + return 1; +} + +int cdb_findnext(struct cdb *c,char *key,unsigned int len) +{ + char buf[8]; + uint32 pos; + uint32 u; + + if (!c->loop) { + u = cdb_hash(key,len); + if (cdb_read(c,buf,8,(u << 3) & 2047) == -1) return -1; + uint32_unpack(buf + 4,&c->hslots); + if (!c->hslots) return 0; + uint32_unpack(buf,&c->hpos); + c->khash = u; + u >>= 8; + u %= c->hslots; + u <<= 3; + c->kpos = c->hpos + u; + } + + while (c->loop < c->hslots) { + if (cdb_read(c,buf,8,c->kpos) == -1) return -1; + uint32_unpack(buf + 4,&pos); + if (!pos) return 0; + c->loop += 1; + c->kpos += 8; + if (c->kpos == c->hpos + (c->hslots << 3)) c->kpos = c->hpos; + uint32_unpack(buf,&u); + if (u == c->khash) { + if (cdb_read(c,buf,8,pos) == -1) return -1; + uint32_unpack(buf,&u); + if (u == len) + switch(match(c,key,len,pos + 8)) { + case -1: + return -1; + case 1: + uint32_unpack(buf + 4,&c->dlen); + c->dpos = pos + 8 + len; + return 1; + } + } + } + + return 0; +} + +int cdb_find(struct cdb *c,char *key,unsigned int len) +{ + cdb_findstart(c); + return cdb_findnext(c,key,len); +} + +uint32 cdb_hashadd(uint32 h,unsigned char c) +{ + h += (h << 5); + return h ^ c; +} + +uint32 cdb_hash(char *buf,unsigned int len) +{ + uint32 h; + + h = CDB_HASHSTART; + while (len) { + h = cdb_hashadd(h,*buf++); + --len; + } + return h; +} diff --git a/conf-build b/conf-build new file mode 100644 index 0000000..a2428c4 --- /dev/null +++ b/conf-build @@ -0,0 +1,30 @@ +#!/bin/sh +# Build and install options for qlibs +# +#******************************************************************************** +# default compiler flags - usually this shouldn't be changed +CFLAGS='-O2 -Iinclude -fPIC -Wall' + +# under OmniOS the binaries are build as 32 bit version; except enabling this: +#CFLAGS='-O2 -Iinclude -Wall -fPIC -m64' + +# You can check the ELFCLASS while calling eg.: 'file open.o'. +#******************************************************************************** +# destination folder(s) (install options of qlibs) +# +# Define a destination where libs and/or header files will be installed into. If +# a var is empty, then nothing will be installed (copied). The term 'libs' here +# means all '*.a' files and additional all files given by $OFILES (see below). +# Example 1: +# LIBDIR=.. - copy all libs into the upper folder +# HDRDIR= - don't copy header files ('*.h') +# Example 2: +# LIBDIR=/usr/local/lib - copy all libs into /usr/local/lib +# HDRDIR=$LIBDIR/include - copy all header files into /usr/local/lib/include +# +LIBDIR=.. +HDRDIR= + +#******************************************************************************** +# Additional libs (object files, delimited by space, enclosed in "'") +OFILES='base64.o byte.o fmt.o prot.o pathexec.o readclose.o scan.o constmap.o' diff --git a/configure b/configure new file mode 100755 index 0000000..34785ac --- /dev/null +++ b/configure @@ -0,0 +1,69 @@ +#!/bin/sh +#******************************************************************************** +# Very simple configure script for qlibs + +. ./conf-build +# Add $HDRDIR as include option to default $CFLAGS +[ "$HDRDIR" ] && CFLAGS="$CFLAGS -I$HDRDIR" + +#******************************************************************************** +# check for system header files +# +# Usually, 'select.h' should (have to) be in this location ... +SELECT_H="/usr/include/sys/select.h" +echo -n "Checking for select.h ..." +if [ -f $SELECT_H ] ; then CFLAGS="$CFLAGS -DHAS_SELECT_H" + else echo -n " not" ; fi +echo " found!" + +# On linux we have flock, other systems have lockf instead +echo -n "Checking for flock/lockf ..." +which flock 2>/dev/null >/dev/null +if [ $? -eq 0 ] ; then CCOPTS="$CFLAGS -DHASFLOCK=1" ; echo -n " flock" + else echo -n " lockf" ; fi +echo " found!" + +# Current systems provide a poll interface ... via +POLL_H="/usr/include/poll.h" +POLL_H1="/usr/include/sys/poll.h" +echo -n "Checking for poll.h ..." +if [ -f $POLL_H -o -f $POLL_H1 ] ; then CFLAGS="$CFLAGS -DHAS_POLL_H" + else echo -n " not" ; fi +echo " found!" + +# Current systems support for 8 and 64 bit integers ... via +STDINT_H="/usr/include/stdint.h" +echo -n "Checking for uint8_t ..." +if [ `grep -c uint_least8_t $STDINT_H` ] ; then CFLAGS="$CFLAGS -DHAS_UINT8_H" + else echo -n " not" ; fi +echo " found!" +echo -n "Checking for uint64_t ..." +if [ `grep -c uint_least64_t $STDINT_H` ] ; then CFLAGS="$CFLAGS -DHAS_UINT64_H" + else echo -n " not" ; fi +echo " found!" + +#******************************************************************************** +# Create compile, load, makelib, sharedlib +echo -n "Checking for compile ... " +CC="cc" +( echo '#!/bin/sh' + echo exec "$CC" "$CFLAGS" -c '${1+"$@"}' ) > compile +chmod 755 compile +echo " created!" + +echo -n "Checking for makelib ... " +( echo '#!/bin/sh' ; + echo "" ; + echo 'main="$1"; shift' ; \ + echo 'rm -f "$main"' ; \ + echo 'ar cr "$main" ${1+"$@"}' ; \ + echo 'ranlib "$main"') > makelib +chmod 755 makelib +echo " created!" + +echo -n "Checking for sharedlib ... " +CC="cc" +( echo '#!/bin/sh' + echo exec "$CC" -shared '${1+"$@"}' ) > sharedlib +chmod 755 sharedlib +echo " created!" diff --git a/constmap.c b/constmap.c new file mode 100644 index 0000000..ecd5a92 --- /dev/null +++ b/constmap.c @@ -0,0 +1,169 @@ +#include "constmap.h" +#include "alloc.h" +#include "case.h" + +static constmap_hash hash(char *s,int len) +{ + unsigned char ch; + constmap_hash h; + h = 5381; + while (len > 0) { + ch = *s++ - 'A'; + if (ch <= 'Z' - 'A') ch += 'a' - 'A'; + h = ((h << 5) + h) ^ ch; + --len; + } + return h; +} + +char *constmap(struct constmap *cm,char *s,int len) +{ + constmap_hash h; + int pos; + h = hash(s,len); + pos = cm->first[h & cm->mask]; + while (pos != -1) { + if (h == cm->hash[pos]) + if (len == cm->inputlen[pos]) + if (!case_diffb(cm->input[pos],len,s)) + return cm->input[pos] + cm->inputlen[pos] + 1; + pos = cm->next[pos]; + } + return 0; +} + +int constmap_init(struct constmap *cm,char *s,int len,int flagcolon) +{ + int i; + int j; + int k; + int pos; + constmap_hash h; + + cm->num = 0; + for (j = 0; j < len; ++j) if (!s[j]) ++cm->num; + + h = 64; + while (h && (h < cm->num)) h += h; + cm->mask = h - 1; + + cm->first = (int *) alloc(sizeof(int) * h); + if (cm->first) { + cm->input = (char **) alloc(sizeof(char *) * cm->num); + if (cm->input) { + cm->inputlen = (int *) alloc(sizeof(int) * cm->num); + if (cm->inputlen) { + cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num); + if (cm->hash) { + cm->next = (int *) alloc(sizeof(int) * cm->num); + if (cm->next) { + for (h = 0; h <= cm->mask; ++h) + cm->first[h] = -1; + pos = 0; + i = 0; + for (j = 0; j < len; ++j) + if (!s[j]) { + k = j - i; + if (flagcolon) { + for (k = i; k < j; ++k) + if (s[k] == ':') break; + if (k >= j) { i = j + 1; continue; } + k -= i; + } + cm->input[pos] = s + i; + cm->inputlen[pos] = k; + h = hash(s + i,k); + cm->hash[pos] = h; + h &= cm->mask; + cm->next[pos] = cm->first[h]; + cm->first[h] = pos; + ++pos; + i = j + 1; + } + return 1; + } + alloc_free(cm->hash); + } + alloc_free(cm->inputlen); + } + alloc_free(cm->input); + } + alloc_free(cm->first); + } + return 0; +} + +int constmap_init_char(struct constmap *cm,char *s,int len,int flagcolon,char flagchar) +{ + int i; + int j; + int k; + int pos; + constmap_hash h; + + if (!flagchar || flagchar == 0 || flagchar == '\0') { + flagchar = ':'; + } + + cm->num = 0; + for (j = 0; j < len; ++j) if (!s[j]) ++cm->num; + + h = 64; + while (h && (h < cm->num)) h += h; + cm->mask = h - 1; + + cm->first = (int *) alloc(sizeof(int) * h); + if (cm->first) { + cm->input = (char **) alloc(sizeof(char *) * cm->num); + if (cm->input) { + cm->inputlen = (int *) alloc(sizeof(int) * cm->num); + if (cm->inputlen) { + cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num); + if (cm->hash) { + cm->next = (int *) alloc(sizeof(int) * cm->num); + if (cm->next) { + for (h = 0; h <= cm->mask; ++h) + cm->first[h] = -1; + pos = 0; + i = 0; + for (j = 0; j < len; ++j) { + if (!s[j]) { + k = j - i; + if (flagcolon) { + for (k = i; k < j; ++k) + if (s[k] == flagchar) break; + if (k >= j) { i = j + 1; continue; } + k -= i; + } + cm->input[pos] = s + i; + cm->inputlen[pos] = k; + h = hash(s + i,k); + cm->hash[pos] = h; + h &= cm->mask; + cm->next[pos] = cm->first[h]; + cm->first[h] = pos; + ++pos; + i = j + 1; + } + } + return 1; + } + alloc_free(cm->hash); + } + alloc_free(cm->inputlen); + } + alloc_free(cm->input); + } + alloc_free(cm->first); + } + return 0; +} + +void constmap_free(struct constmap *cm) +{ + alloc_free(cm->next); + alloc_free(cm->hash); + alloc_free(cm->inputlen); + alloc_free(cm->input); + alloc_free(cm->first); +} diff --git a/dnsstub/Makefile b/dnsstub/Makefile new file mode 100644 index 0000000..6c3bab3 --- /dev/null +++ b/dnsstub/Makefile @@ -0,0 +1,23 @@ + +COMPILE=../compile +MAKELIB=../makelib +CCFLAGS=-I../include + +default: clean check dnsresolv.a + +check: + @[ -f $(COMPILE) ] && [ -f $(MAKELIB) ] || ( cd .. ; ./configure ; ) + +clean: + rm -f `cat TARGETS` + +dnsresolv.a: \ +dns_domain.c dns_dtda.c dns_ip.c dns_ipq.c dns_name.c \ +dns_nd.c dns_packet.c dns_random.c dns_rcip.c dns_rcrw.c dns_resolve.c \ +dns_sortip.c dns_transmit.c dns_txt.c + $(COMPILE) $(CCFLAGS) dns_domain.c dns_dfd.c dns_dtda.c dns_ip.c dns_ipq.c \ + dns_mx.c dns_name.c dns_nd.c dns_packet.c dns_random.c dns_rcip.c \ + dns_rcrw.c dns_resolve.c dns_sortip.c dns_transmit.c dns_txt.c dns_cname.c + $(MAKELIB) dnsresolv.a dns_domain.o dns_dfd.o dns_dtda.o dns_ip.o dns_ipq.o \ + dns_mx.o dns_name.o dns_nd.o dns_packet.o dns_random.o dns_rcip.o \ + dns_rcrw.o dns_resolve.o dns_sortip.o dns_transmit.o dns_txt.o dns_cname.o diff --git a/dnsstub/README.md b/dnsstub/README.md new file mode 100644 index 0000000..95cd11c --- /dev/null +++ b/dnsstub/README.md @@ -0,0 +1,171 @@ +/*! \mainpage + +Stub Resolver +============= + +Simple DJBDNS stub-resolver based on 'djbdns-1.05(IPv6)' allowing for each +calling application individually to include up to 16 DNSCACHEIP(s) +as DNS forwarding/resolving servers to be tried sequentially. + +IP Addresses +------------ + +Here, + - global IPv6, + - IPv6 ULA, and + - IPv6 LLU addresses with a given Interface-Id +can be specified. The IPv4 format could be either a + - legacy dotted-decimal or a + - IPv4-mapped IPv6 address. + +In any case, compactified IPv6 addresses are understood. +IPv4/IPv6 addresses in brackets are understood by dns_ip. + +Resolver Call +------------- + +If $DNSCACHEIP is not provided as environment variable, the stub-resolver +will use the system-wide + - /etc/resolv.conf +file; however now without the capability for IPv6 LLU addresses. +While IPv4-mapped IPv6 addresses are supported here by default as well, +care has to taken not to jeopardize other client's usage. + +Name Qualification +------------------ + +If provided, the stub-resolver uses either a system-wide configuration file + - /etc/dnsrewritefile or assumes this file to available as given in + - $DNSREWRITEFILE +in order to define persistent mapping-rules of local domain names to public +ones (for lookup) or IP addresses (for direct matching). + +Well-known domain names 'localhost', 'ip4-loopback' and 'ip6-loopback' +are handled locally, thus no DNS query is used (RFC 6761). +'localhost' is advertised as '::1' and '::ff:127.0.0.1' in it's native +IPv6 format. It is up to the caller to convert the IPv6-mapped IPv4 +address to the IPv4 format. + +Local domain names can be alternatively specified (per application) using +the environment variable + - $LOCALDOMAIN +to be appended to unqualified hostnames dynamically. This is roughly equivalent +with the 'search' string in /etc/resolv. Several domains names may be +specified within $LOCALDOMAIN separated by blanks. + +See: https://cr.yp.to/djbdns/qualify.html + + +Specific DNS Record type lookup +------------------------------- + +* dns_ip (A, AAAAA) +* dns_name (PTR) +* dns_cname (CNAME) +* dns_txt (TXT) -- now considering several 'labels' +* dns_mx (MX) + + +Internals +--------- + +* UDP message size: +Unlike other implementations, this DNS stub-resolver supports UDP packet +sizes up to 1028 byte without the need for (E)DNS0 packet enhancements. + +* DNS UDP query retrials: +In case the NS is not able to initally reply to the query, +it is retried again at the intervalls {1, 2, 4, 8, 16} secs. + +* DNS name qualification (dns_ip_qualify): +Well-known domain names are qualified locally without invoking a DNS query +while handling IPv4 and IPv6 addresses separately. + +* NS qualification/sorting for NS replies: +NS qualification is not supported (yet), thus we use a randomly sorted +list of NS IP addresses. + +* Query/Reply to/from DNS Cache servers/forwarders: +Neither message (CurveDNS) nor transport layer (TLS) encryption is provided; +the sub-resolver 'trusts' it's upstream caches/forwarders. We recommend to +setup communication on private IPv4/IPv6 addresses; if applicable. + +* DNS TXT Records: +The label substructure is now recognized in the RDATA section; +each label may have the size of 255 byte. +The length information is excluded from the output. +Only printable characters are recognized in the output. + +* Return Codes: +Different from DJB's initial routines, the DNS front-end routines + dns_cname*, dns_ip*, dns_mx*, dns_name*, dns dns_txt* +return now the number of replies received (not bytes!). +Thus, three cases need to be considered: + + - rc < 0: Problem occured (SOFTFAIL, HARDFAIL) + - rc = 0: No answer obtained (but query was successful) = NXDOMAIN + - rc > 0: rc answers received; positive reply + +For return codes < 0, the following conventions have been applied: + + include/dnsresolv.h + +\#define DNS_NXD 0 +\#define DNS_MEM -1 +\#define DNS_ERR -2 /* parsing errors and others */ +\#define DNS_COM -3 /* (socket) communication errors */ +\#define DNS_INT -4 /* internal errors */ +\#define DNS_SOFT -5 /* either -2 or -3 */ +\#define DNS_HARD -6 /* CNAME loop problem */ + +The modification of the return code is typically not problematic, +since mostly just rc = -1 is checked. + +In the future, these return codes are subject of change. +Thus, instead of + + if (dns_XX(...) == -1) + +one shoud use the more general syntax + + if (dns_XX(...) < 0) + +to check for 'negative' results, allowing further actions +and refinements given the calling sequence. + + + +Environment Variables Read +-------------------------- + +$DNSCACHEPIP The upstream resolver's IP[v4|v6] addresses (up to 32). + IPv6 LLU addresses may be suffixed with the interface name. +$DNSREWRITEFILE Alternate location for the system-wide + /etc/dnsrewrite +file +$LOCALDOMAIN Additional local domain name appended to unqualified + hostnames dynamically. + +Sample for the file /etc/dnsrewrite: + +\#annything.local -> me +\-.example.com:me +\# me -> 127.0.0.1 +\=me:127.0.0.1 +\# any.name.a -> any.name.af.mil +\*.a:.af.mil +\# any-name-without-dots -> any-name-without-dots.heaven.af.mil +\?:.heaven.af.mil +\# remove trailing dot +\*.: + +and DJB's explanations are given here: + +Instructions are followed in order, each at most once. There are four types of instructions: + +\=post:new means that the host name post is replaced by new. +\*post:new means that any name of the form prepost is replaced by prenew. +\?post:new means that any name of the form prepost, where pre does not contain dots or brackets, is replaced by prenew. +\-post:new means that any name of the form prepost is replaced by new. + +Erwin Hoffmann, June 2023. diff --git a/dnsstub/TARGETS b/dnsstub/TARGETS new file mode 100644 index 0000000..62f40be --- /dev/null +++ b/dnsstub/TARGETS @@ -0,0 +1,18 @@ +dns_cname.o +dns_dfd.o +dns_domain.o +dns_dtda.o +dns_ip.o +dns_ipq.o +dns_mx.o +dns_name.o +dns_nd.o +dns_packet.o +dns_random.o +dns_rcip.o +dns_rcrw.o +dns_resolve.o +dns_sortip.o +dns_transmit.o +dns_txt.o +dnsresolv.a diff --git a/dnsstub/dns_cname.c b/dnsstub/dns_cname.c new file mode 100644 index 0000000..408949a --- /dev/null +++ b/dnsstub/dns_cname.c @@ -0,0 +1,59 @@ +#include "stralloc.h" +#include "uint_t.h" +#include "byte.h" +#include "ip.h" +#include "case.h" +#include "dnsresolv.h" + +/** + @file dns_cname.c + @author feh + @brief DNS cname lookup +*/ + +static char *q = 0; + +int dns_cname_packet(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[12]; + uint16 numanswers; + uint16 datalen; + int ranswers = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_CNAME)) + if (byte_equal(header + 2,2,DNS_C_IN)) { + if (!dns_packet_getname(buf,len,pos,&q)) return DNS_ERR; + if (dns_domain_todot_cat(out,q) <= 0) return DNS_ERR; + } + pos += datalen; + ++ranswers; + } + + return ranswers; +} + +int dns_cname(stralloc *out,stralloc *fqdn) +{ + int rc; + + if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; + if (dns_resolve(q,DNS_T_CNAME) < 0) return DNS_ERR; + if ((rc = dns_cname_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + if (case_equals(out->s,fqdn->s)) rc = -6; // loop DNS_HARD + + return rc; +} diff --git a/dnsstub/dns_dfd.c b/dnsstub/dns_dfd.c new file mode 100644 index 0000000..756a1f8 --- /dev/null +++ b/dnsstub/dns_dfd.c @@ -0,0 +1,76 @@ +#include "error.h" +#include "alloc.h" +#include "byte.h" +#include "dnsresolv.h" + +/** + @file dns_dfd.c + @author djb + @source ucspi-tcp + @brief domain name qualification (domain from dot) +*/ + +int dns_domain_fromdot(char **out,const char *buf,unsigned int n) +{ + char label[63]; + unsigned int labellen = 0; /* <= sizeof label */ + char name[255]; + unsigned int namelen = 0; /* <= sizeof name */ + char ch; + char *x; + + errno = EPROTO; + + for (;;) { + if (!n) break; + ch = *buf++; --n; + if (ch == '.') { + if (labellen) { + if (namelen + labellen + 1 > sizeof(name)) return 0; + name[namelen++] = labellen; + byte_copy(name + namelen,labellen,label); + namelen += labellen; + labellen = 0; + } + continue; + } + if (ch == '\\') { // octal -> decimal + if (!n) break; + ch = *buf++; --n; + if ((ch >= '0') && (ch <= '7')) { + ch -= '0'; + if (n && (*buf >= '0') && (*buf <= '7')) { + ch <<= 3; + ch += *buf - '0'; + ++buf; --n; + if (n && (*buf >= '0') && (*buf <= '7')) { + ch <<= 3; + ch += *buf - '0'; + ++buf; --n; + } + } + } + } + if (labellen >= sizeof(label)) return 0; + label[labellen++] = ch; + } + + if (labellen) { + if (namelen + labellen + 1 > sizeof(name)) return 0; + name[namelen++] = labellen; + byte_copy(name + namelen,labellen,label); + namelen += labellen; + labellen = 0; + } + + if (namelen + 1 > sizeof(name)) return 0; + name[namelen++] = 0; + + x = alloc(namelen); + if (!x) return DNS_MEM; + byte_copy(x,namelen,name); + + if (*out) alloc_free(*out); + *out = x; + return 1; +} diff --git a/dnsstub/dns_domain.c b/dnsstub/dns_domain.c new file mode 100644 index 0000000..654a827 --- /dev/null +++ b/dnsstub/dns_domain.c @@ -0,0 +1,80 @@ +#include "alloc.h" +#include "case.h" +#include "byte.h" +#include "dnsresolv.h" + +/** + @file dns_domain.c + @author djb + @source ucspi-tcp + @brief domain qualification +*/ + +unsigned int dns_domain_length(const char *dn) +{ + const char *x; + unsigned char c; + + x = dn; + while ((c = *x++)) + x += (unsigned int) c; + return x - dn; +} + +void dns_domain_free(char **out) +{ + if (*out) { + alloc_free(*out); + *out = 0; + } +} + +int dns_domain_copy(char **out,const char *in) +{ + unsigned int len; + char *x; + + len = dns_domain_length(in); + x = alloc(len); + if (!x) return 0; + byte_copy(x,len,in); + if (*out) alloc_free(*out); + *out = x; + return 1; +} + +int dns_domain_equal(const char *dn1,const char *dn2) +{ + unsigned int len; + + len = dns_domain_length(dn1); + if (len != dns_domain_length(dn2)) return 0; + + if (case_diffb((char *)dn1,len,(char *)dn2)) return 0; /* safe since 63 < 'A' */ + return 1; +} + +int dns_domain_suffix(const char *big,const char *little) +{ + unsigned char c; + + for (;;) { + if (dns_domain_equal(big,little)) return 1; + c = *big++; + if (!c) return 0; + big += c; + } +} + +unsigned int dns_domain_suffixpos(const char *big,const char *little) +{ + const char *orig = big; + unsigned char c; + + for (;;) { + if (dns_domain_equal(big,little)) return big - orig; + c = *big++; + if (!c) return 0; + big += c; + } +} diff --git a/dnsstub/dns_dtda.c b/dnsstub/dns_dtda.c new file mode 100644 index 0000000..38358a2 --- /dev/null +++ b/dnsstub/dns_dtda.c @@ -0,0 +1,43 @@ +#include "stralloc.h" +#include "dnsresolv.h" + +/** + @file dns_dtda.c + @author djb + @source ucspi-tcp + @brief domain to dot append +*/ + +int dns_domain_todot_cat(stralloc *out,const char *d) +{ + char ch; + char ch2; + unsigned char ch3; + char buf[4]; + + if (!*d) + return stralloc_append(out,"."); + + for (;;) { + ch = *d++; + while (ch--) { + ch2 = *d++; + if ((ch2 >= 'A') && (ch2 <= 'Z')) ch2 += 32; // FQDN -> lowercase + if (((ch2 >= 'a') && (ch2 <= 'z')) || + ((ch2 >= '0') && (ch2 <= '9')) || + (ch2 == '-') || (ch2 == '_')) { + if (!stralloc_append(out,&ch2)) return DNS_MEM; + } + else { // decimal -> octal + ch3 = ch2; + buf[3] = '0' + (ch3 & 7); ch3 >>= 3; + buf[2] = '0' + (ch3 & 7); ch3 >>= 3; + buf[1] = '0' + (ch3 & 7); + buf[0] = '\\'; + if (!stralloc_catb(out,buf,4)) return DNS_MEM; + } + } + if (!*d) return 1; + if (!stralloc_append(out,".")) return DNS_MEM; + } +} diff --git a/dnsstub/dns_ip.c b/dnsstub/dns_ip.c new file mode 100644 index 0000000..f89728c --- /dev/null +++ b/dnsstub/dns_ip.c @@ -0,0 +1,198 @@ +#include "stralloc.h" +#include "uint_t.h" +#include "byte.h" +#include "ip.h" +#include "dnsresolv.h" + +/** + @file dns_ip.c + @author djb, fefe, feh + @source ucspi-tcp6 + @brief DNS IP query +*/ + +static char *q = 0; + +int dns_ip4_packet(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[12]; + uint16 numanswers; + uint16 datalen; + int ranswers = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_A)) + if (byte_equal(header + 2,2,DNS_C_IN)) + if (datalen == 4) { + if (!dns_packet_copy(buf,len,pos,header,4)) return DNS_ERR; + if (!stralloc_catb(out,header,4)) return DNS_MEM; + } + pos += datalen; + ++ranswers; + } + + dns_sortip4(out->s,out->len); + return ranswers; +} + +int dns_ip4(stralloc *out,stralloc *fqdn) +{ + unsigned int i; + char code = 0; + int dot = 0; + char ch; + char ip[4]; + int r; + int rc = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + if (!stralloc_readyplus(fqdn,1)) return DNS_MEM; + + fqdn->s[fqdn->len] = 0; /* test FQDN string */ + for (i = 1; i < fqdn->len; i++) { + if (fqdn->s[i] >= '_') { code = 127; break; } + if (fqdn->s[i] == '.') dot++; + } + + if (code != 127 && dot == 3) /* if FQDN is just IPv4 */ + if (ip4_scan(fqdn->s,ip) || ip4_scanbracket(fqdn->s,ip)) { + if (!stralloc_copyb(out,ip,4)) return DNS_MEM; + return 1; + } + + code = 0; + for (i = 0; i <= fqdn->len; ++i) { + if (i < fqdn->len) + ch = fqdn->s[i]; + else + ch = '.'; + + if ((ch == '[') || (ch == ']')) continue; + if (ch == '.') { + if (!stralloc_append(out,&code)) return DNS_MEM; + code = 0; + continue; + } + if ((ch >= '0') && (ch <= '9')) { + code *= 10; + code += ch - '0'; + continue; + } + + if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; // fdqn -> A query -> response + if (dns_resolve(q,DNS_T_A) >= 0) { + if ((r = dns_ip4_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + rc += r; + } + + return rc; + } + + out->len &= ~3; + return 0; +} + +int dns_ip6_packet(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[16]; + uint16 numanswers; + uint16 datalen; + int ranswers = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_AAAA)) { + if (byte_equal(header + 2,2,DNS_C_IN)) + if (datalen == 16) { + if (!dns_packet_copy(buf,len,pos,header,16)) return DNS_ERR; + if (!stralloc_catb(out,header,16)) return DNS_MEM; + } + } else if (byte_equal(header,2,DNS_T_A)) + if (byte_equal(header + 2,2,DNS_C_IN)) + if (datalen == 4) { + byte_copy(header,12,V4mappedprefix); + if (!dns_packet_copy(buf,len,pos,header + 12,4)) return DNS_ERR; + if (!stralloc_catb(out,header,16)) return DNS_MEM; + } + pos += datalen; + ++ranswers; + } + + dns_sortip6(out->s,out->len); + return ranswers; +} + +int dns_ip6(stralloc *out,stralloc *fqdn) +{ + unsigned int i; + char code; + char ch; + char ip[16]; + int r; + int rc = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + if (!stralloc_readyplus(fqdn,1)) return DNS_MEM; + + fqdn->s[fqdn->len] = 0; /* if FQDN is just IPv6 */ + if (ip6_scan(fqdn->s,ip) || ip6_scanbracket(fqdn->s,ip)) { + if (!stralloc_copyb(out,ip,16)) return DNS_MEM; + return 1; + } + + code = 0; + for (i = 0; i <= fqdn->len; ++i) { + if (i < fqdn->len) + ch = fqdn->s[i]; + else + ch = '.'; + + if ((ch == '[') || (ch == ']')) continue; + if (ch == '.') { + if (!stralloc_append(out,&code)) return DNS_MEM; + code = 0; + continue; + } + if ((ch >= '0') && (ch <= '9')) { + code *= 10; + code += ch - '0'; + continue; + } + + if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; // fqdn -> AAAA query -> response + if (dns_resolve(q,DNS_T_AAAA) >= 0) { + if ((r = dns_ip6_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + rc += r; + } + + return rc; + } + + out->len &= ~3; + return 0; +} diff --git a/dnsstub/dns_ipq.c b/dnsstub/dns_ipq.c new file mode 100644 index 0000000..26c3818 --- /dev/null +++ b/dnsstub/dns_ipq.c @@ -0,0 +1,236 @@ +#include "case.h" +#include "byte.h" +#include "str.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "socket_if.h" +#include "ip.h" + +/** + @file dns_ipq.c + @author djb, feh + @source ucspi-tcp + @brief DNS hostname qualification for ipv4 and ipv6 +*/ + +/** + @fn int doit -> @return number of added chars to name +*/ +static int doit(stralloc *work,const char *rule) +{ + char ch; + unsigned int colon; + unsigned int prefixlen; + + ch = *rule++; + if ((ch != '?') && (ch != '=') && (ch != '*') && (ch != '-')) return 1; + colon = str_chr((char *)rule,':'); + if (!rule[colon]) return 1; + + if (work->len < colon) return 1; + prefixlen = work->len - colon; + if ((ch == '=') && prefixlen) return 1; + if (case_diffb((char *)rule,colon,work->s + prefixlen)) return 1; + if (ch == '?') { + if (byte_chr(work->s,prefixlen,'.') < prefixlen) return 1; + if (byte_chr(work->s,prefixlen,'[') < prefixlen) return 1; + if (byte_chr(work->s,prefixlen,']') < prefixlen) return 1; + } + + work->len = prefixlen; + if (ch == '-') work->len = 0; + return stralloc_cats(work,rule + colon + 1); +} + +/** @fn int dns_ip4_qualify_rules -> @return number of IPv4 addresss with rules */ + +int dns_ip4_qualify_rules(stralloc *ipout,stralloc *fqdn,const stralloc *in,const stralloc *rules) +{ + unsigned int i; + unsigned int j; + unsigned int plus; + unsigned int fqdnlen; + int rc = 0; + + if (!stralloc_copy(fqdn,(stralloc *)in)) return DNS_MEM; + + for (j = i = 0; j < rules->len; ++j) + if (!rules->s[j]) { + if (!doit(fqdn,rules->s + i)) return DNS_INT; + i = j + 1; + } + + fqdnlen = fqdn->len; + plus = byte_chr(fqdn->s,fqdnlen,'+'); + if (plus >= fqdnlen) + return dns_ip4(ipout,fqdn); + + i = plus + 1; + for (;;) { + j = byte_chr(fqdn->s + i,fqdnlen - i,'+'); + byte_copy(fqdn->s + plus,j,fqdn->s + i); + fqdn->len = plus + j; + if (rc += dns_ip4(ipout,fqdn) < 0) return DNS_ERR; + i += j; + if (i >= fqdnlen) return rc; + ++i; + } + return 0; +} + +/** @fn int dns_ip4_qualify -> @return number of IPv4 addresss qualified */ + +int dns_ip4_qualify(stralloc *ipout,stralloc *fqdn,const stralloc *in) +{ + int r; + static stralloc rules; + + if ((r = dns_ip_qualify_localhost(ipout,fqdn,in)) > 0 ) return r; + if (dns_resolvconfrewrite(&rules) < 0) return DNS_INT; + return dns_ip4_qualify_rules(ipout,fqdn,in,&rules); +} + +/** @fn int dns_ip4_qualify_rules -> @return number of IPv6 addresss with rules */ + +int dns_ip6_qualify_rules(stralloc *ipout,stralloc *fqdn,const stralloc *in,const stralloc *rules) +{ + unsigned int i; + unsigned int j; + unsigned int plus; + unsigned int fqdnlen; + int rc = 0; + + if (!stralloc_copy(fqdn,(stralloc *)in)) return DNS_MEM; + + for (j = i = 0; j < rules->len; ++j) + if (!rules->s[j]) { + if (!doit(fqdn,rules->s + i)) return DNS_INT; + i = j + 1; + } + + fqdnlen = fqdn->len; + plus = byte_chr(fqdn->s,fqdnlen,'+'); + if (plus >= fqdnlen) + return dns_ip6(ipout,fqdn); + + i = plus + 1; + for (;;) { + j = byte_chr(fqdn->s + i,fqdnlen - i,'+'); + byte_copy(fqdn->s + plus,j,fqdn->s + i); + fqdn->len = plus + j; + if ((rc += dns_ip6(ipout,fqdn)) < 0) return DNS_ERR; + i += j; + if (i >= fqdnlen) return rc; + ++i; + } + return 0; +} + +/** @fn int dns_ip6_qualify -> @return number of IPv6 addresss qualified */ + +int dns_ip6_qualify(stralloc *ipout,stralloc *fqdn,const stralloc *in) +{ + int r; + static stralloc rules; + + if ((r = dns_ip_qualify_localhost(ipout,fqdn,in)) > 0) return r; + if (dns_resolvconfrewrite(&rules) < 0) return DNS_INT; + return dns_ip6_qualify_rules(ipout,fqdn,in,&rules); +} + +/** @fn int dns_ip_qualify_rules -> @return number of IPv6+IPv4 addresss with rules */ + +int dns_ip_qualify_rules(stralloc *ipout,stralloc *fqdn,const stralloc *in,const stralloc *rules) +{ + unsigned int i; + unsigned int j; + unsigned int k; + unsigned int plus; + unsigned int fqdnlen; + stralloc tmp = {0}; + int rc = 0; + + if (!stralloc_copy(fqdn,(stralloc *)in)) return DNS_MEM; + if (!stralloc_copys(ipout,"")) return DNS_MEM; + + for (j = i = 0; j < rules->len; ++j) + if (!rules->s[j]) { + if (!doit(fqdn,rules->s + i)) return DNS_INT; + i = j + 1; + } + + fqdnlen = fqdn->len; + plus = byte_chr(fqdn->s,fqdnlen,'+'); + if (plus >= fqdnlen) { + rc = dns_ip6(ipout,fqdn); + if (dns_ip4(&tmp,fqdn) > 0) { + for (k = 0; k < tmp.len; k += 4) { + if (!stralloc_catb(ipout,(const char *) V4mappedprefix,12)) return DNS_MEM; + if (!stralloc_catb(ipout,tmp.s + k,4)) return DNS_MEM; + rc++; + } + } + return rc; + } + + i = plus + 1; + for (;;) { + j = byte_chr(fqdn->s + i,fqdnlen - i,'+'); + byte_copy(fqdn->s + plus,j,fqdn->s + i); + fqdn->len = plus + j; + if (!stralloc_copys(ipout,"")) return DNS_MEM; + rc = dns_ip6(&tmp,fqdn); + if (rc) if (!stralloc_cat(ipout,&tmp)) return DNS_MEM; + if (dns_ip4(&tmp,fqdn) > 0) { + for (k = 0; k < tmp.len; k += 4) { + if (!stralloc_catb(ipout,(const char *) V4mappedprefix,12)) return DNS_MEM; + if (!stralloc_catb(ipout,tmp.s + k,4)) return DNS_MEM; + rc++; + } + } + + if (rc < 0) return DNS_ERR; + i += j; + if (i >= fqdnlen) return rc; + ++i; + } + return 0; +} + +/** @fn int dns_ip_qualify_localhost -> @return number of IP addresss */ + +int dns_ip_qualify_localhost(stralloc *ipout,stralloc *fqdn,const stralloc *in) +{ + if (!stralloc_copys(ipout,"")) return DNS_MEM; + if (!stralloc_copys(fqdn,"")) return DNS_MEM; + ipout->len = 0; + + if (byte_equal(in->s,9,LOCALHOST)) { + if (!stralloc_copyb(ipout,(const char *) V6loopback,16)) return DNS_MEM; + if (!stralloc_catb(ipout,(const char *) V46loopback,16)) return DNS_MEM; + if (!stralloc_copys(fqdn,"localhost.localhost.")) return DNS_MEM; + } + if (byte_equal(in->s,13,IP4_LOOPBACK)) { + if (!stralloc_copyb(ipout,(const char *) V46loopback,16)) return DNS_MEM; + if (!stralloc_copys(fqdn,"ip4-loopback.localhost.")) return DNS_MEM; + } + if (byte_equal(in->s,13,IP6_LOOPBACK)) { + if (!stralloc_copyb(ipout,(const char *) V6loopback,16)) return DNS_MEM; + if (!stralloc_copys(fqdn,"ip6-loopback.localhost.")) return DNS_MEM; + } +// if (!stralloc_0(fqdn)) return DNS_MEM; // don't do it + + return ipout->len ? ipout->len % 15 : 0; +} + +/** @fn int dns_ip_qualify -> @return number of IP addresss */ + +int dns_ip_qualify(stralloc *ipout,stralloc *fqdn,const stralloc *in) +{ + int r; + static stralloc rules; + + if ((r = dns_ip_qualify_localhost(ipout,fqdn,in)) > 0 ) return r; + if (dns_resolvconfrewrite(&rules) < 0) return DNS_INT; + return dns_ip_qualify_rules(ipout,fqdn,in,&rules); +} diff --git a/dnsstub/dns_mx.c b/dnsstub/dns_mx.c new file mode 100644 index 0000000..c0845ef --- /dev/null +++ b/dnsstub/dns_mx.c @@ -0,0 +1,63 @@ +#include "stralloc.h" +#include "byte.h" +#include "uint_t.h" +#include "dnsresolv.h" + +/** + @file dns_mx.c + @author djb + @source qmail + @brief dns MX query + @param (on output) stralloc out +*/ + +static char *q = 0; + +int dns_mx_packet(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[12]; + char pref[2]; + uint16 numanswers; + uint16 datalen; + int ranswers = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_MX)) + if (byte_equal(header + 2,2,DNS_C_IN)) { + if (!dns_packet_copy(buf,len,pos,pref,2)) return DNS_ERR; + if (!dns_packet_getname(buf,len,pos + 2,&q)) return DNS_ERR; + if (!stralloc_catb(out,pref,2)) return DNS_MEM; + if (dns_domain_todot_cat(out,q) <= 0) return DNS_ERR; + if (!stralloc_0(out)) return DNS_MEM; + } + pos += datalen; + ++ranswers; + } + + return ranswers; +} + +int dns_mx(stralloc *out,const stralloc *fqdn) +{ + int rc = 0; + + if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; + if (dns_resolve(q,DNS_T_MX) >= 0) { + if ((rc = dns_mx_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + } + + return rc; +} diff --git a/dnsstub/dns_name.c b/dnsstub/dns_name.c new file mode 100644 index 0000000..0723a8f --- /dev/null +++ b/dnsstub/dns_name.c @@ -0,0 +1,80 @@ +#include "stralloc.h" +#include "uint_t.h" +#include "byte.h" +#include "ip.h" +#include "dnsresolv.h" + +/** + @file dns_name.c + @author djb, fefe, feh + @source ucspi-tcp + @brief DNS name query (ptr) +*/ + +static char *q = 0; + +int dns_name_packet(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[12]; + uint16 numanswers; + uint16 datalen; + + if (!stralloc_copys(out,"")) return DNS_MEM; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_PTR)) + if (byte_equal(header + 2,2,DNS_C_IN)) { + if (!dns_packet_getname(buf,len,pos,&q)) return DNS_ERR; + if (dns_domain_todot_cat(out,q) <= 0) return DNS_ERR; + return 1; + } + pos += datalen; + } + + return 0; +} + +int dns_name4(stralloc *out,const char ip[4]) +{ + int rc; + char name[DNS_NAME4_DOMAIN]; + + dns_name4_domain(name,ip); + if (dns_resolve(name,DNS_T_PTR) < 0) return DNS_ERR; + if ((rc = dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + + return rc; +} + +int dns_name6(stralloc *out,const char ip[16]) +{ + int rc; + char name[DNS_NAME6_DOMAIN]; + + dns_name6_domain(name,ip); + if (dns_resolve(name,DNS_T_PTR) < 0) return DNS_ERR; + if ((rc = dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + + return rc; +} + +int dns_name(stralloc *out,const char ip[16]) +{ + if (ip6_isv4mapped(ip)) + return dns_name4(out,ip+12); + else + return dns_name6(out,ip); +} diff --git a/dnsstub/dns_nd.c b/dnsstub/dns_nd.c new file mode 100644 index 0000000..6ce8ed9 --- /dev/null +++ b/dnsstub/dns_nd.c @@ -0,0 +1,48 @@ +#include "byte.h" +#include "fmt.h" +#include "ip.h" +#include "dnsresolv.h" + +/** + @file dns_nd.c + @autor djb, fefe + @source ucspi-tcp + @brief DNS domain name for ip (wire format) +*/ + +int dns_name4_domain(char name[DNS_NAME4_DOMAIN],const char ip[4]) +{ + unsigned int namelen; + unsigned int i; + + namelen = 0; + i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[3]); + name[namelen++] = i; + namelen += i; + i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[2]); + name[namelen++] = i; + namelen += i; + i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[1]); + name[namelen++] = i; + namelen += i; + i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[0]); + name[namelen++] = i; + namelen += i; + byte_copy(name + namelen,14,"\7in-addr\4arpa\0"); + return namelen+14; +} + +int dns_name6_domain(char name[DNS_NAME6_DOMAIN],const char ip[16]) +{ + unsigned int j; + + for (j = 0; j < 16; j++) { + name[j * 4] = 1; + name[j * 4 + 1] = tohex(ip[15 - j] & 15); + name[j * 4 + 2] = 1; + name[j * 4 + 3] = tohex((unsigned char)ip[15 - j] >> 4); + } + byte_copy(name + 4 * 16,10,"\3ip6\4arpa\0"); + return 4 * 16 + 10; +} + diff --git a/dnsstub/dns_packet.c b/dnsstub/dns_packet.c new file mode 100644 index 0000000..ce322ea --- /dev/null +++ b/dnsstub/dns_packet.c @@ -0,0 +1,85 @@ +#include "error.h" +#include "dnsresolv.h" + +/** + @file dns_packet.c + @author djb + @source ucspi-tcp + @brief DNS low level packet routine + @brief DNS should have used LZ77 instead of its own sophomoric compression algorithm. +*/ + +unsigned int dns_packet_copy(const char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen) +{ + while (outlen) { + if (pos >= len) { errno = EPROTO; return 0; } + *out = buf[pos++]; + ++out; --outlen; + } + return pos; +} + +unsigned int dns_packet_skipname(const char *buf,unsigned int len,unsigned int pos) +{ + unsigned char ch; + + for (;;) { + if (pos >= len) break; + ch = buf[pos++]; + if (ch >= 192) return pos + 1; + if (ch >= 64) break; + if (!ch) return pos; + pos += ch; + } + + errno = EPROTO; + return 0; +} + +unsigned int dns_packet_getname(const char *buf,unsigned int len,unsigned int pos,char **d) +{ + unsigned int loop = 0; + unsigned int state = 0; + unsigned int firstcompress = 0; + unsigned int where; + unsigned char ch; + char name[255]; + unsigned int namelen = 0; + + for (;;) { + if (pos >= len) goto PROTO; + ch = buf[pos++]; + if (++loop >= 1000) goto PROTO; + + if (state) { + if (namelen + 1 > sizeof(name)) goto PROTO; + name[namelen++] = ch; + --state; + } else { + while (ch >= 192) { + where = ch; where -= 192; where <<= 8; + if (pos >= len) goto PROTO; + ch = buf[pos++]; + if (!firstcompress) firstcompress = pos; + pos = where + ch; + if (pos >= len) goto PROTO; + ch = buf[pos++]; + if (++loop >= 1000) goto PROTO; + } + if (ch >= 64) goto PROTO; + if (namelen + 1 > sizeof(name)) goto PROTO; + name[namelen++] = ch; + if (!ch) break; + state = ch; + } + } + + if (!dns_domain_copy(d,name)) return 0; + + if (firstcompress) return firstcompress; + return pos; + + PROTO: + errno = EPROTO; + return 0; +} diff --git a/dnsstub/dns_random.c b/dnsstub/dns_random.c new file mode 100644 index 0000000..200cd6c --- /dev/null +++ b/dnsstub/dns_random.c @@ -0,0 +1,70 @@ +#include <unistd.h> +#include "taia.h" +#include "uint_t.h" +#include "dnsresolv.h" + +/** + @file dns_random.c + @author djb + @source ucspi-tcp + @brief random use of DNS resolvers given their IP +*/ + +static uint32 seed[32]; +static uint32 in[12]; +static uint32 out[8]; +static int outleft = 0; + +#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b)))) +#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b)); + +static void surf(void) +{ + uint32 t[12]; uint32 x; uint32 sum = 0; + int r; int i; int loop; + + for (i = 0; i < 12; ++i) t[i] = in[i] ^ seed[12 + i]; + for (i = 0; i < 8; ++i) out[i] = seed[24 + i]; + x = t[11]; + for (loop = 0; loop < 2; ++loop) { + for (r = 0; r < 16; ++r) { + sum += 0x9e3779b9; + MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13) + MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13) + MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13) + } + for (i = 0; i < 8; ++i) out[i] ^= t[i + 4]; + } +} + +void dns_random_init(const char data[128]) +{ + int i; + struct taia t; + char tpack[16]; + + for (i = 0; i < 32; ++i) + uint32_unpack((char *)data + 4 * i,seed + i); + + taia_now(&t); + taia_pack(tpack,&t); + for (i = 0; i < 4; ++i) + uint32_unpack(tpack + 4 * i,in + 4 + i); + + in[8] = getpid(); + in[9] = getppid(); + /* more space in 10 and 11, but this is probably enough */ +} + +unsigned int dns_random(unsigned int n) +{ + if (!n) return 0; + + if (!outleft) { + if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; + surf(); + outleft = 8; + } + + return out[--outleft] % n; +} diff --git a/dnsstub/dns_rcip.c b/dnsstub/dns_rcip.c new file mode 100644 index 0000000..93b0daa --- /dev/null +++ b/dnsstub/dns_rcip.c @@ -0,0 +1,114 @@ +#include "taia.h" +#include "readclose.h" +#include "byte.h" +#include "ip.h" +#include "env.h" +#include "dnsresolv.h" +#include "socket_if.h" + +/** + @file dns_rcip.c + @author djb, fefe, feh + @source ucspi-tcp + @brief DNS receive for query +*/ + +static stralloc data = {0}; +static stralloc ifname = {0}; + +static int init(char ip[QUERY_MAXIPLEN],uint32 sid[QUERY_MAXNS]) +{ + int i; + int j; + int k = 0; + int iplen = 0; + char *x; + char ip4[4]; + +/* Read (compactified) IPv4|v6 addresses of resolvers + Store them in array IP with fixed length : + ip(64) -> 16 IPv4 addresses (not used anymore) + ip(512) -> 16*2 IPv6 addresses (we use IPv4 mapped IPv6 addresses) + sid(32) -> the scope for the respective IPv6 or 0 +*/ + for (i = 0; i < QUERY_MAXNS; ++i) sid[i] = 0; + + x = env_get("DNSCACHEIP"); + if (x) + while (iplen <= 240 && *x != '\0') { + if (*x == ' ') + ++x; + else + if ((i = ip6_ifscan(x,ip + iplen,&ifname))) { + if (ifname.len > 2) sid[k] = socket_getifidx(ifname.s); + iplen += 16; k++; + if (*(x += i) == '\0') break; + } + } + + if (!iplen) { + i = openreadclose("/etc/resolv.conf",&data,64); + if (i == -1) return DNS_INT; + if (i) { + if (!stralloc_append(&data,"\n")) return DNS_MEM; + i = 0; + for (j = 0; j < data.len; ++j) + if (data.s[j] == '\n') { + if (byte_equal("nameserver ",11,data.s + i) || byte_equal("nameserver\t",11,data.s + i)) { + i += 10; + while ((data.s[i] == ' ') || (data.s[i] == '\t')) + i++; + if (iplen <= 240) { + data.s[j] = '\0'; /* ip6_ifscan needs terminated string on input */ + if (ip4_scan(data.s + i,ip4)) { + if (byte_equal(ip4,4,"\0\0\0\0")) + byte_copy(ip4,4,"\177\0\0\1"); + byte_copy(ip + iplen,12,V4mappedprefix); + byte_copy(ip + iplen + 12,4,ip4); + sid[k] = 0; iplen += 16; k++; + } else if (ip6_ifscan(data.s + i,ip + iplen,&ifname)) { + if (ifname.len > 2) sid[k] = socket_getifidx(ifname.s); + iplen += 16; k++; + } + } + } + i = j + 1; + } + } + } + + if (!iplen) { + byte_copy(ip,16,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"); + iplen = 16; + } + byte_zero(ip + iplen,QUERY_MAXIPLEN - iplen); + return 0; +} + +static int ok = 0; +static unsigned int uses; +static struct taia deadline; +static char ip[QUERY_MAXIPLEN]; /* defined if ok */ +static uint32 scopes[QUERY_MAXNS]; + +int dns_resolvconfip(char s[QUERY_MAXIPLEN],uint32 scope[QUERY_MAXNS]) +{ + struct taia now; + + taia_now(&now); + if (taia_less(&deadline,&now)) ok = 0; + if (!uses) ok = 0; + + if (!ok) { + if (init(ip,scopes) < 0) return DNS_INT; + taia_uint(&deadline,600); + taia_add(&deadline,&now,&deadline); + uses = 10000; + ok = 1; + } + + --uses; + byte_copy(s,QUERY_MAXIPLEN,ip); + byte_copy(scope,128,scopes); + return 0; +} diff --git a/dnsstub/dns_rcrw.c b/dnsstub/dns_rcrw.c new file mode 100644 index 0000000..4633fed --- /dev/null +++ b/dnsstub/dns_rcrw.c @@ -0,0 +1,141 @@ +#include <unistd.h> +#include "taia.h" +#include "env.h" +#include "byte.h" +#include "str.h" +#include "readclose.h" +#include "dnsresolv.h" + +/** + @file dns_rcrw.c + @author djb + @source ucspi-tcp + @brief DNS receive rewrite +*/ + +static stralloc data = {0}; + +static int init(stralloc *rules) +{ + char host[256]; + const char *x; + int i; + int j; + int k; + + if (!stralloc_copys(rules,"")) return DNS_MEM; + + x = env_get("DNSREWRITEFILE"); + if (!x) x = "/etc/dnsrewrite"; + + i = openreadclose(x,&data,64); + if (i == -1) return DNS_INT; + + if (i) { + if (!stralloc_append(&data,"\n")) return DNS_MEM; + i = 0; + for (j = 0; j < data.len; ++j) + if (data.s[j] == '\n') { + if (!stralloc_catb(rules,data.s + i,j - i)) return DNS_MEM; + while (rules->len) { + if (rules->s[rules->len - 1] != ' ') + if (rules->s[rules->len - 1] != '\t') + if (rules->s[rules->len - 1] != '\r') + break; + --rules->len; + } + if (!stralloc_0(rules)) return DNS_MEM; + i = j + 1; + } + return 0; + } + + x = env_get("LOCALDOMAIN"); + if (x) { + if (!stralloc_copys(&data,x)) return DNS_MEM; + if (!stralloc_append(&data," ")) return DNS_MEM; + if (!stralloc_copys(rules,"?:")) return DNS_MEM; + i = 0; + for (j = 0; j < data.len; ++j) + if (data.s[j] == ' ') { + if (!stralloc_cats(rules,"+.")) return DNS_MEM; + if (!stralloc_catb(rules,data.s + i,j - i)) return DNS_MEM; + i = j + 1; + } + if (!stralloc_0(rules)) return DNS_MEM; + if (!stralloc_cats(rules,"*.:")) return DNS_MEM; + if (!stralloc_0(rules)) return DNS_MEM; + return 0; + } + + i = openreadclose("/etc/resolv.conf",&data,64); + if (i == -1) return DNS_INT; + + if (i) { + if (!stralloc_append(&data,"\n")) return DNS_MEM; + i = 0; + for (j = 0; j < data.len; ++j) + if (data.s[j] == '\n') { + if (byte_equal("search ",7,data.s + i) || + byte_equal("search\t",7,data.s + i) || + byte_equal("domain ",7,data.s + i) || + byte_equal("domain\t",7,data.s + i)) { + if (!stralloc_copys(rules,"?:")) return DNS_MEM; + i += 7; + while (i < j) { + k = byte_chr(data.s + i,j - i,' '); + k = byte_chr(data.s + i,k,'\t'); + if (!k) { ++i; continue; } + if (!stralloc_cats(rules,"+.")) return DNS_MEM; + if (!stralloc_catb(rules,data.s + i,k)) return DNS_MEM; + i += k; + } + if (!stralloc_0(rules)) return DNS_MEM; + if (!stralloc_cats(rules,"*.:")) return DNS_MEM; + if (!stralloc_0(rules)) return DNS_MEM; + return 0; + } + i = j + 1; + } + } + + host[0] = 0; + if (gethostname(host,sizeof(host)) == -1) return DNS_ERR; + host[(sizeof(host)) - 1] = 0; + i = str_chr(host,'.'); + if (host[i]) { + if (!stralloc_copys(rules,"?:")) return DNS_MEM; + if (!stralloc_cats(rules,host + i)) return DNS_MEM; + if (!stralloc_0(rules)) return DNS_MEM; + } + if (!stralloc_cats(rules,"*.:")) return DNS_MEM; + if (!stralloc_0(rules)) return DNS_MEM; + + return 0; +} + +static int ok = 0; +static unsigned int uses; +static struct taia deadline; +static stralloc rules = {0}; /* defined if ok */ + +int dns_resolvconfrewrite(stralloc *out) +{ + struct taia now; + + taia_now(&now); + if (taia_less(&deadline,&now)) ok = 0; + if (!uses) ok = 0; + + if (!ok) { + if (init(&rules) < 0) return DNS_INT; + taia_uint(&deadline,600); + taia_add(&deadline,&now,&deadline); + uses = 10000; + ok = 1; + } + + --uses; + if (!stralloc_copy(out,&rules)) return DNS_MEM; + return 0; +} diff --git a/dnsstub/dns_resolve.c b/dnsstub/dns_resolve.c new file mode 100644 index 0000000..bcc4308 --- /dev/null +++ b/dnsstub/dns_resolve.c @@ -0,0 +1,39 @@ +#include "iopause.h" +#include "taia.h" +#include "byte.h" +#include "ip.h" +#include "dnsresolv.h" + +/** + @file dns_resolve.c + @author djb, fefe, feh + @source ucspi-tcp + @brief high-level DNS resolve function +*/ + +struct dns_transmit dns_resolve_tx = {0}; + +int dns_resolve(const char *q,const char qtype[2]) +{ + struct taia stamp; + struct taia deadline; + char servers[QUERY_MAXIPLEN]; + uint32 scopes[QUERY_MAXNS]; + iopause_fd x[1]; + int r; + + if (dns_resolvconfip(servers,scopes) < 0) return DNS_INT; + + if (dns_transmit_start6(&dns_resolve_tx,servers,1,q,qtype,(const char *)V6localnet,scopes) < 0) return DNS_COM; + + for (;;) { + taia_now(&stamp); + taia_uint(&deadline,120); + taia_add(&deadline,&deadline,&stamp); + dns_transmit_io(&dns_resolve_tx,x,&deadline); + iopause(x,1,&deadline,&stamp); + r = dns_transmit_get(&dns_resolve_tx,x,&stamp); + if (r < 0) return DNS_COM; + if (r == 1) return 0; + } +} diff --git a/dnsstub/dns_sortip.c b/dnsstub/dns_sortip.c new file mode 100644 index 0000000..56742e0 --- /dev/null +++ b/dnsstub/dns_sortip.c @@ -0,0 +1,45 @@ +#include "byte.h" +#include "ip.h" +#include "dnsresolv.h" + +/** + @file dns_sortip.c + @authors djb, fefe, feh + @source ucspi-tcp6 + @brief random sort of DNS servers per IP +*/ + +/* XXX: sort servers by configurable notion of closeness? */ +/* XXX: pay attention to competence of each server? */ +/* XXX: pay attention to qualification (DNSSec, DNSCurve) of each server? */ +/* YYY: we use a randomly sorted list of NS; not depending on answer */ + +void dns_sortip4(char *s,unsigned int n) +{ + unsigned int i; + char tmp[4]; + + n >>= 2; /* 4 byte per IPv4 address */ + while (n > 1) { + i = dns_random(n); + --n; + byte_copy(tmp,4,s + (i << 2)); + byte_copy(s + (i << 2),4,s + (n << 2)); + byte_copy(s + (n << 2),4,tmp); + } +} + +void dns_sortip6(char *s,unsigned int n) +{ + unsigned int i; + char tmp[16]; + + n >>= 4; /* 16 byte per IPv4 address */ + while (n > 1) { + i = dns_random(n); + --n; + byte_copy(tmp,16,s + (i << 4)); + byte_copy(s + (i << 4),16,s + (n << 4)); + byte_copy(s + (n << 4),16,tmp); + } +} diff --git a/dnsstub/dns_transmit.c b/dnsstub/dns_transmit.c new file mode 100644 index 0000000..2513565 --- /dev/null +++ b/dnsstub/dns_transmit.c @@ -0,0 +1,436 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include "socket_if.h" +#include "alloc.h" +#include "error.h" +#include "byte.h" +#include "uint_t.h" +#include "ip.h" +#include "dnsresolv.h" + +/** + @file dns_transmit.c + @authors djb, fefe, feh + @source qlibs + @brief DNS query function + @brief scope_ids[32] -> 32 LLU root servers supported +*/ + +#define DNSPORT 53 + +uint32 scope_ids[QUERY_MAXNS]; + +static const int timeouts[5] = { 1, 2, 4, 8, 16 }; /* quadratic, not exponentially */ + +int getscopeid(const struct dns_transmit *d,const char *ip) +{ + int i; + + if (byte_diff(ip,2,V6linklocal)) return 0; + for (i = 0; i < QUERY_MAXNS; ++i) + if (byte_equal(d->servers + 16 * i,16,ip)) + return scope_ids[i]; + + return 0; +} + +int serverwantstcp(const char *buf,unsigned int len) +{ + char out[12]; + + if (!dns_packet_copy(buf,len,0,out,12)) return 1; + if (out[2] & 2) return 1; + + return 0; +} + +int serverfailed(const char *buf,unsigned int len) +{ + char out[12]; + unsigned int rcode; + + if (!dns_packet_copy(buf,len,0,out,12)) return 1; + rcode = out[3]; + rcode &= 15; + if (rcode && (rcode != 3)) { errno = EAGAIN; return 1; } + + return 0; +} + +int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len) +{ + char out[12]; + char *dn; + unsigned int pos; + + pos = dns_packet_copy(buf,len,0,out,12); if (!pos) return 1; + if (byte_diff(out,2,d->query + 2)) return 1; + if (out[4] != 0) return 1; + if (out[5] != 1) return 1; + + dn = 0; + pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1; + if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; } + alloc_free(dn); + + pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1; + if (byte_diff(out,2,d->qtype)) return 1; + if (byte_diff(out + 2,2,DNS_C_IN)) return 1; + + return 0; +} + +void packetfree(struct dns_transmit *d) +{ + if (!d->packet) return; + alloc_free(d->packet); + d->packet = 0; +} + +void queryfree(struct dns_transmit *d) +{ + if (!d->query) return; + alloc_free(d->query); + d->query = 0; +} + +void socketfree(struct dns_transmit *d) +{ + if (!d->s1) return; + close(d->s1 - 1); + d->s1 = 0; +} + +void dns_transmit_free(struct dns_transmit *d) +{ + queryfree(d); + socketfree(d); + packetfree(d); +} + +int randombind6(struct dns_transmit *d) +{ + int j; + + for (j = 0; j < 10; ++j) { + if (socket_bind6(d->s1 - 1,d->localip,1025 + dns_random(64510),d->scope_id) == 0) + return 0; + } + if (socket_bind6(d->s1 - 1,d->localip,0,d->scope_id) == 0) + return 0; + + return DNS_COM; +} + +int randombind4(struct dns_transmit *d) +{ + int j; + + for (j = 0; j < 10; ++j) { + if (socket_bind4(d->s1 - 1,d->localip + 12,1025 + dns_random(64510)) == 0) + return 0; + } + if (socket_bind4(d->s1 - 1,d->localip + 12,0) == 0) + return 0; + + return DNS_COM; +} + +int thisudp(struct dns_transmit *d) +{ + const char *ip; + + socketfree(d); + + while (d->udploop < 5) { + for (; d->curserver < QUERY_MAXNS; ++d->curserver) { + ip = d->servers + 16 * d->curserver; + if (byte_diff(ip,16,V6localnet)) { + d->query[2] = dns_random(256); + d->query[3] = dns_random(256); + + if (ip6_isv4mapped(ip)) { + d->s1 = 1 + socket_udp4(); + if (!d->s1) { dns_transmit_free(d); return DNS_COM; } + if (randombind4(d) < 0) { dns_transmit_free(d); return DNS_COM; } + } else { + d->s1 = 1 + socket_udp6(); + if (!d->s1) { dns_transmit_free(d); return DNS_COM; } + if (randombind6(d) < 0) { dns_transmit_free(d); return DNS_COM; } + } + + if (byte_equal(ip,2,V6linklocal) && !d->scope_id) + d->scope_id = getscopeid(d,ip); + if (socket_connect(d->s1 - 1,ip,DNSPORT,d->scope_id) == 0) + if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) { + struct taia now; + taia_now(&now); + taia_uint(&d->deadline,timeouts[d->udploop]); + taia_add(&d->deadline,&d->deadline,&now); + d->tcpstate = 0; + return 0; + } + socketfree(d); + } + } + ++d->udploop; + d->curserver = 0; + } + + dns_transmit_free(d); return DNS_COM; +} + +int firstudp(struct dns_transmit *d) +{ + d->curserver = 0; + return thisudp(d); +} + +int nextudp(struct dns_transmit *d) +{ + ++d->curserver; + return thisudp(d); +} + +int thistcp(struct dns_transmit *d) +{ + struct taia now; + const char *ip; + + socketfree(d); + packetfree(d); + + for (; d->curserver < QUERY_MAXNS; ++d->curserver) { + ip = d->servers + 16 * d->curserver; + if (byte_diff(ip,16,V6localnet)) { + d->query[2] = dns_random(256); + d->query[3] = dns_random(256); + + if (ip6_isv4mapped(ip)) { + d->s1 = 1 + socket_tcp4(); + if (!d->s1) { dns_transmit_free(d); return DNS_COM; } + if (randombind4(d) < 0) { dns_transmit_free(d); return DNS_COM; } + } else { + d->s1 = 1 + socket_tcp6(); + if (!d->s1) { dns_transmit_free(d); return DNS_COM; } + if (randombind6(d) < 0) { dns_transmit_free(d); return DNS_COM; } + } + + taia_now(&now); + taia_uint(&d->deadline,10); + taia_add(&d->deadline,&d->deadline,&now); + + if (byte_equal(ip,2,V6linklocal) && !d->scope_id) + d->scope_id = getscopeid(d,ip); + if (socket_connect(d->s1 - 1,ip,DNSPORT,d->scope_id) == 0) { + d->tcpstate = 2; + return 0; + } + if ((errno == EINPROGRESS) || (errno == EWOULDBLOCK)) { + d->tcpstate = 1; + return 0; + } + + socketfree(d); + } + } + + dns_transmit_free(d); + return DNS_COM; +} + +int firsttcp(struct dns_transmit *d) +{ + d->curserver = 0; + return thistcp(d); +} + +int nexttcp(struct dns_transmit *d) +{ + ++d->curserver; + return thistcp(d); +} + +int dns_transmit_start(struct dns_transmit *d,const char servers[QUERY_MAXIPLEN], \ + int flagrecursive,const char *q,const char qtype[2],const char localip[16]) +{ + unsigned int len; + + dns_transmit_free(d); + errno = EIO; + + len = dns_domain_length(q); + d->querylen = len + 18; + d->query = alloc(d->querylen); + if (!d->query) return DNS_COM; + + uint16_pack_big(d->query,len + 16); + byte_copy(d->query + 2,12,flagrecursive ? "\0\0\1\0\0\1\0\0\0\0\0\0" : \ + "\0\0\0\0\0\1\0\0\0\0\0\0gcc-bug-workaround"); + byte_copy(d->query + 14,len,q); + byte_copy(d->query + 14 + len,2,qtype); + byte_copy(d->query + 16 + len,2,DNS_C_IN); + + byte_copy(d->qtype,2,(char *) qtype); + d->servers = servers; + byte_copy(d->localip,16,(char *) localip); + + d->udploop = flagrecursive ? 1 : 0; + + if (len + 16 > MSGSIZE) return firsttcp(d); + return firstudp(d); +} + +int dns_transmit_start6(struct dns_transmit *d,const char servers[QUERY_MAXIPLEN], \ + int flagrecursive,const char *q,const char qtype[2], \ + const char localip[16],const uint32 scopes[QUERY_MAXNS]) +{ + byte_copy(scope_ids,128,(char *) scopes); + + return dns_transmit_start(d,servers,flagrecursive,q,qtype,localip); +} + +void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline) +{ + x->fd = d->s1 - 1; + + switch (d->tcpstate) { + case 0: case 3: case 4: case 5: + x->events = IOPAUSE_READ; + break; + case 1: case 2: + x->events = IOPAUSE_WRITE; + break; + } + + if (taia_less(&d->deadline,deadline)) + *deadline = d->deadline; +} + +int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when) +{ + char udpbuf[MSGSIZE + 1]; + unsigned char ch; + int r; + int fd; + + errno = EIO; + fd = d->s1 - 1; + + if (!x->revents) { + if (taia_less((struct taia *)when,&d->deadline)) return 0; + errno = ETIMEDOUT; + if (d->tcpstate == 0) return nextudp(d); + return nexttcp(d); + } + +/* +have attempted to send UDP query to each server udploop times +have sent query to curserver on UDP socket s +*/ + if (d->tcpstate == 0) { + r = recv(fd,udpbuf,sizeof(udpbuf),0); + if (r <= 0) { + if (errno == ECONNREFUSED) if (d->udploop == 2) return 0; + return nextudp(d); + } + if (r + 1 > sizeof(udpbuf)) return 0; + + if (irrelevant(d,udpbuf,r)) return 0; + if (serverwantstcp(udpbuf,r)) return firsttcp(d); + if (serverfailed(udpbuf,r)) { + if (d->udploop == 2) return 0; + return nextudp(d); + } + socketfree(d); + + d->packetlen = r; + d->packet = alloc(d->packetlen); + if (!d->packet) { dns_transmit_free(d); return DNS_COM; } + byte_copy(d->packet,d->packetlen,udpbuf); + queryfree(d); + return 1; + } + +/* +have sent connection attempt to curserver on TCP socket s +pos not defined +*/ + if (d->tcpstate == 1) { + if (!socket_connected(fd)) return nexttcp(d); + d->pos = 0; + d->tcpstate = 2; + return 0; + } + +/* +have connection to curserver on TCP socket s +have sent pos bytes of query +*/ + if (d->tcpstate == 2) { + r = write(fd,d->query + d->pos,d->querylen - d->pos); + if (r <= 0) return nexttcp(d); + d->pos += r; + if (d->pos == d->querylen) { + struct taia now; + taia_now(&now); + taia_uint(&d->deadline,10); + taia_add(&d->deadline,&d->deadline,&now); + d->tcpstate = 3; + } + return 0; + } + +/* +have sent entire query to curserver on TCP socket s +pos not defined +*/ + if (d->tcpstate == 3) { + r = read(fd,&ch,1); + if (r <= 0) return nexttcp(d); + d->packetlen = ch; + d->tcpstate = 4; + return 0; + } + +/* +have sent entire query to curserver on TCP socket s +pos not defined +have received one byte of packet length into packetlen +*/ + if (d->tcpstate == 4) { + r = read(fd,&ch,1); + if (r <= 0) return nexttcp(d); + d->packetlen <<= 8; + d->packetlen += ch; + d->tcpstate = 5; + d->pos = 0; + d->packet = alloc(d->packetlen); + if (!d->packet) { dns_transmit_free(d); return DNS_COM; } + return 0; + } + +/* +have sent entire query to curserver on TCP socket s +have received entire packet length into packetlen +packet is allocated +have received pos bytes of packet +*/ + if (d->tcpstate == 5) { + r = read(fd,d->packet + d->pos,d->packetlen - d->pos); + if (r <= 0) return nexttcp(d); + d->pos += r; + if (d->pos < d->packetlen) return 0; + + socketfree(d); + if (irrelevant(d,d->packet,d->packetlen)) return nexttcp(d); + if (serverwantstcp(d->packet,d->packetlen)) return nexttcp(d); + if (serverfailed(d->packet,d->packetlen)) return nexttcp(d); + + queryfree(d); + return 1; + } + + return 0; +} diff --git a/dnsstub/dns_txt.c b/dnsstub/dns_txt.c new file mode 100644 index 0000000..9a1b56a --- /dev/null +++ b/dnsstub/dns_txt.c @@ -0,0 +1,64 @@ +#include "stralloc.h" +#include "uint_t.h" +#include "byte.h" +#include "dnsresolv.h" + +int dns_txt_packet(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[12]; + uint16 numanswers; + uint16 datalen; + char ch; + unsigned int txtlen; + int i; + int ranswers = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_TXT)) + if (byte_equal(header + 2,2,DNS_C_IN)) { + if (pos + datalen > len) return DNS_ERR; + txtlen = (unsigned char) buf[pos]; + for (i = 1; i < datalen; ++i) { + ch = buf[pos + i]; + if (i == txtlen + 1) // next label + txtlen += (unsigned char) ch + 1; + else { + if (ch < 32) ch = '?'; + if (ch > 126) ch = '?'; + if (!stralloc_append(out,&ch)) return DNS_MEM; + } + } + } + pos += datalen; + ++ranswers; + if (numanswers) if (!stralloc_append(out,"\n")) return DNS_MEM; + } + + return ranswers; +} + +static char *q = 0; + +int dns_txt(stralloc *out,const stralloc *fqdn) +{ + int rc; + + if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; + if (dns_resolve(q,DNS_T_TXT) < 0) return DNS_ERR; + if ((rc = dns_txt_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + + return rc; +} @@ -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 */ +} @@ -0,0 +1,30 @@ +#include <fcntl.h> +#include "fd.h" + +/** + @file fd.c + @autor djb + @source qmail + @brief file descriptor manipulation +*/ + +int close(int __fd); /* we won't use unistd.h here */ + +int fd_copy(int to,int from) +{ + if (to == from) return 0; + if (fcntl(from,F_GETFL,0) == -1) return -1; + close(to); + if (fcntl(from,F_DUPFD,to) == -1) return -1; + return 0; +} + +int fd_move(int to,int from) +{ + if (to == from) return 0; + if (fd_copy(to,from) == -1) return -1; + close(from); + return 0; +} + +int fd_coe(int fd) {return fcntl(fd,F_SETFD,1); } @@ -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; +} @@ -0,0 +1,43 @@ +#include "byte.h" +#include "getln.h" + +/** + @file getln.c + @author djb + @source qmail + @brief evaluting tokenized input arguments +*/ + +int getln(buffer *b,stralloc *sa,int *match,int sep) +{ + char *cont; + unsigned int clen; + + if (sgetln(b,sa,&cont,&clen,sep) == -1) return -1; + if (!clen) { *match = 0; return 0; } + if (!stralloc_catb(sa,cont,clen)) return -1; + *match = 1; + return 0; +} + +int sgetln(buffer *b,stralloc *sa,char **cont,unsigned int *clen,int sep) +{ + register char *x; + register unsigned int i; + int n; + + if (!stralloc_ready(sa,0)) return -1; + sa->len = 0; + + for (;;) { + n = buffer_feed(b); + if (n < 0) return -1; + if (n == 0) { *clen = 0; return 0; } + x = buffer_PEEK(b); + i = byte_chr(x,n,sep); + if (i < n) { buffer_SEEK(b,*clen = i + 1); *cont = x; return 0; } + if (!stralloc_readyplus(sa,n)) return -1; + i = sa->len; + sa->len = i + buffer_get(b,sa->s + i,n); + } +} diff --git a/getoptb.c b/getoptb.c new file mode 100644 index 0000000..edee6b8 --- /dev/null +++ b/getoptb.c @@ -0,0 +1,100 @@ +#include "buffer.h" +#include "getoptb.h" + +/** + @file getoptb.c + @author djb + @source ucspi-tcp + @brief 'getopt' version w/o stdlib +*/ + +#define optind subgetoptind +#define optproblem subgetoptproblem + +int opterr = 1; +char *optprogname = 0; + +int getopt(int argc,char **argv,char *opts) +{ + int c; + char *s; + + if (!optprogname) { + optprogname = *argv; + if (!optprogname) optprogname = ""; + for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1; + } + c = subgetopt(argc,argv,opts); + if (opterr) + if (c == '?') { + char chp[2]; chp[0] = optproblem; chp[1] = '\n'; + buffer_puts(buffer_2,optprogname); + if (argv[optind] && (optind < argc)) + buffer_puts(buffer_2,": illegal option -- "); + else + buffer_puts(buffer_2,": option requires an argument -- "); + buffer_put(buffer_2,chp,2); + buffer_flush(buffer_2); + } + return c; +} + +#define optpos subgetoptpos +#define optarg subgetoptarg +#define optdone subgetoptdone + +int optind = 1; +int optpos = 0; +char *optarg = 0; +int optproblem = 0; +int optdone = SUBGETOPTDONE; + +int subgetopt(int argc,char **argv,char *opts) +{ + int c; + char *s; + + optarg = 0; + if (!argv || (optind >= argc) || !argv[optind]) return optdone; + if (optpos && !argv[optind][optpos]) { + ++optind; + optpos = 0; + if ((optind >= argc) || !argv[optind]) return optdone; + } + if (!optpos) { + if (argv[optind][0] != '-') return optdone; + ++optpos; + c = argv[optind][1]; + if ((c == '-') || (c == 0)) { + if (c) ++optind; + optpos = 0; + return optdone; + } + /* otherwise c is reassigned below */ + } + c = argv[optind][optpos]; + ++optpos; + s = opts; + while (*s) { + if (c == *s) { + if (s[1] == ':') { + optarg = argv[optind] + optpos; + ++optind; + optpos = 0; + if (!*optarg) { + optarg = argv[optind]; + if ((optind >= argc) || !optarg) { /* argument past end */ + optproblem = c; + return '?'; + } + ++optind; + } + } + return c; + } + ++s; + if (*s == ':') ++s; + } + optproblem = c; + return '?'; +} diff --git a/include/alloc.h b/include/alloc.h new file mode 100644 index 0000000..56a4a52 --- /dev/null +++ b/include/alloc.h @@ -0,0 +1,12 @@ +#ifndef ALLOC_H +#define ALLOC_H + +extern /*@null@*//*@out@*/char *alloc(); +extern void alloc_free(); +extern int alloc_re(); + +/* use these names in the future */ +#define qfree alloc_free +#define qrealloc alloc_re + +#endif diff --git a/include/base64.h b/include/base64.h new file mode 100644 index 0000000..a9164c0 --- /dev/null +++ b/include/base64.h @@ -0,0 +1,7 @@ +#ifndef BASE64_H +#define BASE64_H + +extern int b64decode(); +extern int b64encode(); + +#endif diff --git a/include/buffer.h b/include/buffer.h new file mode 100644 index 0000000..57cd960 --- /dev/null +++ b/include/buffer.h @@ -0,0 +1,63 @@ +#ifndef BUFFER_H +#define BUFFER_H +#include <sys/types.h> /* need type: ssize_t */ + +typedef struct buffer { + char *x; + unsigned int p; + size_t n; + int fd; + ssize_t (*op)(); +} buffer; + +#define BUFFER_INIT(op,fd,buf,len) { (buf), 0, (len), (fd), (op) } +#define BUFFER_SMALL 256 +#define BUFFER_INSIZE 8192 +#define BUFFER_OUTSIZE 8192 +#define BUFFER_MTUSIZE 1450 + +extern void buffer_init(buffer *,ssize_t (*op)(),int,char *,size_t); + +extern int buffer_flush(buffer *); +extern int buffer_put(buffer *,const char *,size_t); +extern int buffer_putalign(buffer *,const char *,size_t); +extern int buffer_putflush(buffer *,const char *,size_t); +extern int buffer_puts(buffer *,const char *); +extern int buffer_putsalign(buffer *,const char *); +extern int buffer_putsflush(buffer *,const char *); + +#define buffer_PUTC(s,c) \ + ( ((s)->n != (s)->p) \ + ? ( (s)->x[(s)->p++] = (c), 0 ) \ + : buffer_put((s),&(c),1) \ + ) + +extern int buffer_get(buffer *,char *,size_t); +extern int buffer_bget(buffer *,char *,size_t); +extern int buffer_feed(buffer *); + +extern char *buffer_peek(buffer *); +extern void buffer_seek(buffer *,size_t); + +#define buffer_PEEK(s) ( (s)->x + (s)->n ) +#define buffer_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) ) + +#define buffer_GETC(s,c) \ + ( ((s)->p > 0) \ + ? ( *(c) = (s)->x[(s)->n], buffer_SEEK((s),1), 1 ) \ + : buffer_get((s),(c),1) \ + ) + +extern int buffer_copy(buffer *,buffer *); + +extern ssize_t buffer_unixread(int,char *,size_t); +extern ssize_t buffer_unixwrite(int,char *,size_t); + +extern buffer *buffer_0; +extern buffer *buffer_1; +extern buffer *buffer_2; +extern buffer *buffer_0small; +extern buffer *buffer_1small; +extern buffer *buffer_2small; + +#endif diff --git a/include/byte.h b/include/byte.h new file mode 100644 index 0000000..f437341 --- /dev/null +++ b/include/byte.h @@ -0,0 +1,21 @@ +#ifndef BYTE_H +#define BYTE_H + +/** + @file byte.h + @author djb, feh + @source s/qmail + @comment no declaration of argument types; too many compiler errors +*/ + +extern unsigned int byte_chr(); +extern unsigned int byte_rchr(); +extern void byte_copy(); +extern void byte_copyr(); +extern int byte_diff(); +extern void byte_zero(); +extern void byte_fill(); + +#define byte_equal(s,n,t) (!byte_diff((s),(n),(t))) + +#endif diff --git a/include/case.h b/include/case.h new file mode 100644 index 0000000..d4cead2 --- /dev/null +++ b/include/case.h @@ -0,0 +1,17 @@ +#ifndef CASE_H +#define CASE_H + +extern void case_lowers(char *); +extern void case_lowerb(char *,unsigned int); +extern void case_uppers(char *); +extern void case_upperb(char *,unsigned int); +extern int case_diffs(char *,char *); +extern int case_diffrs(char *,char *); +extern int case_diffb(char *,unsigned int,char *); +extern int case_starts(char *,char *); +extern int case_startb(char *,unsigned int,char *); + +#define case_equals(s,t) (!case_diffs((s),(t))) +#define case_equalrs(s,t) (!case_diffrs((s),(t))) + +#endif diff --git a/include/cdbmake.h b/include/cdbmake.h new file mode 100644 index 0000000..9c20d2d --- /dev/null +++ b/include/cdbmake.h @@ -0,0 +1,39 @@ +/* Public domain. */ + +#ifndef CDB_MAKE_H +#define CDB_MAKE_H + +#include "buffer.h" +#include "uint_t.h" + +#define CDB_HPLIST 1000 + +struct cdb_hp { uint32 h; uint32 p; } ; + +struct cdb_hplist { + struct cdb_hp hp[CDB_HPLIST]; + struct cdb_hplist *next; + int num; +} ; + +struct cdb_make { + char bspace[8192]; + char final[2048]; + uint32 count[256]; + uint32 start[256]; + struct cdb_hplist *head; + struct cdb_hp *split; /* includes space for hash */ + struct cdb_hp *hash; + uint32 numentries; + buffer b; + uint32 pos; + int fd; +} ; + +extern int cdb_make_start(struct cdb_make *,int); +extern int cdb_make_addbegin(struct cdb_make *,unsigned int,unsigned int); +extern int cdb_make_addend(struct cdb_make *,unsigned int,unsigned int,uint32); +extern int cdb_make_add(struct cdb_make *,char *,unsigned int,char *,unsigned int); +extern int cdb_make_finish(struct cdb_make *); + +#endif diff --git a/include/cdbread.h b/include/cdbread.h new file mode 100644 index 0000000..bee4fd4 --- /dev/null +++ b/include/cdbread.h @@ -0,0 +1,38 @@ +/* Public domain. */ + +#ifndef CDB_H +#define CDB_H + +#include "uint_t.h" + +#define CDB_HASHSTART 5381ULL +extern uint32 cdb_hashadd(uint32,unsigned char); +extern uint32 cdb_hash(char *,unsigned int); +extern uint32 cdb_unpack(unsigned char *); + +struct cdb { + char *map; /* 0 if no map is available */ + int fd; + uint32 size; /* initialized if map is nonzero */ + uint32 loop; /* number of hash slots searched under this key */ + uint32 khash; /* initialized if loop is nonzero */ + uint32 kpos; /* initialized if loop is nonzero */ + uint32 hpos; /* initialized if loop is nonzero */ + uint32 hslots; /* initialized if loop is nonzero */ + uint32 dpos; /* initialized if cdb_findnext() returns 1 */ + uint32 dlen; /* initialized if cdb_findnext() returns 1 */ +} ; + +extern void cdb_free(struct cdb *); +extern void cdb_init(struct cdb *,int fd); + +extern int cdb_read(struct cdb *,char *,unsigned int,uint32); + +extern void cdb_findstart(struct cdb *); +extern int cdb_findnext(struct cdb *,char *,unsigned int); +extern int cdb_find(struct cdb *,char *,unsigned int); + +#define cdb_datapos(c) ((c)->dpos) +#define cdb_datalen(c) ((c)->dlen) + +#endif diff --git a/include/close.h b/include/close.h new file mode 100644 index 0000000..fc5b7bb --- /dev/null +++ b/include/close.h @@ -0,0 +1,12 @@ +#ifndef CLOSE_H +#define CLOSE_H + +/* + * Revision 20160713, Kai Peter + * + * (POSIX: 'close' is defined in <unistd.h>) +*/ + +extern int close(int __fd); + +#endif diff --git a/include/constmap.h b/include/constmap.h new file mode 100644 index 0000000..750702e --- /dev/null +++ b/include/constmap.h @@ -0,0 +1,21 @@ +#ifndef CONSTMAP_H +#define CONSTMAP_H + +typedef unsigned long constmap_hash; + +struct constmap { + int num; + constmap_hash mask; + constmap_hash *hash; + int *first; + int *next; + char **input; + int *inputlen; +} ; + +int constmap_init(struct constmap *,char *,int,int); +int constmap_init_char(struct constmap *,char *,int,int,char); +void constmap_free(); +char *constmap(); + +#endif diff --git a/include/direntry.h b/include/direntry.h new file mode 100644 index 0000000..d1628a9 --- /dev/null +++ b/include/direntry.h @@ -0,0 +1,10 @@ +#ifndef DIRENTRY_H +#define DIRENTRY_H + +/* sysdep: +dirent */ + +#include <sys/types.h> +#include <dirent.h> +#define direntry struct dirent + +#endif diff --git a/include/dnsresolv.h b/include/dnsresolv.h new file mode 100644 index 0000000..406af50 --- /dev/null +++ b/include/dnsresolv.h @@ -0,0 +1,202 @@ +#ifndef DNSRESOLV_H +#define DNSRESOLV_H + +/* + * Revision 20230613, Erwin Hoffmann + * - DNS_NXD (return code 0) for NXDOMAIN and NODATA added + * - DNS_SOFT as shortcut for DNS_ERR or DNS_COM + * - DNS_HARD indicates DNS loop problems + * Revision 20221101, Erwin Hoffmann + * - DNS_COM has now return code -3 (as documented; tx. Franz S.) + * Revision 20210922, Erwin Hoffmann + * - Added constants MAXMSGSIZE and MAXSEGMENT - not used yet + * Revision 20210401, Erwin Hoffmann + * - removed obsolete dns_sortip(); not going to work with GCC-10 anyway + * Revision 20200719, Erwin Hoffmann + * - added dns_qualify_localhost function including fqdn retrun + * Revision 20190730, Erwin Hoffmann + * - revised DNS_* return codes to make them compliant with ucspi-* + * Revision 20190430, Erwin Hoffmann + * - added DNS_SOFT/HARD/MEM complient to s/qmail + * - code changes in all decendent modules + * Revision 20180222, Erwin Hoffmann + * - we consider in total 32 NS IPs (IPv4 + IPv6) + * - added dns_transmit_start6 + * - added uint32 scope_ids[32], + * the initial NS scopes read from /etc/resolv.conf et al. + * Revision 20180118, Erwin Hoffmann + * - included MSGSIZE for DNS messages (instead of MTUSIZE) + * Revision 20171231, Erwin Hoffmann + * - renamed to dnsresolv.h and removed *qmail declarations + * Revision 20170902, Erwin Hoffmann + * - added old definitions from *qmail for (temp) backwards compatibility + * - added more DNS RR definitions +*/ + +#include "stralloc.h" +#include "iopause.h" +#include "taia.h" + +/* Note: The conventions are subject of change in forthcoming versions */ + +#define DNS_NXD 0 /* NXDOMAIN, NODATA */ +#define DNS_MEM -1 /* out of memory; fatal */ +#define DNS_ERR -2 /* parsing errors and others */ +#define DNS_COM -3 /* (socket) communication errors: SERVFAIL */ +#define DNS_INT -4 /* internal errors */ +#define DNS_SOFT -5 /* DNS_ERR or DNS_COM */ +#define DNS_HARD -6 /* DNS loop problem */ + +#define MSGSIZE MTUSIZE /* todays default */ +// #define MSGSIZE 512 /* RFC 1035 */ +#define MAXMSGSIZE 4096 /* 4069 seen with EDNS0 */ +#define MAXSEGMENT 65535 /* Max TCP buffer size */ + +#define QUERY_MAXNS 32 /* 16 IPv4 + 16 IPv6 NS */ +#define QUERY_MAXIPLEN 512 /* QUERY_MAXNS * 16 */ + +/* Note: These following definitions are subject of change */ + +#define DNS_C_IN "\0\1" +#define DNS_C_ANY "\0\377" + +#define DNS_T_A "\0\1" +#define DNS_T_NS "\0\2" +#define DNS_T_CNAME "\0\5" +#define DNS_T_SOA "\0\6" +#define DNS_T_PTR "\0\14" +#define DNS_T_HINFO "\0\15" +#define DNS_T_MX "\0\17" +#define DNS_T_TXT "\0\20" +#define DNS_T_RP "\0\21" +#define DNS_T_SIG "\0\30" +#define DNS_T_KEY "\0\31" +#define DNS_T_AAAA "\0\34" +#define DNS_T_SRV "\0\41" +#define DNS_T_NAPTR "\0\43" +#define DNS_T_CERT "\0\45" +#define DNS_T_OPT "\0\51" +#define DNS_T_DS "\0\53" +#define DNS_T_SSHFP "\0\54" +#define DNS_T_IPSECKEY "\0\55" +#define DNS_T_RRSIG "\0\56" +#define DNS_T_NSEC "\0\57" +#define DNS_T_DNSKEY "\0\60" +#define DNS_T_NSEC3 "\0\62" +#define DNS_T_NSEC3PARAM "\0\63" +#define DNS_T_TLSA "\0\64" +#define DNS_T_HIP "\0\67" +#define DNS_T_OPENPGPKEY "\0\75" +#define DNS_T_SPF "\0\143" +#define DNS_T_AXFR "\0\374" +#define DNS_T_ANY "\0\377" +#define DNS_T_CAA "\1\1" + +#define LOCALHOST "localhost" /* no clear distinction IPv4/IPv6 */ +#define IP4_LOOPBACK "ip4-loopback" +#define IP6_LOOPBACK "ip6-loopback" + +struct dns_transmit { + char *query; /* 0, or dynamically allocated */ + unsigned int querylen; + char *packet; /* 0, or dynamically allocated */ + unsigned int packetlen; + int s1; /* 0, or 1 + an open file descriptor */ + int tcpstate; + unsigned int udploop; + unsigned int curserver; + struct taia deadline; + unsigned int pos; + const char *servers; + uint32 scope_id; + char localip[16]; + char qtype[2]; +} ; + +/* General */ + +extern void dns_random_init(const char *); +extern unsigned int dns_random(unsigned int); + +extern void dns_domain_free(char **); +extern int dns_domain_copy(char **,const char *); +extern unsigned int dns_domain_length(const char *); +extern int dns_domain_equal(const char *,const char *); +extern int dns_domain_suffix(const char *,const char *); +extern unsigned int dns_domain_suffixpos(const char *,const char *); +extern int dns_domain_fromdot(char **,const char *,unsigned int); +extern int dns_domain_todot_cat(stralloc *,const char *); +extern int dns_ip_qualify(stralloc *,stralloc *,const stralloc *); +extern int dns_ip_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *); +extern int dns_ip_qualify_localhost(stralloc *,stralloc *,const stralloc *); + +extern unsigned int dns_packet_copy(const char *,unsigned int,unsigned int,char *,unsigned int); +extern unsigned int dns_packet_getname(const char *,unsigned int,unsigned int,char **); +extern unsigned int dns_packet_skipname(const char *,unsigned int,unsigned int); + +extern struct dns_transmit dns_resolve_tx; +extern int dns_transmit_start(struct dns_transmit *,const char *,int,const char *,const char *,const char *); +extern void dns_transmit_free(struct dns_transmit *); +extern void dns_transmit_io(struct dns_transmit *,iopause_fd *,struct taia *); +extern int dns_transmit_get(struct dns_transmit *,const iopause_fd *,const struct taia *); + +/* Common IPv4 + IPv6 */ + +extern int dns_resolvconfip(char *,uint32 *); +extern int dns_resolvconfrewrite(stralloc *); +extern int dns_resolve(const char *,const char *); + +extern int dns_name(stralloc *,const char *); +extern int dns_name_packet(stralloc *,const char *,unsigned int); +extern int dns_txt_packet(stralloc *,const char *,unsigned int); +extern int dns_txt(stralloc *,const stralloc *); +extern int dns_mx_packet(stralloc *,const char *,unsigned int); +extern int dns_mx(stralloc *,const stralloc *); + +/* IPv4 specific */ + +extern int dns_ip4_packet(stralloc *,const char *,unsigned int); +extern int dns_ip4(stralloc *,stralloc *); +extern void dns_sortip4(char *,unsigned int); + +extern int dns_ip4_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *); +extern int dns_ip4_qualify(stralloc *,stralloc *,const stralloc *); + +#define DNS_NAME4_DOMAIN 31 +extern int dns_name4_domain(char *,const char *); +extern int dns_name4(stralloc *,const char *); +extern int randombind4(struct dns_transmit *); + +/* IPv6 specific */ + +extern int dns_ip6_packet(stralloc *,const char *,unsigned int); +extern int dns_ip6(stralloc *,stralloc *); +extern void dns_sortip6(char *,unsigned int); + +extern int dns_ip6_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *); +extern int dns_ip6_qualify(stralloc *,stralloc *,const stralloc *); + +#define DNS_NAME6_DOMAIN (4*16+11) +extern int dns_name6_domain(char *,const char *); +extern int dns_name6(stralloc *,const char *); + +extern int dns_cname_packet(stralloc *,const char *,unsigned int); +extern int dns_cname(stralloc *,stralloc *); + +extern int dns_transmit_start6(struct dns_transmit *,const char *,int,const char *,const char *,const char *,const uint32 *); +extern int randombind6(struct dns_transmit *); + +/* General */ + +extern void socketfree(struct dns_transmit *); +extern void queryfree(struct dns_transmit *); +extern void packetfree(struct dns_transmit *); +extern int serverwantstcp(const char *,unsigned int); +extern int serverfailed(const char *,unsigned int); +extern int getscopeid(const struct dns_transmit *,const char *); +extern int firstudp(struct dns_transmit *); +extern int nextudp(struct dns_transmit *); +extern int firsttcp(struct dns_transmit *); +extern int nexttcp(struct dns_transmit *); + +#endif diff --git a/include/env.h b/include/env.h new file mode 100644 index 0000000..8f6ea87 --- /dev/null +++ b/include/env.h @@ -0,0 +1,29 @@ +#ifndef ENV_H +#define ENV_H + +/* + * Revision 20171220, Erwin Hoffmann + * - removed variable names + * Revision 20160628, Kai Peter + * - updated code (*env_get) like in ucspi-tcp-0.88 + * - commented out 'env_pick' and 'env_clear' +*/ + +extern char **environ; + +extern int env_isinit; +extern int env_init(); + +extern /*@null@*/char *env_get(char *); +extern int env_put(char *,char *); +extern int env_puts(char *); +extern int env_set(char *); +extern int env_unset(char *); +extern char *env_pick(); +extern void env_clear(); + +extern char *env_findeq(char *); + +#define env_put2 env_put /* backwards compatibility */ + +#endif diff --git a/include/error.h b/include/error.h new file mode 100644 index 0000000..7685c9e --- /dev/null +++ b/include/error.h @@ -0,0 +1,57 @@ +#ifndef ERROR_H +#define ERROR_H + +#include <errno.h> + +#ifndef EPROTO /* OpenBSD compat */ +#define EPROTO EINTR +#endif + +#define error_str(i) errstr(i) +extern char *error_str(int); + +/* Exception handling notes: + (1) system errors RECEIVED according to <errno.h> during operation and handed over + (2) application errors DEFINED internally and ennumerated alongside with <errno.h> +*/ + +/* djb backwards compatibility - deprecated form of system errors */ + /* Comparison of error codes and constants: + intern Linux FreeBSD OmniOS */ +#define error_intr EINTR /* -1 4 4 4 */ +#define error_nomem ENOMEM /* -2 12 12 12 */ +#define error_noent ENOENT /* -3 2 2 2 */ +#define error_txtbsy ETXTBSY /* -4 26 26 26 */ +#define error_io EIO /* -5 5 5 5 */ +#define error_exist EEXIST /* -6 17 17 17 */ +#define error_timeout ETIMEDOUT /* -7 110 60 145 */ +#define error_inprogress EINPROGRESS /* -8 115 36 160 */ +#define error_wouldblock EWOULDBLOCK /* -9 EAGAIN EAGAIN EAGAIN */ +#define error_again EAGAIN /* -10 11 35 11 */ +#define error_pipe EPIPE /* -11 32 32 32 */ +#define error_perm EPERM /* -12 1 1 1 */ +#define error_acces EACCES /* -13 13 13 13 */ +#define error_nodevice ENODEV /* -14 (6) (6) 19 */ +#define error_proto EPROTO /* -15 71 92 71 */ +#define error_isdir EISDIR /* -16 21 21 21 */ +#define error_connrefused ECONNREFUSED /* -17 111 61 146 */ +//extern int error_notdir; /* -18 20 20 20 */ +#define error_rofs EROFS /* -19 30 30 30 */ +#define error_connreset ECONNRESET /* -20 104 54 131 */ + +/* djb uses some internal application error and class definitions -- revised (feh) */ +#define CAT -10 /* raw message w/o terminating \n */ +#define LOG -90 /* generic logging */ +#define INFO -91 /* named logging */ +#define TEMP -97 /* (triggered) temporay alert condition */ +#define ALERT -98 /* (triggered) alert condition */ +#define WARN -99 /* exception condition */ +#define ESOFT -100 /* soft error, reversed negative */ +#define EHARD -111 /* hard error, reversed negative */ +#define USAGE 100 /* usage error on call -- explicit usage() */ +#define SYNTAX 101 /* usage/syntax error on call -- explicit syntaxerror() */ +#define DROP 110 /* connection dropped -- explicit dropped() */ +#define FATAL 111 /* internal error -- all */ +#define ERROR 112 /* application error */ + +#endif diff --git a/include/exit.h b/include/exit.h new file mode 100644 index 0000000..f74b741 --- /dev/null +++ b/include/exit.h @@ -0,0 +1,13 @@ +#ifndef EXIT_H +#define EXIT_H + +/** + @file exit.h + @author djb, feh + @source qmail + @brief convenience header +*/ + +extern void _exit(int); + +#endif diff --git a/include/fd.h b/include/fd.h new file mode 100644 index 0000000..21047c9 --- /dev/null +++ b/include/fd.h @@ -0,0 +1,8 @@ +#ifndef FD_H +#define FD_H + +extern int fd_copy(int,int); +extern int fd_move(int,int); +extern int fd_coe(int fd); + +#endif diff --git a/include/fifo.h b/include/fifo.h new file mode 100644 index 0000000..3c1bf68 --- /dev/null +++ b/include/fifo.h @@ -0,0 +1,12 @@ +#ifndef FIFO_H +#define FIFO_H + +/** + @file fifo.h + @author djb, feh + @source s/qmail +*/ + +int fifo_make(char *,int); + +#endif diff --git a/include/fmt.h b/include/fmt.h new file mode 100644 index 0000000..6e68d60 --- /dev/null +++ b/include/fmt.h @@ -0,0 +1,36 @@ +#ifndef FMT_H +#define FMT_H + +/** + @file fmt.h + @author djb, kp, feh + @source qmail + @brief conversion function declarations + */ + +#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */ +#define FMT_LEN ((char *) 0) /* convenient abbreviation */ + +extern unsigned int fmt_str(char *,char *); +extern unsigned int fmt_strn(char *,char *,unsigned int); +extern unsigned int fmt_uint(char *,unsigned int); +extern unsigned int fmt_uint0(char *,unsigned int,unsigned int); +extern unsigned int fmt_ulong(char *,unsigned long); +extern unsigned int fmt_xlong(char *,unsigned long); + +extern int fromhex(unsigned char); +extern char tohex(char); + +/* for future releases */ +// extern unsigned int fmt_xint(char *,unsigned int); +// extern unsigned int fmt_nbbint(char *,unsigned int,unsigned int,unsigned int,unsigned int); +// extern unsigned int fmt_ushort(char *,unsigned short); +// extern unsigned int fmt_xshort(char *,unsigned short); +// extern unsigned int fmt_nbbshort(char *,unsigned int,unsigned int,unsigned int,unsigned short); +// extern unsigned int fmt_nbblong(char *,unsigned int,unsigned int,unsigned int,unsigned long); +// extern unsigned int fmt_plusminus(char *,int); +// extern unsigned int fmt_minus(char *,int); +// extern unsigned int fmt_0x(char *,int); +/* to be done */ + +#endif diff --git a/include/genalloc.h b/include/genalloc.h new file mode 100644 index 0000000..ee489af --- /dev/null +++ b/include/genalloc.h @@ -0,0 +1,74 @@ +#ifndef GENALLOC_H +#define GENALLOC_H + +/* + * Revision 20210307, Erwin Hoffmann + * - +*/ + +#include <sys/types.h> + +/* GEN_ALLOC demystified: + + GEN_ALLOC generates a list of self-defined types (structs) in an + allocated contiguous heap chunk while copying the content of the + entire field members or appending the existing field. + GEN_ALLOC types care of currently used and/or allocated bytes of field. + +Macros: + GEN_ALLOC_ready (ta,type,field,len,a,i,n,x,base,ta_ready) + GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) + GEN_ALLOC_append (ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) + + 0. ta: 'type alloc' - typedef'ed struct name (aka ipalloc et al.) + 1. type: defined struct (used for size information) + 2. field: declared public name of struct + 2. len: used length of string + 4. a: allocated size + 5. i: current allocated size for member x + 6. n: bytes to allocate; in 'ready' mode: +size of one entry; + in 'readyplus' mode: +size of +used size of one entry + 7. x: local name (alias to field name) + 8. base: size of single entry + 9. ta_ready/ta_readyplus (operation) + 10. ta_append (operation) + +*/ + +/* file: gen_alloc.h */ +#define GEN_ALLOC_typedef(ta,type,field,len,a) \ + typedef struct ta { type *field; unsigned int len; unsigned int a; } ta; + +/* file: gen_allocdefs.h (deprecated) */ +// used in: ipalloc, prioq, qmail-remote, qmail-inject, token822 +#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \ +int ta_ready(x,n) register ta *x; register unsigned int n; \ +{ register unsigned int i; \ + if (x->field) { \ + i = x->a; \ + if (n > i) { \ + x->a = base + n + (n >> 3); \ + if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \ + x->a = i; return 0; } \ + return 1; } \ + x->len = 0; \ + return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); } + +#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \ +int ta_rplus(x,n) register ta *x; register unsigned int n; \ +{ register unsigned int i; \ + if (x->field) { \ + i = x->a; n += x->len; \ + if (n > i) { \ + x->a = base + n + (n >> 3); \ + if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \ + x->a = i; return 0; } \ + return 1; } \ + x->len = 0; \ + return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); } + +#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \ +int ta_append(x,i) register ta *x; register type *i; \ +{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; } + +#endif diff --git a/include/getln.h b/include/getln.h new file mode 100644 index 0000000..23eb58b --- /dev/null +++ b/include/getln.h @@ -0,0 +1,10 @@ +#ifndef GETLN_H +#define GETLN_H + +#include "buffer.h" +#include "stralloc.h" + +extern int getln(buffer *,stralloc *,int *,int); +extern int sgetln(buffer *,stralloc *,char **,unsigned int *,int); + +#endif diff --git a/include/getoptb.h b/include/getoptb.h new file mode 100644 index 0000000..0338a15 --- /dev/null +++ b/include/getoptb.h @@ -0,0 +1,28 @@ +#ifndef BGETOPT_H +#define BGETOPT_H + +/* + * Revision 20160714, Kai Peter + * - consolidated 'sgetopt.h' and 'subgetopt.h' into '(b)getopt.h' +*/ + +#define optarg subgetoptarg +#define optind subgetoptind +#define optpos subgetoptpos +#define optproblem subgetoptproblem +#define optprogname subgetoptprogname +#define opteof subgetoptdone + +#define SUBGETOPTDONE -1 + +extern int subgetopt(int,char **,char *); +extern char *subgetoptarg; +extern int subgetoptind; +extern int subgetoptpos; +extern int subgetoptproblem; +extern char *subgetoptprogname; +extern int subgetoptdone; + +extern int opterr; + +#endif diff --git a/include/iopause.h b/include/iopause.h new file mode 100644 index 0000000..e3cfa01 --- /dev/null +++ b/include/iopause.h @@ -0,0 +1,28 @@ +#ifndef IOPAUSE_H +#define IOPAUSE_H + +#define IOPAUSE_POLL + +#include <sys/types.h> +#ifdef HAS_POLL_H +#include <poll.h> + +typedef struct pollfd iopause_fd; +#define IOPAUSE_READ POLLIN +#define IOPAUSE_WRITE POLLOUT +#else +typedef struct { + int fd; + short events; + short revents; +} iopause_fd; +#define IOPAUSE_READ 1 +#define IOPAUSE_WRITE 4 +#endif + +#include "taia.h" + +extern int iopause(iopause_fd *,unsigned int,struct taia *,struct taia *); +extern unsigned long pollmax; + +#endif diff --git a/include/ip.h b/include/ip.h new file mode 100644 index 0000000..3f0c299 --- /dev/null +++ b/include/ip.h @@ -0,0 +1,107 @@ +#ifndef IP_H +#define IP_H + +/* + * Revision 20200603, Erwin Hoffmann + * - added V46loopback address + * Revision 20190414, Erwin Hoffmann + * - removed ip_scan and ip_scanbracket (comp. versions). + * - added ia6_fmt and ia4_fmt (for qmail) + * Revision 20180314, Erwin Hoffmann + * - ip4_cidr added and argument list changed (also for ip6_cidr) + * Revision 20180213, Erwin Hoffmann + * - ip4_scan/ip6_scan/ip4_scanbracket/ip6_fmt_flat revised + * Revision 20180206, Erwin Hoffmann + * - added V6linklocal address + * Revision 20170319, Kai Peter + * - rewrite (consolidation and compatibility) + * Revision 20170210, Kai Peter + * - added definition 'V4loopback' and redefinition 'ip4loopback' + * Revision 20170908, Erwin Hoffmann + * - added some definitions from s/qmail + required for djbdns +*/ + +/* Consolidated header files ip.h from *qmail (with IPv6) and ip4.h/ip6.h + from libowfat. Thus it could be used with 'older' and 'newer' code. +*/ + +#include "byte.h" +#include "stralloc.h" + +#define V4MAPPREFIX "::ffff:" +#define HOSTNAMELEN 1025 +#define IP4MLMTU 512 +#define IP6MLMTU 1280 /* RFC 8200 */ +#define MTUSIZE IP6MLMTU + +#define IP4_FMT 20 /* backwards compatibility */ +#define IP6_FMT 40 /* " */ +#define IPFMT 72 /* used in qmail-remote only (temp?) */ +/* may be better: */ +//#define IP4_FMT IPFMT /* backwards compatibility */ +//#define IP6_FMT IPFMT /* " */ +/* deprecated: */ +#define FMT_IP4 IP4_FMT /* more backwards compatibility */ +#define FMT_IP6 IP6_FMT /* " */ + +/* these structs are going deprecated (should replaced by socket lib) */ +struct ip4_address { unsigned char d[4]; }; /* 4 decimal pieces */ +struct ip6_address { unsigned char d[16]; }; /* 16 hex pieces */ +#define ip_address ip4_address /* backward compatibility */ + +unsigned int ip4_bytestring(stralloc *,char [4],int); +unsigned int ip4_cidr(char *,char [4],unsigned long *); +unsigned int ip4_fmt(char *,char [4]); +unsigned int ip4_scan(const char *,char [4]); +unsigned int ip4_scanbracket(const char *,char [4]); +unsigned int ia4_fmt(char *,char [4]); + +const static char V4loopback[4] = {127,0,0,1}; +const static char V4localnet[4] = {0,0,0,0}; +const static char V4broadcast[4] = {-1,-1,-1,-1}; // all bits 1 + +/*** + * Compactified IPv6 addresses are really ugly to parse. + * Syntax: (h = hex digit) [RFC 5952] + * 1. hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh + * 2. leading 0s in any octet can be suppressed + * 3. any number of 0000 may be abbreviated as "::" + * a) but only once; + * b) the longest piece or on equal match the first piece, + * c) a single instance of 0000 has to displayed as 0 + * 4. The last two words may be written as IPv4 address + * + * Flat ip6 address syntax: + * hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh (32 hex digits) + * + * struct ip6_address format: + * cccccccccccccccc (16 chars; d[16]) -- each char swallows two hex values + * + * Bitstring representation with length prefix: + * bbbbbbb.........bbbb (max 128 bits); stralloc(ip6string); b = 0 || 1 + * + */ + +unsigned int ip6_bytestring(stralloc *,char *,int); +unsigned int ip6_cidr(char *,char [16],unsigned long *); +unsigned int ip6_fmt(char *,char [16]); +unsigned int ip6_fmt_flat(char *,char [16]); +unsigned int ip6_ifscan(char *,char [16],stralloc *); +unsigned int ip6_scan(const char *,char [16]); +unsigned int ip6_scanbracket(const char *,char [16]); +unsigned int ip6_scan_flat(const char *,char [16]); +unsigned int ia6_fmt(char *,char [16]); + +static const unsigned char V4mappedprefix[12] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff}; +static const unsigned char V46loopback[16] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 127,0,0,1}; +static const unsigned char V6loopback[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1}; +static const unsigned char V6localnet[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; +static const unsigned char V6linklocal[2] = {0xfe,0x80}; + +int fromhex(unsigned char); +char tohex(char); + +#define V6any V6localnet /* backward compatibility */ +#define ip6_isv4mapped(ip) (byte_equal(ip,12,V4mappedprefix)) + +#endif diff --git a/include/lock.h b/include/lock.h new file mode 100644 index 0000000..74df4c9 --- /dev/null +++ b/include/lock.h @@ -0,0 +1,14 @@ +#ifndef LOCK_H +#define LOCK_H + +/** + @file lock.h + @author djb, feh + @source s/qmail +*/ + +int lock_ex(int); +int lock_un(int); +int lock_exnb(int); + +#endif diff --git a/include/logmsg.h b/include/logmsg.h new file mode 100644 index 0000000..c480c8b --- /dev/null +++ b/include/logmsg.h @@ -0,0 +1,31 @@ +#ifndef LOGMSG_H +#define LOGMSG_H + +#include <errno.h> +#include <stdlib.h> +#include "error.h" + +extern void logmsg(const char *who, int ecode, unsigned int class, + const char *msg); + +/* useful combinations of params */ +/* class: FATAL (hard) - system error */ +#define err_sys(w,e) logmsg(w,e,FATAL,"") +#define err_sys_plus(w,e,m) logmsg(w,e,FATAL,m) +/* class: WARN (application) - temporary error */ +#define err_tmp(w,e,m) logmsg(w,e,WARN,m) +#define err_tmp_plus(w,e,m) logmsg(w,e,WARN,m) +/* class: facultative ('int'ernal definition */ +#define err_int(w,e,c) logmsg(w,e,c,"") +#define err_int_plus(w,e,c,m) logmsg(w,e,c,m) +/* log messages */ +/* #define log(w,m) logmsg(w,0,LOG,m) // obsoleted by */ +#define log_who(w,m) logmsg(w,0,LOG,m) +#define log_anon(m) logmsg("",0,LOG,m) +#define log_cat(n) logmsg("",0,CAT,m) + +/* build log message from multiple partial strings */ +extern char *build_log_msg(const char *[]); +#define B(...) build_log_msg((const char *[]){__VA_ARGS__,NULL}) // K/R sect. 7.3 + +#endif diff --git a/include/ndelay.h b/include/ndelay.h new file mode 100644 index 0000000..7a03a8d --- /dev/null +++ b/include/ndelay.h @@ -0,0 +1,13 @@ +#ifndef NDELAY_H +#define NDELAY_H + +/** + @brief ndelay.h + @author djb, feh + @source s/qmail +*/ + +int ndelay_on(int); +int ndelay_off(int); + +#endif diff --git a/include/open.h b/include/open.h new file mode 100644 index 0000000..428ebfb --- /dev/null +++ b/include/open.h @@ -0,0 +1,16 @@ +#ifndef OPEN_H +#define OPEN_H + +/** + @file open.h + @author djb, feh + @source s/qmail +*/ + +int open_read(const char *); +int open_excl(const char *); +int open_append(const char *); +int open_trunc(const char *); +int open_write(const char *); + +#endif diff --git a/include/pathexec.h b/include/pathexec.h new file mode 100644 index 0000000..9db6fb4 --- /dev/null +++ b/include/pathexec.h @@ -0,0 +1,10 @@ +#ifndef PATHEXEC_H +#define PATHEXEC_H +#include "stralloc.h" + +extern void pathexec_run(const char *,char *const *,char *const *); +extern int pathexec_env(const char *,const char *); +extern int pathexec_multienv(stralloc *); +extern void pathexec(char *const *); + +#endif diff --git a/include/prot.h b/include/prot.h new file mode 100644 index 0000000..7dd0503 --- /dev/null +++ b/include/prot.h @@ -0,0 +1,7 @@ +#ifndef PROT_H +#define PROT_H + +extern int prot_gid(int); +extern int prot_uid(int); + +#endif diff --git a/include/readclose.h b/include/readclose.h new file mode 100644 index 0000000..a95f4f5 --- /dev/null +++ b/include/readclose.h @@ -0,0 +1,14 @@ +#ifndef READCLOSE_H +#define READCLOSE_H + +#include "stralloc.h" +#include "close.h" + +extern int readclose_append(int,stralloc *,unsigned int); +extern int readclose(int,stralloc *,unsigned int); + +#define slurpclose readclose /* backwards compatibility */ + +extern int openreadclose(const char *,stralloc *,unsigned int); + +#endif diff --git a/include/readwrite.h b/include/readwrite.h new file mode 100644 index 0000000..40b427f --- /dev/null +++ b/include/readwrite.h @@ -0,0 +1,9 @@ +#ifndef READWRITE_H +#define READWRITE_H + +#include <unistd.h> + +// extern int read(); +// extern int write(); + +#endif diff --git a/include/rename.h b/include/rename.h new file mode 100644 index 0000000..641f68a --- /dev/null +++ b/include/rename.h @@ -0,0 +1,6 @@ +#ifndef RENAME_H +#define RENAME_H + +extern int rename(const char *, const char *); + +#endif diff --git a/include/scan.h b/include/scan.h new file mode 100644 index 0000000..f1d4030 --- /dev/null +++ b/include/scan.h @@ -0,0 +1,12 @@ +#ifndef SCAN_H +#define SCAN_H + +extern unsigned int scan_0x(const char *,unsigned int *); +extern unsigned int scan_xint(const char *,unsigned int *); +extern unsigned int scan_8long(const char *,unsigned long *); +extern unsigned int scan_uint(const char *,unsigned int *); +extern unsigned int scan_long(const char *,long *); +extern unsigned int scan_ulong(const char *,unsigned long *); +extern unsigned int scan_xlong(const char *,unsigned long *); + +#endif diff --git a/include/seek.h b/include/seek.h new file mode 100644 index 0000000..06aad97 --- /dev/null +++ b/include/seek.h @@ -0,0 +1,15 @@ +#ifndef SEEK_H +#define SEEK_H + +typedef unsigned long seek_pos; + +extern seek_pos seek_cur(int); + +extern int seek_set(int,seek_pos); +extern int seek_end(int); + +extern int seek_trunc(int,seek_pos); + +#define seek_begin(fd) (seek_set((fd),(seek_pos) 0)) + +#endif diff --git a/include/select.h b/include/select.h new file mode 100644 index 0000000..646dd50 --- /dev/null +++ b/include/select.h @@ -0,0 +1,13 @@ +#ifndef SELECT_H +#define SELECT_H + +#include <sys/types.h> +#include <sys/time.h> + +#ifdef HAS_SELECT_H +#include <sys/select.h> +#endif + +extern int select(); + +#endif diff --git a/include/sig.h b/include/sig.h new file mode 100644 index 0000000..3efdd32 --- /dev/null +++ b/include/sig.h @@ -0,0 +1,63 @@ +#ifndef SIG_H +#define SIG_H + +/* + * Revision 20160714, Kai Peter + * - updated some declarations no new(er) one's from ucspi-tcp-0.88 +*/ + +/* new(er) declarations from ucspi-tcp-0.88: */ +extern int sig_alarm; +extern int sig_child; +extern int sig_cont; +extern int sig_hangup; +extern int sig_pipe; +extern int sig_term; + +extern void (*sig_defaulthandler)(); +extern void (*sig_ignorehandler)(); + +extern void sig_catch(int,void (*)()); +#define sig_ignore(s) (sig_catch((s),sig_ignorehandler)) +#define sig_uncatch(s) (sig_catch((s),sig_defaulthandler)) + +extern void sig_block(int); +extern void sig_unblock(int); +extern void sig_blocknone(void); +extern void sig_pause(void); + +extern void sig_dfl(int); + +/* declaration of (net)qmail package (untouched) */ +extern void sig_miscignore(); +extern void sig_bugcatch(); + +extern void sig_pipeignore(); +extern void sig_pipedefault(); + +extern void sig_contblock(); +extern void sig_contunblock(); +extern void sig_contcatch(); +extern void sig_contdefault(); + +extern void sig_termblock(); +extern void sig_termunblock(); +extern void sig_termcatch(); +extern void sig_termdefault(); + +extern void sig_alarmblock(); +extern void sig_alarmunblock(); +extern void sig_alarmcatch(); +extern void sig_alarmdefault(); + +extern void sig_childblock(); +extern void sig_childunblock(); +extern void sig_childcatch(); +extern void sig_childdefault(); + +extern void sig_hangupblock(); +extern void sig_hangupunblock(); +extern void sig_hangupcatch(); +extern void sig_hangupdefault(); + +#endif diff --git a/include/socket_if.h b/include/socket_if.h new file mode 100644 index 0000000..54cfa5f --- /dev/null +++ b/include/socket_if.h @@ -0,0 +1,97 @@ +#ifndef SOCKETIF_H +#define SOCKETIF_H + +/* Revsision 20220608 + * - removed obsolete socket_local4() and socket_remote4() + * Revision 20210828, Erwin Hoffmann + * - added socket_accept4() + * Revision 20210226, Erwin Hoffmann + * - removed dependency on ipv4socket (loose coupling) + * - only single socket_tcp() and + * - single socket_udp() function serving both IPv4 + IPv6 + * Revision 20181125, Erwin Hoffmann + * - switched to 'uint_t.h' + * - changed 'socket_tcp' --> 'socket_tcp4', 'socket_udp' --> 'socket_udp4' + * - added 'socket_ip6optionskill' + * - reordered and included backword compatible IPv6 calls + * - commented multicast socket declarations + * - enlarged usage for ipv4socket -> dual bind IPv4/IPv6 +*/ + +#define __APPLE_USE_RFC_3542 /* MacOS Anycast support */ + +#include "uint_t.h" + +/* IPv4 only */ +extern int socket_accept4(int,char [4],uint16 *); +extern int socket_bind4(int,const char [4],uint16); +extern int socket_bind4_reuse(int,const char [4],uint16); +extern int socket_connect4(int,const char [4],uint16); +extern int socket_send4(int,const char *,unsigned int,const char [4],uint16); +extern int socket_broadcast(int,const char *,unsigned int,uint16); + +/* Backward compatibility */ +#define socket_local4 socket_local +#define socket_remote4 socket_remote + +/* IPv6 only */ +extern int socket_bind6(int,const char [16],uint16,uint32); +extern int socket_bind6_reuse(int,const char [16],uint16,uint32); +extern int socket_connect6(int,const char [16],uint16,uint32); +extern int socket_send6(int,const char *,unsigned int,const char [16],uint16,uint32); +extern const char* socket_getifname(uint32); +extern uint32 socket_getifidx(const char *); +extern int socket_ip6optionskill(int); +extern int socket_ip6anycast(int); + +/* Common IPv4 & IPv6 */ +extern int socket_accept(int,char [16],uint16 *,uint32 *); +extern int socket_bind(int,const char [16],uint16,uint32); +extern int socket_bind_reuse(int,const char [16],uint16,uint32); +extern int socket_connect(int,const char [16],uint16,uint32); +extern int socket_connected(int); +extern int socket_listen(int,int); +extern int socket_local(int,char [16],uint16 *,uint32 *); +extern int socket_recv(int,char *,unsigned int,char [16],uint16 *,uint32 *); +extern int socket_remote(int,char [16],uint16 *,uint32 *); +extern int socket_send(int,const char *,unsigned int,const char [16],uint16,uint32); +extern void socket_tryreservein(int,int); +extern int socket_ipoptionskill(int); +extern int socket_dualstack(int); +extern int socket_nodualstack(int); + +/* Backward compatibility */ +#define socket_accept6 socket_accept +#define socket_local6 socket_local +#define socket_recv6 socket_recv +#define socket_remote6 socket_remote + +/* TCP */ +extern int socket_tcp4(void); +extern int socket_tcp6(void); +extern int socket_tcp(void); +extern int socket_tcpnodelay(int); + +/* UDP */ +extern int socket_udp4(void); +extern int socket_udp6(void); +extern int socket_udp(void); + +/*********** For future use ***********************************/ +/* enable sending udp packets to the broadcast address */ +// extern int socket_broadcast(int); +/* join a multicast group on the given interface */ +// extern int socket_mcjoin4(int,char *,char *); +// extern int socket_mcjoin6(int,char *,int); +/* leave a multicast group on the given interface */ +// extern int socket_mcleave4(int,char *); +// extern int socket_mcleave6(int,char *); +/* set multicast TTL/hop count for outgoing packets */ +// extern int socket_mcttl4(int,char); +// extern int socket_mcttl6(int,char); +/* enable multicast loopback */ +// extern int socket_mcloop4(int,char); +// extern int socket_mcloop6(int,char); +/**************************************************************/ + +#endif diff --git a/include/str.h b/include/str.h new file mode 100644 index 0000000..fe36d0b --- /dev/null +++ b/include/str.h @@ -0,0 +1,25 @@ +#ifndef STR_H +#define STR_H + +/* + * Revision 20170918, Kai Peter + * - added 'str_copyb()', thanks Erwin Hoffmann + * Revision 20170501, Kai Peter + * - added '*str_append' and 'str_cat' +*/ + +extern unsigned int str_copy(char *,const char *); +extern unsigned int str_copyb(char *,const char *,unsigned int); +extern int str_diff(const char *,const char *); +extern int str_diffn(const char *,const char *,unsigned int); +//extern unsigned int str_len(char *); // --> this produces lot of warnings !!! +extern unsigned int str_len(); +extern unsigned int str_chr(const char *,int); +extern unsigned int str_rchr(const char *,int); +extern int str_start(const char *,const char *); +extern char *str_append(char *out,const char *s); + +#define str_equal(s,t) (!str_diff((s),(t))) +#define str_cat(s,t) str_append(s,t) + +#endif diff --git a/include/stralloc.h b/include/stralloc.h new file mode 100644 index 0000000..0f4a7d2 --- /dev/null +++ b/include/stralloc.h @@ -0,0 +1,49 @@ +#ifndef STRALLOC_H +#define STRALLOC_H + +/* + * Revision 20210307, Erwin Hoffmann + * - +*/ + +#include <sys/types.h> + +/* stralloc is the internal data structure all functions are working on. + * s is the string. + * len is the used length of the string. + * a is the allocated length of the string. + */ + +typedef struct stralloc { + char* s; + size_t len; + size_t a; +} stralloc; + +//extern int stralloc_ready(stralloc *,unsigned int); +extern int stralloc_ready(stralloc *sa,size_t len); +//extern int stralloc_readyplus(stralloc *,unsigned int); +extern int stralloc_readyplus(stralloc *sa,size_t len); +extern int stralloc_copy(stralloc *,stralloc *); +extern int stralloc_cat(stralloc *,stralloc *); +extern int stralloc_copys(stralloc *,const char *); +extern int stralloc_cats(stralloc *,const char *); +extern int stralloc_copyb(stralloc *,const char *,unsigned int); +extern int stralloc_catb(stralloc *,const char *,unsigned int); +//extern int stralloc_append(stralloc *,char *); /* beware: this takes a pointer to 1 char */ +extern int stralloc_append(stralloc *sa,const char *in); /* beware: this takes a pointer to 1 char */ +extern int stralloc_starts(stralloc *,const char *); + +#define stralloc_0(sa) stralloc_append(sa,"") + +extern int stralloc_catulong0(stralloc *,unsigned long,unsigned int); +extern int stralloc_catlong0(stralloc *,long,unsigned int); + +extern void stralloc_free(stralloc *); + +#define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0)) +#define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n))) +#define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n))) +#define stralloc_catint(sa,i) (stralloc_catlong0((sa),(i),0)) + +#endif diff --git a/include/tai.h b/include/tai.h new file mode 100644 index 0000000..37d24e0 --- /dev/null +++ b/include/tai.h @@ -0,0 +1,74 @@ +#ifndef TAI_H +#define TAI_H + +/* + * Revision 20160728, Kai Peter + * - switched to 'uint_t.h' +*/ +/* this header file comes from libowfat, http://www.fefe.de/libowfat/ */ + +/* Times with 1 second precision */ + +#include "uint_t.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* A struct tai value is an integer between 0 inclusive and 2^64 + * exclusive. The format of struct tai is designed to speed up common + * operations; applications should not look inside struct tai. + * + * A struct tai variable is commonly used to store a TAI64 label. Each + * TAI64 label refers to one second of real time. TAI64 labels span a + * range of hundreds of billions of years. + * + * A struct tai variable may also be used to store the numerical + * difference between two TAI64 labels. + * See http://cr.yp.to/libtai/tai64.html */ + +typedef struct tai { + uint64 x; +} tai64; + + +#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u))) + +/* tai_now puts the current time into t. More precisely: tai_now puts + * into t its best guess as to the TAI64 label for the 1-second interval + * that contains the current time. + * + * This implementation of tai_now assumes that the time_t returned from + * the time function represents the number of TAI seconds since + * 1970-01-01 00:00:10 TAI. This matches the convention used by the + * Olson tz library in ``right'' mode. */ +void tai_now(struct tai *); + +/* tai_approx returns a double-precision approximation to t. The result + * of tai_approx is always nonnegative. */ +#define tai_approx(t) ((double) ((t)->x)) + +/* tai_add adds a to b modulo 2^64 and puts the result into t. The + * inputs and output may overlap. */ +void tai_add(struct tai *,const struct tai *,const struct tai *); +/* tai_sub subtracts b from a modulo 2^64 and puts the result into t. + * The inputs and output may overlap. */ +void tai_sub(struct tai *,const struct tai *,const struct tai *); +/* tai_less returns 1 if a is less than b, 0 otherwise. */ +#define tai_less(t,u) ((t)->x < (u)->x) + +#define TAI_PACK 8 +/* tai_pack converts a TAI64 label from internal format in t to external + * TAI64 format in buf. */ +void tai_pack(char *,const struct tai *); +/* tai_unpack converts a TAI64 label from external TAI64 format in buf + * to internal format in t. */ +void tai_unpack(const char *,struct tai *); + +void tai_uint(struct tai *,unsigned int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/taia.h b/include/taia.h new file mode 100644 index 0000000..03f455c --- /dev/null +++ b/include/taia.h @@ -0,0 +1,40 @@ +#ifndef TAIA_H +#define TAIA_H + +/* + * Revision 20170329, Kai Peter + * - changed type of 'taia_now()' from void to int +*/ + +/* time with precision of 1 attosecond */ + +#include "tai.h" + +struct taia { + struct tai sec; + unsigned long nano; /* 0...999999999 */ + unsigned long atto; /* 0...999999999 */ +} ; + +extern void taia_tai(struct taia *,struct tai *); + +extern int taia_now(struct taia *); + +extern double taia_approx(struct taia *); +extern double taia_frac(struct taia *); + +extern void taia_add(struct taia *,struct taia *,struct taia *); +extern void taia_sub(struct taia *,struct taia *,struct taia *); +extern void taia_half(struct taia *,struct taia *); +extern int taia_less(struct taia *,struct taia *); + +#define TAIA_PACK 16 +extern void taia_pack(char *,struct taia *); +extern void taia_unpack(char *,struct taia *); + +#define TAIA_FMTFRAC 19 +extern unsigned int taia_fmtfrac(char *,struct taia *); + +extern void taia_uint(struct taia *,unsigned int); + +#endif diff --git a/include/timeout.h b/include/timeout.h new file mode 100644 index 0000000..1b45e9f --- /dev/null +++ b/include/timeout.h @@ -0,0 +1,7 @@ +#ifndef TIMEOUT_H +#define TIMEOUT_H + +extern int timeoutread(int,int,char *,int); +extern int timeoutwrite(int,int,char *,int); + +#endif diff --git a/include/timeoutconn.h b/include/timeoutconn.h new file mode 100644 index 0000000..308ec5a --- /dev/null +++ b/include/timeoutconn.h @@ -0,0 +1,10 @@ +#ifndef TIMEOUTCONN_H +#define TIMEOUTCONN_H + +#include "uint_t.h" + +extern int timeoutconn4(int,char *,uint16,unsigned int); +extern int timeoutconn6(int,char *,uint16,unsigned int,uint32); +extern int timeoutconn(int,char *,uint16,unsigned int,uint32); + +#endif diff --git a/include/uint_t.h b/include/uint_t.h new file mode 100644 index 0000000..84eb06e --- /dev/null +++ b/include/uint_t.h @@ -0,0 +1,76 @@ +#include <stdint.h> + +/** + @file uint_t.h + @author djb, kp, feh + @source qmail, djbdns6 + @brief additional types and pack routines + @brief define basic integer types and size through <stdint.h> +*/ + +#ifndef UINT8_H +#define UINT8_H + +#ifdef HAS_UINT8_H +typedef uint8_t uint8; +#else +typedef unsigned char uint8; +#endif + +#endif + +#ifndef UINT16_H +#define UINT16_H + +typedef uint16_t uint16; + +extern void uint16_pack(char *,uint16); +extern void uint16_pack_big(char *,uint16); +extern void uint16_unpack(char *,uint16 *); +extern void uint16_unpack_big(char *,uint16 *); +#endif + +#ifndef UINT32_H +#define UINT32_H + +typedef uint32_t uint32; + +extern void uint32_pack(char *,uint32); +extern void uint32_pack_big(char *,uint32); +extern void uint32_unpack(char *,uint32 *); +extern void uint32_unpack_big(char *,uint32 *); +#endif + +#ifndef UINT64_H +#define UINT64_H + +#ifdef HAS_UINT64_H +typedef uint64_t uint64; +#else +typedef unsigned long long uint64; +#endif + +extern void uint64_pack(char *,uint64); +extern void uint64_pack_big(char *,uint64); +extern void uint64_unpack(char *,uint64 *); +extern void uint64_unpack_big(char *,uint64 *); +#endif + +#ifndef UINT128_H +#define UINT128_H + +/* uint128 used for native IPv6 address presentation */ + +struct uint128_t +{ + uint64_t hi; /* routing area */ + uint64_t lo; /* local area */ +}; + +typedef struct uint128_t uint128; + +extern void uint128_pack(char *,uint128); +extern void uint128_pack_big(char *,uint128); +extern void uint128_unpack(char *,uint128 *); +extern void uint128_unpack_big(char *,uint128 *); +#endif diff --git a/include/wait.h b/include/wait.h new file mode 100644 index 0000000..f7cfc60 --- /dev/null +++ b/include/wait.h @@ -0,0 +1,20 @@ +#ifndef WAIT_H +#define WAIT_H + +/** + @file wait.h + @author djb + @source s/qmail +*/ + +int wait_pid(int *,int); +int wait_nohang(int *); +int wait_stop(); +int wait_stopnohang(); + +#define wait_crashed(w) ((w) & 127) +#define wait_exitcode(w) ((w) >> 8) +#define wait_stopsig(w) ((w) >> 8) +#define wait_stopped(w) (((w) & 127) == 127) + +#endif @@ -0,0 +1,14 @@ +#!/bin/sh + +. ./conf-build + +install_libs() { cp *.a $OFILES "$LIBDIR" ; } +install_hdrs() { cp include/*.h "$HDRDIR" ; } + +echo -n "Installing qlibs ..." +mkdir -p "$LIBDIR" "$HDRDIR" 2>/dev/null + +[ "$LIBDIR" ] && install_libs +[ "$HDRDIR" ] && install_hdrs + +echo " done!" diff --git a/iopause.c b/iopause.c new file mode 100644 index 0000000..94a6fea --- /dev/null +++ b/iopause.c @@ -0,0 +1,84 @@ +#include <poll.h> +#include "taia.h" +#include "select.h" +#include "iopause.h" + +/** + @file iopause.c + @author djb + @source qmail + @brief stateful reading from net + @return > 0 if successful +*/ + +int iopause(iopause_fd *x,unsigned int len,struct taia *deadline,struct taia *stamp) +{ + struct taia t; + int millisecs; + double d; + int i, r; + + if (taia_less(deadline,stamp)) + millisecs = 0; + else { + t = *stamp; + taia_sub(&t,deadline,&t); + d = taia_approx(&t); + if (d > 1000.0) d = 1000.0; + millisecs = d * 1000.0 + 20.0; + if (millisecs < 0) millisecs = 20.0; + } + + for (i = 0; i < len; ++i) + x[i].revents = 0; + +#ifdef IOPAUSE_POLL + r = poll(x,len,millisecs); + + /* XXX: some kernels apparently need x[0] even if len is 0 */ + /* XXX: how to handle EAGAIN? are kernels really this dumb? */ + /* XXX: how to handle EINVAL? when exactly can this happen? */ + +#else + struct timeval tv; + fd_set rfds; + fd_set wfds; + int nfds; + int fd; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + + nfds = 1; + for (i = 0; i < len; ++i) { + fd = x[i].fd; + if (fd < 0) continue; + if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/ + + if (fd >= nfds) nfds = fd + 1; + if (x[i].events & IOPAUSE_READ) FD_SET(fd,&rfds); + if (x[i].events & IOPAUSE_WRITE) FD_SET(fd,&wfds); + } + + tv.tv_sec = millisecs / 1000; + tv.tv_usec = 1000 * (millisecs % 1000); + + r = select(nfds,&rfds,&wfds,(fd_set *) 0,&tv); + if (r <= 0) return r; + + /* XXX: for EBADF, could seek out and destroy the bad descriptor */ + + for (i = 0; i < len; ++i) { + fd = x[i].fd; + if (fd < 0) continue; + if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/ + + if (x[i].events & IOPAUSE_READ) + if (FD_ISSET(fd,&rfds)) x[i].revents |= IOPAUSE_READ; + if (x[i].events & IOPAUSE_WRITE) + if (FD_ISSET(fd,&wfds)) x[i].revents |= IOPAUSE_WRITE; + } + +#endif + return r; +} @@ -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; +} @@ -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; +} @@ -0,0 +1,22 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/file.h> +#include <fcntl.h> +#include "lock.h" + +/** + @file lock.c + @author djb + @source qmail + @brief locking of resources +*/ + +#ifdef HASFLOCK +int lock_ex(int fd) { return flock(fd,LOCK_EX); } +int lock_exnb(int fd) { return flock(fd,LOCK_EX | LOCK_NB); } +int lock_un(int fd) { return flock(fd,LOCK_UN); } +#else +int lock_ex(int fd) { return lockf(fd,1,0); } +int lock_exnb(int fd) { return lockf(fd,2,0); } +int lock_un(int fd) { return lockf(fd,0,0); } +#endif diff --git a/logmsg.c b/logmsg.c new file mode 100644 index 0000000..3f079f4 --- /dev/null +++ b/logmsg.c @@ -0,0 +1,97 @@ +#include <unistd.h> +#include <stdarg.h> +#include <errno.h> +#include "buffer.h" +#include "fmt.h" +#include "str.h" +#include "stralloc.h" +#include "logmsg.h" + +/** + @file logmsg.c + @author kp, feh + @source qlibs + @brief unified system and error message handling +*/ + +#define WHO "logmsg" + +char *build_log_msg(const char *x[]) +{ + stralloc sa = {0}; + stralloc_copys(&sa,""); + + while(*x) { if (!stralloc_cats(&sa,*x++)) err_sys(WHO,errno); } /* concatenate *x */ + + if (!stralloc_0(&sa)) err_sys(WHO,errno); + return(sa.s); +} + +void logmsg(const char *who,int ecode,unsigned int class,const char *msg) +{ + char strnum[FMT_ULONG]; + char *codestr = ""; + char *classstr = ""; + char *errmsg = ""; + + errno = 0; /* re-initialize errno, value is in 'code' now */ + +/* Part 1: obtain the (error) code -- perhaps received from OS */ + + if (ecode != 0) { + codestr = ""; + if (ecode < 0) { // check for negative error codes + ecode = (ecode^-1) + 1; + codestr = "-"; + } + strnum[fmt_ulong(strnum,ecode)] = 0; /* format for output */ + char *temp = strnum; + codestr = str_cat(codestr,temp); + } + +/* Part 2: behavioral on error */ + + switch (class) { + case ERROR: classstr = "error: "; break; // info + exit + case FATAL: classstr = "fatal: "; break; // info + exit + case DROP: classstr = "drop: "; break; // info + next call/iteration + case ALERT: classstr = "alert: "; break; // info + next statement + case WARN: classstr = "warning: "; break; // info + next statement + case INFO: classstr = "info: "; break; // info + continue + case SYNTAX: classstr = "syntax: "; break; // info + exit + case USAGE: classstr = "usage: "; break; // info + exit + case TEMP: classstr = "temp: "; break; // info + exit + case CAT: classstr = ""; break; // info w/o \n + default: + class = LOG; classstr = ""; break; // custom info + continue + } + +/* Part 3: get system error message */ + + if (class == FATAL || class == DROP) + errmsg = error_str(errno); + +/* Part 4: construct log message: Source: Class (Ecode) Message: Errmsg */ + + buffer_puts(buffer_2,who); + buffer_puts(buffer_2,": "); + buffer_puts(buffer_2,classstr); + if (class == FATAL || class == DROP || class == ERROR) { + buffer_puts(buffer_2,"("); + buffer_puts(buffer_2,codestr); + buffer_puts(buffer_2,") "); + } + buffer_puts(buffer_2,msg); + if (errno) { + buffer_puts(buffer_2,": "); + buffer_puts(buffer_2,errmsg); + } + if (class != CAT) { + buffer_puts(buffer_2,"\n"); + buffer_flush(buffer_2); + } + + if (class == USAGE) _exit(USAGE); + if (class == SYNTAX) _exit(SYNTAX); + if (class == FATAL || class == DROP || class == ERROR) _exit(ecode); +} diff --git a/man/INSTALL b/man/INSTALL new file mode 100644 index 0000000..7281ed9 --- /dev/null +++ b/man/INSTALL @@ -0,0 +1,12 @@ +Imstallation of fehQlibs man pages +================================== + +a) It is expected that the fehQlibs are installed at /usr/local. +b) Check your manpath settings: $ manpath +c) You might want to compress the manpages: gzip *.3 +d) If available, the compressed or raw man pages can be simply copied to + - /usr/local/man/man3 or + - /usr/local/share/man/man3 + 'root' is required to complete this step. + +That's it! diff --git a/man/alloc.3 b/man/alloc.3 new file mode 100644 index 0000000..5bd40d2 --- /dev/null +++ b/man/alloc.3 @@ -0,0 +1,68 @@ +.TH qlibs: alloc 3 +.SH NAME +alloc \- allocate memory +.SH SYNTAX +.B #include \(dqalloc.h\(dq + +char *\fBalloc\fP(\fInew\fR); + +void \fBalloc_free\fP(\fIx\fR); + +void \fBalloc_re\fP(&\fIx\fR,\fIold\fR,\fInew\fR); + +char *\fIx\fR; +.br +unsigned int \fIold\fR; +.br +unsigned int \fInew\fR; +.SH DESCRIPTION +.B alloc +allocates enough space from the heap for +.I new +bytes of data, +adequately aligned for any data type. +.I new +may be 0. +.B alloc +returns a pointer to the space. +If space is not available, +.B alloc +returns 0, +setting +.B errno +appropriately. + +.B alloc_free +returns space to the heap. + +.B alloc_re +expands the space allocated to +.I x +from +.I old +bytes to +.I new +bytes. +It allocates new space, +copies +.I old +bytes from the old space to the new space, +returns the old space to the heap, +and changes +.I x +to point to the new space. +It then returns 1. +If space is not available, +.B alloc_re +returns 0, +leaving the old space alone. +.SH "CVE-2005-1513" +This version of +.B alloc +respects +.I limits.h +to avoid memory resource exhaustion. +.SH "SEE ALSO" +sbrk(2), +malloc(3), +error(3) diff --git a/man/buffer.3 b/man/buffer.3 new file mode 100644 index 0000000..2d4d0d2 --- /dev/null +++ b/man/buffer.3 @@ -0,0 +1,185 @@ +.TH buffer 3 +.SH NAME +buffer \- generic read/write buffering +.SH SYNTAX +.B #include \(dqbuffer.h\(dq + +buffer* buffer_0; /* like stdio's stdin */ +.br +buffer* buffer_1; /* like stdio's stdout */ +.br +buffer* buffer_2; /* like stdio's stderr */ + +void \fBbuffer_init\fR(buffer &\fIb\fR,ssize_t (*\fIop\fR)(int,char *,size_t), + int \fIfd\fR, char *\fIy\fR, size_t \fIylen\fR); +.br +ssize_t \fBbuffer_get\fP(buffer *\fIb\fR,char *\fIx\fR,size_t \fIlen\fR); + +int \fBbuffer_put\fP(buffer *\fIb\fR,const char *\fIx\fR,size_t \fIlen\fR); +.br +int \fBbuffer_puts\fP(buffer *\fIb\fR,const char *\fIx\fR); +.br +int \fBbuffer_putalign\fP(buffer *\fIb\fR,char *\fIx\fR,unsigned int \fIlen\fR); +.br +int \fBbuffer_putsalign\fP(buffer *\fIb\fR,char *\fIx\fR); + +int \fBbuffer_putflush\fP(buffer *\fIb\fR,char *\fIx\fR,unsigned int \fIlen\fR); +.br +int \fBbuffer_putsflush\fP(buffer *\fIb\fR,char *\fIx\fR); + +int \fBbuffer_flush\fP(buffer *\fIb\fR); +.br +int \fBbuffer_copy\fP(buffer *\fIbo\fR,buffer *\fIbi\fR); + +int \fBbuffer_unixread\fP(int \fIfd\fR,char *\fIbuf\fR,size_t \fIlen\fR); +.br +int \fBbuffer_unixwrite\fP(int \fIfd\fR,char *\fIbuf\fR,size_t \fIlen\fR); +.SH DESCRIPTION +.B buffer.h +describes a generic buffer interface that can be used for +read and write buffering. Buffers must be initialized with +\fBbuffer_init\fR. + +A buffer can only be used for reading or writing at the same time, not +both. + +Unlike +.BR stdio , +these write buffers are not flushed automatically at +program termination; you must manually call \fBbuffer_flush\fR, +\fBbuffer_putflush\fR, or \fBbuffer_putsflush\fR. + +.B buffer_init +prepares \fIb\fR to store a string in \fIy\fR[0], \fIy\fR[1], ..., +\fIy\fR[\fIylen\fR-1]. Initially the string is empty. + +.B buffer_init +also prepares \fIb\fR to use the read/write operation specified by +\fIop\fR and \fIfd\fR. + +You can use + + buffer \fIb\fR = BUFFER_INIT(\fIop\fR,\fIfd\fR,\fIy\fR,\fIylen\fR); + +to initialize \fIb\fR statically if \fIop\fR, \fIfd\fR, \fIy\fR, and \fIylen\fR +are compile-time constants. + +You can call +.B buffer_init +again at any time. Note that this discards the currently buffered string. + +.B buffer_get +copies data to \fIx\fR[0], \fIx\fR[1], ..., +\fIx\fR[\fIlen\fR-1] from the beginning of a string stored in +preallocated space; removes these \fIlen\fR bytes from the string; and +returns \fIlen\fR. + +If, however, the string has fewer than \fIlen\fR (but more than 0) +bytes, +.I buffer_get +copies only that many bytes, and returns that number. + +If the string is empty, +.B buffer_get +first uses a \fBread operation\fR to +feed data into the string. The \fBread operation\fR may indicate end of +input. + +The preallocated space and the \fBread operation\fR are specified by +\fIb\fR. You must initialize \fBb\fR using +.B buffer_init +before calling +.B buffer_get +(or use the pre-initialized \fIbuffer_0\fR). + +.B buffer_put +writes \fIlen\fR bytes from \fIx\fR to \fIb\fR. + +The difference to +.B buffer_putalign +is that, when there isn't enough space +for new data, +.B buffer_put +calls +.B buffer_flush +before copying any data, while +.B buffer_putalign +fills all available space with data before calling +.B buffer_flush. + +.B buffer_copy +copies one buffer to other one. +The output buffer needs to have at least the +preallocated size of the input buffer. +.B buffer_unixread +and +.B buffer_unixwrite +perform the same operation like standard Unix +.B read +or +.BR write. +.SH MACROS +Apart from this basic usage, some helpful macro +definitions are provided: +.B BUFFER_INIT(op,fd,buf,len) +uses +.I op +function to operate (read/write) on +.I buf +with +.I len +to file descriptor +.IR fd . +.B buffer_GETC(buf,c) +returns the upmost position of character +.I c +within buffer +.I buf +or 0. +.B buffer_PUTC(buf,c) +adds character +.I c +to buffer +.IR buf. +.SH EXAMPLE + #include <buffer.h> + #include <open.h> + + char buf[BUFFER_INSIZE]; // defined in buffer.h + int fd = open_read("/etc/services"); + buffer input; + + if (fd >= 0) { + char x; + buffer_init(&input,read,fd,buf,sizeof buf); + while (buffer_get(&input,&x,1) == 1) { + buffer_put(buffer_1,&x,1); + if (x == '\\n') break; + } + buffer_flush(buffer_1); + } +.SH "RETURN CODES" +.B buffer_put +and +.B buffer_get +return +.I 0 +if everything was fine, +.IR -1 , +on error (setting \fIerrno\fR). +.B buffer_copy +returns +.IR -2 , +if the input buffer can't be read, and +.IR -3 , +if the data can't successfully copied +to the output buffer. On success +.B buffer_copy +returns +.IR 0 . +.SH "REFERENCES" +https://cr.yp.to/lib/buffer_get.html + +https://cr.yp.to/lib/buffer_put.html +.SH "SEE ALSO" +stdio(3) diff --git a/man/byte.3 b/man/byte.3 new file mode 100644 index 0000000..277e479 --- /dev/null +++ b/man/byte.3 @@ -0,0 +1,82 @@ +.TH qlibs byte 3 +.SH NAME +byte \- byte manipulation/evaluation +.SH SYNTAX +.B #include \(dqbyte.h\(dq + +unsigned int \fBbyte_chr\fP(const char *\fIhaystack\fR,unsigned int \fIlen\fR,char \fIneedle\fR); +.br +unsigned int \fBbyte_rchr\fP(const char *\fIhaystack\fR,unsigned int \fIlen\fR,char \fIneedle\fR); + +void \fBbyte_copy\fP(char *\fIout\fR,unsigned int \fIlen\fR,const char *\fIin\fR); +.br +void \fBbyte_copyr\fP(char *\fIout\fR,unsigned int \fIlen\fR,const char *\fIin\fR); + +int \fBbyte_diff\fP(const char *\fIone\fR,unsigned int \fIlen\fR,const char *\fItwo\fR); + +int \fBbyte_equal\fP(const char *\fIone\fR,unsigned int \fIlen\fR,const char *\fItwo\fR); + +void \fBbyte_fill\fP(char *\fIout\fR,unsigned int \fIlen\fR,const char \fIc\fR); +.br +void \fBbyte_zero\fP(char *\fIout\fR,unsigned int \fIlen\fR); +.SH DESCRIPTION +.B byte_chr +returns the smallest integer \fIi\fR between 0 and +\fIlen\fR-1 inclusive such that \fIone\fR[\fIi\fR] equals \fIneedle\fR. +If no such integer exists, +.B byte_chr +returns \fIlen\fR. +.B byte_chr +may read all bytes \fIone\fR[0], \fIone\fR[1], ..., +\fIone\fR[\fIlen\fR-1], even if not all the bytes are relevant to the +answer. + +.B byte_rchr +returns the largest integer \fIi\fR between 0 and +\fIlen\fR-1 inclusive such that \fIone\fR[\fIi\fR] equals \fIneedle\fR. +If no such integer exists, +.B byte_rchr +returns \fIlen\fR. +.B byte_rchr +may read all bytes \fIone\fR[0], \fIone\fR[1], ..., +\fIone\fR[\fIlen\fR-1], even if not all the bytes are relevant to the +answer. + +.B byte_copy +copies \fIin\fR[0] to \fIout\fR[0], \fIin\fR[1] to +\fIout\fR[1], etc., and finally \fIin\fR[\fIlen\fR-1] to +\fIout\fR[\fIlen\fR-1]. + +.B byte_copyr +copies \fIin\fR[\fIlen\fR-1] to \fIout\fR[\fIlen\fR-1], +\fIin\fR[\fIlen\fR-2] to \fIout\fR[\fIlen\fR-2], etc., and +\fIin\fR[0] to \fIout\fR[0]. + +.B byte_diff +returns negative, 0, or positive, depending on whether +the string \fIone\fR[0], \fIone\fR[1], ..., \fIone\fR[\fIlen\fR-1] is +lexicographically smaller than, equal to, or greater than the string +\fIone\fR[0], \fIone\fR[1], ..., \fIone\fR[\fIlen\fR-1]. +When the strings are different, +.B byte_diff +does not read bytes past the first difference. + +.B byte_equal +returns +.I 1 +if the strings are equal, +.I 0 +otherwise. +When the strings are different, +.B byte_equal +does not read bytes past the first difference. + +.B byte_fill +fills \fIout\fR[0], \fIout\fR[1], ..., \fIout\fR[\fIlen\fR-1] +with a single byte \fIc\fR. + +.B byte_zero +sets \fIout\fR[0], \fIout\fR[1], ..., \fIout\fR[\fIlen\fR-1] to 0. +.SH "SEE ALSO" +case(3), +stralloc(3) diff --git a/man/case.3 b/man/case.3 new file mode 100644 index 0000000..c9a879a --- /dev/null +++ b/man/case.3 @@ -0,0 +1,124 @@ +.TH qlibs: case 3 +.SH NAME +case \- case independent string manipulation/evaluation +.SH SYNTAX +.B #include \(dqcase.h\(dq + +void \fBcase_lowers\fP(\fIs\fR); +.br +void \fBcase_lowerb\fP(\fIs\fR,\fIlen\fR); +.br +void \fBcase_uppers\fP(\fIs\fR); +.br +void \fBcase_upperb\fP(\fIs\fR,\fIlen\fR); +.br +int \fBcase_diffs\fP(\fIs\fR,\fIt\fR); +.br +int \fBcase_equals\fP(\fIs\fR,\fIt\fR); +.br +int \fBcase_diffrs\fP(\fIs\fR,\fIt\fR); +.br +int \fBcase_equalrs\fP(\fIs\fR,\fIt\fR); +.br +int \fBcase_starts\fP(\fIs\fR,\fIt\fR); +.br +int \fBcase_diffb\fP(\fIs\fR,\fIlen\fR,\fIt\fR); +.br +int \fBcase_startb\fP(\fIs\fR,\fIlen\fR,\fIt\fR); + +char *\fIs\fR; +.br +char *\fIt\fR; +.br +unsigned int \fIlen\fR; +.SH DESCRIPTION +.B case_lowers +converts each uppercase byte in the string +.I s +to lowercase. +.I s +must be 0-terminated. + +.B case_lowerb +converts each uppercase byte in the buffer +.IR s , +of length +.IR len , +to lowercase. + +.B case_uppers +converts each lowercase byte in the string +.I s +to uppercase. +.I s +must be 0-terminated. + +.B case_upperb +converts each lowercase byte in the buffer +.IR s , +of length +.IR len , +to uppercase. + +.B case_diffs +lexicographically compares lowercase versions of the strings +.I s +and +.IR t . +It returns something positive, negative, or zero +when the first is larger than, smaller than, or equal to the second. +.I s +and +.I t +must be 0-terminated. + +.B case_equals +means +.BR !case_diffs . + +.B case_diffrs +and +.B case_equalrs +compare strings from right to left instead from left to right. + +.B case_starts +returns 1 if a lowercase version of +.I s +starts with a lowercase version of +.IR t . +.I s +and +.I t +must be 0-terminated. + +.B case_diffb +lexicographically compares lowercase versions of the buffers +.I s +and +.IR t , +each of length +.IR len . +It returns something positive, negative, or zero +when the first is larger than, smaller than, or equal to the second. + +.B case_startb +returns 1 if a lowercase version of the buffer +.IR s , +of length +.IR len , +starts with a lowercase version of the string +.IR t . +.I t +must be 0-terminated. + +The +.B case +routines +are ASCII-specific. +They are suitable for programs that handle +case-independent networking protocols. + +All comparisons are performed on unsigned bytes. +.SH "SEE ALSO" +byte(3), +stralloc(3) diff --git a/man/cdbmake.3 b/man/cdbmake.3 new file mode 100644 index 0000000..7e7fd3e --- /dev/null +++ b/man/cdbmake.3 @@ -0,0 +1,61 @@ +.TH qlibs:cdbmake 3 +.SH NAME +cdmake \- generate and fill a constant database +.SH SYNTAX +.B #include \(dqcdmake.h\(dq + +int \fBcdb_make_start\fP(struct cdb *\fIc\fR,int \fIfd\fR); +.br +int \fBcdb_make_add\fP(struct cdb *\fIc\fR,char *\fIkey\fR,unsigned int \fIkeylen\fR, + char *\fIdata\fR,unsigend int \fIdatalen\fR); +.br +int \fBcdb_make_finish\fP(struct cdb *\fIc\fR); +.SH DESCRIPTION +.B cdb_make_start +generates and intitialises a new cdb named +.I c +and makes it available via file descriptor +.IR fd . + +.B cdb_make_add +adds entries using the file descriptor +.I fd +given as +.I key +with length +.I keylen +into the cdb returning the hashed values +.I data +with length +.IR datalen . + +.B cdb_make_finish +finalises the data structure provided as +.IR fd . + +.SH "RETURN CODES" +Usually, the +.B cdb_make_* +routines provide a return code of +.I 0 +for successful operations and +.I -1 +if anything is going wrong. + +.SH EXAMPLE +#include <cdbmake.h> + + int fd; + stralloc data = {0}; + stralloc key = {0}; + + struct cdb_make c; + + if (cdb_make_start(&c,fd) == -1) die_write(); + if (cdb_make_add(&c,key.s,key.len,data.s,data.len) == -1) + die_write(); + if (cdb_make_finish(&c) == -1) die_write(); + if (fsync(fd) == -1) die_write(); + +.SH "SEE ALSO" +cdbread(3) diff --git a/man/cdbread.3 b/man/cdbread.3 new file mode 100644 index 0000000..6d4641f --- /dev/null +++ b/man/cdbread.3 @@ -0,0 +1,177 @@ +.TH qlibs:cdbread 3 +.SH NAME +cdbread \- fetch information from a constant database +.SH SYNTAX +.B #include \(dqcdbread.h\(dq + +void \fBcdb_init\fP(struct cdb *\fIc\fR,int \fIfd\fR); +.br +int \fBcdb_read\fP(struct cdb *\fIc\fR,char *\fIdata\fR,unsigned int \fIdlen\fR,uint32 \fIpos\fR); +.br +int \fBcdb_findstart\fP(int \fIfd\fR,char *\fIkey\fR,unsigned int \fIlen\fR); +.br +int \fBcdb_findnext\fP(int \fIfd\fR,char *\fIkey\fR,unsigned int \fIlen\fR); +.br +int \fBcdb_find\fP(int \fIfd\fR,char *\fIney\fR,unsigned int \fIlen\fR); + +.br +int \fBcdb_findnext\fP(int \fIfd\fR,char *\fIkey\fR,unsigned int \fIlen\fR); +.br +void \fBcdb_free\fP(struct cdb *\fIc\fR): +.SH DESCRIPTION +.B cdb_free +unallocates +.I c +if +.I c +is allocated. +Otherwise it leaves +.I c +alone. +.B cdb_free +does not close +.IR fd . + +.B cdb_init +allocates +.B c +to hold information about a constant database read by descriptor +.IR fd . +You may call +.B cdb_init +repeatedly; if +.I c +is already allocated, +.B cdb_init +unallocates it first. + +.B cdb_read +reads +.I dlen +bytes into +.I d +from byte position +.I dpos +in the database. You must allocate +.I c +before calling +.BR cdb_read . +Normally +.B cdb_read +returns +.IR 0 . +If the database file is shorter than +.I dpos+dlen +bytes, or if there is a disk read error, +.B cdb_read +returns +.IR -1 , +setting +.I errno +appropriately. + +.B cdb_findstart +prepares +.I c +to search for the first record under a new +.IR key . +You must allocate +.I c +before calling +.BR cdb_findstart , +and you must call +.B cdb_findstart +before calling +.BR cdb_findnext . + +.B cdb_findnext +looks for the nth record under +.I key +in the database, where +.I n +is the number of calls to +.B cdb_findnext +after the most recent call to +.BR cdb_findstart . +If it finds the record, +.B cdb_findnext +returns +.IR 1 ; +if there are exactly n-1 such records, +.B cdb_findnext +returns +.IR 0 ; +if there are fewer than n-1 such records, the behavior of +.B cdb_findnext +is undefined; if there is a database format error or disk error, +.B cdb_findnext +returns +.IR -1 , setting +.I errno +appropriately. Each call to +.B cdb_findnext +(before another call to +.BR cdb_findstart ) +must use the same +.I k +and +.IR klen . + +If +.B cdb_findnext +returns +.IR 1 , +it arranges for +.B cdb_datapos +to return the starting byte position of the data in the record, and for +.B cdb_datalen +to return the number of bytes of data in the record. +Otherwise the results of +.B cdb_datapos +and +.B cdb_datalen +are undefined. + +.B cdb_find +is the same as +.B cdb_findstart +followed by +.BR cdb_findnext : +it finds the first record under +.IR key. + +.B cdb_datapos +and +.B cdb_datalen +are macros pointing to the found information following +.I key +in the cdb and returning their length. +.SH EXAMPLE +#include <cdbread.h> + + int fd; + char *data; + unsigned int len; + stralloc key = {0}; + + static struct cdb c; + + cdb_init(&c,fd); + + switch (cdb_find(&c,key.s,key.len)) { + case -1: return -1; + case 0: return 0; + } + + len = cdb_datalen(&c); + data = alloc(len); + if (!data) return -1; + + if (cdb_read(&c,data,datalen,cdb_datapos(&c)) == -1) { + alloc_free(data); + return -1; + } + + cdb_free(&c); +.SH "SEE ALSO" +cdbmake(3) diff --git a/man/constmap.3 b/man/constmap.3 new file mode 100644 index 0000000..e28f5f4 --- /dev/null +++ b/man/constmap.3 @@ -0,0 +1,56 @@ +.TH qlibs:constmap 3 +.SH NAME +constmap \- fetch matching strings from a hashed data structure in constant time +.SH SYNTAX +.B #include \(dqconstmap.h\(dq +.SH SYNOPSIS +int \fBconstmap_init\fP(struct constmap \fI*cm\fR,char \fI*string\fR,int \fIlen\fR,int \fIflagcolon\fR); +.br +int \fBconstmap_init_char\fP(struct constmap \fI*cm\fR,char \fI*string\fR,int \fIlen\fR,int \fIflagcolon\fR,char \fIflagchar\fR); +.br +char *\fBconstmap\fP(struct constmap \fI*cm\fR,char \fI*string\fR,int \fIlen\fR); +.br +void \fBconstmap_free\fP(\fIstruct constmap *cm\fR); +.SH DESCRIPTION +Reading a file perhaps with +.I control_readfile(&cmap,"path/filename",0) +.B constmap_init +can be used to convert its content into a constant time search map: +.IR constmap . +Here, you can specify whether the entries in +.I constmap +are plain or key/value structured setting +.I flagcolon +to one and assuming the delimiter equals to a colon: +.IR : . +If the delimiter needs to be particulary tailored, use +.BR constmap_init_char . + +Given the search +.I string +and providing its length +.I len +(without the trailing \\0) +.B constmap +will now retrieve the information in constant time +returning a pointer to the search result or +.IR 0 . + +The datastructure can be freed by means of +.BR constmap_free . +.SH EXAMPLE +#include <constmap.h> + + struct constmap cmap; + stralloc result = {0}; + char *info; + char *search; + + if (control_readfile(&cmap,"control/conf",0)) + constmap_init(&cmap,result.s,result.len,1)); + + info = constmap(&cmap,search,str_len (search)); + if (!info) return 0; + +.SH "SEE ALSO" +cdbread(3), cdbmake(3) diff --git a/man/dns.3 b/man/dns.3 new file mode 100644 index 0000000..837450c --- /dev/null +++ b/man/dns.3 @@ -0,0 +1,249 @@ +.TH qlibs: dnsresolv +.SH NAME +dns \- dns resolver routines +.SH SYNTAX +.B #include \(dqdnsresolv.h\(dq + +int \fBdns_ip4_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR); +.br +int \fBdns_ip4\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR); + +int \fBdns_ip6_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR); +.br +int \fBdns_ip6\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR); + +int \fBdns_mx\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR); +.br +int \fBdns_mx_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR); + +void \fBdns_name4_domain\fP(char \fIq\fR[DNS_NAME4_DOMAIN],const char *\fIip\fR[4]); +.br +int \fBdns_name4\fP(stralloc *\fIout\fR,const char *\fIip\fR[4]); + +void \fBdns_name6_domain\fP(char \fIq\fR[DNS_NAME6_DOMAIN],const char *\fIip\fR[16]); +.br +int \fBdns_name6\fP(stralloc *\fIout\fR,const char *\fIip\fR[16]); + +int \fBdns_name\fP(stralloc *\fIout\fR,const char *\fIip\fR[16]); +.br +int \fBdns_name_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR); + +int \fBdns_txt\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR); +.br +int \fBdns_txt_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR); + +int \fBdns_cname\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR); +.br +int \fBdns_cname_packet\fP(stralloc *\fIout\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR); + +int \fBdns_ip4_qualify\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR,const stralloc *\fIudn\fR); +.br +int \fBdns_ip6_qualify\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR,const stralloc *\fIudn\fR); +.br +int \fBdns_ip_qualify\fP(stralloc *\fIout\fR,stralloc *\fIfqdn\fR,const stralloc *\fIudn\fR); +.SH DESCRIPTION +.B dns_ip[4|6]_packet +is a low-level component of +.BR dns_ip[4|6] , +designed to support asynchronous DNS lookups. +It reads a DNS packet of length \fIlen\fR from +\fIbuf\fR, extracts the IP(4|6) addresses from the answer section of the packet and +puts the addresses into \fIout\fR. + +.B dns_ip[4|6] +looks up 4/16-byte IPv6 addresses for the fully qualified domain name of +\fIfqdn\fR. It puts the concatenation of the IPv[4|6] addresses into \fIout\fR and +returns the number of received answers for \fIfqdn\fR or \fI0\fR if none are replied. +If the domain does not exist in DNS, or has no IP addresses, +\fIout\fR will be empty. More generally, if \fIfqdn\fR is considered by +.B dns_ip4 +to be a dotted-decimal IPv4 address, it is provissioned as \fIout\fR +without checking the DNS while returning \fI1\fR. +Brackets may enclose the dotted-decimal IP address; they are ignored. + +.B dns_name[4|6]_packet +is a low-level component of +.B dns_name[4|6] +designed to support asynchronous DNS lookups. +It reads a DNS packet of length \fIlen\fR from \fIbuf\fR, + +.B dns_name[4|6]_domain +is a low-level component of +.BR dns_name[4|6] . +It converts an IP address such as +.I 1.2.3.4 +or +.I 4321:0:1:2:3:4:567:89ab +into a domain name such as +.I 4.3.2.1.in-addr.arpa +or +.I b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa +and places the packet-encoded domain name into \fIq\fR. +.I q +is zero terminated. +.I q +must have space for DNS_NAME[4|6]_DOMAIN bytes. + +.B dns_mx_packet +is a low-level component of +.BR dns_mx , +designed to support asynchronous DNS lookups. +It reads a DNS packet of length \fIlen\fR from \fIbuf\fR, +extracts the MX records from the answer section of the packet, puts the +result into \fIout\fR, and returns the number of replies received +or \fI-1\fR the same way as +.BR dns_mx . + +.B dns_mx +looks up MX records for the fully-qualified domain name in +\fIfqdn\fR. It puts the MX records into \fIout\fR and returns the number of +received records or \fI0\fR. +Each MX record is a two-byte MX distance (big endian) followed by a +\\0-terminated dot-encoded domain name. If the domain does not exist in +DNS, or has no MX records, \fIout\fR will be empty. + +.B dns_txt_packet +is a low-level component of +.BR dns_txt , +designed to support +asynchronous DNS lookups. It reads a DNS packet of length \fIlen\fR from \fIbuf\fR, +extracts the TXT records from the answer section of the packet, puts the +result into \fIout\fR, and returns the number of replies +or \fI-1\fR the same way as \fBdns_txt\fR. + +.B dns_txt +looks up TXT records for the fully-qualified domain name in +\fIfqdn\fR. It puts the concatenation of the TXT records into \fIout\fR +and returns the number of replies received. +If the domain does not exist in DNS, or has no TXT records, \fIout\fR will be empty. + +.B dns_cname_packet +is a low-level component of +.BR dns_cname , +designed to support +asynchronous DNS lookups. It reads a DNS packet of length \fIlen\fR from \fIbuf\fR, +extracts the TXT records from the answer section of the packet, puts the +result into \fIout\fR, and returns the number of replies received or +\fI-1\fR the same way as \fBdns_cname\fR. + +.B dns_cname +looks up the canonical name for a given +.IR fqdn . + +.B dns_ip[4|6]_qualify +feeds the name \fIudn\fR through qualification and looks up their +4-byte/16-byte IP addresses. It puts the fully qualified domain name +into \fIfqdn\fR and the concatenation of the IP addresses into \fIout\fR, +while returning the number of encountered IP addresses. +.B dns_ip[4|6]_qualify +evaluates the environment variables +.I $DNSREWRITEFILE +pointing to +.I /etc/dnsrewrite +and +.I $LOCALDOMAIN +and finally reading +.I /etc/resolv.conf +to build the +.I Full Qualified Domain Name (FQDN) +using the +.I domain suffix +for +.IR hostname . +.B dns_ip_qualify +returns +.I out +as +.I IP[4|6] +and +.IR FQDN . +If the domain does not exist in DNS, or has no IP addresses, +\fIout\fR will be empty and the return code is \fI0\fR. + +In case +.B dns_ip[4|6]_qualify +is fed with an +.I IP[4|6] +addresses instead of domain names, +it recognizes those not to be subject of +.IR qualification . +The particular names +.IR localhost , +.I ip4-loopback +and +.I ip6-loopback +are treated locally and mapped to +the respective +.I IP[4|6] +addresses (and vice versa) +without facilitating a DNS lookup. +.SH "INPUT" +For the high-level routines +.BR dns_ip[4|6] , +.BR dns_ip_qualify[4|6] , +.BR dns_name[4|6] , +.BR dns_mx , +.BR dns_name , +.BR dns_txt +and +.BR dns_cname +the provided input variable +.I stralloc \*\fqdn +needs to be un-terminated, thus the given +string length is identitical to the number of +.I fqdn +characters. +.SH "OUTPUT" +The returned IP addresses are given as character (byte) strings +with 4 or 16 bytes: +.B dns_ip4 +and +.B dns_ip4_qualifiy +return 4 byte IPv4 addresses, where as +.B dns_ip6 +and +.B dns_ip +as well as +.B dns_ip6_qualify +and +.B dns_ip_qualify +return sets of 16 byte IPv6 addresses, where +the potential IPv4 addresses are given in their +IPv6-mapped-IPv4 representation. +.SH "RETURN CODES" +The dns routines +.BR dns_cname* , +.BR dns_ip* , +.BR dns_mx* , +.B dns_name* +and +.BR dns_txt* +return the number of answers received, which may be +.I 0 +and potentially +.I -1 +in case of a memory failure. The full set of return codes: + +.EX +Value | Macro | Explaination +------+----------+------------------------------------- + n>0 | | n = number of answers reeceived + 0 | DNS_NXD | either NXDOMAIN or NODATA + -1 | DNS_MEM | out of memory (fatal) + -2 | DNS_ERR | parsing errors + -3 | DNS_COM | socket communicaton errror; SERVFAIL + -4 | DNS_INT | internal error + -5 | DNS_SOFT | DNS_ERR or DNS_COM + -6 | DNS_HARD | DNS loop problem (CNAME) +.EE + +.I errno +is set appropriately. +In case of a failure, the respective output variables like +\fIout\fR and \fIfqdn\fR may or may not change. +Since a received reply may be empty, always check the length of the +received output additionally. +.SH "SEE ALSO" +ip4(3), +ip6(3), +dnsstub(3) diff --git a/man/dnsstub.3 b/man/dnsstub.3 new file mode 100644 index 0000000..6bce8a3 --- /dev/null +++ b/man/dnsstub.3 @@ -0,0 +1,109 @@ +.TH qlibs: dnsstub +.SH NAME +dns \- stub resolver +.SH SYNTAX +.B #include \(dqdnsresolv.h\(dq + +int \fBdns_resolvconfip\fP(char \fIservers\fR[512],uint32 \fIscope\fR[32]); + +int \fBdns_transmit_start\fP(struct dns_transmit *\fIq\fR,const char \fIservers\fR[512], + int \fIflagrecursive\fR,const char *\fIq\fR,const char \fIqtype\fR[2], + const char \fIlocalip\fR[16]); +.br +int \fBdns_transmit_start6\fP(struct dns_transmit *\fIq\fR,const char \fIservers\fR[512], + int \fIflagrecursive\fR,const char *\fIq\fR,const char \fIqtype\fR[2], + const char \fIlocalip\fR[16],const uint32 \fIscopes\fR[32]); + +int \fBdns_sortip4\fP(char *\fIservers\fR,unsigned int \fIn\fR); +.br +int \fBdns_sortip6\fP(char *\fIservers\fR,unsigned int \fIn\fR); +.SH DESCRIPTION +.B dns_resolvconfip +reads the name servers defined in +.I /etc/resolv.conf +and uses +.B ip6_ifscan +to fetch their IPv4/IPv6 addresses together with the scope for the LLU address. +Up to +.I 32 +name servers can be specified. + +Apart from the system-wide +.I /etc/resolv.conf +the IP addresses of name servers can preferrably be provided by means of the +environment variable +.I $DNSCACHEIP +for each user application. The variable +.I $DNSCACHEIP +may include up to 32 name server IP addresses separated by white spaces: + +.EX + DNSCACHEIP="10.0.1.53 fe80::1%lo0 ::1" +.EE + +.B dns_transmit_start +and +.B dns_transmit_start6 +use the list of name server IP's for a recursive or none-recursive +query +.I q +of type +.I qtype +starting from IP address +.I localip +for which the +.I struct dns_transmit +provides the required book keeping information. +.B dns_transmit_start6 +additionally is able to evaluate the given +.I scope_id +as information for the local interface in order +to bind to the remote IPv6 LLU addresses. Usually +.I scope_id +defaults to +.IR 0 . + +.B dns_sortip4 +and +.B dns_sortip6 +randomize the list of name server IPs upon call and as result the first address +is used to facilitate the name lookup. +.IR FQDN . +.SH "RETURN CODES" +Different from the original djb implemtation, the +following return code scheme is used: +.TP 2 +rc > 0 +A 'positive' answer is given, thus results returned. +.TP 2 +rc = 0 +A 'neutral' answer was provided, thus typically the +operation succeeded, but without replies. +.TP 2 +rc < 0 +An operational error was encountered. +.TP 0 +Potentially, this needs to be treated specifically: +.TP 2 +DNS_INT (rc = -4) +Internal DNS operational error; ie. resources were not +available. +.TP 2 +DNS_COM (rc = -3) +DNS communication error. +.TP 2 +DNS_ERR (rc = -2) +Parsing errors and others. +.TP 2 +DNS_MEM (rc = -1) +Memory error occured. +.TP 0 +A 'safe' usage of return codes thus checks not only the presence +of a return code, but rather additionally its sign. +Negative return codes point to errors. +.SH "SEE ALSO" +http://cr.yp.to/djbdns/qualify.html, +dns(3), +ip4(3), +ip6(3), +socket_if(3) diff --git a/man/env.3 b/man/env.3 new file mode 100644 index 0000000..c8d0bf2 --- /dev/null +++ b/man/env.3 @@ -0,0 +1,68 @@ +.TH qlibs: env 3 +.SH NAME +env \- manage variables in the environment +.SH SYNTAX +.B #include \(dqenv.h\(dq + +char **\fBenviron\fP; + +char *\fBenv_put\fP(char *\fINAME\fR,char *\fIvalue\fR); +.br +char *\fBenv_puts\fP(char *\fINAME=value\fR); +.br +char *\fBenv_set\fP(char *\fINAME=value\fR); +.br +char *\fBenv_unset\fP(char *\fINAME\fR); +.br +char *\fBenv_get\fP(char *\fINAME\fR); +.br +char *\fBenv_pick\fP(); +.br +char *\fBenv_clear\fP(); +.SH DESCRIPTION +The environment, +.BR environ , +is a 0-terminated array of 0-terminated strings, +called environment variables. +Each environment variable is of the form +.IR NAME\fB=\fIvalue . + +.B env_puts +puts the string +.I \'NAME=value\' +into the environment. + +.B env_put +assigns the new enviromment variable +.I NAME +with +.IR value . + +.B env_set +assigns an existing environment variable +.I NAME +with +.IR value : +.IR \'NAME=value\' . + +.B env_unset +unsets an existing environment variable given as +.IR NAME . + +.B env_get +returns the assigned value of the first variable whose name is +.IR NAME , +or +.I 0 +if there is no such variable. + +.B env_pick +returns any variable in the environment, +or +.I 0 +if the environment is empty. + +.B env_clear +clears the whole envionment. +.SH "SEE ALSO" +environ(7) diff --git a/man/error.3 b/man/error.3 new file mode 100644 index 0000000..b96be1f --- /dev/null +++ b/man/error.3 @@ -0,0 +1,75 @@ +.TH qlibs: error 3 +.SH NAME +error \- syscall error codes and information +.SH SYNTAX +.B #include \(dqlogmsg.h\(dq + +extern int \fBerrno\fP; + +extern int \fBerror_intr\fP; +.br +extern int \fBerror_nomem\fP; +.br +extern int \fBerror_noent\fP; +.br +extern int \fBerror_txtbsy\fP; +.br +extern int \fBerror_io\fP; +.br +extern int \fBerror_exist\fP; +.br +extern int \fBerror_timeout\fP; +.br +extern int \fBerror_inprogress\fP; +.br +extern int \fBerror_wouldblock\fP; +.br +extern int \fBerror_again\fP; +.br +extern int \fBerror_pipe\fP; +.br +extern int \fBerror_perm\fP; +.br +extern int \fBerror_access\fP; + +char *\fBerror_str\fP(int \fIe\fR); + +int \fBerror_temp\fP(int \fIe\fR); +UNIX syscalls provide detailed error codes in the +.B errno +variable. +The +.B error +library provides portable names for a variety of possible +.B errno +values. + +.B error_str +returns a printable string describing syscall error code +.IR e . +Normally +.I e +is +.BR errno . + +.B error_temp +returns +.I 1 +if syscall error code +.I e +is a soft error, +.I 0 +if it is a hard error. Normally +.I e +is +.BR errno . + +A hard error is persistent: +file not found, read-only file system, symbolic link loop, etc. + +A soft error is usually transient: +out of memory, out of disk space, I/O error, disk quota exceeded, +connection refused, host unreachable, etc. +.SH "SEE ALSO" +sysmsg(3), +errno(2) diff --git a/man/fd.3 b/man/fd.3 new file mode 100644 index 0000000..b22338d --- /dev/null +++ b/man/fd.3 @@ -0,0 +1,79 @@ +.TH qlibs: fd 3 +.SH NAME +fd \- duplicate, move or close a file descriptor +.SH SYNTAX +.B #include \(dqfd.h\(dq + +int \fBfd_copy\fP(int \fIto\fR,int \fIfrom\fR); +.br +int \fBfd_move\fP(int \fIto\fR,int \fIfrom\fR); +.br +int \fBfd_coe\fP(int \fIfd\fR); +.SH DESCRIPTION +.B fd_copy +copies +descriptor +.I from +to descriptor +.IR to . +If +.I to +was already open, +.B fd_copy +closes it. +.B fd_copy +always leaves +.I from +intact; +if +.I to +and +.I from +are the same number, +.B fd_copy +does nothing. +.B fd_copy +does not guarantee that +.I to +will remain open, if it was open, in case of error. + +.B fd_move +moves +descriptor +.I from +to descriptor +.IR to . +If +.I to +was already open, +.B fd_move +closes it. +If the move is successful, +.B fd_move +closes +.IR from . +Exception: +if +.I to +and +.I from +are the same number, +.B fd_move +does nothing. + +.B fd_coe +closes +.IR fd . +.SH "RETURN CODES" +.BR fd_copy , +.BR fd_move , +and +.B fd_coe +return +.I 0 +on success, +and +.I -1 +on error. +.SH "SEE ALSO" +dup(2), fcntl(2) diff --git a/man/fmt.3 b/man/fmt.3 new file mode 100644 index 0000000..ac9c985 --- /dev/null +++ b/man/fmt.3 @@ -0,0 +1,90 @@ +.TH qlibs: fmt 3 +.SH NAME +fmt \- ASCII representation of strings and integers +.SH SYNTAX +.B #include \(dqfmt.h\(dq + +unsigned int \fBfmt_str\fP(char *\fIdest\fR,const char *\fIsource\fR); +.br +unsigned int \fBfmt_strn\fP(char *\fIdest\fR,const char *\fIsource\fR,unsigned int \fImaxlen\fR); + +unsigned int \fBfmt_uint\fP(char *\fIdest\fR,unsigned int \fIsource\fR); +.br +unsigned int \fBfmt_uint0\fP(char *\fIdest\fR,unsigned int \fIsource\fR,unsigned int \fIn\fR); +.br +unsigned int \fBfmt_ulong\fP(char *\fIdest\fR,unsigned long \fIsource\fR); +.br +unsigned int \fBfmt_xlong\fP(char *\fIdest\fR,unsigned long \fIsource\fR); +.br +char \fBtohex\fP(char \fInum\fR); +.br +int \fBfromhex\fP(unsigned char \fIc\fR); +.SH DESCRIPTION +.B fmt_str +copies all leading nonzero bytes from \fIsource\fR to \fIdest\fR +and returns the number of bytes it copied. +.B fmt_str +does not append \\0. + +.B fmt_strn +copies at most \fImaxlen\fR leading nonzero bytes from +\fIsource\fR to \fIdest\fR and returns the number of bytes it copied. +.B fmt_strn +does not append \\0. + +.B fmt_uint +writes an ASCII representation ('0' to '9', base 10) of +\fIsource\fR to \fIdest\fR and returns the number of bytes written. +.B fmt_uint +does not append \\0. + +.B fmt_uint0 +writes an ASCII representation ('0' to '9', base 10) of +\fIsource\fR to \fIdest\fR and returns the number of bytes written. +The output is padded with '0'-bytes until it encompasses at least +\fIn\fR bytes, but it will not be truncated if it does not fit. +.B fmt_uint0 +does not append \\0. + +.B fmt_ulong +writes an ASCII representation ('0' to '9', base 10) of +\fIsource\fR to \fIdest\fR and returns the number of bytes written +perhaps including a trailing \\0. +.B fmt_ulong +does not append \\0. + +.B fmt_xlong +writes an ASCII representation ('0' to '9' and 'a' to 'f', base 16) +of \fIsource\fR to \fIdest\fR and returns the number of bytes +written. +.B fmt_xlong +does not append \\0. + +.B tohex +reads the ASCII representation of a decimal \fInum\fR and returns its +hexadecimal ASCII value; thus \'0\' -> \'0\' ... \'9\' -> \'9\', +\'10\' -> \'a\' and finally \'15\' -> f'. + +.B fromhex +reads the ACSII representation of a hexadecimal number \'0\' to \'f\' +irrelevant of its case and returns its integer value. + +For convenience, +.B fmt.h +defines the integers +.I FMT_LEN +and +.I FMT_ULONG +to be big enough to the number of bytes it would have written. + + +.SH "RETURN CODES" +If \fIdest\fR equals FMT_LEN (i.e. is zero), all +.B fmt_* +routins return the number of bytes it would have written +i.e. the number of leading nonzero bytes of \fIsource\fR +perhaps including a \\0. +.SH "SEE ALSO" +byte(3), +case(3), +scan(3) diff --git a/man/getln.3 b/man/getln.3 new file mode 100644 index 0000000..bb4ae7c --- /dev/null +++ b/man/getln.3 @@ -0,0 +1,102 @@ +.TH glibs: getln 3 +.SH NAME +getln \ - read one line of data +.SH SYNTAX +.B #include \(dqgetln.h\(dq + +int \fBgetln\fP(&buffer_0,&sa,&match,sep); +.br +int \fBgetln2\fP(&buffer_0,&sa,&cont,&clen,sep); + +buffer \fIbuffer_0\fR; +.br +stralloc \fIsa\fR; +.br +int \fImatch\fR; +.br +int \fIsep\fR; +.br +char *\fIcont\fR; +.br +unsigned int \fIclen\fR; +.SH DESCRIPTION +.B getln +reads a line of characters, terminated by a sep character, from +.IR buffer_0 . +It returns the line in +.I sa +and sets match to +.IR 1 . +If +.B getln +sees end-of-input before it sees +.IR sep , +it returns the partial line in +.I sa +and sets match to +.IR 0 . + +.B getln2 +reads a line of characters, terminated by a +.I sep +character, from +.IR buffer_0 . +The line is returned in two pieces. The first piece is stored in +.IR sa . +The second piece is +.IR cont , +a pointer to +.I clen +characters inside the +.I buffer_0 +buffer. The second piece must be copied somewhere else before +.I ss +is used again. +If +.B getln2 +sees end-of-input before it sees +.IR sep , +it sets +.I clen +to +.I 0 +and does not set +.IR cont . +It puts the partial line into +.IR sa. +.SH "RETURN CODES" +.B getln +normally returns +.IR 0 . +If it runs out of memory, or encounters an error from +.IR ss , +it returns +.IR -1 , +setting +.I errno +appropriately. + +.B getln2 +normally returns +.IR 0 . +If it runs out of memory, or encounters an error from +.IR ss , +it returns +.IR -1 , +setting +.I errno +appropriately. +.SH NOTE +The input buffer +.I buffer_0 +is already pre-allocated. +It can be used without initialization as synonym for STDIN. +.SH CREDITS +The +.B getln +and +.B getln2 +man page were taken from Bruce Guenther and +originally published by Dan Bernstein for qmail-1.03. +.SH SEE ALSO +stralloc(3) diff --git a/man/getoptb.3 b/man/getoptb.3 new file mode 100644 index 0000000..f3e329b --- /dev/null +++ b/man/getoptb.3 @@ -0,0 +1,22 @@ +.TH qlibs: getoptb 3 +.SH NAME +getoptb \- get option character from command line +.SH SYNTAX +.B #include \(dqgetoptb.h\(dq +.SH DESCRIPTION +Qlib's +.B getopt +is a replacement for the standard Unix +.B getopt +library, based on +.B sgetopt +and printing errors by means of +.B buffer +rather than +.BR stdio . + +See +.B getopt(3) +for interface details. +.SH "SEE ALSO" +buffer(3) diff --git a/man/iopause.3 b/man/iopause.3 new file mode 100644 index 0000000..c7ab9fb --- /dev/null +++ b/man/iopause.3 @@ -0,0 +1,87 @@ +.TH qlibs: iopause 3 +.SH NAME +iopause \- stateful handling of events based on the poll interface or file descriptors +.SH SYNTAX +.B #include \(dqioause.h\(dq + +int \fBiopause\fP(iopause_fd *\fIx\fR,unsigned int \fIlen\fR,struct taia *\fIdeadline\f$,struct taia *\fIstamp\fR); +.SH DESCRIPTION +.B iopause +checks for file descriptor readability or writeability as specified +by \fIx\fR[0].fd, \fIx\fR[0].events, \fIx\fR[1].fd, \fIx\fR[1].events, ..., \fIx\fR[\fIlen\fR-1].fd, +\fIx\fR[\fIlen\fR-1].events. If \fIx\fR[i].events includes the bit IOPAUSE_READ, +.B iopause +checks for readability of the descriptor \fIx\fR[i].fd; +if \fIx\fR[i].events includes the bit IOPAUSE_WRITE, +.B iopause +checks for writability of the descriptor +\fIx\fR[i].fd; other bits in \fIx\fR[i].events have undefined effects. + +.B iopause +sets the IOPAUSE_READ bit in \fIx\fR[i].revents if it finds that \fIx\fR[i].fd +is readabled and it sets the IOPAUSE_WRITE bit in \fIx\fR[i].revents if it finds +that \fIx\fR[i].fd is writable. +Beware that readability and writeability may be destroyed at any moment +by other processes with access to the same file \fIx\fR[i].fd refers to. + +If there is no readability or writeability to report, +.B iopause +waits until \fIdeadline\fR for something to happen. +.B iopause +will return before \fIdeadline\fR if a +descriptor becomes readable or writeable, or an interrupting signal +arrives, or some system-defined amount of time passes. +.B iopause +sets +.I revents +in any case. + +You must put a current timestamp into \fIstamp\fR before calling +.BR iopause . +In case the the current timestamp is older than the previous one (ie. due to daylight-saving) +negative values are omitted. + +.SH "IMPLEMENTATION NOTES" +The current implementation of +.B iopause +uses the \fBpoll\fR interface if that is available. +On some systems, \fBpoll\fR needs to dynamically allocate kernel +memory. In case not enough memory is available, +.B iopause +will return immediately and will report (often incorrectly) that no descriptors are +readable or writeable. + +If the \fBpoll\fR interface is not available, +.B iopause +uses the \fBselect\fR function. This function +cannot see descriptor numbers past a system-defined limit, typically 256 +or 1024; +.I iopause +will artificially pretend that those descriptors are never readable or writeable. + +Future implementations of +.B iopause +may work around these problems on some +systems, at the expense of chewing up all available CPU time. + +Both \fBpoll\fR and \fBselect\fR use relative timeouts rather than absolute deadlines. +Some kernels round the timeout down to a multiple of 10 milliseconds; this +can burn quite a bit of CPU time as the deadline approaches. +.B iopause +compensates for this by adding 20 milliseconds to the timeout. +In case the current timestamp is older than the previous one (ie. due to daylight-saving) +the timeout is set to 20 milliseconds. +.SH "RETURN CODES" +.B iopause +reads and deploys the return codes of the +.I poll +and +.I select +call. Only positive return values shall be considered by the calling routines for a succcessful +invocation. +.SH CREDITS +Parts of the description are taken from the 'libowfat' man page. +.SH "SEE ALSO" +select(2), +poll(3), +taia(3) diff --git a/man/ip4.3 b/man/ip4.3 new file mode 100644 index 0000000..d6b5928 --- /dev/null +++ b/man/ip4.3 @@ -0,0 +1,73 @@ +.TH qlibs: ipv4 +.SH NAME +ipv4 \- IPv4 address evaluation and conversion +.SH SYNTAX +.B #include \(dqip.h\(dq + +unsigned int \fBip4_fmt\fP(char *\fIs\fR,char \fIip[4]\fR); +.br +unsigned int \fBip4_scan\fP(const char *\fIs\fR,char \fIip[4]\fR); +.br +unsigned int \fBip4_scanbracket\fP(char *\fIs\fR,char \fIip[4]\fR); +.br +unsigned int \fBip_scanbracket\fP(char *\fIip_str\fR,char *\fIs\fR); +.br +unsigned int \fBip4_cidr\fP(char *\fIs\fR,char \fIip[4]\fR,unsigned long *\fIplen\fR); +.br +unsigned int \fBip4_bytestring\fP(stralloc *\fIip4string\fR,char \fIip[4]\fR,int \fIplen\fR); +.SH DESCRIPTION +.B ip4_fmt +reads the +.I char[4] +IPv4 address and returns a dotted-decimal IPv4 address string +.IR 1.2.3.4 . + +.B ip4_scan +reads an IPv4 address string +.I 1.2.3.4 +and converts it to the +.I char[4] +IPv4 address. + +.B ip4_scanbracket +reads an IPv4 address string enclosed in brackets +.I [1.2.3.4] +removes the brackets and calls +.B ip4_scan +on the result. + +.B ip_scanbracket +reads an IP address string enclosed in brackets +.I [1.2.3.4] +or +.IR [fe80::1] , +removes the brackets and calls +.B ip4_scan +or +.B ip6_scan +upon detecting an IPv6 address on the result. + +.B ip4_cidr +reads the CIDR IPv4 address string +.I 1.2.3.4/15 +determines the prefix as integer +.I plen +and calls +.BR ip4_scan . +If no prefix is identified, it returns 32. + +.B ip4_bytestring +reads the IPv4 address given as +.I char[4] +while returning a 0-terminated 'bytestring' representation +.I 1001001.... +up to the given prefix length +.IR plen . +.SH "RETURN CODES" +The +.B ip(4)* +programs return the number of bytes processed. +.B ip4_bytestring +returns regative numbers on failure. +.SH "SEE ALSO" +ip6(3), socket_if(3) diff --git a/man/ip6.3 b/man/ip6.3 new file mode 100644 index 0000000..f3f5cb1 --- /dev/null +++ b/man/ip6.3 @@ -0,0 +1,98 @@ +.TH qlibs: ipv6 +.SH NAME +ipv6 \- IPv6 address evaluation and conversion +.SH SYNOPSIS +.B #include \(dqip.h\(dq + +unsigned int \fBip6_fmt\fP(char *\fIs\fR,char \fIip[16]\fR); +.br +unsigned int \fBip6_fmt_flat\fP(char *\fIs\fR,char \fIip[16]\fR); +.br +unsigned int \fBip6_scan_flat\fP(const char *\fIs\fR,char \fIip[16]\fR); +.br +unsigned int \fBip6_scan\fP(const char *\fIs\fR,char \fIip[16]\fR); +.br +unsigned int \fBip6_scanbracket\fP(const char *\fIs\fR,char \fIip[16]\fR); +.br +unsigned int \fBip6_ifscan\fP(char *\fIs\fR,char \fIip[16]\fR,stralloc *\fIifname\fR); +.br +unsigned int \fBip6_cidr\fP(char *\fIs\fR,char \fIip[16]\fR,unsigned long *\fIplen\fR); +.br +unsigned int \fBip6_bytestring\fP(stralloc *\fIip6string\fR,char \fIip[16]\fR,int \fIplen\fR); +.SH DESCRIPTION +.B ip6_fmt +reads the +.I char[16] +IPv6 address and returns a compactified hexadecimal IPv6 address string +.IR fe80::fefe . + +.B ip6_fmt_flat +reads the +.I char[16] +IPv6 address and returns all hexadecimal IPv6 address labels as string +.IR fe80:0000:....:fefe . + +.B ip6_scan +reads a compactified IPv6 address string +.I fe80::fefe +and converts it to the +.I char[16] +IPv6 address. + +.B ip6_scan_flat +reads an uncompressed IPv6 address als hexadecimal string +and returns it's +.I char[16] +IPv6 address. + +.B ip6_scanbracket +reads a compactified IPv6 address string enclosed in brackets +.I [fe80::fefe] +removes the brackets and calls +.B ip6_scan +on the result. + +.B ip6_ifscan +reads the compactified IPv6 address string appended with the +interface_name +.I fe80::fefe%eth0 +returns +.I ifname +and calls +.B ip6_scan +for the (stripped) IPv6 address. + +.B ip6_cidr +reads the compactified CIDR IPv6 address string +.I fe80::fefe/64 +determines the prefix as integer +.I plen +and calls +.BR ip6_scan . +If no prefix is identfied, it returns 128. + +.B ip6_bytestring +reads the IPv6 address given as +.I char[16] +while returning a 0-terminated 'bytestring' representation +.I 1001001.... +up to the given prefix length +.IR plen . + +The macro +.B ipv6_v4mapped +reads the IPv6 addresses given as +.I char[16] +and returns +.I 0 +in case the given IPv6 address is not +a IPv4 mapped address. +.SH "RETURN CODES" +The +.B ip(6)* +programs return the number of bytes processed. +.B ip6_bytestring +returns negative numbers on failure. +.SH "SEE ALSO" +ip4(3), +socket_if(3) diff --git a/man/logmsg.3 b/man/logmsg.3 new file mode 100644 index 0000000..f819a18 --- /dev/null +++ b/man/logmsg.3 @@ -0,0 +1,109 @@ +.TH qlibs: logmsg 3 +.SH NAME +logmsg \- handle system errors and application log messages +.SH SYNTAX +.B #include \(dqlogmsg.h\(dq + +int logmsg(const char *who, int ecode, unsigned int classs, char *msg) + +\fBerr_sys\fR(w,e) logmsg(w,e,FATAL,"") +.br +\fBerr_sys_plus\fR(w,e,m) logmsg(w,e,FATAL,m) +.br +\fBerr_tmp\fR(w,e) logmsg(w,e,WARN,"") +.br +\fBerr_tmp_plus\fR(w,e,m) logmsg(w,e,WARN,m) +.br +\fBerr_int\fR(w,e,c) logmsg(w,e,c,"") +.br +\fBerr_int_plus\fR(w,e,c,m) logmsg(w,e,c,m) +.br +\fBlog_who\fR(w,m) logmsg(w,0,LOG,m) +.br +\fBlog_anon\fR(m) logmsg("",0,LOG,m) +.SH DESCRIPTION +\fBlogmsg\fR prints error, warning, or info/logging messages to stderr +and potentially terminates the calling program, depending on the \fIclass\fR given. +\fIwho\fR is the name of the program, \fIecode\fR is an error code, +\fIclass\fR determines the behavior upon call and \fImsg\fR is the logging message. +Read "error.h" to learn more about related constants. +.SH ECODE +\fIecode\fR is the error code and subject to be displayed in the log file and +potentially used upon exit if the \fIclass\fR equals \fBERROR\fR, \fBFATAL\fR, or \fBDROP\fR. + +To avoid conflicts with syscall error codes, appplication defined error codes should be negative. +The values \fI-15\fR, \fI-100\fR and \fI-111\fR are reserved for backward compatibility. +.SH CLASS +The \fIclass\fr parameter indicates how the application handles exceptions and displays the +log message. +.TP 4 +o +\fBLOG\fR, \fBINFO\fR, \fBALERT\fR, \fBWARN\fR - display message and continue operation +.TP 4 +o +\fBDROP\fR - display warning message and continue while returning to the calling program +.TP 4 +o +\fBUSAGE\fR, \fBSYNTAX\fR, \fBFATAL\fR, \fBERROR\fR +- display error message and exit application with error code +.RE + +\fBINFO\fR, \fBALERT\fR, \fBWARN\fR, \fBDROP\fR, \fBUSAGE\fR, and \fBFATAL\fR as well +as \fBERROR\fR display the respective class string like \fIwarning:\fR in the log message, +while \fBLOG\fR shows the log message only. + +The class \fBFATAL\fR should be used for system error codes only, rather \fBERROR\fR +and \fBWARN\fR shall be set in conjunction with an application error/warning. +.SH MESSAGE +If the custom message \fImsg\fR is given, it will be printed additionally. +.SH FORMAT +The log message format consists of the tokens +\fIwho\fR: (\fIecode\fR) \fImsg\fR : \fImsg\fR. +.I ecode +is displayed only for classes \fBFATAL\fR, \fBERROR\fR, or \fBDROP\fR. +.I msg +is the system's explanation according to the variable +.I errno +if provided. +.SH NOTES +.I logmsg.c +uses +.I errstr.c +routines. +Error codes and classes are defined in +.I error.h +and included by +.IR logmsg.h . +.SH "EXIT CODE" +\fBlogmsg\fR exits \fIecode\fR for classes \fBERROR\fR, \fBFATAL\fR, \fBSYNTAX\fR, +and \fBUSAGE\fR terminating the application. +.SH HISTORY +Dan Bernstein used sets of \fIstrerr_dieY*()\fR and \fIstrerr_warnY()\fR messages +which explicitely determine the message and behavior class. +Other classes were occasionally defined on demand, such als \fIusage()\fR. + +Kai Peter introduced the \fIerrmsg\fR facility in his \fBqlibs\fR +including a \fBsyslog\fR compliant \fIseverity\fR as second parameter. +.SH EXAMPLES +The macro definitions uses \fBw\fR for the calling program, +\fBe\fR for error code, \fBc\fR for class, and \fBm\fR for message. + + #include "logmsg.h" + #define WHO "my_prog" + + err_sys(WHO,errno); + err_sys_plus(WHO,-111,"additional message"); + + err_tmp("",-100); + err_tmp_plus("",errno,"additional message"); + + log_who(WHO,"message"); + +log_anon() is like log_who() but doesn't print the caller name. + +An user defined message \fBs\fR can be build from multiple arguments by using the \fIB\fR +(build) macro: + + err_sys_plus((errno),B("unable to run: ",*argv)); +.SH "SEE ALSO" +syslog(3) diff --git a/man/pathexec.3 b/man/pathexec.3 new file mode 100644 index 0000000..41dc978 --- /dev/null +++ b/man/pathexec.3 @@ -0,0 +1,124 @@ +.TH qlibs: pathexec 3 +.SH NAME +pathexec \- run a program within a given environment +.SH SYNTAX +.B #include \(dqpathexec.h\(dq + +\fBpathexec_run\fP(const char *\fIp\fR,char **\fIa\fR,char **\fIe\fR); +.br +\fBpathexec\fP(char *const *\fIa\fR); +.br +\fBpathexec_env\fP(const char *\fIs\fR,char *\fIt\fR); +.SH DESCRIPTION +.B pathexec_run +searches for a program named +.IR p . +It replaces the current process with a copy of that program. +The main function in that program will be given arguments +.I a +and environment +.IR e . +.B pathexec_run +looks for +.I p +as specified by the +.I $PATH +environment variable. +.I $PATH +is a colon-separated list of directories +.IR d ; +.B pathexec_run +tries +.B execve +on files named +.IR d/p , +in the order that the directories appear inside +.IR $PATH . +An empty directory name is treated as a single dot. + +If +.I $PATH +is not set, +.B pathexec_run +uses the path +.IR /bin:/usr/bin ; +i.e., it tries +.B execve +on +.IR /bin/p , +then +.IR /usr/bin/p . + +If +.I p +contains a slash, +.B pathexec_run +ignores +.I $PATH +and simply runs +.B execve +on a file named +.IR p . + +.B pathexec +calls +.B pathexec_run +with program name +.IR a[0] , +arguments +.IR a , +and the same environment as the current process, +modified as described below. +.B pathexec +has the same return behavior as +.BR pathexec_run . +.B pathexec_env +modifies the environment used by +.BR pathexec . +It removes a variable named +.IR s, +if one exists. It then adds a variable named +.I s +with value +.IR t, +if the pointer +.I t +is nonzero. The name +.I s +must not contain +.IR = . +.SH "RETURN CODES" +Normally +.B pathexec_run +does not return, because the process has been replaced. +However, if all the +.B execve +attempts fail, +.B pathexec_run +returns, setting +.I errno +to the most interesting error returned by +.BR execve . +Furthermore, +.B pathexec_run +returns immediately if an +.B execve +attempt fails with an error other than +.IR error_noent , +.IR error_acces , +.IR error_perm , +or +.IR error_isdir . +This list is subject to change. + +Normally +.B pathexec_env +returns +.IR 1 . +If it is unable to allocate memory, it returns +.IR 0 , +leaving the +.B pathexec +environment alone. +.SH "SEE ALSO" +error(3) diff --git a/man/scan.3 b/man/scan.3 new file mode 100644 index 0000000..a466daa --- /dev/null +++ b/man/scan.3 @@ -0,0 +1,81 @@ +.TH qlibs: scan 3 +.SH NAME +scan \- string to integer conversion +.SH SYNTAX +.B #include \(dqscan.h\(dq + +unsigned int \fBscan_8long\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR); +.br +unsigned int \fBscan_uint\fP(const char *\fIsrc\fR,int *\fIdest\fR); +.br +unsigned int \fBscan_long\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR); +.br +unsigned int \fBscan_ulong\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR); +.br +unsigned int \fBscan_xint\fP(const char *\fIsrc\fR,int *\fIdest\fR); +.br +unsigned int \fBscan_xlong\fP(const char *\fIsrc\fR,unsigned long *\fIdest\fR); +.SH DESCRIPTION +.B scan_8long +parses an unsigned long integer in octal ASCII representation +from \fIsrc\fR and writes the result into \fIdest\fR. It returns the +number of bytes read from \fIsrc\fR. + +.B scan_uint +parses an unsigned integer in decimal ASCII representation +from \fIsrc\fR and writes the result into \fIdest\fR. It returns the +number of bytes read from \fIsrc\fR. + +.B scan_ulong +parses an unsigned long integer in decimal ASCII representation +from \fIsrc\fR and writes the result into \fIdest\fR. It returns the +number of bytes read from \fIsrc\fR. +Leading + or - or space (or anything outside of 0-9) is not accepted. +The libc conventions of "023" for octal or "0x23" for hexadecimal are +not supported. +.B scan_ulong +will abort the scan if the next character is not a digit, or +if it is a digit but adding it to the number would lead to an integer +overflow. +.B scan_ulong +returns the number of characters successfully scanned and +processed from src. + +.B scan_long +includes the same logic as +.B scan_ulong +but now on a signed long integer in decimal ASCII format while +recognizing the leading '+' or '-' sign. + +.B scan_xint +parses an unsigned integer in hexadecimal ASCII representation +from \fIsrc\fR and writes the result into \fIdest\fR. It returns the +number of bytes read from \fIsrc\fR. + +.B scan_xlong +parses an unsigned long integer in hexadecimal ASCII +representation from \fIsrc\fR and writes the result into \fIdest\fR. It +returns the number of bytes read from \fIsrc\fR. +.B scan_xlong +understands both upper and lower case letters. +.B scan_xlong +does not expect or understand a "0x" prefix. +.SH EXAMPLES +scan_ulong("23",&i) -> i=23, return 2 + +scan_ulong("+23",&i) -> return 0 + +scan_ulong("-23",&i) -> return 0 + +scan_ulong(" 23",&i) -> return 0 + +scan_ulong("23,42",&i) -> i=23, return 2 + +scan_ulong("023",&i) -> i=23, return 3 + +scan_ulong("0x23",&i) -> i=0, return 1 + +scan_ulong("4294967296",&i) -> i=429496729, return 9 // 32-bit system +.SH "SEE ALSO" +case(3), +byte(3) diff --git a/man/socket_bind.3 b/man/socket_bind.3 new file mode 100644 index 0000000..434e45c --- /dev/null +++ b/man/socket_bind.3 @@ -0,0 +1,117 @@ +.TH qlibs: socket_bind 3 +.SH NAME +socket_bind \- binding a TCP/UDP socket to a local IP address, port, +and perhaps scope_id +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_bind4\fP(int \fIs\fR,char \fIip\fR[4],uint16 \fIport\fR); +.br +int \fBsocket_bind4_reuse\fP(int \fIs\fR,char \fIip\fR[4],uint16 \fIport\fR); + +int \fBsocket_bind6\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR); +.br +int \fBsocket_bind6_reuse\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR); + +int \fBsocket_bind\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR); +.br +int \fBsocket_bind_reuse\fP(int \fIs\fR,char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR); +.SH DESCRIPTION +.B socket_bind4 +sets the local IP address and TCP/UDP port of a TCP/UDP +socket \fIs\fR to \fIip\fR and \fIport\fR respectively. + +.B socket_bind4_reuse +sets the local IP address and TCP/UDP port of a +TCP/UDP socket \fIs\fR to \fIip\fR and \fIport\fR respectively. +Unlike +.BR socket_bind4 , +this function will also tell the operating system +that the address is to be reused soon, which turns off the normal pause +before this IP and port can be bound again. + +.B socket_bind6 +sets the local IP address and TCP/UDP port of a TCP/UDP +socket \fIs\fR to \fIip\fR, \fIport\fR and \fIscope_id\fR respectively. + +.B socket_bind6_reuse +sets the local IP address and TCP/UDP port of a TCP/UDP socket \fIs\fR +to \fIip\fR, \fIport\fR and \fIscope_id\fR respectively. +Unlike +.BR socket_bind6 , +this function will also tell the operating system +that the address is to be reused soon, which turns off the normal pause +before this IP and port can be bound again. + +.B socket_bind +sets the local IPv4/IPv6 address and TCP/UDP port of a TCP/UDP +socket \fIs\fR to \fIip\fR, \fIport\fR, and \fIscope_id\fR respectively. + +For IPv4 and IPv4-mapped IPv6 addresses +.B socket_bind +will use +.B socket_bind4 +or otherwise +.BR socket_bind6 . + +.B socket_bind_reuse +sets the local IPv4/IPv6 address and TCP/UDP port of a TCP/UDP socket \fIs\fR +to \fIip\fR, \fIport\fR, and \fIscope_id\fR respectively. +Unlike +.BR socket_bind , +this function will also tell the operating system +that the address is to be reused soon, which turns off the normal pause +before this IP and port can be bound again. + +For IPv4 and IPv4-mapped IPv6 addresses +.B socket_bind_reuse +will use +.B socket_bind4_reuse +or otherwise +.BR socket_bind6_reuse . +.SH "AUTOMATIC BINDING" +If the IPv4 address is 0 or the IPv6 address is ::, the operating system +chooses a local IP address. +If \fIport\fR is 0, the operating system chooses a port. +\fIscope_id\fR is usually 0, except for IPv6 LLU addresses where +.B socket_getifidx +can be used to determine +.I scope_id +from the interface name. +.SH "RETURN CODES" +Normally, all +.I socket_bind* +routines return +.IR 0 . +If anything goes wrong, the return code is +.I -1 +and setting +.I errno +appropriately. +.SH EXAMPLE + #include <socket_if.h> + #include <ip.h> + + int \fIs\fR; + char \fIlcoalip\fR[16]; + char \fIremoteip\fR[16]; + uint16 \fIp\fR = 0; + uint32 \fIscope_id\fR = 0; + + if (ip6_isv4mapped(ip)) + s = socket_tcp4(); + else + s = socket_tcp6(); + if (s == -1) + err_tmp(111,"unable to create TCP socket: "); + + socket_connect(s,remoteip,p,scope_id); +.SH "SEE ALSO" +socket_if(3), +socket_connect(3), +socket_info(3), +socket_recv(3), +socket_send(3), +socket_setup(3), +socket_tcp(3), +socket_udp(3) diff --git a/man/socket_connect.3 b/man/socket_connect.3 new file mode 100644 index 0000000..e289393 --- /dev/null +++ b/man/socket_connect.3 @@ -0,0 +1,110 @@ +.TH qlibs: socket_connect 3 +.SH NAME +socket_connect \- initiate or test a socket connection to a remote IPv4/IPv6 address +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_connect4\fP(int \fIs\fR,const char \fIip\fR[4],uint16 \fIport\fR); +.br +int \fBsocket_connect6\fP(int \fIs\fR,const char \fIip\fR[16],uint16 \fIport\fR, + uint32 \fIscope_id\fR); +.br +int \fBsocket_connect\fP(int \fIs\fR,const char \fIip\fR[16],uint16 \fIport\fR, + uint32 \fIscope_id\fR); + +int \fBsocket_connected\fP(int \fIs\fR); +.SH DESCRIPTION +.B socket_connect4 +attempts to make a connection from TCP or UDP socket \fIs\fR to +TCP port \fIport\fR on IP address \fIip\fR. +You can call +.B socket_connect4 +without calling +.BR socket_bind4 . +This has the effect as first calling +.B socket_bind4 +with IP address 0.0.0.0 and port 0. + +.B socket_connect6 +attempts to make a connection from TCP or UDP socket \fIs\fR to +TCP port \fIport\fR on IP address \fIip\fR and \fIscope_id\fR. +The meaning of \fIscope_id\fR is dependent on the implementation and +IPv6 IP. For link-local IPv6 addresses it specifies the outgoing +interface index. From a given interface name (e.g. "eth0") +it's index can be retrieved with +.BR socket_getifidx . +\fIscope_id\fR should normally be set to 0. +You can call +.B socket_connect6 +without calling +.BR socket_bind6 . +This has the effect as first calling +.B socket_bind6 +with IP address :: and port 0. + +.B socket_connect +attempts to make a connection from TCP socket \fIs\fR to +TCP port \fIport\fR on IP address \fIip\fR and \fIscope_id\fR +calling +.BR socket_connect6 . +If however, \fIip\fR is an IPv4 or IPv4-mapped IPv6 address +.B socket_connect4 +is called instead. + +Once a socket is connected, you can use the read and write +system calls to transmit data. + +.B socket_connected +can be used to verify, whether a background connection failed or +succeeded, thus \fIs\fR became writable or not. +.SH EXAMPLE + #include <socket_if.h> + + int \fIs\fR; + char \fIlocalip\fR[16]; + char \fIremoteip\fR[16]; + uint16 \fIp\fR = 0; + + s = socket_tcp(); + socket_bind(s,localip,p,0); + socket_connect(s,remoteip,p,0); + + if (socket_connected(s) != 1) + err_tmp(""111,fatal,"unable to setup TCP connection: "); +.SH "RETURN CODES" +.BR socket_connect4 , +.BR socket_connect6 +and +.BR socket_connect +may return +.IR 0 , +to indicate that the connection succeeded (and succeeded immediately, +if the socket is non-blocking) +.IR -1 , +setting +.I errno +to error_inprogress or error_wouldblock, to indicate +that the socket is non-blocking +.IR -1 , +setting +.I errno +to something else, to indicate that the connection +failed (and failed immediately, if the socket is non-blocking). + +.B socket_connected +returns +.I 1 +if \fIs\fR is a socket and a connection is established, +.I 0 +otherwise and setting +.I errno +appropriately. +.SH "SEE ALSO" +socket_if(3), +socket_info(3), +socket_bind(3), +socket_recv(3), +socket_send(3), +socket_setup(3), +socket_tcp(3), +socket_udp(3) diff --git a/man/socket_if.3 b/man/socket_if.3 new file mode 100644 index 0000000..1c1ef70 --- /dev/null +++ b/man/socket_if.3 @@ -0,0 +1,97 @@ +.TH qlibs: socket_if 3 +.SH NAME +socket_if \- retrieve scope_id for interface name and vice versa +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +uint32 \fBsocket_getifidx\fP(const char *\fIifname\fR); + +const char *\fBsocket_getifname\fP(uint32 \fIscope_id\fR); +.SH DESCRIPTION +.B socket_getifidx +returns the \fIscope_id\fR of an interface named as +.I ifname +typically ''eth0''. + +.B socket_getifname +retrieves from the operating system's assigned +.I scope_id +the interface name +.IR ifname . +.SH INTERFACE_NAME VERSUS SCOPE_ID +For IPv6 LLU addresses an additional +.I ifname +(interface name) has to be provided accompanying the IPv6 address: +.IR fe80::1%eth0 . +The operating systems rather uses +.I scope_id +as index for +.IR ifname . + +For global IPv6 and ULA addresses +.I ifname +can be set to +.IR 0 . +Since IPv4 addresses on any interface are always unique, +simply use +.I 0 +for all cases. +.SH BACKGROUND +Qlib's socket routines provide an easy API to setup +TCP or UDP connections over IPv4 or IPv6 networks. Together with +Qlib's IP address parsing capabilities, a set of high-level +socket routines allow a common IPv4/IPv6 handling. +.SH SOCKET FILES +.TP 5 +.B socket_bind.c +bind to or reuse the local IPv4/IPv6 address and port +for a socket connection +.TP 5 +.B socket_connect.c +attempts to setup a TCP or UDP client connection +.TP 5 +.B socket_info.c +get local/remote IPv4/IPv6 address of socket +.TP 5 +.B socket_recv.c +set up a receiving IPv4/IPv6 connection +.TP 5 +.B socket_send.c +send UDP datagram over a IPv4 or IPv6 connection +.TP 5 +.B socket_setup.c +listen to and accept an IPv4/IPv6 TCP socket connection +.TP 5 +.B socket_tcp.c +create a non-blocking TCP stream socket +.TP 5 +.B socket_udp.c +create a non-blocking UDP datagram socket +.SH USAGE +Most of the above files include their IPv4 and +IPv6 counterparts together with a combined usage +requiring in addition a +.I scope_id +or simply +.IR 0 . +IPv4 addresses are usually converted upon reading to +IPv4-mapped IPv6 addresses using Qlib's IP address +parsing functions. +IPv4 and IPv6 socket calls - if required - +need to be distinguished +by the calling routines testing + +.EX + ip6_isv4mapped(ip) +.EE + +Otherwise, the unified IPv6/IPv4 versions will be used. +.SH "SEE ALSO" +socket_bind(3), +socket_connect(3), +socket_info(3), +socket_recv(3), +socket_send(3), +socket_setup(3), +socket_tcp(3), +socket_udp(3) diff --git a/man/socket_info.3 b/man/socket_info.3 new file mode 100644 index 0000000..899a7cb --- /dev/null +++ b/man/socket_info.3 @@ -0,0 +1,47 @@ +.TH socket_info 3 +.SH NAME +socket_info \- retrieving IP connection information for an existing socket +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_local\fP(int \fIs\fR,char \fIip\fR[16], + uint16 *\fIport\fR,uint32 *\fIscope_id\fR); + +int \fBsocket_remote\fP(int \fIs\fR,char \fIip\fR[16], + uint16 *\fIport\fR,uint32 *\fIscope_id\fR); +.SH DESCRIPTION +.B socket_local +returns the local IPv6 or IPv4-mapped IPv6 address as \fIip\fR, +port as \fIport\fR and perhaps the scope_id as \fIscope_id\fR for the UDP or TCP socket. + +.B socket_remote +returns the remote IPv6 or IPv4-mapped IPv6 address as \fIip\fR, +port as \fIport\fR and perhaps the scope_id as \fIscope_id\fR for the UDP or TCP socket. + +.SH "RETURN CODES" +If something goes wrong, +.B socket_local +and +.B socket_remote +return +.IR -1 , +setting +.I errno +appropriately. +.SH EXAMPLE +#include <socket_if.h> + int \fIs\fR; + char \fIip\fR[16]; + uint16 \fIport\fR; + + if (socket_remote(s,ip,&port,0) == -1) + err_tmp("",111,"unable to get remote address: "); +.SH "SEE ALSO" +socket_if(3), +socket_bind(3), +socket_connect(3), +socket_recv(3), +socket_send(3), +socket_setup(3), +socket_tcp(3), +socket_udp(3) diff --git a/man/socket_recv.3 b/man/socket_recv.3 new file mode 100644 index 0000000..bebef08 --- /dev/null +++ b/man/socket_recv.3 @@ -0,0 +1,56 @@ +.TH qlibs: socket_recv 3 +.SH NAME +socket_recv \- receive UDP datagrams over IPv4/IPv6 connections +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_recv\fP(int \fIs\fR, const char *\fIbuf\fR, unsigned int \fIlen\fR, + const char \fIip\fR[16], uint16 *\fIport\fR, uint32 *\fIscope_id\fR); +.SH DESCRIPTION +.B socket_recv +reads \fIlen\fR bytes starting at \fIbuf\fR in a UDP datagram +over the socket \fIs\fR while providing information about the +remote IP address \fIip\fR and the UDP \fIport\fR and +the perhaps \fIscope_id\fR of the receiving interface. + +You can call +.B socket_recv +without calling +.BR socket_bind . +This has the effect as first calling +.B socket_bind +with IP address :: and port 0. +.SH RETURN VALUE +.B socket_recv +returns +.IR 0 , +otherwise +.I -1 +and sets +.I errno +appropriately. +.SH EXAMPLE + #include <socket_if.h> + #include <ip.h> + + int \fIs\fR; + char \fIlocalip\fR[16]; + char \fIremoteip\fR[16]; + uint16 \fIp\fR, \fIport\fR; + uint32 \fIscope_id\fR; + unsigned int \fIlen\fR; + int \fIr\fR; + char buf[MTUSIZE+1]; + + s = socket_udp(); + socket_bind_reuse(s,localip,p,0); + r = socket_recv(s,buf,len,remoteip,&port,&scope_id); +.SH SEE ALSO +socket_if(3), +socket_info(3), +socket_bind(3), +socket_connect(3) +socket_send(3), +socket_setup(3), +socket_tcp(3), +socket_udp(3) diff --git a/man/socket_send.3 b/man/socket_send.3 new file mode 100644 index 0000000..cf74300 --- /dev/null +++ b/man/socket_send.3 @@ -0,0 +1,76 @@ +.TH qlibs: socket_send 3 +.SH ROUTINES +socket_send \- sending data over a UDP socket +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_send4\fP(int \fIs\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR, + const char \fIip\fR[4],uint16 \fIport\fR); +.br +int \fBsocket_send6\fP(int \fIs\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR, + const char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR); +.br +int \fBsocket_send\fP(int \fIs\fR,const char *\fIbuf\fR,unsigned int \fIlen\fR, + const char \fIip\fR[16],uint16 \fIport\fR,uint32 \fIscope_id\fR); +.SH DESCRIPTION +.B socket_send4 +sends \fIlen\fR bytes starting at \fIbuf\fR in a UDP +datagram over the socket \fIs\fR to UDP port \fIport\fR on IPv4 address +\fIip\fR. + +.B socket_send6 +sends \fIlen\fR bytes starting at \fIbuf\fR in a UDP datagram +over the socket \fIs\fR to UDP port \fIport\fR on IPv6 address \fIip\fR and perhaps +using \fIscope_id\fR as outging interface. + +For link-local IPv6 (LLU) addresses \fIscope_id\fR specifies the outgoing +interface index. +.I socket_id +can be queried for the given name of the interface (e.g. "eth0") by means of +.BR socket_getifidx . +\fIscope_id\fR should normally be set to 0 except for link local IPv6 addresses + +.B socket_send +sends \fIlen\fR bytes starting at \fIbuf\fR in a UDP datagram +over the socket \fIs\fR to UDP port \fIport\fR on IP address \fIip\fR and perhaps +using \fIscope_id\fR as outging interface. + +You can call +.B socket_send* +without calling +.BR socket_bind* . +This has the effect as first calling +.B socket_bind4 +with IP address 0.0.0.0 and port 0 +or +.B socket_bind6 +with IP address :: and port 0. +.SH RETURN VALUE +.B socket_send* +returns +.I 0 +if the datagram was sent successfully. If not, +it returns +.I -1 +and sets +.I errno +appropriately. +.SH EXAMPLE + #include <socket_if.h> + + int \fIs\fR; + char \fIip\fR[4]; + uint16 \fIp\fR; + + s = socket_udp(); + socket_bind(s,ip,p); + socket_send(s,"hello, world",12,ip,p,0); +.SH "SEE ALSO" +socket_if(3), +socket_info(3), +socket_bind(3), +socket_connect(3), +socket_recv(3), +socket_setup(3), +socket_tcp(3), +socket_udp(3) diff --git a/man/socket_setup.3 b/man/socket_setup.3 new file mode 100644 index 0000000..639b28b --- /dev/null +++ b/man/socket_setup.3 @@ -0,0 +1,105 @@ +.TH qlibs: socket_setup 3 +.SH NAME +socket_setup \- listen to or accept socket for incoming TCP connections +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_listen\fP(int \fIs\fR,int \fIn\fR); + +int \fBsocket_accept\fP(int \fIs\fR,char \fIip\fR[16], + uint16 *\fIport\fR,uint32 *\fIscope_id\fR); + +int \fBsocket_ipoptionskill\fR(int \fIs\fR); + +int \fBsocket_ip6anycast(\fR(int \fIs\fR); + +int \fBsocket_dualstack\fR(int \fIs\fR); + +int \fBsocket_nodualstack\fR(int \fIs\fR); +.SH DESCRIPTION +.B socket_listen +prepares TCP socket \fIs\fR to accept TCP connections. +It allows a backlog of approximately \fIn\fR TCP SYNs. +(On systems supporting SYN cookies, the backlog is irrelevant.) + +.B socket_accept +accepts the connection. It creates a new socket +for the connection and returns a file descriptor pointing to the new +socket; you can use the read and write system calls to transmit data +through that file descriptor. +Further, it provides information about client's +\fIip\fR address and TCP \fIport\fR number +perhaps together with local receiving interface \fIscope_id\fR. + +.B socket_ipoptionskill +is used to disable previously defined options in IPv4 or IPv6 packets +like Source Routing prior of using this socket for data exchange. +.B socket_ipoptionskill +uses the +.BR setsockopt . + +.B socket_ip6anycast +enables unspecified reversed anycasting on the listening socket +.IR s +with IPv6 address +.IR :: . +Upon receiving IPv6 packets, the socket records the +incoming IPv6 address and the receiving \fIscope_id\fR +in order provide additional routing information. + +.B socket_dualstack +and +.B socket_nodualstack +can be used to force or forbid dual-stack behavior +setting the +.B setsockopt +variable +.I IPV6_V6ONLY +appropriately. In the last case, a potential servers +needs two instances to accept incoming IPv6 and IPv6 packets. +.SH "RETURN CODES" +Normally +.BR socket_listen , +.B socket_accept +and +.B socket_ipotionskill +as well as +.B socket_dualstack +and +.B socket_nodualstack +return +.I 0 +and if anything goes wrong it returns +.IR -1 , +setting +.I errno +appropriately. +.SH EXAMPLE + #include <socket_if.h> + + int \fIs\fR, \fIt\fR; + int \fIr\fR; + char \fIip\fR[16]; + uint16 \fIp\fR; + + if ((s = socket_tcp()) == -1) + err_tmp("",111,"unable to create TCP socket: "); + r = socket_ipoptionskill(s); + if (socket_bind_reuse(s,(char *)V6localnet,8002,0) == -1) + err_tmp("",111,"unable to bind: "); + if (socket_listen(s,1) == -1) + err_tmp("",111,"unable to listen: "); + + t = socket_tcp(); + socket_bind(t,ip,p,0); + socket_listen(s,16); + socket_accept(t,ip,&p,&scope_id); +.SH "SEE ALSO" +socket_if(3), +socket_bind(3), +socket_connect(3), +socket_info(3), +socket_recv(3), +socket_send(3), +socket_tcp(3), +socket_udp(3) diff --git a/man/socket_tcp.3 b/man/socket_tcp.3 new file mode 100644 index 0000000..3616ed7 --- /dev/null +++ b/man/socket_tcp.3 @@ -0,0 +1,58 @@ +.TH qlibs: socket_tcp 3 +.SH NAME +socket_tcp \- setting up TCP sockets +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_tcp4\fP(); +.br +int \fBsocket_tcp6\fP(); +.br +int \fBsocket_tcp\fP(); +.SH DESCRIPTION +.B socket_tcp4 +creates a non-blocking TCP/IPv4 stream socket and +providing a file descriptor pointing to that socket. + +.B socket_tcp6 +creates a non-blocking TCP/IPv6 stream socket and +providing a file descriptor pointing to that socket. + +.B socket_tcp +creates a non-blocking IPv6 TCP/IP socket calling +.B socket_tcp6 +unless it can't bind to IPv6 and now facilitating +.BR socket_tcp4 . +.SH "REMOTE CODES" +All these routines return +.IR 0 +except in case of failures, returning +.I -1 +and setting +.I errno +appropriately, without allocating any resources. +.SH EXAMPLE + #include <socket_if.h> + + int \fIt\fR; + char \fIlocalip\fR[16]; + char \fIremoteip\fR[16]; + uint16 \fIp\fR; + + if (ip6_isv4mapped(remoteip)) { + t = socket_tcp4(); + socket_bind4(t,localip + 12,0); + } else { + t = socket_tcp6(); + socket_bind6(t,localip,0,0); + } + socket_connect(s,remoteip,p,0); +.SH "SEE ALSO" +socket_if(3), +socket_bind(3), +socket_connect(3), +socket_info(3), +socket_recv(3), +socket_send(3), +socket_setup(3), +socket_udp(3) diff --git a/man/socket_udp.3 b/man/socket_udp.3 new file mode 100644 index 0000000..eff0290 --- /dev/null +++ b/man/socket_udp.3 @@ -0,0 +1,58 @@ +.TH qlibs: socket_udp 3 +.SH NAME +socket_udp \- setting up UDP datagram sockets +.SH SYNTAX +.B #include \(dqsocket_if.h\(dq + +int \fBsocket_udp4\fP(); +.br +int \fBsocket_udp6\fP(); +.br +int \fBsocket_udp\fP(); +.SH DESCRIPTION +.B socket_udp4 +creates a non-blocking UDP/IPv4 datagram socket and +providing a file descriptor pointing to that socket. + +.B socket_udp6 +creates a non-blocking UDP/IPv6 datagram socket and +providing a file descriptor pointing to that socket. + +.B socket_udp +creates a non-blocking IPv6 UDP socket calling +.B socket_udp6 +unless it can't bind to IPv6 and now facilitating +.BR socket_tcp4 . + +.SH "REMOTE CODES" +All these routines return +.IR 0 +except in case of failures, returning +.I -1 +and setting +.I errno +appropriately, without allocating any resources. +.SH EXAMPLE + #include <socket_if.h> + int \fIu\fR; + char \fIlocalip\fR[16]; + char \fIremoteip\fR[16]; + uint16 \fIp\fR; // port + + if (ip6_isv4mapped(remoteip)) { + u = socket_udp4(); + socket_bind4(u,localip + 12,p); + } else + u = socket_udp6(); + socket_bind6(u,localip,p,0); + } + socket_connect(u,remoteip,p,0); +.SH "SEE ALSO" +socket_if(3), +socket_bind(3), +socket_connect(3), +socket_info(3), +socket_recv(3), +socket_send(3), +socket_setup(3), +socket_tcp(3) diff --git a/man/str.3 b/man/str.3 new file mode 100644 index 0000000..c8643ed --- /dev/null +++ b/man/str.3 @@ -0,0 +1,61 @@ +.TH qlibs: str 3 +.SH NAME +str \- string evaluation and comparision (for allocated strings) +.SH SYNTAX +.B #include \(dqstr.h\(dq + +unsigned int \fBstr_copy\fR(char *\fIdst\fR, const char *\fIsrc\fR); +.br +unsigned int \fBstr_copyb\fR(char *\fIdst\fR, const char *\fIsrc\fR, unsigned int \fIlen\fR); +.br +unsigned int \fBstr_chr\fR(const char *\fIs\fR, int \fIc\fR); +.br +unsigned int \fBstr_rchr\fR(const char *\fIs\fR, int \fIc\fR); +.br +unsigned int \fBstr_diff\fR(const char *\fIs\fR, const char *\fIt\fR); +.br +unsigned int \fBstr_diffn\fR(const char *\fIs\fR, const char *\fIt\fR, unsigned int \fIlen\fR); +.br +unsigned int \fBstr_equal\fR(const char *\fIs\fR, const char *\fIt\fR); +.br +unsigned int \fBstr_len\fP(const char *\fIs\fR); +.br +unsigned int \fBstr_starts\fP(const char *\fIdst\fR, const char *\fIsrc\fR); +.br +char *\fBstr_append\fP(char *\fIdst\fR, const char *\fIs\fR); +.SH DESCRIPTION +\fBstr_copy\fR copies \fIsrc\fR into \fIdst\fR up to the first occurance of \\0 while including it. +It returns the number of bytes written. +If \fIdst\fR is smaller than \fIsrc\fR, \fBstr_copy\fR fills up \fIdst\fR +to its allocated size and returns the number of bytes it would have be written. +\fBstr_copyb\fR copies \fIlen\fR bytes from \fIsrc\fR into \fIdst\fR. + +\fBstr_chr\fR and \fBstr_rchr\fR searches for a single character in a string +(from left-to-right or from right-to-left) and returns the position of +the \fIfirst\fR occurrance of \fIc\fR or the length \fIlen\fR of \fIs\fR. + +\fBstr_len\fR determines the length of a string. It returns the position +of the first occurance of \\0 in \fIs\fR. + +\fBstr_diff\fR, \fBstr_diffn\fR, and \fBstr_equal\fR compare two strings. +\fBstr_diff\fR and \fBstr_diffn\fR returns negative, zero or positive, depending +whether the string \fIs\fR[0], \fIs\fR[1], ..., \fIs\fR[n]=='\\0' is +ASCII lexicographically smaller than, equal to, or greater than the string +\fIt\fR[0], \fIt\fR[1], ..., \fIt\fR[m-1]=='\\0'. If the strings are different, +both functions do not read bytes past the first difference. +\fBstr_diffn\fR considers the strings equal if the first \fIn\fR characters match. + +\fBstr_equal\fR returns 1, if \fIs\fR and \fIt\fR match up to and including the +first occurance of \\0 or returns 0 otherwise. It is the opposite of \fBstr_diff\fR. + +\fBstr_start\fR compares the two strings from the begining. +It returns 1 if \fIt\fR is a prefix of \fIs\fR or 0 otherwise. + +\fB*str_append\fR appends a single byte \fIs\fR to \fIout\fR given \fIout\fR +is allocated with enough space. +.SH "NOTE" +For the \fBstr_copy*\fR and \fB*str_append\fP commands \fIdst\fR +has to have a preallocated space. +Otherwise the result may be unexpected. +.SH "SEE ALSO" +byte(3), stralloc(3) diff --git a/man/stralloc.3 b/man/stralloc.3 new file mode 100644 index 0000000..55da25d --- /dev/null +++ b/man/stralloc.3 @@ -0,0 +1,160 @@ +.TH qlibs stralloc 3 +.SH NAME +stralloc \- dynamically allocated strings +.SH SYNTAX +.B #include \(dqstralloc.h\(dq + +int \fBstralloc_ready\fP(&\fIsa\fR,\fIlen\fR); +.br +int \fBstralloc_readyplus\fP(&\fIsa\fR,\fIlen\fR); + +int \fBstralloc_copy\fP(&\fIsa\fR,&\fIsa2\fR); +.br +int \fBstralloc_copys\fP(&\fIsa\fR,\fIbuf\fR); +.br +int \fBstralloc_copyb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR); + +int \fBstralloc_cat\fP(&\fIsa\fR,&\fIsa2\fR); +.br +int \fBstralloc_cats\fP(&\fIsa\fR,\fIbuf\fR); +.br +int \fBstralloc_catb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR); + +int \fBstralloc_append\fP(&\fIsa\fR,\fIbuf\fR); +.br +int \fBstralloc_0\fP(&\fIsa\fR); + +int \fBstralloc_starts\fP(&\fIsa\fR,\fIbuf\fR); + +stralloc \fIsa\fR = {0}; +.br +stralloc \fIsa2\fR = {0}; +.br +unsigned int \fIlen\fR; +.br +char *\fIbuf\fR; +.SH DESCRIPTION +A +.B stralloc +variable holds a string in dynamically allocated space. +String length is limited only by memory. +String contents are unrestricted. + +The +.B stralloc +structure has three components: +.I sa\fB.s +is a pointer to the string, or 0 if it is not allocated; +.I sa\fB.len +is the number of bytes in the string, if it is allocated; +.I sa\fB.a +is the number of bytes allocated for the string, if it is allocated. +A +.B stralloc +variable should be initialized to {0}, +meaning unallocated. + +.B stralloc_ready +makes sure that +.I sa +has enough space allocated for +.I len +characters. +It allocates extra space if necessary. + +.B stralloc_readyplus +makes sure that +.I sa +has enough space allocated for +.I len +characters more than its current length. +If +.I sa +is unallocated, +.B stralloc_readyplus +is the same as +.BR stralloc_ready . + +.B stralloc_copy +copies +.I sa2 +to +.IR sa , +allocating space if necessary. +Here +.I sa2 +is an allocated +.B stralloc +variable. + +.B stralloc_copys +copies a 0-terminated string, +.IR buf , +to +.IR sa , +without the 0. + +.B stralloc_copyb +copies +.I len +characters from +.I buf +to +.IR sa . + +.B stralloc_cat +appends +.I sa2 +to +.IR sa , +allocating space if necessary. +If +.I sa +is unallocated, +.B stralloc_cat +is the same as +.BR stralloc_copy . + +.B stralloc_cats +and +.B stralloc_catb +are analogous to +.B stralloc_copys +and +.BR stralloc_copyb . + +.B stralloc_append +adds a single character, +.IR *buf , +to +.IR sa , +allocating space if necessary. + +.B stralloc_0 +adds a single 0 character +to +.IR sa . + +.B stralloc_starts +returns 1 if the 0-terminated string +.IR buf , +without the 0, +is a prefix of +.IR sa . +.SH "ERROR HANDLING" +If a +.B stralloc +routine runs out of memory, +it leaves +.I sa +alone and returns 0, +setting +.B errno +appropriately. +On success it returns 1; +this guarantees that +.I sa +is allocated. +.SH "SEE ALSO" +alloc(3), +error(3) diff --git a/man/taia.3 b/man/taia.3 new file mode 100644 index 0000000..0aeb4e0 --- /dev/null +++ b/man/taia.3 @@ -0,0 +1,91 @@ +.TH taia 3 +.SH NAME +taia \- Temps Atomique International (Attosecond) time routines +.SH SYNTAX +.B #include \(dqtaia.h\(dq + +extern int \fBtaia_add\fP(struct taia *\fIt\fR,const struct taia *\fIa\fR,const struct taia *\fIb\fR); +.br +extern int \fBtaia_addsec\fP(struct taia *\fIt\fR,const struct taia *\fIs\fR,int \fIsecs\fR); +.br +extern int \fBtaia_approx\fP(const struct taia *\fIt\fR); +.br +int \fBtaia_frac\fP(const struct taia *\fIt\fR); +.br +extern int \fBtaia_less\fP(const struct tai *\fIa\fR,const struct tai *\fIb\fR); +.br +extern int \fBtaia_now\fP(struct taia *\fIt\fR); +.br +extern int \fBtaia_pack\fP(char *buf,const struct taia *\fIt\fR); +.br +extern int \fBtaia_sub\fP(struct taia *\fIt\fR,const struct taia *\fIa\fR,const struct taia *\fIb\fR); +.br +extern int \fBtaia_tai\fP(const struct taia *\fIt\fR,struct tai *\fIsec\fR); +.br +extern int \fBtaia_uint\fP(struct taia *\fIt\fR,unsigned int \fIsecs\fR); +.br +extern int \fBtaia_unpack\fP(const char *buf,struct taia *\fIt\fR); +.SH DESCRIPTION +.B taia_add +adds \fIa\fR to \fIb\fR and writes the result to \fIt\fR. +The inputs and output may overlap. + +.B taia_addsec +adds \fIsecs\fR seconds to \fIs\fR and writes the result to \fIt\fR. +The inputs and output may overlap. + +.B taia_approx +returns a double-precision approximation of \fIt\fR. +The result of +.B taia_approx +is always nonnegative. + +.B taia_frac +returns a double-precision approximation to the fraction part +of \fIt\fR. The result of +.B taia_frac +is always nonnegative. + +.B taia_less +returns 1 if \fIa\fR is less than \fIb\fR, 0 otherwise. + +.B taia_now +puts the current time into \fIt\fR. More precisely: +.B tai_now +puts into \fIt\fR its best guess as to the TAI64NA label +for the 1-attosecond interval that contains the current time. + +This implementation of taia_now assumes that the +.I time_t +returned from the time function represents the number of TAI seconds since 1970-01-01 +00:00:10 TAI. This matches the convention used by the Olson tz library +in ``right'' mode. + +Beware that many clocks are not set accurately, and even the best +scientific clocks are nowhere near 1-attosecond accuracy; however, an +inaccurate clock may still produce reasonably accurate time differences. + +.B taia_pack +converts a TAI64NA label from internal format in \fIt\fR to external +TAI64NA format in \fIbuf\fR. + +.B taia_sub +subtracts \fIb\fR from \fIa\fR and writes the result to \fIt\fR. +The inputs and output may overlap. + +.B taia_tai +places into \fIsec\fR the integer part of \fIt\fR. If \fIt\fR +contains a TAI64NA label then \fIsec\fR will contain the corresponding +TAI64 label. + +.B taia_uint +converts \fIsecs\fR into a struct taia (setting the fractional part to zero). + +.B taia_unpack +converts a TAI64NA label from external TAI64NA format in +\fIbuf\fR to internal format in \fIt\fR. +.SH "SEE ALSO" +time(1), +utime(3), +ctime(3), +locale(1) diff --git a/man/timeout.3 b/man/timeout.3 new file mode 100644 index 0000000..6113f27 --- /dev/null +++ b/man/timeout.3 @@ -0,0 +1,36 @@ +.TH qlibs: timeout 3 +.SH NAME +timeout \- reading from and writing to a file descriptor +.SH SYNTAX +.B #include \(dqtimeout.h\(dq + +int \fBtimeoutread\fP(int \fIt\fR,int \fIfd\fR,char *\fIbuf\fR,int \fIlen\fR); +.br +int \fBtimeoutwrite\fP(int,int,char *,int); +.SH DESCRIPTION +.B timeoutread +reads from file descriptor +.I fd +.I len +bytes into +.I buf +returning +.I 0 +on success and +.I -1 +in case a timeout has occured. +.B timeoutwrite +writes to file descriptor +.I fd +.I len +bytes from +.I buf +returning +.I 0 +on success and +.I -1 +in case a timeout has occured. +.SH "SEE ALSO" +wait(3), +timeoutconn(3) +taia(3). diff --git a/man/timeoutconn.3 b/man/timeoutconn.3 new file mode 100644 index 0000000..dad9e95 --- /dev/null +++ b/man/timeoutconn.3 @@ -0,0 +1,52 @@ +.TH qlibs: timeoutconn 3 +.SH NAME +timeoutconn \- set up an outoing stream socket for IPv4/IPv6 +.SH SYNTAX +.B #include \(dqtimeoutconn.h\(dq + +int \fBtimeoutconn4\fP(int \fIs\fR,char \fIip[4]\fR,uint16 \fIp\fR,unsigned int \fIt\fR); +.br +int \fBtimeoutconn6\fP(int \fIs\fR,char \fIip[16]\fR,uint16 \fIp\fR,unsigned int \fIt\fR,uint32 \fInetif\fR); +.br +int \fBtimeoutconn\fP(int \fIs\fR,char \fIip[16]\fR,uint16 \fIp\fR,unsigned int \fIt\fR,uint32 \fInetif\fR); +.SH DESCRITPION +.B timeoutconn +tries to establish a TCP stream socket to address +.I ip +on remoteport +.I p +and waiting for +.I t +seconds and on success returns the +.I s +socket identifier. + +In case +.I addresss +is an IPv6 LLU address, one needs to define the +.I netif +interface index, otherwise +.I 0 +is the default. +.SH INTERNALS +.B timeoutconn +depends on +.B taia +and +.B iopuase +to determine the timespan and perhaps +.B ndelay +to succeed. +.SH RETURN CODES +.B timeoutconn +returns +.I 0 +if the connection is established +and +.I -1 +if an error has occured. +.SH SEE ALSO +socket_if(3), +timeout(3) +taia(3), +wait(3) diff --git a/man/wait.3 b/man/wait.3 new file mode 100644 index 0000000..c3b6c63 --- /dev/null +++ b/man/wait.3 @@ -0,0 +1,96 @@ +.TH qlibs: wait 3 +.SH NAME +wait \- check child process status +.SH SYNTAX +.B #include \(dqwait.h\(dq + +int \fBwait_nohang\fP(&\fIwstat\fR); +.br +int \fBwait_stop\fP(&\fIwstat\fR); +.br +int \fBwait_stopnohang\fP(&\fIwstat\fR); +.br +int \fBwait_pid\fP(&\fIwstat\fR,\fIpid\fR); + +int \fBwait_exitcode\fP(\fIwstat\fR); +.br +int \fBwait_crashed\fP(\fIwstat\fR); +.br +int \fBwait_stopped\fP(\fIwstat\fR); +.br +int \fBwait_stopsig\fP(\fIwstat\fR); + +int \fIpid\fR; +.br +int \fIwstat\fR; +.SH DESCRIPTION +.B wait_nohang +looks for zombies (child processes that have exited). +If it sees a zombie, +it eliminates the zombie, +puts the zombie's exit status into +.IR wstat , +and returns the zombie's process ID. +If there are several zombies, +.B wait_nohang +picks one. +If there are children but no zombies, +.B wait_nohang +returns +.IR 0 . +If there are no children, +.B wait_nohang +returns +.IR -1 , +setting +.B errno +appropriately. + +.B wait_stopnohang +is similar to +.BR wait_nohang , +but it also looks for children that have stopped. + +.B wait_stop +is similar to +.BR wait_stopnohang , +but if there are children it will pause waiting for one of them +to stop or exit. + +.B wait_pid +waits for child process +.I pid +to exit. +It eliminates any zombie that shows up in the meantime, +discarding the exit status. + +.B wait_stop +and +.B wait_pid +retry upon +.BR error_intr . +.SH "STATUS PARSING" +If the child stopped, +.B wait_stopped +is nonzero; +.B wait_stopsig +is the signal that caused the child to stop. + +If the child exited by crashing, +.B wait_stopped +is zero; +.B wait_crashed +is nonzero. + +If the child exited normally, +.B wait_stopped +is zero; +.B wait_crashed +is zero; +and +.B wait_exitcode +is the child's exit code. +.SH "SEE ALSO" +pathexec(3), +wait(2), +error(3) diff --git a/man/x.html b/man/x.html new file mode 100644 index 0000000..224edc1 --- /dev/null +++ b/man/x.html @@ -0,0 +1,99 @@ +<HTML> +<BODY> +<PRE> +<!-- Manpage converted by man2html 3.0.1 --> +[1mSYNTAX[0m + [1m#include "logmsg.h"[0m + + int logmsg(const char *who, int ecode, unsigned int classs, char *msg) + + [1merr_sys[22m(w,e) logmsg(w,e,FATAL,"") + [1merr_sys_plus[22m(w,e,m) logmsg(w,e,FATAL,m) + [1merr_tmp[22m(w,e) logmsg(w,e,WARN,"") + [1merr_tmp_plus[22m(w,e,m) logmsg(w,e,WARN,m) + [1merr_int[22m(w,e,c) logmsg(w,e,c,"") + [1merr_int_plus[22m(w,e,c,m) logmsg(w,e,c,m) + [1mlog_who[22m(w,m) logmsg(w,0,LOG,m) + [1mlog_anon[22m(m) logmsg("",0,LOG,m) + +[1mDESCRIPTION[0m + [1mlogmsg [22mprints error, warning, or info/logging messages to stderr and + potentially terminates the calling program, depending on the [4mclass[0m + given. [4mwho[24m is the name of the program, [4mecode[24m is an error code, [4mclass[0m + determines the behavior upon call and [4mmsg[24m is the logging message. Read + "error.h" to learn more about related constants. + +[1mECODE[0m + [4mecode[24m is the error code and subject to be displayed in the log file and + potentially used upon exit if the [4mclass[24m equals [1mERROR[22m, [1mFATAL[22m, or [1mDROP[22m. + + To avoid conflicts with syscall error codes, appplication defined error + codes should be negative. The values [4m-15[24m, [4m-100[24m and [4m-111[24m are reserved + for backward compatibility. + +[1mCLASS[0m + The [4mclass[24m [4mparameter[24m [4mindicates[24m [4mhow[24m [4mthe[24m [4mapplication[24m [4mhandles[24m [4mexceptions[0m + [4mand[24m [4mdisplays[24m [4mthe[24m [4mlog[24m [4mmessage.[0m + + [4mo[24m [1mLOG[22m, [1mINFO[22m, [1mALERT[22m, [1mWARN [22m- display message and continue operation + + o [1mDROP [22m- display warning message and continue while returning to the + calling program + + o [1mUSAGE[22m, [1mSYNTAX[22m, [1mFATAL[22m, [1mERROR [22m- display error message and exit appli‐ + cation with error code + + [1mINFO[22m, [1mALERT[22m, [1mWARN[22m, [1mDROP[22m, [1mUSAGE[22m, and [1mFATAL [22mas well as [1mERROR [22mdisplay the + respective class string like [4mwarning:[24m in the log message, while [1mLOG[0m + shows the log message only. + + The class [1mFATAL [22mshould be used for system error codes only, rather [1mER‐[0m + [1mROR [22mand [1mWARN [22mshall be set in conjunction with an application er‐ + ror/warning. + +[1mMESSAGE[0m + If the custom message [4mmsg[24m is given, it will be printed additionally. + + Dan Bernstein used sets of [4mstrerr_dieY*()[24m and [4mstrerr_warnY()[24m messages + which explicitely determine the message and behavior class. Other + classes were occasionally defined on demand, such als [4musage()[24m. + + Kai Peter introduced the [4merrmsg[24m facility in his [1mqlibs [22mincluding a [1msys‐[0m + [1mlog [22mcompliant [4mseverity[24m as second parameter. + +[1mEXAMPLES[0m + The macro definitions uses [1mw [22mfor the calling program, [1me [22mfor error + code, [1mc [22mfor class, and [1mm [22mfor 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 [1ms [22mcan be build from multiple arguments by using + the [4mB[24m (build) macro: + + err_sys_plus((errno),B("unable to run: ",*argv)); + +[1mSEE ALSO[0m + <B>syslog(3)</B> + + + + 3 qlibs:(logmsg) +</PRE> +<HR> +<ADDRESS> +Man(1) output converted with +<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a> +</ADDRESS> +</BODY> +</HTML> diff --git a/ndelay.c b/ndelay.c new file mode 100644 index 0000000..f4b5eb8 --- /dev/null +++ b/ndelay.c @@ -0,0 +1,24 @@ +#include <sys/types.h> +#include <fcntl.h> +#include "ndelay.h" + +/** + @file ndelay.c + @author djb + @soure qmail + @brief delaying of IO operations +*/ + +#ifndef O_NONBLOCK +#define O_NONBLOCK O_NDELAY +#endif + +int ndelay_on(int fd) +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK); +} + +int ndelay_off(int fd) +{ + return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK); +} @@ -0,0 +1,25 @@ +#include <sys/types.h> +#include <fcntl.h> +#include "open.h" + +/** + @file open.c + @author djb + @source qmail + @brief open a file +*/ + +int open_append(const char *fn) +{ return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); } + +int open_excl(const char *fn) +{ return open(fn,O_WRONLY | O_EXCL | O_CREAT,0644); } + +int open_read(const char *fn) +{ return open(fn,O_RDONLY | O_NDELAY); } + +int open_trunc(const char *fn) +{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); } + +int open_write(const char *fn) +{ return open(fn,O_WRONLY | O_NDELAY); } diff --git a/pathexec.c b/pathexec.c new file mode 100644 index 0000000..2c1e7d1 --- /dev/null +++ b/pathexec.c @@ -0,0 +1,121 @@ +#include <unistd.h> +#include "alloc.h" +#include "error.h" +#include "stralloc.h" +#include "str.h" +#include "byte.h" +#include "env.h" +#include "pathexec.h" + +/** + @file pathexec.c + @author djb + @source ucspi-tcp, ucspi-ssl + @brief populate environment after fork +*/ + +static stralloc plus; +static stralloc tmp; + +int pathexec_env(const char *s,const char *t) +{ + if (!s) return 1; + if (!stralloc_copys(&tmp,s)) return 0; + if (t) { + if (!stralloc_cats(&tmp,"=")) return 0; + if (!stralloc_cats(&tmp,t)) return 0; + } + if (!stralloc_0(&tmp)) return 0; + return stralloc_cat(&plus,&tmp); +} + +int pathexec_multienv(stralloc *sa) +{ + if (!sa) return 1; + return stralloc_cat(&plus,sa); +} + +void pathexec(char * const *argv) +{ + char **e; + unsigned int elen; + unsigned int i; + unsigned int j; + unsigned int split; + unsigned int t; + + if (!stralloc_cats(&plus,"")) return; + + elen = 0; + for (i = 0; environ[i]; ++i) + ++elen; + for (i = 0; i < plus.len; ++i) + if (!plus.s[i]) + ++elen; + + e = (char **) alloc((elen + 1) * sizeof(char *)); + if (!e) return; + + elen = 0; + for (i = 0; environ[i]; ++i) + e[elen++] = environ[i]; + + j = 0; + for (i = 0; i < plus.len; ++i) + if (!plus.s[i]) { + split = str_chr(plus.s + j,'='); + for (t = 0; t < elen; ++t) + if (byte_equal(plus.s + j,split,e[t])) + if (e[t][split] == '=') { + --elen; + e[t] = e[elen]; + break; + } + if (plus.s[j + split]) + e[elen++] = plus.s + j; + j = i + 1; + } + e[elen] = 0; + + pathexec_run(*argv,argv,e); + alloc_free(e); +} + +void pathexec_run(const char *file,char *const *argv,char *const *envp) +{ + char *path; + unsigned int split; + int savederrno; + + if (file[str_chr(file,'/')]) { + execve(file,argv,envp); + return; + } + + path = env_get("PATH"); + if (!path) path = "/bin:/usr/bin"; + + savederrno = 0; + for (;;) { + split = str_chr(path,':'); + if (!stralloc_copyb(&tmp,path,split)) return; + if (!split) + if (!stralloc_cats(&tmp,".")) return; + if (!stralloc_cats(&tmp,"/")) return; + if (!stralloc_cats(&tmp,file)) return; + if (!stralloc_0(&tmp)) return; + + execve(tmp.s,argv,envp); + if (errno != ENOENT) { + savederrno = errno; + if ((errno != EACCES) && (errno != EPERM) && (errno != EISDIR)) return; + } + + if (!path[split]) { + if (savederrno) errno = savederrno; + return; + } + path += split; + path += 1; + } +} @@ -0,0 +1,31 @@ +#include <sys/types.h> +#include <unistd.h> +#include <grp.h> +//#include "hasshsgr.h" +#include "prot.h" + +/** + @file prot.c + @author djb + @source qmail + @brief setting up uid an gid for OS (short group was for ancient solaris) +*/ + +/* XXX: there are more portability problems here waiting to leap out at me */ + +int prot_gid(int gid) +{ +//#ifdef HASSHORTSETGROUPS +// short x[2]; +// x[0] = gid; x[1] = 73; /* catch errors */ +// if (setgroups(1,x) == -1) return -1; +//#else + if (setgroups(1,(gid_t *)&gid) == -1) return -1; +//#endif + return setgid(gid); /* _should_ be redundant, but on some systems it isn't */ +} + +int prot_uid(int uid) +{ + return setuid(uid); +} diff --git a/readclose.c b/readclose.c new file mode 100644 index 0000000..b0bce7e --- /dev/null +++ b/readclose.c @@ -0,0 +1,43 @@ +#include <unistd.h> +#include "open.h" +#include "error.h" +#include "readclose.h" + +/** + @file readclose.c + @author kp + @source qlibs + @brief This is the successor of the older 'slurpclose.c' file. The function + 'slurpclose' is now called 'readclose_append'. The other function + 'readclose' was introduced here initial. +*/ + +int readclose_append(int fd,stralloc *sa,unsigned int bufsize) +{ + int r; + for (;;) { + if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; } + r = read(fd,sa->s + sa->len,bufsize); + if (r == -1) if (errno == EINTR) continue; + if (r <= 0) { close(fd); return r; } + sa->len += r; + } +} + +int readclose(int fd,stralloc *sa,unsigned int bufsize) +{ + if (!stralloc_copys(sa,"")) { close(fd); return -1; } + return readclose_append(fd,sa,bufsize); +} + +int openreadclose(const char *fn,stralloc *sa,unsigned int bufsize) +{ + int fd; + fd = open_read((char *) fn); + if (fd == -1) { + if (errno == ENOENT) return 0; + return -1; + } + if (readclose(fd,sa,bufsize) == -1) return -1; + return 1; +} @@ -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); +} @@ -0,0 +1,30 @@ +#include <sys/types.h> +#include "seek.h" + +/** + @file seek.c + @author djb + @source qmail + @brief seek in an open file descritor +*/ + +off_t lseek(int fd,off_t offset,int whence); +int ftruncate(int fd, off_t length); + +#define CUR 1 /* sigh */ + +seek_pos seek_cur(int fd) +{ return lseek(fd,(off_t) 0,CUR); } + +#define END 2 /* sigh */ + +int seek_end(int fd) +{ if (lseek(fd,(off_t) 0,END) == -1) return -1; return 0; } + +#define SET 0 /* sigh */ + +int seek_set(int fd,seek_pos pos) +{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; } + +int seek_trunc(int fd,seek_pos pos) +{ return ftruncate(fd,(off_t) pos); } diff --git a/sharedlib b/sharedlib new file mode 100755 index 0000000..3bf4f26 --- /dev/null +++ b/sharedlib @@ -0,0 +1,2 @@ +#!/bin/sh +exec cc -shared ${1+"$@"} @@ -0,0 +1,110 @@ +#include <signal.h> +#include "sig.h" + +/** + @file sig.c + @author djb + @source qmail + @brief signal handling functions +*/ + +void sig_alarmblock() { sig_block(SIGALRM); } +void sig_alarmunblock() { sig_unblock(SIGALRM); } +void sig_alarmcatch(f) void (*f)(); { sig_catch(SIGALRM,f); } +void sig_alarmdefault() { sig_catch(SIGALRM,SIG_DFL); } +int sig_alarm = SIGALRM; + +void sig_block(int sig) +{ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss,sig); + sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0); +} + +void sig_unblock(int sig) +{ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss,sig); + sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0); +} + +void sig_blocknone() +{ + sigset_t ss; + sigemptyset(&ss); + sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0); +} + +void sig_catch(int sig,void (*f)()) +{ + struct sigaction sa; + sa.sa_handler = f; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(sig,&sa,(struct sigaction *) 0); +} + +void sig_pause() +{ + sigset_t ss; + sigemptyset(&ss); + sigsuspend(&ss); +} + +void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); } +void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); } +int sig_pipe = SIGPIPE; + +void sig_childblock() { sig_block(SIGCHLD); } +void sig_childunblock() { sig_unblock(SIGCHLD); } +void sig_childcatch(f) void (*f)(); { sig_catch(SIGCHLD,f); } +void sig_childdefault() { sig_catch(SIGCHLD,SIG_DFL); } +int sig_child = SIGCHLD; + +void sig_hangupblock() { sig_block(SIGHUP); } +void sig_hangupunblock() { sig_unblock(SIGHUP); } +void sig_hangupcatch(f) void (*f)(); { sig_catch(SIGHUP,f); } +void sig_hangupdefault() { sig_catch(SIGHUP,SIG_DFL); } +int sig_hangup = SIGHUP; + +void sig_termblock() { sig_block(SIGTERM); } +void sig_termunblock() { sig_unblock(SIGTERM); } +void sig_termcatch(f) void (*f)(); { sig_catch(SIGTERM,f); } +void sig_termdefault() { sig_catch(SIGTERM,SIG_DFL); } +int sig_term = SIGTERM; + +void sig_bugcatch(void (*f)()) +{ + sig_catch(SIGILL,f); + sig_catch(SIGABRT,f); + sig_catch(SIGFPE,f); + sig_catch(SIGBUS,f); + sig_catch(SIGSEGV,f); +#ifdef SIGSYS + sig_catch(SIGSYS,f); +#endif +#ifdef SIGEMT + sig_catch(SIGEMT,f); +#endif +} +void (*sig_defaulthandler)() = SIG_DFL; + +void sig_miscignore() +{ + sig_catch(SIGVTALRM,SIG_IGN); + sig_catch(SIGPROF,SIG_IGN); + sig_catch(SIGQUIT,SIG_IGN); + sig_catch(SIGINT,SIG_IGN); + sig_catch(SIGHUP,SIG_IGN); +#ifdef SIGXCPU + sig_catch(SIGXCPU,SIG_IGN); +#endif +#ifdef SIGXFSZ + sig_catch(SIGXFSZ,SIG_IGN); +#endif +} +void (*sig_ignorehandler)() = SIG_IGN; + +int sig_cont = SIGCONT; diff --git a/socket_bind.c b/socket_bind.c new file mode 100644 index 0000000..b942e20 --- /dev/null +++ b/socket_bind.c @@ -0,0 +1,79 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "byte.h" +#include "socket_if.h" +#include "ip.h" + +/** + @file socket_bind.c + @author djb, fefe, feh + @source qmail, djbdns, ucspi-tcp6 + @brief binding a socket to a local resource +*/ + +int socket_bind4(int s,const char ip[4],uint16 port) +{ + struct sockaddr_in sa; + + byte_zero(&sa,sizeof(sa)); + sa.sin_family = AF_INET; + uint16_pack_big((char *)&sa.sin_port,port); + byte_copy((char *)&sa.sin_addr,4,ip); + + return bind(s,(struct sockaddr *)&sa,sizeof(sa)); +} + +int socket_bind4_reuse(int s,const char ip[4],uint16 port) +{ + int opt = 1; + setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); + return socket_bind4(s,ip,port); +} + +/* seems not to be used here -- djbdns requires it */ +void socket_tryreservein(int s,int size) +{ + while (size >= 1024) { + if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size)) == 0) return; + size -= (size >> 5); + } +} + +int socket_bind6(int s,const char ip[16],uint16 port,uint32 scope_id) +{ + struct sockaddr_in6 sa; + + byte_zero(&sa,sizeof(sa)); + sa.sin6_family = AF_INET6; + uint16_pack_big((char *)&sa.sin6_port,port); +/* implicit: sa.sin6_flowinfo = 0; */ + byte_copy((char *)&sa.sin6_addr,16,ip); + sa.sin6_scope_id = scope_id; + + return bind(s,(struct sockaddr *)&sa,sizeof(sa)); +} + +int socket_bind6_reuse(int s,const char ip[16],uint16 port,uint32 scope_id) +{ + int opt = 1; + setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); + return socket_bind6(s,ip,port,scope_id); +} + +int socket_bind(int s,const char ip[16],uint16 port,uint32 scope_id) +{ + if (ip6_isv4mapped(ip)) + return socket_bind4(s,ip + 12,port); + + return socket_bind6(s,ip,port,scope_id); +} + +int socket_bind_reuse(int s,const char ip[16],uint16 port,uint32 scope_id) +{ + if (ip6_isv4mapped(ip)) + return socket_bind4_reuse(s,ip + 12,port); + + return socket_bind6_reuse(s,ip,port,scope_id); +} diff --git a/socket_connect.c b/socket_connect.c new file mode 100644 index 0000000..7b20659 --- /dev/null +++ b/socket_connect.c @@ -0,0 +1,66 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <errno.h> +#include "byte.h" +#include "socket_if.h" +#include "ip.h" + +/** + @file socket_connect.c + @author djb, fefe, feh, kp + @source qmail, ucscpi-tcp6 + @brief connection to remote IP host +*/ + +int socket_connect6(int s,const char ip[16],uint16 port,uint32 scope_id) +{ + struct sockaddr_in6 sa; + + byte_zero(&sa,sizeof(sa)); + sa.sin6_family = AF_INET6; + uint16_pack_big((char *)&sa.sin6_port,port); + sa.sin6_flowinfo = 0; + sa.sin6_scope_id = scope_id; + byte_copy((char *)&sa.sin6_addr,16,ip); + + return connect(s,(struct sockaddr *)&sa,sizeof(sa)); +} + +/* this explizit declaration is needed to prevent compiler warnings */ +int read(int _str, void *_buf, int _b); + +int socket_connect4(int s,const char ip[4],uint16 port) +{ + struct sockaddr_in sa; + + byte_zero(&sa,sizeof(sa)); + sa.sin_family = AF_INET; + uint16_pack_big((char *)&sa.sin_port,port); + byte_copy((char *)&sa.sin_addr,4,ip); + + return connect(s,(struct sockaddr *)&sa,sizeof(sa)); +} + +int socket_connect(int s,const char ip[16],uint16 port,uint32 scope_id) +{ + if (ip6_isv4mapped(ip)) + return socket_connect4(s,ip + 12,port); + + return socket_connect6(s,ip,port,scope_id); +} + +int socket_connected(int s) +{ + struct sockaddr_in6 sa; + int dummy; + char ch; + + dummy = sizeof(sa); + if (getpeername(s,(struct sockaddr *)&sa,(socklen_t *)&dummy) == -1) { + read(s,&ch,1); /* sets errno */ + return 0; + } + return 1; +} diff --git a/socket_if.c b/socket_if.c new file mode 100644 index 0000000..f55af4e --- /dev/null +++ b/socket_if.c @@ -0,0 +1,36 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <net/if.h> +#include "socket_if.h" + +/** + @file socket_if.c + @author fefe, feh + @source ucspi-tcp6 + @brief interface handling for LLU +*/ + +const unsigned char V4loopback[4] = {127,0,0,1}; +const unsigned char V4localnet[4] = {0,0,0,0}; +/* the 'V4mappedprefix' constant is needed by 'ip.a' too */ +const unsigned char V4mappedprefix[12] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff}; +const unsigned char V6localnet[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; +const unsigned char V6loopback[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1}; + +uint32 socket_getifidx(const char *ifname) +{ + return if_nametoindex(ifname); +} + +static char ifname[IFNAMSIZ]; + +const char* socket_getifname(uint32 scope_id) +{ + char *tmp = if_indextoname(scope_id,ifname); + if (tmp) + return tmp; + else + return "[unknown]"; +} diff --git a/socket_info.c b/socket_info.c new file mode 100644 index 0000000..e644798 --- /dev/null +++ b/socket_info.c @@ -0,0 +1,58 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "byte.h" +#include "socket_if.h" +#include "ip.h" + +/** + @file socket_info.c + @author djb, fefe, feh + @source ucspi-tcp6 + @brief querying local and remote info for socket +*/ + +int socket_local(int s,char ip[16],uint16 *port,uint32 *scope_id) +{ + struct sockaddr_in6 sa; + unsigned int dummy = sizeof(sa); + + if (getsockname(s,(struct sockaddr *)&sa,&dummy) == -1) return -1; + + if (sa.sin6_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in*)&sa; + byte_copy(ip,12,V4mappedprefix); + byte_copy(ip+12,4,(char *)&sa4->sin_addr); + uint16_unpack_big((char *)&sa4->sin_port,port); + if (scope_id) *scope_id = 0; + } else { + byte_copy(ip,16,(char *)&sa.sin6_addr); + uint16_unpack_big((char *)&sa.sin6_port,port); + if (scope_id) *scope_id = sa.sin6_scope_id; + } + + return 0; +} + +int socket_remote(int s,char ip[16],uint16 *port,uint32 *scope_id) +{ + struct sockaddr_in6 sa; + unsigned int dummy = sizeof(sa); + + if (getpeername(s,(struct sockaddr *)&sa,&dummy) == -1) return -1; + + if (sa.sin6_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in*)&sa; + byte_copy(ip,12,V4mappedprefix); + byte_copy(ip+12,4,(char *)&sa4->sin_addr); + uint16_unpack_big((char *)&sa4->sin_port,port); + *scope_id = 0; + } else { + byte_copy(ip,16,(char *)&sa.sin6_addr); + uint16_unpack_big((char *)&sa.sin6_port,port); + *scope_id = sa.sin6_scope_id; + } + + return 0; +} diff --git a/socket_recv.c b/socket_recv.c new file mode 100644 index 0000000..de8c856 --- /dev/null +++ b/socket_recv.c @@ -0,0 +1,39 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "byte.h" +#include "ip.h" +#include "socket_if.h" + +/** + @file socket_recv.c + @author djb, fefe + @source ucspi-tcp6 + @brief setup receiving socket +*/ + +int socket_recv(int s,char *buf,unsigned int len,char ip[16],uint16 *port,uint32 *scope_id) +{ + struct sockaddr_in6 sa; + unsigned int dummy = sizeof(sa); + int r; + + byte_zero(&sa,dummy); + r = recvfrom(s,buf,len,0,(struct sockaddr *)&sa,&dummy); + if (r == -1) return -1; + + if (sa.sin6_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in *)&sa; + byte_copy(ip,12,V4mappedprefix); + byte_copy(ip+12,4,(char *)&sa4->sin_addr); + uint16_unpack_big((char *)&sa4->sin_port,port); + if (scope_id) *scope_id = 0; + } else { + byte_copy(ip,16,(char *)&sa.sin6_addr); + uint16_unpack_big((char *)&sa.sin6_port,port); + if (scope_id) *scope_id = sa.sin6_scope_id; + } + + return r; +} diff --git a/socket_send.c b/socket_send.c new file mode 100644 index 0000000..9f09520 --- /dev/null +++ b/socket_send.c @@ -0,0 +1,62 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "byte.h" +#include "ip.h" +#include "socket_if.h" + +/** + @file socket_send.c + @author djb, fefe, feh + @source ucspi-tcp6 + @brief setup sending socket +*/ + +int socket_send4(int s,const char *buf,unsigned int len,const char ip[4],uint16 port) +{ + struct sockaddr_in sa; + + byte_zero(&sa,sizeof(sa)); + + sa.sin_family = AF_INET; + uint16_pack_big((char *)&sa.sin_port,port); + byte_copy((char *)&sa.sin_addr,4,ip); + + return sendto(s,buf,len,0,(struct sockaddr *)&sa,sizeof(sa)); +} + +int socket_send6(int s,const char *buf,unsigned int len,const char ip[16],uint16 port,uint32 scope_id) +{ + struct sockaddr_in6 sa; + + byte_zero(&sa,sizeof(sa)); + + sa.sin6_family = AF_INET6; + sa.sin6_scope_id = scope_id; + uint16_pack_big((char *)&sa.sin6_port,port); + byte_copy((char *)&sa.sin6_addr,16,ip); + + return sendto(s,buf,len,0,(struct sockaddr *)&sa,sizeof(sa)); +} + +int socket_send(int s,const char *buf,unsigned int len,const char ip[16],uint16 port,uint32 scope_id) +{ + if (ip6_isv4mapped(ip)) + return socket_send4(s,buf,len,ip + 12,port); + else + return socket_send6(s,buf,len,ip,port,scope_id); +} + +int socket_broadcast4(int s,const char *buf,unsigned int len,uint16 port) +{ + struct sockaddr_in sa; + + byte_zero(&sa,sizeof(sa)); + + sa.sin_family = AF_INET; + uint16_pack_big((char *)&sa.sin_port,port); + byte_copy((char *)&sa.sin_addr,4,V4broadcast); + + return sendto(s,buf,len,0,(struct sockaddr *)&sa,sizeof(sa)); +} diff --git a/socket_setup.c b/socket_setup.c new file mode 100644 index 0000000..fb65fa2 --- /dev/null +++ b/socket_setup.c @@ -0,0 +1,99 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "byte.h" +#include "socket_if.h" +#include "ip.h" + +/** + @file socket_setup.c + @author djb, feh + @source ucspi-tcp6 + @brief setup listening socket +*/ + +int socket_accept(int s,char ip[16],uint16 *port,uint32 *scope_id) +{ + struct sockaddr_in6 sa; + unsigned int dummy = sizeof(sa); + int fd; + + fd = accept(s,(struct sockaddr *)&sa,&dummy); + if (fd == -1) return -1; + + if (sa.sin6_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in*)&sa; + byte_copy(ip,12,V4mappedprefix); + byte_copy(ip+12,4,(char *)&sa4->sin_addr); + uint16_unpack_big((char *)&sa4->sin_port,port); + if (scope_id) *scope_id = 0; + } else { + byte_copy(ip,16,(char *)&sa.sin6_addr); + uint16_unpack_big((char *)&sa.sin6_port,port); + if (scope_id) *scope_id = sa.sin6_scope_id; + } + + return fd; +} + +int socket_accept4(int s,char ip[4],uint16 *port) +{ + struct sockaddr_in sa; + unsigned int dummy = sizeof(sa); + int fd; + + fd = accept(s,(struct sockaddr *) &sa,&dummy); + if (fd == -1) return -1; + + byte_copy(ip,4,(char *) &sa.sin_addr); + uint16_unpack_big((char *) &sa.sin_port,port); + + return fd; +} + +int socket_listen(int s,int backlog) +{ + return listen(s,backlog); +} + +int socket_ipoptionskill(int s) +{ + int r; + + r = setsockopt(s,IPPROTO_IP,1,(char *) 0,0); /* 1 == IP_OPTIONS */ + r = setsockopt(s,IPPROTO_IPV6,1,(char *) 0,0); + + return r; +} + +int socket_ip6anycast(int s) +{ + int opt = 1; + int r; + +#ifdef GEN_IP_PKTINFO /* Linux */ + r = setsockopt(s,IPPROTO_IP,GEN_IP_PKTINFO,&opt,sizeof(opt)); +#elif IP_PKTINFO /* Solaris */ + r = setsockopt(s,IPPROTO_IP,IP_PKTINFO,&opt,sizeof(opt)); +#elif IP_RECVDSTADDR /* BSD */ + r = setsockopt(s,IPPROTO_IP,IP_RECVDSTADDR,&opt,sizeof(opt)); +#elif IPV6_RECVDSTADDR + r = setsockopt(s,IPPROTO_IPV6,IP_RECVDSTADDR,&opt,sizeof(opt)); +#endif + return r; +} + +int socket_dualstack(int s) +{ + int opt = 0; + + return setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&opt,sizeof(opt)); +} + +int socket_nodualstack(int s) +{ + int opt = 1; + + return setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,&opt,sizeof(opt)); +} diff --git a/socket_tcp.c b/socket_tcp.c new file mode 100644 index 0000000..1ff050e --- /dev/null +++ b/socket_tcp.c @@ -0,0 +1,63 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <errno.h> +#include "close.h" /* better use unistd.h ? */ +#include "ndelay.h" +#include "socket_if.h" +#include "error.h" + +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT EINVAL +#endif + +/** + @file socket_tcp.c + @author djb, fefe, feh + @source ucspi-tcp6 + @brief setup TCP stream socket +*/ + +int socket_tcp4(void) +{ + int s; + + s = socket(AF_INET,SOCK_STREAM,0); + if (s != -1) + if (ndelay_on(s) == -1) { close(s); return -1; } + + return s; +} + +int socket_tcp6(void) +{ + int s; + + s = socket(AF_INET6,SOCK_STREAM,0); + if (s != -1) + if (ndelay_on(s) == -1) { close(s); return -1; } + + return s; +} + +int socket_tcp(void) +{ + int s; + + s = socket(AF_INET6,SOCK_STREAM,0); + if (s == -1) + if (errno == EINVAL || errno == EAFNOSUPPORT || errno == EPROTO || errno == EPROTONOSUPPORT) + s = socket(AF_INET,SOCK_STREAM,0); + + if (s != -1) + if (ndelay_on(s) == -1) { close(s); return -1; } + + return s; +} + +int socket_tcpnodelay(int s) +{ + int opt = 1; + return setsockopt(s,IPPROTO_TCP,1,&opt,sizeof(opt)); /* 1 == TCP_NODELAY */ +} diff --git a/socket_udp.c b/socket_udp.c new file mode 100644 index 0000000..743cdf1 --- /dev/null +++ b/socket_udp.c @@ -0,0 +1,57 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <errno.h> +#include "close.h" /* better use unistd.h ? */ +#include "ndelay.h" +#include "socket_if.h" +#include "error.h" + +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT EINVAL +#endif + +/** + @file socket_udp.c + @author djb, fefe, feh + @source ucspi-tcp6 + @brief setup a UDP message socket +*/ + +int socket_udp6(void) +{ + int s; + + s = socket(AF_INET6,SOCK_DGRAM,0); + if (s != -1) + if (ndelay_on(s) == -1) { close(s); return -1; } + + return s; +} + +int socket_udp4(void) +{ + int s; + + s = socket(AF_INET,SOCK_DGRAM,0); + if (s != -1) + if (ndelay_on(s) == -1) { close(s); return -1; } + + return s; +} + +int socket_udp(void) +{ + int s; + + s = socket(AF_INET6,SOCK_DGRAM,0); + if (s == -1) + if (errno == EINVAL || errno == EAFNOSUPPORT || errno == EPROTO || errno == EPROTONOSUPPORT) + s = socket(AF_INET,SOCK_DGRAM,0); + + if (s != -1) + if (ndelay_on(s) == -1) { close(s); return -1; } + + return s; +} @@ -0,0 +1,135 @@ +#include "str.h" +#include "stralloc.h" + +/** + @file str.c + @author djb + @source qmail + @brief string handling functions +*/ + +unsigned int str_copy(register char *s,register const char *t) +{ + register int len; + + len = 0; + for (;;) { + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + } +} + +unsigned int str_copyb(register char *s,register const char *t,unsigned int max) +{ + register int len; + + len = 0; + + while (max-- > 0) { + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + if (!(*s = *t)) { return len; } ++s; ++t; ++len; + } + + return len; +} + +int str_diff(register const char *s,register const char *t) +{ + register char x; + + for (;;) { + x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + } + return ((int)(unsigned int)(unsigned char) x) + - ((int)(unsigned int)(unsigned char) *t); +} + +int str_diffn(register const char *s,register const char *t,unsigned int len) +{ + register char x; + + for (;;) { + if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + if (!len--) { return 0; } x = *s; if (x != *t) { break; } if (!x) { break; } ++s; ++t; + } + return ((int)(unsigned int)(unsigned char) x) + - ((int)(unsigned int)(unsigned char) *t); +} + +unsigned int str_len(register const char *s) +{ + register const char *t; + + t = s; + for (;;) { + if (!*t) { return t - s; } ++t; + if (!*t) { return t - s; } ++t; + if (!*t) { return t - s; } ++t; + if (!*t) { return t - s; } ++t; + } +} + +unsigned int str_chr(register const char *s,int c) +{ + register char ch; + register const char *t; + + ch = c; + t = s; + for (;;) { + if (!*t) { break; } if (*t == ch) { break; } ++t; + if (!*t) { break; } if (*t == ch) { break; } ++t; + if (!*t) { break; } if (*t == ch) { break; } ++t; + if (!*t) { break; } if (*t == ch) { break; } ++t; + } + return t - s; +} + +unsigned int str_rchr(register const char *s,int c) +{ + register char ch; + register const char *t; + register const char *u; + + ch = c; + t = s; + u = 0; + for (;;) { + if (!*t) { break; } if (*t == ch) { u = t; } ++t; + if (!*t) { break; } if (*t == ch) { u = t; } ++t; + if (!*t) { break; } if (*t == ch) { u = t; } ++t; + if (!*t) { break; } if (*t == ch) { u = t; } ++t; + } + if (!u) u = t; + return u - s; +} + +int str_start(register const char *s,register const char *t) +{ + register char x; + + for (;;) { + x = *t++; if (!x) return 1; if (x != *s++) return 0; + x = *t++; if (!x) return 1; if (x != *s++) return 0; + x = *t++; if (!x) return 1; if (x != *s++) return 0; + x = *t++; if (!x) return 1; if (x != *s++) return 0; + } +} + +char *str_append(char *dest, char const *s) +{ + static stralloc sa = {0}; + + stralloc_copys(&sa,dest); + stralloc_catb(&sa,s,sizeof(s)); + return sa.s; +} diff --git a/stralloc.c b/stralloc.c new file mode 100644 index 0000000..8c0335e --- /dev/null +++ b/stralloc.c @@ -0,0 +1,126 @@ +#include <stdlib.h> +#include "byte.h" +#include "str.h" +#include "stralloc.h" +#include "alloc.h" + +/** + @file stralloc.c + @author djb + @source qmail, ucspi-tcp + @brief genious dynamic string handling +*/ + +int stralloc_starts(stralloc *sa,const char *s) +{ + int len; + len = str_len(s); + return (sa->len >= len) && byte_equal(s,len,sa->s); +} + +int stralloc_cat(stralloc *sato,stralloc *safrom) +{ + return stralloc_catb(sato,safrom->s,safrom->len); +} + +int stralloc_catb(stralloc *sa,const char *s,unsigned int n) +{ + if (!sa->s) return stralloc_copyb(sa,s,n); + if (!stralloc_readyplus(sa,n + 1)) return 0; + byte_copy(sa->s + sa->len,n,s); + sa->len += n; + sa->s[sa->len] = 'Z'; /* ``offensive programming'' */ + return 1; +} + +int stralloc_cats(stralloc *sa,const char *s) +{ + return stralloc_catb(sa,s,str_len(s)); +} + +int stralloc_copy(stralloc *sato,stralloc *safrom) +{ + return stralloc_copyb(sato,safrom->s,safrom->len); +} + + +int stralloc_ready(stralloc *sa,size_t len) +{ + register size_t wanted = len+(len>>3)+30; /* heuristic from djb */ + if (wanted<len) wanted = len; + if (!sa->s || sa->a<len) { + register char* tmp; + if (!(tmp = realloc(sa->s,wanted))) // !!! needs stdlib (realloc) + return 0; + sa->a = wanted; + sa->s = tmp; + } + return 1; +} + +int stralloc_readyplus(stralloc *sa,size_t len) +{ + if (sa->s) { + if (sa->len + len < len) return 0; /* catch integer overflow */ + return stralloc_ready(sa,sa->len+len); + } else + return stralloc_ready(sa,len); +} + +int stralloc_copyb(stralloc *sa,const char *s,unsigned int n) +{ + if (!stralloc_ready(sa,n + 1)) return 0; + byte_copy(sa->s,n,s); + sa->len = n; + sa->s[n] = 'Z'; /* ``offensive programming'' */ + return 1; +} + +int stralloc_copys(stralloc *sa,const char *s) +{ + return stralloc_copyb(sa,s,str_len(s)); +} + +int stralloc_catulong0(stralloc *sa,unsigned long u,unsigned int n) +{ + unsigned int len; + unsigned long q; + char *s; + + len = 1; + q = u; + while (q > 9) { ++len; q /= 10; } + if (len < n) len = n; + + if (!stralloc_readyplus(sa,len)) return 0; + s = sa->s + sa->len; + sa->len += len; + while (len) { s[--len] = '0' + (u % 10); u /= 10; } + + return 1; +} + +int stralloc_catlong0(stralloc *sa,long l,unsigned int n) +{ + if (l < 0) { + if (!stralloc_append(sa,"-")) return 0; + l = -l; + } + return stralloc_catulong0(sa,l,n); +} + +int stralloc_append(stralloc *sa,const char *in) +{ + if (stralloc_readyplus(sa,1)) { + sa->s[sa->len] = *in; + ++sa->len; + return 1; + } + return 0; +} + +void stralloc_free(stralloc *sa) { + if (sa->s) free(sa->s); + sa->s = 0; + sa->a = sa->len = 0; +} @@ -0,0 +1,59 @@ +#include <time.h> +#include "tai.h" + +/** + @file tai.c + @author djb + @source qmail + @brief 'temps atomic' time handling +*/ + +void tai_add(struct tai *t,const struct tai *u,const struct tai *v) +{ + t->x = u->x + v->x; +} + +void tai_now(struct tai *t) +{ + tai_unix(t,time((time_t *) 0)); +} + +void tai_pack(char *s,const struct tai *t) +{ + uint64 x; + + x = t->x; + s[7] = (char)x; x >>= 8; + s[6] = (char)x; x >>= 8; + s[5] = (char)x; x >>= 8; + s[4] = (char)x; x >>= 8; + s[3] = (char)x; x >>= 8; + s[2] = (char)x; x >>= 8; + s[1] = (char)x; x >>= 8; + s[0] = (char)x; +} + +void tai_sub(struct tai *t,const struct tai *u,const struct tai *v) +{ + t->x = u->x - v->x; +} + +void tai_uint(struct tai *t,unsigned int u) +{ + t->x = u; +} + +void tai_unpack(const char *s,struct tai *t) +{ + uint64 x; + + x = (unsigned char) s[0]; + x <<= 8; x += (unsigned char) s[1]; + x <<= 8; x += (unsigned char) s[2]; + x <<= 8; x += (unsigned char) s[3]; + x <<= 8; x += (unsigned char) s[4]; + x <<= 8; x += (unsigned char) s[5]; + x <<= 8; x += (unsigned char) s[6]; + x <<= 8; x += (unsigned char) s[7]; + t->x = x; +} @@ -0,0 +1,104 @@ +#include <sys/types.h> +#include <sys/time.h> +#include "taia.h" + +/** + @file taia.c + @author djb + @source qmail + @brief 'tai' attosecond time handling +*/ + +/* XXX: breaks tai encapsulation */ + +void taia_add(struct taia *t,struct taia *u,struct taia *v) +{ + t->sec.x = u->sec.x + v->sec.x; + t->nano = u->nano + v->nano; + t->atto = u->atto + v->atto; + if (t->atto > 999999999UL) { + t->atto -= 1000000000UL; + ++t->nano; + } + if (t->nano > 999999999UL) { + t->nano -= 1000000000UL; + ++t->sec.x; + } +} + +double taia_approx(struct taia *t) +{ + return tai_approx(&t->sec) + taia_frac(t); +} + +double taia_frac(struct taia *t) +{ + return (t->atto * 0.000000001 + t->nano) * 0.000000001; +} + +int taia_less(struct taia *t,struct taia *u) +{ + if (t->sec.x < u->sec.x) return 1; + if (t->sec.x > u->sec.x) return 0; + if (t->nano < u->nano) return 1; + if (t->nano > u->nano) return 0; + return t->atto < u->atto; +} + +int taia_now(struct taia *t) +{ + struct timeval now; + if (gettimeofday(&now,(struct timezone *) 0) == 0) { + tai_unix(&t->sec,now.tv_sec); + t->nano = 1000 * now.tv_usec + 500; + t->atto = 0; + return 0; + } + t->nano = 0; + t->atto = 0; + return -1; +} + +void taia_pack(char *s,struct taia *t) +{ + unsigned long x; + + tai_pack(s,&t->sec); + s += 8; + + x = t->atto; + s[7] = x & 255; x >>= 8; + s[6] = x & 255; x >>= 8; + s[5] = x & 255; x >>= 8; + s[4] = x; + x = t->nano; + s[3] = x & 255; x >>= 8; + s[2] = x & 255; x >>= 8; + s[1] = x & 255; x >>= 8; + s[0] = x; +} + +void taia_sub(struct taia *t,struct taia *u,struct taia *v) +{ + unsigned long unano = u->nano; + unsigned long uatto = u->atto; + + t->sec.x = u->sec.x - v->sec.x; + t->nano = unano - v->nano; + t->atto = uatto - v->atto; + if (t->atto > uatto) { + t->atto += 1000000000UL; + --t->nano; + } + if (t->nano > unano) { + t->nano += 1000000000UL; + --t->sec.x; + } +} + +void taia_uint(struct taia *t,unsigned int s) +{ + t->sec.x = s; + t->nano = 0; + t->atto = 0; +} diff --git a/timeout.c b/timeout.c new file mode 100644 index 0000000..e721b66 --- /dev/null +++ b/timeout.c @@ -0,0 +1,59 @@ +#include <unistd.h> +#include "error.h" +#include "iopause.h" +#include "timeout.h" + +/** + @file timeout.c + @author djb + @source qmail + @brief read/write timeout handling +*/ + +int timeoutread(int t,int fd,char *buf,int len) +{ + struct taia now; + struct taia deadline; + iopause_fd x; + + taia_now(&now); + taia_uint(&deadline,t); + taia_add(&deadline,&now,&deadline); + + x.fd = fd; + x.events = IOPAUSE_READ; + for (;;) { + taia_now(&now); + iopause(&x,1,&deadline,&now); + if (x.revents) break; + if (taia_less(&deadline,&now)) { + errno = ETIMEDOUT; + return -1; + } + } + return read(fd,buf,len); +} + +int timeoutwrite(int t,int fd,char *buf,int len) +{ + struct taia now; + struct taia deadline; + iopause_fd x; + + taia_now(&now); + taia_uint(&deadline,t); + taia_add(&deadline,&now,&deadline); + + x.fd = fd; + x.events = IOPAUSE_WRITE; + for (;;) { + taia_now(&now); + iopause(&x,1,&deadline,&now); + if (x.revents) break; + if (taia_less(&deadline,&now)) { + errno = ETIMEDOUT; + return -1; + } + } + return write(fd,buf,len); +} diff --git a/timeoutconn.c b/timeoutconn.c new file mode 100644 index 0000000..c94f600 --- /dev/null +++ b/timeoutconn.c @@ -0,0 +1,112 @@ +#include "ndelay.h" +#include "socket_if.h" +#include "iopause.h" +#include "error.h" +#include "timeoutconn.h" +#include "ip.h" + +/** + @file timeoutconn.c + @author djb, fefe, feh + @source qmail + @brief socket read/write timeout handling; return code of iopause considered +*/ + +int timeoutconn4(int s,char ip[4],uint16 port,unsigned int timeout) +{ + struct taia now; + struct taia deadline; + iopause_fd x; + unsigned int p = 0; + + if (socket_connect4(s,ip,port) == -1) { + if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) return -1; + x.fd = s; + x.events = IOPAUSE_WRITE; + taia_now(&now); + taia_uint(&deadline,timeout); + taia_add(&deadline,&now,&deadline); + for (;;) { + taia_now(&now); + iopause(&x,1,&deadline,&now); + if (x.revents) break; /* 's' available */ + if (taia_less(&deadline,&now)) { + errno = ETIMEDOUT; /* note that connect attempt is continuing */ + return -1; + } + p++; + } + if (!socket_connected(s)) return -1; + } + + if (ndelay_off(s) == -1) return -1; + return 0; +} + +int timeoutconn6(int s,char ip[16],uint16 port,unsigned int timeout,uint32 netif) +{ + struct taia now; + struct taia deadline; + iopause_fd x; + unsigned int p = 0; + + if (socket_connect6(s,ip,port,netif) == -1) { + if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) return -1; + x.fd = s; + x.events = IOPAUSE_WRITE; + taia_now(&now); + taia_uint(&deadline,timeout); + taia_add(&deadline,&now,&deadline); + for (;;) { + taia_now(&now); + iopause(&x,1,&deadline,&now); + if (x.revents) break; /* 's' available */ + if (taia_less(&deadline,&now)) { + errno = ETIMEDOUT; /* note that connect attempt is continuing */ + return -1; + } + p++; + } + if (!socket_connected(s)) return -1; + } + + if (ndelay_off(s) == -1) return -1; + return 0; +} + +int timeoutconn(int s,char ip[16],uint16 port,unsigned int timeout,uint32 netif) +{ + struct taia now; + struct taia deadline; + iopause_fd x; + unsigned int p = 0; + int r; + + if (ip6_isv4mapped(ip)) + r = socket_connect4(s,ip + 12,port); + else + r = socket_connect6(s,ip,port,netif); + + if (r == -1) { + if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) return -1; + x.fd = s; + x.events = IOPAUSE_WRITE; + taia_now(&now); + taia_uint(&deadline,timeout); + taia_add(&deadline,&now,&deadline); + for (;;) { + taia_now(&now); + iopause(&x,1,&deadline,&now); + if (x.revents) break; /* 's' available */ + if (taia_less(&deadline,&now)) { + errno = ETIMEDOUT; /* note that connect attempt is continuing */ + return -1; + } + p++; + } + if (!socket_connected(s)) return -1; + } + + if (ndelay_off(s) == -1) return -1; + return 0; +} diff --git a/uint128p.c b/uint128p.c new file mode 100644 index 0000000..57c713e --- /dev/null +++ b/uint128p.c @@ -0,0 +1,100 @@ +#include "uint_t.h" + +/** + @file uint128p.c + @author feh, jannis + @source djbdns6 + @brief packing/unpacking 128 bit integer to/from char string +*/ + +void uint128_pack(char s[16],uint128 u) +{ + s[0] = u.lo & 255; u.lo >>= 8; + s[1] = u.lo & 255; u.lo >>= 8; + s[2] = u.lo & 255; u.lo >>= 8; + s[3] = u.lo & 255; u.lo >>= 8; + s[4] = u.lo & 255; u.lo >>= 8; + s[5] = u.lo & 255; u.lo >>= 8; + s[6] = u.lo & 255; u.lo >>= 8; + s[7] = u.lo & 255; u.lo >>= 8; + + s[8] = u.hi & 255; u.hi >>= 8; + s[9] = u.hi & 255; u.hi >>= 8; + s[10] = u.hi & 255; u.hi >>= 8; + s[11] = u.hi & 255; u.hi >>= 8; + s[12] = u.hi & 255; u.hi >>= 8; + s[13] = u.hi & 255; u.hi >>= 8; + s[14] = u.hi & 255; u.hi >>= 8; + s[15] = u.hi & 255; +} +void uint128_pack_big(char s[16],uint128 u) +{ + s[15] = u.lo & 255; u.lo >>= 8; + s[14] = u.lo & 255; u.lo >>= 8; + s[13] = u.lo & 255; u.lo >>= 8; + s[12] = u.lo & 255; u.lo >>= 8; + s[11] = u.lo & 255; u.lo >>= 8; + s[10] = u.lo & 255; u.lo >>= 8; + s[9] = u.lo & 255; u.lo >>= 8; + s[8] = u.lo & 255; u.lo >>= 8; + + s[7] = u.hi & 255; u.hi >>= 8; + s[6] = u.hi & 255; u.hi >>= 8; + s[5] = u.hi & 255; u.hi >>= 8; + s[4] = u.hi & 255; u.hi >>= 8; + s[3] = u.hi & 255; u.hi >>= 8; + s[2] = u.hi & 255; u.hi >>= 8; + s[1] = u.hi & 255; u.hi >>= 8; + s[0] = u.hi & 255; +} + +void uint128_unpack(char s[16],uint128 *u) +{ + uint128 result; + result.hi = result.lo = 0ULL; + + result.hi = (unsigned char) s[15]; result.hi <<= 8; + result.hi += (unsigned char) s[14]; result.hi <<= 8; + result.hi += (unsigned char) s[13]; result.hi <<= 8; + result.hi += (unsigned char) s[12]; result.hi <<= 8; + result.hi += (unsigned char) s[11]; result.hi <<= 8; + result.hi += (unsigned char) s[10]; result.hi <<= 8; + result.hi += (unsigned char) s[9]; result.hi <<= 8; + result.hi += (unsigned char) s[8]; //correct + + result.lo += (unsigned char) s[7]; result.lo <<= 8; + result.lo += (unsigned char) s[6]; result.lo <<= 8; + result.lo += (unsigned char) s[5]; result.lo <<= 8; + result.lo += (unsigned char) s[4]; result.lo <<= 8; + result.lo += (unsigned char) s[3]; result.lo <<= 8; + result.lo += (unsigned char) s[2]; result.lo <<= 8; + result.lo += (unsigned char) s[1]; result.lo <<= 8; + result.lo += (unsigned char) s[0]; + + *u = result; +} +void uint128_unpack_big(char s[16],uint128 *u) +{ + uint128 result; + result.hi = result.lo = 0ULL; + + result.hi = (unsigned char) s[0]; result.hi <<= 8; + result.hi += (unsigned char) s[1]; result.hi <<= 8; + result.hi += (unsigned char) s[2]; result.hi <<= 8; + result.hi += (unsigned char) s[3]; result.hi <<= 8; + result.hi += (unsigned char) s[4]; result.hi <<= 8; + result.hi += (unsigned char) s[5]; result.hi <<= 8; + result.hi += (unsigned char) s[6]; result.hi <<= 8; + result.hi += (unsigned char) s[7]; + + result.lo += (unsigned char) s[8]; result.lo <<= 8; + result.lo += (unsigned char) s[9]; result.lo <<= 8; + result.lo += (unsigned char) s[10]; result.lo <<= 8; + result.lo += (unsigned char) s[11]; result.lo <<= 8; + result.lo += (unsigned char) s[12]; result.lo <<= 8; + result.lo += (unsigned char) s[13]; result.lo <<= 8; + result.lo += (unsigned char) s[14]; result.lo <<= 8; + result.lo += (unsigned char) s[15]; + + *u = result; +} diff --git a/uint16p.c b/uint16p.c new file mode 100644 index 0000000..5dddf21 --- /dev/null +++ b/uint16p.c @@ -0,0 +1,40 @@ +#include "uint_t.h" + +/** + @file uint16p.c + @author djb + @source qmail + @brief packing/unpacking 16 bit integer to/from char string +*/ + +void uint16_pack(char s[2],uint16 u) +{ + s[0] = u & 255; + s[1] = u >> 8; +} + +void uint16_pack_big(char s[2],uint16 u) +{ + s[1] = u & 255; + s[0] = u >> 8; +} + +void uint16_unpack(char s[2],uint16 *u) +{ + uint16 result; + + result = (unsigned char) s[1]; result <<= 8; + result += (unsigned char) s[0]; + + *u = result; +} + +void uint16_unpack_big(char s[2],uint16 *u) +{ + uint16 result; + + result = (unsigned char) s[0]; result <<= 8; + result += (unsigned char) s[1]; + + *u = result; +} diff --git a/uint32p.c b/uint32p.c new file mode 100644 index 0000000..f3f04ea --- /dev/null +++ b/uint32p.c @@ -0,0 +1,47 @@ +#include "uint_t.h" + +/** + @file uint32p.c + @author djb + @source qmail + @brief packing/unpacking 32 bit integer to/from char string +*/ + +void uint32_pack(char s[4],uint32 u) +{ + s[0] = u & 255; u >>= 8; + s[1] = u & 255; u >>= 8; + s[2] = u & 255; + s[3] = u >> 8; +} +void uint32_pack_big(char s[4],uint32 u) +{ + s[3] = u & 255; u >>= 8; + s[2] = u & 255; u >>= 8; + s[1] = u & 255; + s[0] = u >> 8; +} + +void uint32_unpack(char s[4],uint32 *u) +{ + uint32 result; + + result = (unsigned char) s[3]; result <<= 8; + result += (unsigned char) s[2]; result <<= 8; + result += (unsigned char) s[1]; result <<= 8; + result += (unsigned char) s[0]; + + *u = result; +} + +void uint32_unpack_big(char s[4],uint32 *u) +{ + uint32 result; + + result = (unsigned char) s[0]; result <<= 8; + result += (unsigned char) s[1]; result <<= 8; + result += (unsigned char) s[2]; result <<= 8; + result += (unsigned char) s[3]; + + *u = result; +} diff --git a/uint64p.c b/uint64p.c new file mode 100644 index 0000000..41b8ceb --- /dev/null +++ b/uint64p.c @@ -0,0 +1,62 @@ +#include "uint_t.h" + +/** + @file uint64p.c + @author feh, jannis + @source djbdns6 + @brief packing/unpacking 64 bit integer to/from char string +*/ + +void uint64_pack(char s[8],uint64 u) +{ + s[0] = u & 255; u >>= 8; + s[1] = u & 255; u >>= 8; + s[2] = u & 255; u >>= 8; + s[3] = u & 255; u >>= 8; + s[4] = u & 255; u >>= 8; + s[5] = u & 255; u >>= 8; + s[6] = u & 255; u >>= 8; + s[7] = u & 255; +} +void uint64_pack_big(char s[8],uint64 u) +{ + s[7] = u & 255; u >>= 8; + s[6] = u & 255; u >>= 8; + s[5] = u & 255; u >>= 8; + s[4] = u & 255; u >>= 8; + s[3] = u & 255; u >>= 8; + s[2] = u & 255; u >>= 8; + s[1] = u & 255; u >>= 8; + s[0] = u & 255; +} + +void uint64_unpack(char s[8],uint64 *u) +{ + uint64 result; + + result = (unsigned char) s[7]; result <<= 8; + result += (unsigned char) s[6]; result <<= 8; + result += (unsigned char) s[5]; result <<= 8; + result += (unsigned char) s[4]; result <<= 8; + result += (unsigned char) s[3]; result <<= 8; + result += (unsigned char) s[2]; result <<= 8; + result += (unsigned char) s[1]; result <<= 8; + result += (unsigned char) s[0]; + + *u = result; +} +void uint64_unpack_big(char s[8],uint64 *u) +{ + uint64 result; + + result = (unsigned char) s[0]; result <<= 8; + result += (unsigned char) s[1]; result <<= 8; + result += (unsigned char) s[2]; result <<= 8; + result += (unsigned char) s[3]; result <<= 8; + result += (unsigned char) s[4]; result <<= 8; + result += (unsigned char) s[5]; result <<= 8; + result += (unsigned char) s[6]; result <<= 8; + result += (unsigned char) s[7]; + + *u = result; +} diff --git a/uint8p.c b/uint8p.c new file mode 100644 index 0000000..a7a1460 --- /dev/null +++ b/uint8p.c @@ -0,0 +1,39 @@ +#include "uint_t.h" + +/** + @file uint8x.c + @author feh + @brief packing/unpacking 8 bit int to/from char string +*/ + +void uint8_pack(char s[2],uint8 u) +{ + s[0] = u & 255; + s[1] = u >> 4; +} + +void uint8_pack_big(char s[2],uint8 u) +{ + s[1] = u & 255; + s[0] = u >> 4; +} + +void uint8_unpack(char s[2],uint8 *u) +{ + uint8 result; + + result = (unsigned char) s[1]; result <<= 4; + result += (unsigned char) s[0]; + + *u = result; +} + +void uint8_unpack_big(char s[2],uint8 *u) +{ + uint8 result; + + result = (unsigned char) s[0]; result <<= 4; + result += (unsigned char) s[1]; + + *u = result; +} @@ -0,0 +1,25 @@ +#include <sys/types.h> +#include <sys/wait.h> +#include "logmsg.h" + +/** + @file wait.c + @author djb + @source qmail + @brief wait for forked processes +*/ + +int wait_nohang(int *wstat) +{ + return waitpid(-1,wstat,WNOHANG); +} + +int wait_pid(int *wstat,int pid) +{ + int r; + + do + r = waitpid(pid,wstat,0); + while ((r == -1) && (errno == EINTR)); + return r; +} |