nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 6ce6f52bba210de65c6ad058b61a5b1c4129af75
parent 1683c85b3f0cf50d4a0b719e780a7d009099a252
Author: Ellenor Bjornsdottir <ellenor@umbrellix.net>
Date:   Mon, 17 Oct 2022 07:37:05 +0000

it's gone. It's gone. It'll be published separately soon.

Diffstat:
M.gitignore | 1+
MTARGETS | 1+
Mconf-cc | 2+-
Mconf-groups | 4++--
Mconf-ld | 2+-
Mconf-users | 14+++++++-------
Dsrc/mxf-remote/NOTES-mxfr.md | 32--------------------------------
Dsrc/mxf-remote/all.do | 2--
Dsrc/mxf-remote/build.env | 3---
Dsrc/mxf-remote/default.obj.do | 5-----
Dsrc/mxf-remote/errs.c | 161-------------------------------------------------------------------------------
Dsrc/mxf-remote/errs.h | 45---------------------------------------------
Dsrc/mxf-remote/ga_foreach.h | 5-----
Dsrc/mxf-remote/mxf-remote.c | 1199-------------------------------------------------------------------------------
Dsrc/mxf-remote/mxf-remote.do | 4----
Dsrc/mxf-remote/mxf-remote.h | 228-------------------------------------------------------------------------------
Dsrc/mxf-remote/mxf-remote.h.sav | 105-------------------------------------------------------------------------------
Dsrc/mxf-remote/protomapdef | 3---
Dsrc/mxf-remote/typealloc.h | 9---------
Dsrc/mxf-remote/typeallocdefs.h | 33---------------------------------
Dsrc/mxf-remote/typealloclib.h | 70----------------------------------------------------------------------
Dsrc/mxf-remote/typeallocs.c | 26--------------------------
Dsrc/mxf-remote/typeallocs.h | 24------------------------
Dsrc/mxf-remote/uthash.h | 963-------------------------------------------------------------------------------
Msrc/qmail-qmqpd.c | 12++++++------
Msrc/qmail-queue.c | 9++++++++-
26 files changed, 27 insertions(+), 2935 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -381,3 +381,4 @@ qmail-send.service .dep.* *.obj src/mxf-remote/mxf-remote +src/netstrings.o diff --git a/TARGETS b/TARGETS @@ -376,3 +376,4 @@ doc/man/forgeries.0 setup include/qtmp.h qmail-send.service +src/netstrings.o diff --git a/conf-cc b/conf-cc @@ -1,3 +1,3 @@ -cc -g -ggdb3 -O0 -Iinclude/ -Iinclude/fmxs +cc -g -ggdb3 -O0 -Iinclude/ -Iinclude/fmxs -DUSING_SKALIBS -I/package/prog/skalibs/include This will be used to compile .c files. diff --git a/conf-groups b/conf-groups @@ -1,5 +1,5 @@ -qmail -nofiles +nmmail +nmnofi These are the qmail groups. The second group should not have access to any files, but it must be usable for processes; this requirement diff --git a/conf-ld b/conf-ld @@ -1,3 +1,3 @@ -cc +cc /package/prog/skalibs/library/libskarnet.a This will be used to link .o files into an executable. diff --git a/conf-users b/conf-users @@ -1,11 +1,11 @@ -alias -qmaild -qmaill +nmalias +nmaild +nmaill root -qmailp -qmailq -qmailr -qmails +nmailp +nmailq +nmailr +nmails The qmail system is heavily partitioned for security; it does almost nothing as root. diff --git a/src/mxf-remote/NOTES-mxfr.md b/src/mxf-remote/NOTES-mxfr.md @@ -1,32 +0,0 @@ - -# MXF-Remote Notes - -## A word on memory management and hash tables - -One of the structures we use in MXF Remote Connector is a UTHash hash table. UTHash does its own memory management and doesn't use strallocs like the rest of the program does. There will thus be two kinds of strong pointers into the heap in MXF-Remote - stralloc/genalloc from skalibs (and typealloc, which is gen_alloc from Notqmail - this is the same type of thing), and entries in UTHashes. - -We use UTHash to store information about skadns queries we make, and about fds which we select for readability (I hope that we will only ever select skadns fds for readability; client apps communicate with us the same way qmail-remote communicates with qmail-rspawn). With this, once the skadns fd raises readable, we needn't iterate over a costly array datum - we can just ask the fd hash for the fd and in nearly-constant time know that that's the skadns fd, and then ask the query hash for the query ID and in nearly-constant time get a weak pointer to the protocol that it's referring to (the strong pointer being somewhere on genalloc protocols). - -It's also conceivable that we stub out constmap and use UTHash for that (control/<srvservice>routes - static routing for protocols) too. - -### Notes in mxf-remote.c (MXF Remote Connector) - -## Globals - -Global variables, save for buffer_2_, start with a Capital Letter. Typedefs always end with _t. - -## Data structures - -protocol_t is a typedef struct containing a bunch of fields. They are NOT documented here. - -### A word on protocol_t - -This struct has grown to accomodate dynamically allocated pointers to DNS (SRV, MX, AAAA and A) results. A stralloc pointer to mxresult_t[] in each protocol gets sorted using qsort once DNS is done. - -## A word on type management with genallocs. - -genallocs are just strallocs, and they don't contain any type information. Because of this, they are wholly unsuitable for the purpose at hand. They are only used here in interactions with skadns, the DNS resolver. - -## mxf-remote.c:0010 - Address mangling - -Of note here is that we leave address mangling to the client program. We do not have an addrmangle function (although one will be available, surgically taken from qmail-remote.c). diff --git a/src/mxf-remote/all.do b/src/mxf-remote/all.do @@ -1,2 +0,0 @@ -#!/bin/sh -x -redo-ifchange mxf-remote diff --git a/src/mxf-remote/build.env b/src/mxf-remote/build.env @@ -1,3 +0,0 @@ -export CFLAGS="-g -ggdb3 -DUSING_SKALIBS -Werror=all -Werror=pedantic -ferror-limit=60 -Wno-unused-variable -Wno-error=unused-but-set-variable -DREMOTEDEBUG" -export INCLUDES="-I/package/prog/skalibs/include -I/package/web/s6-dns/include -I. -I../../include" -export LDFLAGS="-L/usr/lib" diff --git a/src/mxf-remote/default.obj.do b/src/mxf-remote/default.obj.do @@ -1,5 +0,0 @@ -#!/bin/sh -x -env -redo-ifchange "${2}.c" "${2}.h" -redo-ifcreate "${2}.c" "${2}.h" -${CC:-cc} ${CFLAGS} ${INCLUDES} -c -o "${3}" "${2}.c" diff --git a/src/mxf-remote/errs.c b/src/mxf-remote/errs.c @@ -1,161 +0,0 @@ -#include "mxf-remote.h" -#include <stdlib.h> - -extern stralloc Host; -extern buffer buffer_2_; -#define _HSC { - -void out(const char *s) _HSC - if (buffer_puts(&buffer_2_,s) == -1) _exit(0); - buffer_flush(&buffer_2_); -} -void zero() _HSC - if (buffer_put(&buffer_2_,"\0",1) == -1) _exit(0); -} -void _noreturn_ zerodie() _HSC - zero(); - buffer_flush(&buffer_2_); - _exit(0); -} -void outsafe(stralloc *sa) _HSC - int i; char ch; - for (i = 0;i < sa->len;++i) { - ch = sa->s[i]; - if (ch < 33) ch = '?'; - if (ch > 126) ch = '?'; - if (buffer_put(&buffer_2_,&ch,1) == -1) _exit(0); - } -} - -void _noreturn_ temp_nomem() _HSC - out("Z4.3.0 Out of memory or limits exceeded.\n"); - zerodie(); -} -void _noreturn_ temp_fault() _HSC - out("Z4.3.0 General protection violation in iopause. Attempting to dump core now.\n"); - zero(); - buffer_flush(&buffer_2_); - abort(); -} -void _noreturn_ temp_runfds() _HSC - out("Z4.3.0 run_fds called without any descriptors?\n"); - zerodie(); -} -void _noreturn_ temp_bug() _HSC - out("Z4.3.0 OS gave us EINVAL that we don't know what to do with. Attempting to dump core now.\n"); - zero(); - buffer_flush(&buffer_2_); - abort(); -} -void _noreturn_ temp_intr() _HSC - out("Z4.3.0 Signal interruption in iopause.\n"); - zerodie(); -} -void _noreturn_ temp_oserr() _HSC - out("Z4.3.0 System resources temporarily unavailable.\n"); - zerodie(); -} -void _noreturn_ temp_hasherr() _HSC - out("Z4.3.0 mxf-remote HASH_ADD dup key. Attempting to dump core now.\n"); - zero(); - buffer_flush(&buffer_2_); - abort(); -} -void _noreturn_ temp_finderr() _HSC - out("Z4.3.0 mxf-remote HASH_FIND no entry. Attempting to dump core now.\n"); - zero(); - buffer_flush(&buffer_2_); - abort(); -} -void _noreturn_ temp_noconn() _HSC - out("Z4.4.1 Sorry, I wasn't able to establish a connection to the target mail exchanger.\n"); - zerodie(); -} -void _noreturn_ temp_dnsupd() _HSC - out("Z4.0.0 DNSUPD Sorry, selecting on my DNS resolver gave me an error: \""); - out(error_str(errno)); - out("\"\n"); - zerodie(); -} -void _noreturn_ temp_wtf(char const *my) _HSC - out("Z4.0.0 WTF Sorry, "); out(my); out(" referred me to errno: \""); - out(error_str(errno)); - out("\""); - zerodie(); -} -void _noreturn_ temp_read() _HSC - out("Z4.3.0 Unable to read message.\n"); - zerodie(); -} -void _noreturn_ temp_dnscanon() _HSC - out("Z4.4.3 CNAME lookup failed temporarily.\n"); - zerodie(); -} -void _noreturn_ temp_dns() _HSC - out("Z4.1.2 Sorry, I couldn't find any host by that name.\n"); - zerodie(); -} -void _noreturn_ temp_chdir() _HSC - out("Z4.3.0 Unable to switch to home directory.\n"); - zerodie(); -} -void _noreturn_ temp_control() _HSC - out("Z4.3.0 Missing or malformed control files.\n"); - zerodie(); -} -// The next one belongs in -smtpc only, but is retained here. -void _noreturn_ perm_partialline() _HSC - out("D5.6.2 SMTP cannot transfer messages with partial final lines.\n"); - zerodie(); -} -void _noreturn_ perm_usage() _HSC - out("D5.3.5 I ("); - out(PROG); - out(") was invoked improperly. If this occurs in mxf-remote-qmtpc or mxf-remote-smtpc, that means there is a bug in mxf-remote - contact the developers to report this!\n"); - zerodie(); -} -void _noreturn_ perm_dns() _HSC - out("D5.1.2 Sorry, I couldn't find any host named "); - outsafe(&Host); - out(".\n"); - zerodie(); -} -void _noreturn_ perm_nomx() _HSC - out("D5.4.4 Sorry, I couldn't find a mail exchanger, SRV _smtp._tcp or IP address.\n"); - zerodie(); -} -void _noreturn_ perm_ambigmx() _HSC - out("D5.4.6 Sorry. Although I'm listed as a best-preference MX or A for that host,\n" // this is legal nowadays, so we do it fbo highlighters - "it isn't in my control/locals file, so I don't treat it as local.\n"); - zerodie(); -} -#ifdef REMOTEDEBUG -void info_dbg(stralloc *info) _HSC - out("I"); - buffer_put(&buffer_2_, info->s, info->len); - out("\n"); - return; -} -void info_dbgs(const char *info) _HSC - out("I"); - buffer_puts(&buffer_2_, info); - out("\n"); - return; -} -void info_wtfs(const char *info) _HSC - out("I3.0.0 FYI: "); out(info); out(" referred me to errno: \""); - out(error_str(errno)); - out("\"\n"); - return; -} -#else -void info_dbg(stralloc *info) _HSC - return; -} -void info_dbgs(const char *info) _HSC - return; -} -void info_wtfs(const char *info) _HSC - return; -} -#endif // RESOLVDEBUGC diff --git a/src/mxf-remote/errs.h b/src/mxf-remote/errs.h @@ -1,45 +0,0 @@ -#ifndef MXFR_ERRS_H -#define MXFR_ERRS_H -#include <skalibs/stralloc.h> - -#define _HSC ; - -void out(const char *s) _HSC -void zero() _HSC -void _noreturn_ zerodie() _HSC -void outsafe(stralloc *sa) _HSC -void _noreturn_ temp_nomem() _HSC -void _noreturn_ temp_oserr() _HSC -void _noreturn_ temp_hasherr() _HSC -void _noreturn_ temp_finderr() _HSC -void _noreturn_ temp_fault() _HSC -void _noreturn_ temp_intr() _HSC -void _noreturn_ temp_bug() _HSC -void _noreturn_ temp_noconn() _HSC -void _noreturn_ temp_read() _HSC -void _noreturn_ temp_runfds() _HSC -void _noreturn_ temp_dnscanon() _HSC -void _noreturn_ temp_dns() _HSC -void _noreturn_ temp_dnsupd() _HSC -void _noreturn_ temp_wtf(char const *my) _HSC -void _noreturn_ temp_esc(int b, int c, char const *my) _HSC -void _noreturn_ temp_chdir() _HSC -void _noreturn_ temp_control() _HSC -//void _noreturn_ temp_custom(char *) _HSC -void _noreturn_ perm_partialline() _HSC -void _noreturn_ perm_usage() _HSC -void _noreturn_ perm_dns() _HSC -void _noreturn_ perm_nomx() _HSC -void _noreturn_ perm_ambigmx() _HSC -void _noreturn_ temp_wtf(char const *my) _HSC -void _noreturn_ perm_esc(int b, int c, char const *my) _HSC -#ifdef REMOTEDEBUG -void info_dbg(stralloc *info) _HSC -void info_dbgs(const char *info) _HSC -void info_wtfs(const char *info) _HSC -#else -void info_dbg(stralloc *info) _HSC -void info_dbgs(const char *info) _HSC -void info_wtfs(const char *info) _HSC -#endif // REMOTEDEBUG -#endif // MXFR_ERRS_H diff --git a/src/mxf-remote/ga_foreach.h b/src/mxf-remote/ga_foreach.h @@ -1,5 +0,0 @@ -#ifndef _MXF_REMOTE_GA_FOREACH_H -#define _MXF_REMOTE_GA_FOREACH_H -#include <skalibs/genalloc.h> -#define ga_foreach(type, m, idx) for (idx = 0; (idx * sizeof(type)) < (m)->len; ++idx) -#endif /* _MXF_REMOTE_GA_FOREACH_H */ diff --git a/src/mxf-remote/mxf-remote.c b/src/mxf-remote/mxf-remote.c @@ -1,1199 +0,0 @@ -/* src/mxf-remote/mxf-remote.c - * requires skalibs, s6-networking and s6-dns; if not, you must - * use regular qmail-remote with the attendant loss of - * functionality with qmtp, tls and ipv6. - */ -#ifndef mxf_remote_c -#define mxf_remote_c -#include "mxf-remote.h" -#include "errs.h" -//#include <s6-dns/skadns.h> - -tain Deadline, Stamp, Limit; -//protocol_t protocols[SLICE_LAST]; -//TypeAlloc_typedef(protoAlloc,protocol_t,pt,len,a) -//TypeAlloc_readyplus(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus) -//TypeAlloc_append(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus,protoalloc_catp) -protoAlloc Protocols = TA_ZERO; // genalloc_*(protocol_t -//genalloc maproutes = GENALLOC_ZERO; // genalloc_*(constmap -char Errbuf[512]; -buffer buffer_2_ = BUFFER_INIT(&buffer_write, 2, Errbuf, 512); -//char Errbufsmall[256]; // no use yet -skadns_t Dnsres = SKADNS_ZERO; -//char namechosen[513]; // That's more bytes than we will ever need. -//char progname[61] = "mxf-remote"; -//stralloc sa = STRALLOC_ZERO; // ? -int Timeoutconnect = 60; int Mtpfd; -stralloc Host = STRALLOC_ZERO, Sender = STRALLOC_ZERO, Protocolsraw = STRALLOC_ZERO; -stralloc Ucspitlsclient = STRALLOC_ZERO, Tlsclient = STRALLOC_ZERO; -//genalloc recips = GENALLOC_ZERO; // will be filled with strallocs -//struct constmap maproutes[SLICE_LAST]; -char PROTOMAPTEXT[] = "qmtps 6209 3 Y N mxf-remote-qmtpc\n" \ - "qmtp 209 1 N N mxf-remote-qmtpc\n" \ - "smtp 25 0 N Y mxf-remote-smtpc\n"; -protocol_t *Slicemap[MAXSLICES]; -//genalloc Descriptors; // of type descriptor_t -//TypeAlloc_typedef(saAlloc,stralloc,sa,len,a) -//TypeAlloc_readyplus(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus) -//TypeAlloc_append(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus,saalloc_catsa) -//TypeAlloc_typedef(pfdAlloc,struct pollfd,pfd,len,a) -//TypeAlloc_readyplus(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus) -//TypeAlloc_append(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus,pfdalloc_catpfd) -saAlloc Rcptlist = TA_ZERO; -int Sigfd; -signed int ReadyToSend = 0; /* - * Set this to -1 when the remote domain doesn't accept mail, or 1 when - * we have a complete (unsorted) picture of how they do. - */ - -descriptor_t *Descriptortab = NULL; // NOTES-mxfr - A word on memory management - strong pointers -process_t *Protegetab = NULL; // NOTES-mxfr - A word on memory management -dnsq_t *Dnsqtab = NULL; // NOTES-mxfs - A word on memory management - -void getroutes (protocol_t *protocol); - -// in order: pollfd (single entry), opaque (may be NULL), fdcb(descriptor_t*, void*) -int descriptor_add (struct pollfd fd, void* opaque, - int (*fdcb)(descriptor_t*, short, void*)) -{ - descriptor_t *descriptor = NULL; - - HASH_FIND_INT(Descriptortab, &(fd.fd), descriptor); - if (descriptor != NULL) return (errno = ENOENT, FALSE); - // Otherwise... - descriptor = malloc(sizeof(descriptor_t)); // strong pointer; if we cannot add this to the hash, we must free it - descriptor->fd = fd; - descriptor->opaque = opaque; - descriptor->fdcb = fdcb; - HASH_ADD_INT(Descriptortab, fd.fd, descriptor); - return 1; // if we are returning at all, we have succeeded. -} - -// in order: descriptortab, pollfd (single entry) -// returns true on success, false on nonexistent -int descriptor_del (struct pollfd fd) -{ - descriptor_t *descriptor = NULL; - - HASH_FIND_INT(Descriptortab, &(fd.fd), descriptor); - if (descriptor == NULL) return FALSE; - // Otherwise... - HASH_DEL(Descriptortab, descriptor); - free(descriptor); - return TRUE; // if we are returning at all, we have succeeded. -} - -// in order: processtab, process ID, opaque to be passed to the report callback, report callback -int protege_add (pid_t pid, void* opaque, - int (*pdiecb)(process_t*, int, void*)) -{ - process_t *protege = NULL; - - HASH_FIND(hh, Protegetab, &pid, sizeof(pid_t), protege); - if (protege != NULL) temp_hasherr(); - // Otherwise... - protege = malloc(sizeof(descriptor_t)); // strong pointer; if we cannot add this to the hash, we must free it - protege->pid = pid; - protege->opaque = opaque; - protege->pdiecb = pdiecb; - HASH_ADD(hh, Protegetab, pid, sizeof(pid_t), protege); - return 1; // if we are returning at all, we have succeeded. -} -// in order: descriptortab, pollfd (single entry) -// returns true on success, false on nonexistent -int protege_del (pid_t pid) -{ - process_t *protege = NULL; - - HASH_FIND(hh, Protegetab, &(pid), sizeof(pid_t), protege); - if (protege == NULL) return (errno = ENOENT, FALSE); - HASH_DEL(Protegetab, protege); - free(protege); - return TRUE; -} - -// in order: dnsqtab, process ID, opaque to be passed to the report callback, report callback -int dnsq_add (uint16_t dnsqid, void* opaque, void* opaque2, - int (*qcb)(skadns_t*, dnsq_t*, void*, void*)) -{ - dnsq_t *dnsq = NULL; - - HASH_FIND(hh, Dnsqtab, &dnsqid, sizeof(uint16_t), dnsq); - if (dnsq != NULL) temp_hasherr(); - // Otherwise... - dnsq = malloc(sizeof(dnsq_t)); // strong pointer; if we cannot add this to the hash, we must free it - dnsq->dnsq = dnsqid; - dnsq->opaque = opaque; - dnsq->opaque2 = opaque2; - dnsq->qcb = qcb; - HASH_ADD(hh, Dnsqtab, dnsq, sizeof(uint16_t), dnsq); - return TRUE; // if we are returning at all, we have succeeded. -} -// in order: dnsqtab, pollfd (single entry) -// returns true on success, false on nonexistent -int dnsq_del (uint16_t dnsqid) -{ - dnsq_t *dnsq = NULL; - - HASH_FIND(hh, Dnsqtab, &(dnsqid), sizeof(uint16_t), dnsq); - if (dnsq == NULL) return (errno = ENOENT, FALSE); - HASH_DEL(Dnsqtab, dnsq); - free(dnsq); - return TRUE; -} - -int run_fds (tain *deadline, tain *stamp) -{ - pfdAlloc fds = TA_ZERO; // pollfd - descriptor_t *des = NULL, *tmp = NULL; - struct pollfd *pfds = NULL; size_t pfdsiz = 0; size_t i = 0; - int pollret = 0; - HASH_ITER(hh, Descriptortab, des, tmp) { - if (!pfdalloc_catpfd(&fds, (des->fd))) temp_nomem(); - des->fd.revents = 0; - ++pfdsiz; - } - if (pfdsiz == 0) temp_runfds(); - des = NULL; tmp = NULL; - pfds = (struct pollfd *)(fds.pfd); - pollret = iopause_stamp(pfds, pfdsiz, deadline, stamp); - // and then we wait for iopause to do its thing - // and then iopause wakes us up - switch (pollret) { - case 0: - // we literally have nothing to do. we return. this isn't an error state. - return pollret; - break; - case -1: - // errors AGAIN, FAULT, INTR, INVAL - switch (errno) { - case EAGAIN: - return 0; // we will just get called again - break; - case EFAULT: - temp_fault(); - break; - case EINTR: - temp_intr(); // an uncaught signal has come our way - break; - case EINVAL: - temp_bug(); // invalid data - break; - } - default : - for (i = 0; i < pfdsiz; i++) { - HASH_FIND_INT(Descriptortab, &(pfds[i].fd), des); - if (des == NULL) { - temp_finderr(); // could not find the file descriptor in our tables - what kind of wiseguy do we have on here? - } - if (((pfds[i]).revents & POLLNVAL) != 0) { - // file descriptor is not known to the system; bug - temp_bug(); - } else if ((pfds[i]).revents != 0) { - (*(des->fdcb))(des, (pfds[i]).revents, des->opaque); - } - } - } - // done with fds now? right, we return the number of FDs we processed. - free((fds.pfd)); - return pollret; -} - -int handledeadprocess () -{ - process_t *protege = NULL; - pid_t pid = 0; - int wstat; - while ((pid = wait_nohang(&wstat)) > 0) { - HASH_FIND(hh, Protegetab, &pid, sizeof(pid_t), protege); - if (protege == NULL) continue; // doesn't really matter - (*protege->pdiecb)(protege, wstat, protege->opaque); - protege_del(pid); - } - return TRUE; -} - -int checksignals (descriptor_t *descr, short revents, void* unused) -{ - int sig; - sig = selfpipe_read(); - - switch (sig) { - case SIGCHLD: - return handledeadprocess(); - break; - default : - return FALSE;// Advanced wiseguy shit - } -} - -int protocols_init (protoAlloc *protos, char *protomaptext, size_t length) -{ - size_t i = 0; - char field = 0; /* there are six fields, numbered zero to five - * their identities are: - * protoname, port, protoslice, cantls, musttls, executable-filename - */ - size_t lpos = 0, fpos = 0; // record the position we are at in the line - char comment = 0; char s[15]; //true if line is comment - - for (i = 0; i < MAXSLICES; i++) { - Slicemap[i] = NULL; - } - if (length == 0) { - protomaptext = PROTOMAPTEXT; - length = strlen(PROTOMAPTEXT); // that string has no funny business with nulls so we can do this - } - protocol_t proto; - memset(&proto, 0, sizeof(protocol_t)); - proto.slice = -1; - - for (i = lpos = fpos = 0; i < length; ++i) { - buffer_put(buffer_2, &(protomaptext[i]), 1); - switch (protomaptext[i]) { -/* case '\r': // activate this when we have our own control_readfile; until then it's no use - continue; */ // probably DOS; tolerate bad input. If the user is really using a CR in a filename or w/e, it'll be eaten up by a \ doing runahead. - case '\n': // still needed by the default protomap - case 0: - switch (fpos) { - case 0: continue; // it was an empty line - default : break; - } - fpos = -1; - lpos = 0; - field = 0; - comment = 0; - getroutes(&proto); - if (!stralloc_0(&(proto.app))) temp_nomem(); - buffer_puts(buffer_2, proto.srvservice[0] != 0 ? "srvservice[0] is not null\n" :"srvservice[0] is null\n"); - if (proto.srvservice[0] != 0) if (!protoalloc_catp(protos, proto)) temp_nomem(); - if (proto.slice > -1 && proto.slice < MAXSLICES) Slicemap[proto.slice] = protos->pt + protos->len-1; - buffer_puts(buffer_2, "reset\n"); - memset(&proto, 0, sizeof(protocol_t)); - proto.slice = -1; - break; - case ' ': - switch (comment) { - case 1: continue; - default : break; - } - switch (field) { - case 5: - if (!stralloc_catb(&(proto.app), &(protomaptext[i]), 1)) temp_nomem(); - break; - case 0: - switch (fpos) { - case 0: - ++comment; - break; - default : - proto.srvservice[fpos] = 0; // nullcap the srvservice - fpos = 0; - ++field; - } - break; - case 1: - case 2: - case 3: - case 4: - fpos = -1; - ++field; - break; - } - break; - case '\\': - switch (comment) { - case 1: continue; - default : break; - } - // The synopsis of \ is: Take next character verbatim into current field, no matter what it is. - switch (field) { - case 5: - if (fpos > PATH_MAX) temp_control(); // malformed - if (i >= length) temp_control(); // malformed - if (protomaptext[i] == 0) temp_control(); // malformed - if (!stralloc_catb(&(proto.app), &(protomaptext[i]), 1)) temp_nomem(); // there is no conceivable reason to have a backslash unless it's part of your fname, so we don't eat it. - break; - case 0: - proto.srvservice[fpos] = protomaptext[++i]; - case 1: // It is ignorable in fields 2-5. - case 2: - case 3: - case 4: - if (i >= length) temp_control(); // malformed - if (protomaptext[i] == 0) temp_control(); // malformed - ++lpos; - break; - } - break; - default : - switch (comment) { - case 1: continue; - default : break; - } - switch (field) { - case 0: // protoname - if (fpos < MAXSRVLEN) proto.srvservice[fpos] = protomaptext[i]; - break; - case 5: // protoprog (app) - if (fpos < PATH_MAX) if (!stralloc_catb(&(proto.app), &(protomaptext[i]), 1)) temp_nomem(); // there is no conceivable reason to have a backslash unless it's part of your fname, so we don't eat it. - break; - case 1: // defport - if (protomaptext[i] < '0') temp_control(); - if (protomaptext[i] > '9') temp_control(); - proto.defport = (proto.defport * 10) + (protomaptext[i] - '0'); - break; - case 2: // MXPS slice, DECIMAL - buffer_puts(buffer_2, " field:"); - s[fmt_ulong(s, field)] = 0; - buffer_puts(buffer_2, s); - if (fpos == 0 && !(protomaptext[i] == '-')) proto.slice = 0; // reset slice to 0 as it is -1 - if (proto.slice != -1) { // a slice of -1 means we are not using a slice and this is a SRV only protocol - if (!(protomaptext[i] < '0' || protomaptext[i] > '9')) { - proto.slice = (proto.slice * 10) + (protomaptext[i] - '0'); - s[fmt_ulong(s, proto.slice)] = 0; - buffer_puts(buffer_2, " slice:"); - buffer_puts(buffer_2, s); - buffer_puts(buffer_2, " "); - } else { - info_dbgs("detected non-numeric character in slice number "); - out("it was "); - buffer_put(buffer_2, &protomaptext[i], 1); - info_dbgs(""); - temp_control(); - } - } - break; - case 3: // is TLS - switch (fpos) { - case 0: // we only care about char 1 - if ( - protomaptext[i] == '0' - || protomaptext[i] == 'n' - || protomaptext[i] == 'N' - || protomaptext[i] == 'f' - || protomaptext[i] == 'F' - ) proto.tls = 0; - else if ( - protomaptext[i] == '1' - || protomaptext[i] == 'y' - || protomaptext[i] == 'Y' - || protomaptext[i] == 't' - || protomaptext[i] == 'T' - ) proto.tls = 1; - else temp_control(); - // in default of a valid choice, we have no choice but to blow up. - default : break; - } - break; - case 4: // can TLS (irrelevant if is TLS) - switch (fpos + proto.tls) { - case 0: // we only care about char 1 - if ( - protomaptext[i] == '0' - || protomaptext[i] == 'n' - || protomaptext[i] == 'N' - || protomaptext[i] == 'f' - || protomaptext[i] == 'F' - ) proto.tls = 0; - else if ( - protomaptext[i] == '1' - || protomaptext[i] == 'y' - || protomaptext[i] == 'Y' - || protomaptext[i] == 't' - || protomaptext[i] == 'T' - ) proto.tls = 1; - else temp_control(); - // in default of a valid choice, we have no choice but to blow up. - default : break; - } - break; - } - } - ++lpos; ++fpos; // if comment notreached; lpos and fpos don't run if we are in a comment. - // lpos and fpos only increase 1 for every 2 characters if every first character is a backstroke - } - // the music is over. we're at the end of the file, on a partial line. hopefully app is at least an app we can try. - if (proto.app.len > 1) { - getroutes(&proto); - if (!protoalloc_catp(protos, proto)) return 0; - } - return 1; -} - - -// imported from qmail-remote -// inputs: controlfiles from the filesystem -// outputs: none -// SE: reads control files - -void getcontrols() -{ - if (control_init() == -1) temp_control(); - //if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); - // The client application is responsible for this. - if (control_readint(&Timeoutconnect,"control/timeoutconnect") == -1) - temp_control(); -// if (control_rldef(&helohost,"control/helohost",1,NULL) != 1) -// temp_control(); - if (control_rldef(&Ucspitlsclient,"control/ucspitlsclient",0,UCSPITLSC) != 1) - temp_control(); - if (control_rldef(&Ucspitlsclient,"control/tlsclient",0,TLSC) != 1) - temp_control(); - switch(control_readfile(&Protocolsraw,"control/protocols",0)) { - case -1: - temp_control(); - case 0: - if (!protocols_init(&(Protocols),"",0)) temp_nomem(); break; - case 1: - if (!protocols_init(&(Protocols),Protocolsraw.s,Protocolsraw.len)) temp_nomem(); break; - } - // It strikes me that Protocolsraw is used nowhere else in the file. -} - -void getroutes (protocol_t *protocol) -{ - stralloc ctrl = STRALLOC_ZERO, routes = STRALLOC_ZERO; - stralloc_copys(&ctrl, "control/"); - stralloc_cats(&ctrl, protocol->srvservice); - stralloc_cats(&ctrl, "routes"); - stralloc_0(&ctrl); - switch(control_readfile(&routes,ctrl.s,0)) { - case -1: temp_control(); - case 0: - if (!constmap_init(&(protocol->maproutes),"",0,1)) temp_nomem(); break; - case 1: - if (!constmap_init(&(protocol->maproutes),routes.s,routes.len,1)) temp_nomem(); break; - } - stralloc_free(&ctrl); -} - -int checkdns (descriptor_t *descr, short revents, void* dnsresv) -{ - skadns_t *dnsres = (skadns_t *)dnsresv; - int upd; size_t dnsqlen = 0, i = 0; - uint16_t *dnsqs = NULL; dnsq_t *dnsq = NULL; - upd = skadns_update(dnsres); - switch (upd) { - case -1: - temp_dnsupd(); - return FALSE; // NOTREACHED we should not be here but... - break; - default : // there either has, or has not been, a response to collect. let's do that now. - dnsqs = genalloc_s(uint16_t, &dnsres->list); // have to use genalloc here as that's what Ska uses in skadns - dnsqlen = genalloc_len(uint16_t, &dnsres->list); - for (i = 0; i < dnsqlen; ++i) { - // we must now call the callback and delete the query from the list - HASH_FIND(hh, Dnsqtab, (dnsqs + i), sizeof(uint16_t), dnsq); - (*dnsq->qcb)(dnsres, dnsq, (dnsq->opaque), (dnsq->opaque2)); // not exactly sure the use of passing the full query if we'll just delet it - skadns_release(dnsres, dnsqs[i]); - dnsq_del(dnsqs[i]); - dnsq = NULL; - } - } - return TRUE; -} - -void startdns (skadns_t *dnsres) { - if (!skadns_startf(dnsres, &Deadline, &Stamp)) { - buffer_puts(buffer_2, "ZDNS resolution startup timeout\n"); - buffer_flush(buffer_2); - exit(111); - } -} - -int ipisme (const ipmx_t *ip) { - // wrapper around ipme and ip6me - const ip46full *ix = &(ip->ip); - struct ip_address i4; - struct ip6_address i6; - if (ix->is6) { - memcpy(&(i6.d), &(ix->ip), 16); - return ip6me_is(&i6); - } else { - memcpy(&(i4.d), &(ix->ip), 4); - return ipme_is(&i4); - } -} - -void stopdns (skadns_t *dnsres) { - skadns_end(dnsres); -} - -/* les noms de ces procs sont misleading. - * They should not be thought of as "we definitely have our response" - * but as "we MAY have our response." - */ -int dns_havea (skadns_t *dnsres, dnsq_t *dnsq, void* mxresultp, void* protop) -{ - protocol_t *proto = (protocol_t *)protop; - mxresult_t *mxresult = (mxresult_t *)mxresultp; - //mxrAlloc *mxres = &(proto->mxresults); - //ipmxAlloc *ipmxres = &(mxres->ipmx); - ipmx_t ipmx = {IP46FULL_ZERO}; - ipmx.ip.is6 = 0; - stralloc rrs = STRALLOC_ZERO; - char const *dnsresponse; int dnsresplen, i = 0; - s6dns_message_header_t dnsmh; - - if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) { - proto->lookupdone = 1; - info_wtfs("skadns_packet in dns_havea"); - return FALSE; - } - if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_havea"); // this should succeed if the above didn't fail - if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_havea less than even a header"); } - // s6dns_message_header_unpack(dnsresponse, dnsmh); - switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_a, &rrs)) { - case -1: - temp_wtf("s6dns_message_parse in dns_havea - local error"); - break; - case 0: - info_wtfs("s6dns_message_parse in dns_havea - rcode - maybe should continue"); - mxresult->lookupdone = 1; - mxresult->hasresults = 0; - proto->lookupdone = 1; - break; - case 1: -#ifdef REMOTEDEBUG - info_dbgs("s6dns_message_parse in dns_havea - success no answer"); -#endif // REMOTEDEBUG - mxresult->lookupdone = 1; - mxresult->hasresults = 0; - proto->lookupdone = 1; - break; - case 2: -#ifdef REMOTEDEBUG - info_dbgs("s6dns_message_parse in dns_havea - success answer"); -#endif // REMOTEDEBUG - mxresult->lookupdone = 1; - mxresult->hasresults = 1; - proto->lookupdone = 1; - proto->hasresults = 1; - for (i = 0; i < rrs.len/4; ++i) { - memcpy(&(ipmx.ip.ip), (rrs.s + (i * 4)), 4); - if (!ipmxalloc_catip(&(mxresult->ipmx), ipmx)) temp_nomem(); - if (ipisme(&ipmx)) mxresult->isme = 1; - memset(&ipmx, 0, sizeof(ipmx_t)); ipmx.ip.is6 = 0; - } - break; - } - return TRUE; -} -int dns_haveaaaa (skadns_t *dnsres, dnsq_t *dnsq, void* mxresultp, void* protop) -{ - protocol_t *proto = (protocol_t *)protop; - mxresult_t *mxresult = (mxresult_t *)mxresultp; - //mxrAlloc *mxres = &(proto->mxresults); - //ipmxAlloc *ipmxres = &(mxres->ipmx); - ipmx_t ipmx = {IP46FULL_ZERO}; - ipmx.ip.is6 = 1; - stralloc rrs = STRALLOC_ZERO; - char const *dnsresponse; int dnsresplen, i = 0; - s6dns_message_header_t dnsmh; - - if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) { - info_wtfs("skadns_packet in dns_haveaaaa"); - proto->lookupdone = 1; - mxresult->lookupdone = 1; - return FALSE; - } - if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_haveaaaa"); // this should succeed if the above didn't fail - if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_haveaaaa less than even a header"); } - // s6dns_message_header_unpack(dnsresponse, dnsmh); - switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_aaaa, &rrs)) { - case -1: - temp_wtf("s6dns_message_parse in dns_haveaaaa - local error"); - break; - case 0: - info_wtfs("s6dns_message_parse in dns_haveaaaa - rcode - maybe should continue"); - mxresult->lookupdone = 1; - proto->lookupdone = 1; - break; - case 1: -#ifdef REMOTEDEBUG - info_dbgs("s6dns_message_parse in dns_haveaaaa - success no answer"); -#endif // REMOTEDEBUG - mxresult->lookupdone = 1; - proto->lookupdone = 1; - break; - case 2: -#ifdef REMOTEDEBUG - info_dbgs("s6dns_message_parse in dns_haveaaaa - success answer"); -#endif // REMOTEDEBUG -#ifdef SKALIBS_IPV6_ENABLED - mxresult->lookupdone = 1; - mxresult->hasresults = 1; - proto->lookupdone = 1; - proto->hasresults = 1; - for (i = 0; i < rrs.len/16; ++i) { - memcpy(&(ipmx.ip.ip), (rrs.s + (i * 16)), 16); - if (!ipmxalloc_catip(&(mxresult->ipmx), ipmx)) temp_nomem(); - if (ipisme(&ipmx)) mxresult->isme = 1; - memset(&ipmx, 0, sizeof(ipmx_t)); ipmx.ip.is6 = 1; - } -#endif // SKALIBS_IPV6_ENABLED - break; - } - return TRUE; -} - -int dns_havesrv (skadns_t *dnsres, dnsq_t *dnsq, void* protop, void* unused) -{ - protocol_t *proto = (protocol_t *)protop; - mxrAlloc *mxres = &(proto->mxresults); - mxresult_t mxr = MXRESULT_SRV_ZERO; - genalloc rrs = GENALLOC_ZERO; - tain shortdl; - const char *dnsresponse; char ds[256]; int dnsresplen = 0, dsl = 0, i = 0; - s6dns_message_header_t dnsmh; - s6dns_domain_t dnsdfp; // DNS domain of form packet - s6dns_message_rr_srv_t *rr = NULL; - uint16_t qid4, qid6; - - if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) { - info_wtfs("skadns_packet in dns_havesrv"); - proto->lookupdone = 1; - return FALSE; - } - if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_havesrv"); // this should succeed if the above didn't fail - if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_havesrv less than even a header"); } - // s6dns_message_header_unpack(dnsresponse, dnsmh); - switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_srv, &rrs)) { - case -1: - temp_wtf("s6dns_message_parse in dns_havesrv - local error"); - break; - case 0: - info_wtfs("s6dns_message_parse in dns_havesrv - maybe should continue"); - proto->lookupdone = 1; - break; - case 1: -#ifdef REMOTEDEBUG - info_dbgs("s6dns_message_parse in dns_havesrv - success no answer"); -#endif // REMOTEDEBUG - proto->lookupdone = 1; - proto->hasresults = 0; - break; - case 2: -#ifdef REMOTEDEBUG - info_dbgs("s6dns_message_parse in dns_havesrv - success answer"); -#endif // REMOTEDEBUG - proto->lookupdone = 1; - proto->hasresults = 1; - for (i = 0; i < genalloc_len(s6dns_message_rr_srv_t, &rrs); ++i) { -// rr = (s6dns_message_rr_srv_t *)((rrs.s) + (i * sizeof(s6dns_message_rr_srv_t))); - rr = genalloc_s(s6dns_message_rr_srv_t, &rrs) + i; - mxr.lookupdone = TRUE; - mxr.port = rr->port; - mxr.prio = rr->priority; - mxr.weight = rr->weight; - if (!(dsl = s6dns_domain_tostring(ds, 255, &(rr->target)))) temp_wtf("s6dns_domain_tostring in dns_havesrv"); - if (!stralloc_readyplus(&(mxr.name), dsl)) temp_nomem(); - if (!stralloc_copyb(&(mxr.name), ds, dsl)) temp_nomem(); - if (!stralloc_0(&(mxr.name))) temp_nomem(); - if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havesrv (Doctor?)"); - if (!tain_addsec(&shortdl, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havesrv (Doctor?)"); - if (!mxralloc_catmxr(mxres, mxr)) temp_nomem(); - if (!s6dns_domain_fromstring_noqualify_encode(&dnsdfp, ds, dsl)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_havesrv"); - if (mxr.name.len > 1) { // No point if it's just a dot - if (!skadns_send(&Dnsres, &qid6, &(dnsdfp), S6DNS_T_AAAA, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havesrv"); - if (!dnsq_add(qid6, &(mxres->mxr[(mxres->len)-1]), proto, dns_haveaaaa)) temp_wtf("dnsq_add in dns_havesrv"); - if (!skadns_send(&Dnsres, &qid4, &(dnsdfp), S6DNS_T_A, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havesrv"); - if (!dnsq_add(qid4, &(mxres->mxr[(mxres->len)-1]), proto, dns_havea)) temp_wtf("dnsq_add in dns_havesrv"); - } - memset(&mxr, 0, sizeof(mxresult_t)); - } - break; - } - return TRUE; -} - -int dns_havemx (skadns_t *dnsres, dnsq_t *dnsq, void* protop, void* unused) -{ - // XX - should probably pass the whole protoalloc as protop - protocol_t *proto = (protocol_t *)protop, *smproto; - mxrAlloc *mxres = NULL; - mxresult_t mxr = MXRESULT_MX_ZERO; - genalloc rrs = GENALLOC_ZERO; - tain shortdl; - char const *dnsresponse; char ds[256]; int dnsresplen = 0, dsl = 0, i = 0; - s6dns_domain_t dnsdfp; - s6dns_message_header_t dnsmh; - s6dns_message_rr_mx_t *rr = NULL; - uint16_t qid4, qid6; - - if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) { - info_wtfs("skadns_packet in dns_havemx"); - return FALSE; - } - if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_havemx"); // this should succeed if the above didn't fail - if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_havemx less than even a header"); } - // s6dns_message_header_unpack(dnsresponse, dnsmh); - switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_mx, &rrs)) { - case -1: - temp_wtf("s6dns_message_parse in dns_havemx - local error"); - break; - case 0: - info_wtfs("s6dns_message_parse in dns_havemx - maybe should continue"); - proto->lookupdone = 1; - break; - case 1: -#ifdef REMOTEDEBUG - buffer_puts(&buffer_2_, "Is6dns_message_parse in dns_havemx - success no answer\n"); -#endif // REMOTEDEBUG - break; - case 2: -#ifdef REMOTEDEBUG - buffer_puts(&buffer_2_, "Is6dns_message_parse in dns_havemx - success answer\n"); -#endif // REMOTEDEBUG - for (i = 0; i < genalloc_len(s6dns_message_rr_mx_t, &rrs); ++i) { - //rr = (s6dns_message_rr_mx_t *)((rrs.s) + (i * sizeof(s6dns_message_rr_mx_t))); - rr = genalloc_s(s6dns_message_rr_mx_t, &rrs) + i; - mxr.lookupdone = TRUE; - if (_is_mxps(rr->preference)) { - if ((proto = Slicemap[_mxps_slice(rr->preference)]) == NULL) proto = Slicemap[NOMXPS_SLICE]; - mxr.prio = _mxps_prio(rr->preference); - } else { - proto = Slicemap[NOMXPS_SLICE]; - mxr.prio = rr->preference; - } - if (proto == NULL) {errno = EINVAL; temp_wtf("dns_havemx - postmanager has not configured a zero MXPS slice, and this is necessary.");} - proto->lookupdone = 1; - proto->hasresults = 1; - mxres = &(proto->mxresults); - mxr.port = proto->defport; - mxr.weight = 0; - if (!(dsl = s6dns_domain_tostring(ds, 255, &(rr->exchange)))) temp_wtf("s6dns_domain_tostring in dns_havemx"); - ds[dsl] = 0; - if (!stralloc_readyplus(&(mxr.name), dsl+1)) temp_nomem(); - if (!stralloc_copyb(&(mxr.name), ds, dsl)) temp_nomem(); - if (!stralloc_0(&(mxr.name))) temp_nomem(); - if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havemx (Doctor?)"); - if (!tain_addsec(&shortdl, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havemx (Doctor?)"); - if (!mxralloc_catmxr(mxres, mxr)) temp_nomem(); - if (!s6dns_domain_fromstring_noqualify_encode(&dnsdfp, ds, dsl)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_havemx"); - if (mxr.name.len > 1) { // No point if it's just a dot, NPI. - if (!skadns_send(&Dnsres, &qid4, &(dnsdfp), S6DNS_T_A, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havemx"); - if (!dnsq_add(qid4, &(mxres->mxr[(mxres->len)-1]), proto, dns_havea)) temp_wtf("dnsq_add in dns_havemx"); - if (!skadns_send(&Dnsres, &qid6, &(dnsdfp), S6DNS_T_AAAA, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havemx"); - if (!dnsq_add(qid6, &(mxres->mxr[(mxres->len)-1]), proto, dns_haveaaaa)) temp_wtf("dnsq_add in dns_havemx"); - } - memset(&mxr, 0, sizeof(mxresult_t)); - memset(&dnsdfp, 0, sizeof(s6dns_domain_t)); - mxr.ismx = TRUE; - } - break; - } - return TRUE; -} -/* -int dns_havecname (skadns_t *dnsres, dnsq_t *dnsq, void* protop, void *unused) -{ - // We should only encounter this guy if we are using relayhost. - protocol_t *proto = (protocol_t *)protop; - mxrAlloc *mxres = &(proto->mxresults); - mxresult_t mxr = {0, 0, 0, FALSE, FALSE, STRALLOC_ZERO, TA_ZERO}; - genalloc rrs; - char const *dnsresponse; int dnsresplen; - s6dns_message_header_t dnsmh; -} -*/ - -/* -int protocol_want_srvmx (protocol_t *proto, -int protocol_have_mx (protocol_t *proto, -int protocol_want_ip (protocol_t *proto, -int protocol_have_mxip (protocol_t *proto, -int protocol_wantfd (protocol_t *proto, int mtpfd) -{ - // we'll have to use a selfpipe of some description elsewhere to understand process termination -} - * -int protocol_spin (protocol_t *proto, char stage, unsigned long port, - char **rrecips, char *relayhost, size_t nprotos) -{ -}*/ - -// offset is needed in case we're skipping the first protocol for some reason. -// until then, it should always be zero -// nproto is the number of protocols we're doing -// mxralloc is inside the protocol, so -int dns_wantsrv (protocol_t *protos, size_t nproto) -{ - int i = 0; - uint16_t qtype = S6DNS_T_SRV, id = 0; - s6dns_domain_t dnsdomain; - stralloc domain = STRALLOC_ZERO; - //mxresult_t mxr = {0, 0, 0, STRALLOC_ZERO, TA_ZERO}; - //mxrAlloc *mxrs; - //tain limit, dl; - - for (i = 0; i < nproto; ++i) { - //mxrs = protos[i].mxresults.mxr; - if (!stralloc_copys(&domain, "_")) temp_nomem(); - if (!stralloc_cats(&domain, protos[i].srvservice)) temp_nomem(); - if (!stralloc_cats(&domain, "._tcp.")) temp_nomem(); - if (!stralloc_cats(&domain, Host.s)) temp_nomem(); - //if (!stralloc_0(&domain)) temp_nomem(); - if (!s6dns_domain_fromstring_noqualify_encode(&dnsdomain, domain.s, domain.len)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_wantsrv"); - if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_wantsrv (Doctor?)"); - if (!tain_addsec(&Deadline, &Stamp, 1)) temp_wtf("tain_addsec in dns_wantsrv (Doctor?)"); - if (!skadns_send(&Dnsres, &id, &dnsdomain, qtype, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantsrv"); - if (!dnsq_add(id, (protos + i), NULL, dns_havesrv)) temp_wtf("dnsq_add in dns_wantsrv"); - } - stralloc_free(&domain); - return TRUE; // not used -} - -int dns_wantmx (protocol_t *protos, size_t nproto) -{ - int i = 0; - uint16_t qtype = S6DNS_T_MX, id = 0; - s6dns_domain_t dnsdomain; - stralloc domain = STRALLOC_ZERO; - //tain limit, dl; - // mxresult_t mxr = {0, 0, 0, FALSE, FALSE, TRUE, STRALLOC_ZERO, TA_ZERO}; - - if (!stralloc_cats(&domain, Host.s)) temp_nomem(); - //if (!stralloc_0(&domain)) temp_nomem(); - if (!s6dns_domain_fromstring_noqualify_encode(&dnsdomain, domain.s, domain.len)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_wantmx"); - if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_wantmx (Doctor?)"); - if (!tain_addsec(&Deadline, &Stamp, 1)) temp_wtf("tain_addsec in dns_wantmx (Doctor?)"); - if (!skadns_send(&Dnsres, &id, &dnsdomain, qtype, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantmx"); - if (!dnsq_add(id, protos, NULL, dns_havemx)) temp_wtf("dnsq_add in dns_wantmx"); - stralloc_free(&domain); - return TRUE; // not used -} - -// the only entrypoint to this is a DNS relayhost -int dns_wantip (protocol_t *proto, uint16_t port, char *relayhost) -{ - int i = 0; - uint16_t qid4 = 0, qid6 = 0; - s6dns_domain_t dnsdomain; - stralloc domain = STRALLOC_ZERO; - //tain limit, dl; &(mxres->mxr[(mxres->len)-1]) - mxresult_t mxr = MXRESULT_SRV_ZERO; - mxrAlloc *mxres = &(proto->mxresults); - - mxr.port = port; - - if (!stralloc_cats(&domain, relayhost)) temp_nomem(); - //if (!stralloc_0(&domain)) temp_nomem(); - if (!s6dns_domain_fromstring_noqualify_encode(&dnsdomain, domain.s, domain.len)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_wantip"); - if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_wantip (Doctor?)"); - if (!tain_addsec(&Deadline, &Stamp, 1)) temp_wtf("tain_addsec in dns_wantip (Doctor?)"); - if (!mxralloc_catmxr(mxres, mxr)) temp_nomem(); - if (!skadns_send(&Dnsres, &qid6, &dnsdomain, S6DNS_T_AAAA, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantip"); - if (!dnsq_add(qid6, &(mxres->mxr[(mxres->len)-1]), NULL, dns_haveaaaa)) temp_wtf("dnsq_add in dns_wantip"); - if (!skadns_send(&Dnsres, &qid4, &dnsdomain, S6DNS_T_A, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantip"); - if (!dnsq_add(qid4, &(mxres->mxr[(mxres->len)-1]), NULL, dns_havea)) temp_wtf("dnsq_add in dns_wantip"); - //memset(&dnsdfp, 0, sizeof(s6dns_domain_t)); // not necessary? - stralloc_free(&domain); - return TRUE; // not used -} - -int lookupsdone (protocol_t *proto) { - //protocol_t *proto = NULL; - mxresult_t *mxr = NULL; - int i = 0; - if (proto->lookupdone == 0) return FALSE; - for (i = 0; i < proto->mxresults.len; ++i) { - mxr = (mxresult_t *)((proto->mxresults.mxr) + (i * sizeof(mxresult_t))); - if (mxr->lookupdone == 0) return FALSE; - } - return TRUE; // if we have not returned false by now, all lookups are done -} - -// comparator for qsort -int mxworse (void* mx1p, void* mx2p) { - // These get sorted in ascending order. - // If mx1 is less preferable than mx2, it is greater (TRUE). - // If it is equally preferable, it is equal (FALSE). - // If it is more preferable, it is lesser (-1). - mxresult_t *mx1 = (mxresult_t *)mx1p; - mxresult_t *mx2 = (mxresult_t *)mx2p; - - if ((mx1->ismx) && !(mx2->ismx)) { - // literally need do nothing else, we prefer mx2 - return TRUE; - } - - if (mx1->prio > mx2->prio) { - // mx1 prio is higher (less prior) than mx2 prio, so true. - return TRUE; - } - - if (mx1->prio == mx2->prio) { - // mx1 prio is equal to mx2 prio, so false, unless weight higher. - if (mx1->weight > mx2->weight) return -1; // mx1 is heavier, so more pref (better) - if (mx1->weight < mx2->weight) return TRUE; // mx1 is lighter, so less pref (worse) - return FALSE; - } - - if (mx1->prio < mx2->prio) { - // mx1 prio is lower (less prior) than mx2 prio, so true. - return -1; - } - return FALSE; // Not reached. -} - -void printprotos () { - protocol_t *proto = NULL; - mxresult_t *mxr = NULL; - ipmx_t *ipmx = NULL; - char s[48]; int i = 0, j = 0; unsigned long l = 0; - memset(&s, 0, 48); - out("I3.0.0 "); - out("Protocol element count: "); - s[fmt_ulong(s, Protocols.len)] = 0; - out(s); - out(" Protocol element allocation count: "); - s[fmt_ulong(s, Protocols.a)] = 0; - out(s); - out("\n"); - if (Protocols.len < 1) { - info_dbgs("3.3.0 No protocols on the protocol array!"); - } else { - for (i = 0; i < Protocols.len; ++i) { - proto = (protocol_t *)&(Protocols.pt[i]); - // + (i * sizeof(protocol_t))); - out("I3.0.0 "); - out("Protocol element #"); - s[fmt_ulong(s, i)] = 0; - out(s); - out(": \n srvservice: "); - out(proto->srvservice); - out("\n defport: "); - s[fmt_uint(s, proto->defport)] = 0; - out(s); - if (proto->slice < 0) - out("\n no MXPS slice for this protocol"); - else { - l = proto->slice; - out("\n slice: "); - s[fmt_uint(s, l)] = 0; - out(s); - } - switch (proto->tls) { - case 1: - out("\n will use TLS"); - break; - case 0: - switch (proto->starttls) { - case 1: - out("\n can use TLS"); - break; - case 0: - out("\n cannot use TLS"); - break; - } - } - if (proto->maproutes.num != 0) { - out("\n has "); - out(proto->srvservice); - out("routes"); - } else { - out("\n does not have "); - out(proto->srvservice); - out("routes"); - } - switch (proto->lookupdone) { - case 0: - out("\n DNS lookups have not been done"); - break; - case 1: - switch (proto->hasresults) { - case 3: - case 2: - out("\n direct A results exist"); - break; - case 1: - out("\n MX or SRVmail results exist"); - for (j = 0; j < proto->mxresults.len; ++j) { - out("\n port: "); - s[fmt_uint(s, proto->mxresults.mxr[j].port)] = 0; - out(s); - out(" prio: "); - s[fmt_uint(s, proto->mxresults.mxr[j].prio)] = 0; - out(s); - out(" weight: "); - s[fmt_uint(s, proto->mxresults.mxr[j].weight)] = 0; - out(s); - out(" SRV/MX: "); - out(proto->mxresults.mxr[j].ismx ? "MX" : "SRV"); - out(" lookupdone: "); - out(proto->mxresults.mxr[j].lookupdone == 1 ? "yes" : "no"); - out(" has results: "); - out(proto->mxresults.mxr[j].hasresults == 1 ? "yes" : proto->mxresults.mxr[j].hasresults > 1 ? "literal IP " : "no"); - out(" name: "); - buffer_put(buffer_2, proto->mxresults.mxr[j].name.s, proto->mxresults.mxr[j].name.len); - if (proto->mxresults.mxr[j].ipmx.len > 0) { - out("\n first IP result: "); - s[ip46full_fmt(s, &(proto->mxresults.mxr[j].ipmx.ipmx[0].ip))] = 0; - out(s); - } - } - break; - case 0: - out("\n MX or SRVmail results do not exist"); - break; - } - } - out("\n app: "); - buffer_put(buffer_2, proto->app.s, proto->app.len); - out("\n\n"); - buffer_flush(buffer_2); - } - buffer_flush(buffer_2); - } -} - -int lookupsdone_all (protocol_t *protos, int nprotos) -{ - // protos should be a pointer to the beginning of the protocol list as we are using it. - int i = 0; - for (; i < nprotos; ++i) { - if (!lookupsdone((protos + i))) return FALSE; - } - return TRUE; -} - -// so we need to deduce the hostname to feed to tcpclient, then we need to launch tcpclient (s/qmtpc), wait on its exit, and maybe try again with a different hostname -// we could also, if we wanted, be tcpclient ourselves, and just launch things under ucspitlsclient as relevant - -int main (int argcount, char **args) -{ - char **rrecips, *relayhost; - int i = 0, offset = 0, nprotos = 0; unsigned long port; char skipmx = 0, fullresults = 0; - protocol_t *routesproto = NULL, *proto = NULL; ipmx_t ipmx = {IP46FULL_ZERO}; - sigset_t sigset; mxresult_t rhmxr = MXRESULT_SLIP_ZERO; mxrAlloc mxres = TA_ZERO; // mxresu is unsorted - tain shortdl; - - PROG = "mxf-remote"; - - tain_now(&Stamp); - tain_addsec(&Deadline, &Stamp, 2); // eh, the manpages at skarnet's website said to do this so I will - - // check basic configuration - if (argcount < 4) perm_usage(); - if (chdir(auto_qmail) == -1) temp_chdir(); - if (!stralloc_copys(&Host,args[1])) temp_nomem(); - //if (!stralloc_0(&Host)) temp_nomem(); - if (!stralloc_copys(&Sender,args[2])) temp_nomem(); - //if (!stralloc_0(&Sender)) temp_nomem(); - - getcontrols(); - ipme_init(); - ip6me_init(); - - // trap signals - sig_ignore(SIGPIPE); - Sigfd = selfpipe_init(); // selfpipe.h - sigemptyset(&sigset); - sigaddset(&sigset, SIGCHLD); - if (0 == selfpipe_trapset(&sigset)) temp_oserr(); - struct pollfd sigpfd; - sigpfd.fd = Sigfd; - sigpfd.events = POLLIN; - sigpfd.revents = 0; - descriptor_add(sigpfd, (void *)&Protegetab, &checksignals); - - printprotos(); - /* Of interest here is that ... NOTES-mxfr #0010 */ - - /* So at this stage we know what the protocols are and we can decide - * whether we want to skip SRV and MX. - * - * If we do, it's because we have a <protocol>routes file. protocol->maproutes - * will have the scoop. - * ga_foreach doesn't support non-pointer structs, so we make a pointer here. - */ - startdns(&Dnsres); - struct pollfd dnspfd; - dnspfd.fd = skadns_fd(&Dnsres); - dnspfd.events = POLLIN; - dnspfd.revents = 0; - descriptor_add(dnspfd, (void *)&Dnsres, &checkdns); - rrecips = args + 3; - - for (i = 0; i < Protocols.len; i++) { - // proto = (protocol_t *)((Protocols.pt) + (offset = (i * sizeof(protocol_t))) ); // fallback version - proto = (protocol_t *)&(Protocols.pt[i]); - // This is lifted from qmail-remote.c:333 with minimal changes - // to fit the new system. - // I do not purport to understand it. - for (i = 0;i <= Host.len;++i) { - if ((i == 0) || (i == Host.len) || (Host.s[i] == '.')) - if ((relayhost = constmap(&(proto->maproutes),Host.s + i,Host.len - i))) { - skipmx = 1; - routesproto = proto; - nprotos = 1; - } - } - if (relayhost && !*relayhost) relayhost = 0; - - if (relayhost) { - i = str_chr(relayhost,':'); - if (relayhost[i]) { - scan_ulong(relayhost + i + 1,&port); - relayhost[i] = 0; - } - if (!stralloc_copys(&Host,relayhost)) temp_nomem(); - } - - if (skipmx) { - if (!port) port = routesproto->defport; - break; - } - } - - // fire off the SRV/MX DNS volley; wait Timeoutconnect seconds from when - // we're finished bowling to when we give up on run_fds - // also break if a foreach loop of lookupsdone(Protocols.pt[i]) does not return false - if (!skipmx) { - proto = (protocol_t *)((Protocols.pt) + (offset = 0)); // reset the proto pointer - nprotos = Protocols.len; // it's 1 if we are skipping MX and SRVmail - } - - if (relayhost) { - if (!ip46_scan(relayhost, &(ipmx.ip))) { - dns_wantip(routesproto, port, relayhost); - } else { - routesproto->hasresults = 1; - routesproto->lookupdone = 1; - // I guess we're building an mxr then... - rhmxr.port = port; - rhmxr.hasresults = 2; - // prio/wt dealt with - if (!stralloc_copys(&(rhmxr.name), relayhost)) temp_nomem(); - if (!stralloc_0(&(rhmxr.name))) temp_nomem(); - if (!ipmxalloc_catip(&(rhmxr.ipmx), ipmx)) temp_nomem(); - } - } else { - // our DNS volley - dns_wantsrv(proto, nprotos); - dns_wantmx(proto, nprotos); - out("regular DNS...\n"); - } - - tain_now(&Stamp); - tain_addsec(&Deadline, &Stamp, Timeoutconnect); - tain_addsec(&shortdl, &Stamp, 3); - - while (!lookupsdone_all(proto, nprotos)) { - if (run_fds(&shortdl, &Stamp) == 0) info_dbgs("curious, iopause got nothing but the loop went around"); // This'll update the stamp and run callbacks automatically. - tain_addsec(&shortdl, &Stamp, 3); - if (!tain_less(&Stamp, &Deadline)) { - errno = ETIMEDOUT; - skadns_end(&Dnsres); - info_wtfs("DNS lookup event loop took too long - mostly orderly shutdown executed"); // cut... - break; - } - } // should not run if lookupsdone_all already (SLIP relayhost) - - printprotos(); - /* TODO: actually send the letter */ - -} -#endif diff --git a/src/mxf-remote/mxf-remote.do b/src/mxf-remote/mxf-remote.do @@ -1,4 +0,0 @@ -#!/bin/sh -x -redo-ifchange errs.obj mxf-remote.obj typeallocs.obj -redo-ifcreate errs.obj mxf-remote.obj typeallocs.obj -${CCLD:-${CC:-cc}} ${LDFLAGS} -o "$3" "${2}.obj" errs.obj typeallocs.obj /package/web/s6-dns/library/lib*dns.a ../ipalloc.o ../constmap.o ../ip.o ../ipme.o ../fs.a ../case.a ../auto_qmail.o ../control.o ../getln.a ../substdio.a ../str.a ../error.a /package/prog/skalibs/library/libskarnet.a diff --git a/src/mxf-remote/mxf-remote.h b/src/mxf-remote/mxf-remote.h @@ -1,228 +0,0 @@ -#ifndef MXF_REMOTE_H -#define MXF_REMOTE_H - -#include "ga_foreach.h" - -#include "typeallocdefs.h" -#include "typealloc.h" -// I fold, I will use TypeAlloc - -#include <noreturn.h> -#include <control.h> -// Needs control.o from NMMail - -#include <netstrings.h> -// Needs netstrings.o from NMMail - -#include <str.h> -// Needs str from NMMail - -#include <auto_qmail.h> -// Needs auto-qmail from NMMail - -#include <constmap.h> -// Needs constmap from NMMail - -#include <ipme.h> -// Needs IPMe from NMMail - -#include <scan.h> -// Needs scan from NMMail - -#include <fmt.h> -// Needs fmt from NMMail - -#include <uthash.h> -#undef uthash_fatal -#define uthash_fatal(msg) temp_nomem() -// Special - needs UTHash for mutable hash tables. -#ifndef USING_SKALIBS -#error "You must use skalibs, s6-networking and s6-dns for Nightmare Remote." -#else -#include <stdlib.h> -#include <unistd.h> -#include <signal.h> -#include <poll.h> -#include <limits.h> -#include <skalibs/tai.h> -#include <sys/types.h> -#include <skalibs/djbunix.h> -#include <skalibs/types.h> -#include <skalibs/iopause.h> -#include <skalibs/stralloc.h> -#include <skalibs/sig.h> -#include <skalibs/selfpipe.h> -//#include <skalibs/strerr2.h> -#include <skalibs/genalloc.h> -#include <skalibs/socket.h> -#include <skalibs/ip46.h> -#include <s6-dns/s6dns.h> -#include <s6-dns/skadns.h> -#endif - -#ifndef UCSPITLSC -#define UCSPITLSC "/package/net/s6-networking/command/s6-ucspitlsc" -#endif - -#ifndef TLSC -#define TLSC "/package/net/s6-networking/command/s6-tlsc" -#endif - -/* we are tcpclient, so we don't need this -#ifndef TCPCLIENT -#define TCPCLIENT "/package/net/s6-networking/command/s6-tcpclient" -#endif - */ - -// expected to be in PATH and to be ucspi client tools -/* already configured elsewhere -#ifndef QMTP_CLIENT -#define QMTP_CLIENT "mxf-remote-qmtpc" -#endif - -#ifndef SMTP_CLIENT -#define SMTP_CLIENT "mxf-remote-smtpc" -#endif - */ -#define DIE_RESOURCES 111 -#define DIE_BADPROTO 100 - -// Only use these on verified MXPS distances! -// xmxps_prio will never be used, it is just an example -//#define _xmxps_prio(distance) (((distance - MXPS_MAGIC_NO) & 0xfff0) >> 4) -#define _mxps_prio(distance) ((distance & 0x00f0) >> 4) -#define _mxps_slice(distance) (distance & 0x000f) -#define _is_mxps(distance) ((distance + 1 > MXPS_MAGIC_NO) && (distance - 1 < MXPS_END)) - -#define MXPS_MAGIC_NO 0x3200 -#define MXPS_END 0x32ff -/* -#define SRV_TCP "tcp" -#define SRV_QMTP "qmtp" -#define SRV_QMTPS "qmtps" -#define SRV_SMTP "smtp" -#define SRV_SMTPS "smtps" // officially deregistered by IANA; continued here -#define SLICE_LAST 0x3f -#define SLICE_SRV_SMTP 0x010 // all +16 from the kosher slices -#define SLICE_SRV_QMTP 0x011 -#define SLICE_SRV_SMTPS 0x012 -#define SLICE_SRV_QMTPS 0x013 -#define SLICE_SMTP 0x00 -#define SLICE_QMTP 0x01 -#define SLICE_SMTPS 0x02 // we propose the use of 2 and 3 for -s versions of protocols. -#define SLICE_QMTPS 0x03 - */ -#define NOMXPS_SLICE 0x0 -#define MAXSLICES 16 // there will never be more than 16 slices. advancement in internet mail is likely to use SRV records - -// defaults, in case not a SRV record -/* -#define SMTP_PORT 25 -#define SMTPS_PORT 465 // Should be all but extinct; most SMTPS servers support STARTTLS -#define QMTP_PORT 209 -#define QMTPS_PORT 6209 // Not extinct at all; QMTP has no STARTTLS support and will never grow it. -*/ -#define BREAK -1 // call this from an event callback to break the loop -#define FALSE 0 -#define TRUE 1 -#define MAXSRVLEN 64 - -/* stages of the general automaton - */ -//#define - -struct descriptor_t_s; -typedef struct descriptor_t_s descriptor_t; - -struct protocol_t_s; -typedef struct protocol_t_s protocol_t; - -struct process_t_s; -typedef struct process_t_s process_t; - -struct dnsq_t_s; -typedef struct dnsq_t_s dnsq_t; - -typedef struct { - ip46full ip; // ip46full from skalibs -} ipmx_t; - -TypeAlloc_typedef(ipmxAlloc,ipmx_t,ipmx,len,a) - -#define MXRESULT_SRV_ZERO {0, 0, 0, FALSE, FALSE, FALSE, FALSE, STRALLOC_ZERO, TA_ZERO} -#define MXRESULT_MX_ZERO {0, 0, 0, FALSE, FALSE, TRUE, FALSE, STRALLOC_ZERO, TA_ZERO} -#define MXRESULT_SLIP_ZERO {0, 0, 0, TRUE, TRUE, FALSE, FALSE, STRALLOC_ZERO, TA_ZERO} - -typedef struct { - uint16_t port; - uint16_t prio; // 0 for A/AAAA and SLIP; as published for MX and SRV - uint16_t weight; // 0 for MX, A/AAAA, and SLIP; as published for SRV - unsigned int lookupdone:1; // dns - 1 if SLIP - means ip lookups were done - unsigned int hasresults:2; // dns - >1 if SLIP - means has IPs - unsigned int ismx:1; // dns - 0 (SRV or SLIP) is better - unsigned int isme:1; // if, by some tragedy, after prefsorting, this comes to be the first item in a protocol, balk. - stralloc name; // may be a hostname or a SLIP; will not contain ports or priorities. - ipmxAlloc ipmx; // this'll contain the IPs of this !!single hostname!! -} mxresult_t; // stores processed results; SRV hostnames and ports, MX hostnames and ports, IPs (A/AAAA legacy, string literal IPs) - -//TypeAlloc_typedef(dnslAlloc,dnsl_t,dnsl,len,a) -TypeAlloc_typedef(mxrAlloc,mxresult_t,mxr,len,a) -TypeAlloc_typedef(protoAlloc,protocol_t,pt,len,a) -TypeAlloc_typedef(saAlloc,stralloc,sa,len,a) -TypeAlloc_typedef(pfdAlloc,struct pollfd,pfd,len,a) - -#include <typeallocs.h> - -struct descriptor_t_s { - struct pollfd fd; // stored directly in this struct; used solely as an index for the uthash - void* opaque; // a WEAK pointer to an opaque; may be anything or NULL - int (*fdcb) (descriptor_t *, short, void *); - UT_hash_handle hh; -}; - -struct process_t_s { - pid_t pid; - void* opaque; // a WEAK pointer to an opaque - int (*pdiecb) (process_t *, int, void *); - UT_hash_handle hh; -}; // callback when a protegé reports ("child dies") - -struct protocol_t_s { - char srvservice[MAXSRVLEN]; // by happy coincidence we can also use this as the prefix for control/<service>routes filenames - uint16_t defport; // default port - signed char slice; // plain mx = 0 - unsigned int tls:1; // TLSC - unsigned int starttls:1; // UCSPITLSC - unsigned int lookupdone:1; // dns - means lookups were done - unsigned int hasresults:1; // dns - means has names - stralloc app; - struct constmap maproutes; // control/<service>routes - mxrAlloc mxresults; // results of MX DNS lookups -}; - -struct dnsq_t_s { - uint16_t dnsq; - void* opaque; // a WEAK pointer to an opaque - void* opaque2; // a WEAK pointer to an opaque - int (*qcb) (skadns_t *, dnsq_t *, void *, void *); - UT_hash_handle hh; -}; /* this is our ephemeral dns query structure - * opaque should probably be a dnsl_t - */ -/* -struct dnsl_t_s { - protocol_t *proto; // ptr to the protocol this dnsl relates to - char success; // 0 if not, 1 if so - mxrAlloc mxr; // mxr->len ptrs to mxresult_t structures -}; */ - -/* typedef struct { - uint32_t prio; // lower is to try sooner - genalloc mxresults; // &(mxresults.s) is of type mxresult_t -} mxprio_t; */ - -//extern protocol_t protocols; // 16 slices, plus the 4 SRV pseudo slices plus unknown more -//extern buffer errbuf, errbufsmall; -extern char const *PROG; - -#endif diff --git a/src/mxf-remote/mxf-remote.h.sav b/src/mxf-remote/mxf-remote.h.sav @@ -1,105 +0,0 @@ -#ifndef MXF_REMOTE_H -#define MXF_REMOTE_H - -#include "controls.h" -// Needs control.o from qmail -#ifndef USING_SKALIBS -#error "You must use skalibs, s6-networking and s6-dns for Nightmare Remote." -#else -#include <limits.h> -#include <skalibs/ip46.h> -#include <skalibs/tai.h> -#include <skalibs/types.h> -#include <skalibs/iopause.h> -#include <skalibs/stralloc.h> -#include <skalibs/genalloc.h> -#include <skalibs/socket.h> -#include <s6-dns/s6dns.h> -#include <s6-dns/skadns.h> - - -#ifndef UCSPITLSC -#define UCSPITLSC "/package/net/s6-networking/command/s6-ucspitlsc" -#endif - -#ifndef TLSC -#define TLSC "/package/net/s6-networking/command/s6-tlsc" -#endif - -#ifndef TCPCLIENT -#define TCPCLIENT "/package/net/s6-networking/command/s6-tcpclient" -#endif - -// expected to be in PATH and to be ucspi client tools -#ifndef QMTP_CLIENT -#define QMTP_CLIENT "mxf-remote-qmtpc" -#endif - -#ifndef SMTP_CLIENT -#define SMTP_CLIENT "mxf-remote-smtpc" -#endif - -#define DIE_RESOURCES 111 -#define DIE_BADPROTO 100 -//#define DIE_ - -// Only use these on verified MXPS distances! -// xmxps_prio will never be used, it is just an example -//#define _xmxps_prio(distance) (((distance - MXPS_MAGIC_NO) & 0xfff0) >> 4) -#define _mxps_prio(distance) ((distance & 0x00f0) >> 4) -#define _mxps_slice(distance) (distance & 0x000f) -#define _is_mxps(distance) ((distance + 1 > MXPS_MAGIC_NO) && (distance - 1 < MXPS_END)) - -#define MXPS_MAGIC_NO 0x3200 -#define MXPS_END 0x32ff -#define SRV_TCP "tcp" -#define SRV_RQMTP "qmtp-relay" -#define SRV_QMTP "qmtp" -#define SRV_RQMTPS "qmtps-relay" -#define SRV_QMTPS "qmtps" -#define SRV_SMTP "smtp" -#define SRV_RSMTP "smtp-relay" -#define SRV_SMTPS "smtps" // officially deregistered by IANA; continued here -#define SRV_RSMTPS "smtps-relay" -#define SLICE_SRV_SMTP 0x010 // all +32 from the kosher slices -#define SLICE_SRV_QMTP 0x011 -#define SLICE_SRV_SMTPS 0x012 -#define SLICE_SRV_QMTPS 0x013 -#define SLICE_SMTP 0x00 -#define SLICE_QMTP 0x01 -#define SLICE_SMTPS 0x02 // we propose the use of 2 and 3 for -s versions of protocols. -#define SLICE_QMTPS 0x03 -#define NOMXPS_SLICE 0x0 - -// defaults, in case not a SRV record -#define SMTP_PORT 25 -#define SMTPS_PORT 465 // Should be all but extinct; most SMTPS servers support STARTTLS -#define QMTP_PORT 209 -#define QMTPS_PORT 6209 // Not extinct at all; QMTP has no STARTTLS support and will never grow it. - -#define FALSE 0 -#define TRUE 1 - -typedef struct { - //unsigned int slice:4; // plain mx = 0 - char srvservice[64]; - char tool[PATH_MAX]; - unsigned int starttls:1; // UCSPITLSC - unsigned int tls:1; // TLSC -} protocol_t; - -typedef struct { - uint16_t port; - uint16_t weight; // 0 for MX and A/AAAA; as published for SRV - char name[256]; // may be a hostname or an IP; will not contain ports or priorities. - //unsigned int isv6:1; -} mxresult_t; // stores processed results; SRV hostnames and ports, MX hostnames and ports, IPs (A/AAAA legacy) - -typedef struct { - uint32_t prio; // lower is to try sooner; we want a 32 bit value to apply slice-based offsets ((ffff-slice)<<16; try higher slice numbers sooner) - genalloc mxresults; // should be of type mxresult_t -} mxprio_t; - -extern protocol_t protocols[32]; // 16 slices, plus the 4 SRV pseudo slices plus unknown more - -#endif diff --git a/src/mxf-remote/protomapdef b/src/mxf-remote/protomapdef @@ -1,3 +0,0 @@ -qmtps 6209 3 N Y mxf-remote-qmtpc -qmtp 209 1 N N mxf-remote-qmtpc -smtp 25 0 N Y mxf-remote-smtpc diff --git a/src/mxf-remote/typealloc.h b/src/mxf-remote/typealloc.h @@ -1,9 +0,0 @@ -#ifndef TypeAlloc_H // this will be fine though it's nonstandard -#define TypeAlloc_H - -#define TypeAlloc_typedef(ta,type,field,len,a) \ - typedef struct ta { type *field; size_t len; size_t a; } ta; - -#define TA_ZERO {NULL, 0, 0} - -#endif diff --git a/src/mxf-remote/typeallocdefs.h b/src/mxf-remote/typeallocdefs.h @@ -1,33 +0,0 @@ -#ifndef TypeAlloc_DEFS_H -#define TypeAlloc_DEFS_H -/* de https://github.com/notqmail/notqmail */ - -//#include "alloc.h" -#include "error.h" -// needed from qmail - -#include "oflops.h" -// unclear - -#define TypeAlloc_readyplus(ta,type,field,len,a,base,ta_rplus) \ -int ta_rplus(ta *x, size_t n); - -/* this needs a TypeAlloc_readyplus call before as it reuses the internal helper - * function. */ -#define TypeAlloc_ready(ta,type,field,len,a,base,ta_ready) \ -int ta_ready(ta *x, size_t n); - -#define TypeAlloc_append(ta,type,field,len,a,base,ta_rplus,ta_append) \ -int ta_append(ta *x, type i); - -#define TypeAlloc_appendentries(ta,type,field,len,a,base,ta_rplus,ta_cat) \ -int ta_cat(ta *x, type *i, size_t n); - -// example usage -> ta_copy(saa, saa); -#define TypeAlloc_copy(ta,type,field,len,a,base,ta_ready,ta_copy) \ -int ta_copy(ta *x, ta *y); - -#define TypeAlloc_free(ta,type,field,len,a,base,ta_free) \ -int ta_free(ta *x); - -#endif diff --git a/src/mxf-remote/typealloclib.h b/src/mxf-remote/typealloclib.h @@ -1,70 +0,0 @@ -#ifndef TypeAlloc_DEFS_H -#define TypeAlloc_DEFS_H -/* note - this is to make sure this overrides defs if both are included. */ -/* de https://github.com/notqmail/notqmail */ - -//#include "alloc.h" -#include <stdlib.h> -#include "error.h" -// needed from qmail - -#include "oflops.h" -// unclear - -#define TypeAlloc_readyplus(ta,type,field,len,a,base,ta_rplus) \ -static int ta_rplus ## _internal (ta *x, size_t n, size_t pluslen) \ -{ \ - size_t nlen; \ - errno = error_nomem; \ - if (x->field) { \ - size_t nnum; \ - type *nfield; \ - if (__builtin_add_overflow(n, pluslen, &n)) \ - return 0; \ - if (n <= x->a) \ - return 1; \ - if (__builtin_add_overflow(n, (n >> 3) + base, &nnum)) \ - return 0; \ - if (__builtin_mul_overflow(nnum, sizeof(type), &nlen)) \ - return 0; \ - nfield = realloc(x->field, nlen); \ - if (nfield == NULL) \ - return 0; \ - x->field = nfield; \ - x->a = nnum; \ - return 1; } \ - x->len = 0; \ - if (__builtin_mul_overflow(n, sizeof(type), &nlen)) \ - return 0; \ - x->field = (type *) malloc(nlen); \ - if (!x->field) \ - return 0; \ - x->a = n; \ - return 1; } \ -int ta_rplus(ta *x, size_t n) \ -{ return ta_rplus ## _internal (x, n, x->len); } - -/* this needs a TypeAlloc_readyplus call before as it reuses the internal helper - * function. */ -#define TypeAlloc_ready(ta,type,field,len,a,base,ta_ready) \ -int ta_ready(ta *x, size_t n) \ -{ return ta_ready ## plus_internal (x, n, 0); } - -#define TypeAlloc_append(ta,type,field,len,a,base,ta_rplus,ta_append) \ -int ta_append(ta *x, type i) \ -{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = i; return 1; } - -#define TypeAlloc_appendentries(ta,type,field,len,a,base,ta_rplus,ta_cat) \ -int ta_cat(ta *x, type *i, size_t n) \ -{ if (!ta_rplus(x,n)) return 0; for (size_t j = 0; j < n; ++j) x->field[x->len++] = *(i + j); return 1; } - -// example usage -> ta_copy(saa, saa); -#define TypeAlloc_copy(ta,type,field,len,a,base,ta_ready,ta_copy) \ -int ta_copy(ta *x, ta *y) \ -{ if (!ta_ready(x,y->len)) return 0; x->len = 0; for (size_t j = 0; j < y->len; ++j) x->field[x->len++] = *(y->field + j); return 1; } - -#define TypeAlloc_free(ta,type,field,len,a,base,ta_free) \ -int ta_free(ta *x) \ -{ free(x->field); x->field = NULL; x->a = x->len = 0; return 1; } - -#endif diff --git a/src/mxf-remote/typeallocs.c b/src/mxf-remote/typeallocs.c @@ -1,26 +0,0 @@ -#ifndef TYPEALLOCS_H -#define TYPEALLOCS_H -// ugly hack required to avoid redefining -#include <typealloc.h> -#include <typealloclib.h> -//#include <typeallocs.h> -#include <mxf-remote.h> - -TypeAlloc_readyplus(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus) -TypeAlloc_append(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus,protoalloc_catp) - -TypeAlloc_readyplus(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus) -TypeAlloc_append(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus,saalloc_catsa) - -TypeAlloc_readyplus(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus) -TypeAlloc_append(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus,pfdalloc_catpfd) - -//TypeAlloc_readyplus(dnslAlloc,dnsl_t,dnsl,len,a,10,dnslalloc_readyplus) -//TypeAlloc_append(dnslAlloc,dnsl_t,dnsl,len,a,10,dnslalloc_readyplus,dnslalloc_catdnsl) - -TypeAlloc_readyplus(ipmxAlloc,ipmx_t,ipmx,len,a,10,ipmxalloc_readyplus) -TypeAlloc_append(ipmxAlloc,ipmx_t,ipmx,len,a,10,ipmxalloc_readyplus,ipmxalloc_catip) - -TypeAlloc_readyplus(mxrAlloc,mxresult_t,mxr,len,a,10,mxralloc_readyplus) -TypeAlloc_append(mxrAlloc,mxresult_t,mxr,len,a,10,mxralloc_readyplus,mxralloc_catmxr) -#endif diff --git a/src/mxf-remote/typeallocs.h b/src/mxf-remote/typeallocs.h @@ -1,24 +0,0 @@ -#ifndef TYPEALLOCS_H -#define TYPEALLOCS_H -#include <typealloc.h> -#include <typeallocdefs.h> -#include <mxf-remote.h> - -TypeAlloc_readyplus(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus) -TypeAlloc_append(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus,protoalloc_catp) - -TypeAlloc_readyplus(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus) -TypeAlloc_append(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus,saalloc_catsa) - -TypeAlloc_readyplus(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus) -TypeAlloc_append(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus,pfdalloc_catpfd) - -//TypeAlloc_readyplus(dnslAlloc,dnsl_t,dnsl,len,a,10,dnslalloc_readyplus) -//TypeAlloc_append(dnslAlloc,dnsl_t,dnsl,len,a,10,dnslalloc_readyplus,dnslalloc_catdnsl) - -TypeAlloc_readyplus(ipmxAlloc,ipmx_t,ipmx,len,a,10,ipmxalloc_readyplus) -TypeAlloc_append(ipmxAlloc,ipmx_t,ipmx,len,a,10,ipmxalloc_readyplus,ipmxalloc_catip) - -TypeAlloc_readyplus(mxrAlloc,mxresult_t,mxr,len,a,10,mxralloc_readyplus) -TypeAlloc_append(mxrAlloc,mxresult_t,mxr,len,a,10,mxralloc_readyplus,mxralloc_catmxr) -#endif diff --git a/src/mxf-remote/uthash.h b/src/mxf-remote/uthash.h @@ -1,963 +0,0 @@ -/* -Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef UTHASH_H -#define UTHASH_H - -#include <string.h> /* memcmp,strlen */ -#include <stddef.h> /* ptrdiff_t */ -#include <stdlib.h> /* exit() */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ source) this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#if defined(_MSC_VER) /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define DECLTYPE(x) (decltype(x)) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#define DECLTYPE(x) -#endif -#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) -#define NO_DECLTYPE -#define DECLTYPE(x) -#else /* GNU, Sun and other compilers */ -#define DECLTYPE(x) (__typeof(x)) -#endif - -#ifdef NO_DECLTYPE -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - char **_da_dst = (char**)(&(dst)); \ - *_da_dst = (char*)(src); \ -} while(0) -#else -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - (dst) = DECLTYPE(dst)(src); \ -} while(0) -#endif - -/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ -#if defined (_WIN32) -#if defined(_MSC_VER) && _MSC_VER >= 1600 -#include <stdint.h> -#elif defined(__WATCOMC__) -#include <stdint.h> -#else -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#endif -#else -#include <stdint.h> -#endif - -#define UTHASH_VERSION 1.9.9 - -#ifndef uthash_fatal -#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ -#endif -#ifndef uthash_malloc -#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -#endif -#ifndef uthash_free -#define uthash_free(ptr,sz) free(ptr) /* free fcn */ -#endif - -#ifndef uthash_noexpand_fyi -#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -#endif -#ifndef uthash_expand_fyi -#define uthash_expand_fyi(tbl) /* can be defined to log expands */ -#endif - -/* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ -#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ - -/* calculate the element whose hash handle address is hhe */ -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) - -#define HASH_FIND(hh,head,keyptr,keylen,out) \ -do { \ - out=NULL; \ - if (head != NULL) { \ - unsigned _hf_bkt,_hf_hashv; \ - HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv) != 0) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ - keyptr,keylen,out); \ - } \ - } \ -} while (0) - -#ifdef HASH_BLOOM -#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) -#define HASH_BLOOM_MAKE(tbl) \ -do { \ - (tbl)->bloom_nbits = HASH_BLOOM; \ - (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ - memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ -} while (0) - -#define HASH_BLOOM_FREE(tbl) \ -do { \ - uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0) - -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) -#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) - -#define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) - -#define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) - -#else -#define HASH_BLOOM_MAKE(tbl) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) -#define HASH_BLOOM_TEST(tbl,hashv) (1) -#define HASH_BLOOM_BYTELEN 0U -#endif - -#define HASH_MAKE_TABLE(hh,head) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ - sizeof(UT_hash_table)); \ - if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl->buckets, 0, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ -} while(0) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) - -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -do { \ - replaced=NULL; \ - HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ - if (replaced!=NULL) { \ - HASH_DELETE(hh,head,replaced); \ - } \ - HASH_ADD(hh,head,fieldname,keylen_in,add); \ -} while(0) - -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -do { \ - unsigned _ha_bkt; \ - (add)->hh.next = NULL; \ - (add)->hh.key = (char*)(keyptr); \ - (add)->hh.keylen = (unsigned)(keylen_in); \ - if (!(head)) { \ - head = (add); \ - (head)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh,head); \ - } else { \ - (head)->hh.tbl->tail->next = (add); \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail = &((add)->hh); \ - } \ - (head)->hh.tbl->num_items++; \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ - (add)->hh.hashv, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ - HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ - HASH_FSCK(hh,head); \ -} while(0) - -#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ -do { \ - bkt = ((hashv) & ((num_bkts) - 1U)); \ -} while(0) - -/* delete "delptr" from the hash table. - * "the usual" patch-up process for the app-order doubly-linked-list. - * The use of _hd_hh_del below deserves special explanation. - * These used to be expressed using (delptr) but that led to a bug - * if someone used the same symbol for the head and deletee, like - * HASH_DELETE(hh,users,users); - * We want that to work, but by changing the head (users) below - * we were forfeiting our ability to further refer to the deletee (users) - * in the patch-up process. Solution: use scratch space to - * copy the deletee pointer, then the latter references are via that - * scratch pointer rather than through the repointed (users) symbol. - */ -#define HASH_DELETE(hh,head,delptr) \ -do { \ - struct UT_hash_handle *_hd_hh_del; \ - if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - head = NULL; \ - } else { \ - unsigned _hd_bkt; \ - _hd_hh_del = &((delptr)->hh); \ - if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ - (head)->hh.tbl->tail = \ - (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho); \ - } \ - if ((delptr)->hh.prev != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ - } else { \ - DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ - } \ - if (_hd_hh_del->next != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ - (head)->hh.tbl->hho))->prev = \ - _hd_hh_del->prev; \ - } \ - HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ - } \ - HASH_FSCK(hh,head); \ -} while (0) - - -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -#define HASH_FIND_STR(head,findstr,out) \ - HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out) -#define HASH_ADD_STR(head,strfield,add) \ - HASH_ADD(hh,head,strfield[0],(unsigned int)strlen(add->strfield),add) -#define HASH_REPLACE_STR(head,strfield,add,replaced) \ - HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced) -#define HASH_FIND_INT(head,findint,out) \ - HASH_FIND(hh,head,findint,sizeof(int),out) -#define HASH_ADD_INT(head,intfield,add) \ - HASH_ADD(hh,head,intfield,sizeof(int),add) -#define HASH_REPLACE_INT(head,intfield,add,replaced) \ - HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) -#define HASH_FIND_PTR(head,findptr,out) \ - HASH_FIND(hh,head,findptr,sizeof(void *),out) -#define HASH_ADD_PTR(head,ptrfield,add) \ - HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ - HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) -#define HASH_DEL(head,delptr) \ - HASH_DELETE(hh,head,delptr) - -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. - */ -#ifdef HASH_DEBUG -#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head) \ -do { \ - struct UT_hash_handle *_thh; \ - if (head) { \ - unsigned _bkt_i; \ - unsigned _count; \ - char *_prev; \ - _count = 0; \ - for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ - unsigned _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("invalid hh_prev %p, actual %p\n", \ - _thh->hh_prev, _prev ); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("invalid bucket count %u, actual %u\n", \ - (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid hh item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - /* traverse hh in app order; check next/prev integrity, count */ \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev !=(char*)(_thh->prev)) { \ - HASH_OOPS("invalid prev %p, actual %p\n", \ - _thh->prev, _prev ); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ - (head)->hh.tbl->hho) : NULL ); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid app item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - } \ -} while (0) -#else -#define HASH_FSCK(hh,head) -#endif - -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to - * the descriptor to which this macro is defined for tuning the hash function. - * The app can #include <unistd.h> to get the prototype for write(2). */ -#ifdef HASH_EMIT_KEYS -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ -} while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -#endif - -/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION -#define HASH_FCN HASH_FUNCTION -#else -#define HASH_FCN HASH_JEN -#endif - -/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ -#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _hb_keylen=(unsigned)keylen; \ - const unsigned char *_hb_key=(const unsigned char*)(key); \ - (hashv) = 0; \ - while (_hb_keylen-- != 0U) { \ - (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ - } \ - bkt = (hashv) & (num_bkts-1U); \ -} while (0) - - -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _sx_i; \ - const unsigned char *_hs_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ - } \ - bkt = hashv & (num_bkts-1U); \ -} while (0) -/* FNV-1a variation */ -#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _fn_i; \ - const unsigned char *_hf_key=(const unsigned char*)(key); \ - hashv = 2166136261U; \ - for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ - hashv = hashv ^ _hf_key[_fn_i]; \ - hashv = hashv * 16777619U; \ - } \ - bkt = hashv & (num_bkts-1U); \ -} while(0) - -#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _ho_i; \ - const unsigned char *_ho_key=(const unsigned char*)(key); \ - hashv = 0; \ - for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ - hashv += _ho_key[_ho_i]; \ - hashv += (hashv << 10); \ - hashv ^= (hashv >> 6); \ - } \ - hashv += (hashv << 3); \ - hashv ^= (hashv >> 11); \ - hashv += (hashv << 15); \ - bkt = hashv & (num_bkts-1U); \ -} while(0) - -#define HASH_JEN_MIX(a,b,c) \ -do { \ - a -= b; a -= c; a ^= ( c >> 13 ); \ - b -= c; b -= a; b ^= ( a << 8 ); \ - c -= a; c -= b; c ^= ( b >> 13 ); \ - a -= b; a -= c; a ^= ( c >> 12 ); \ - b -= c; b -= a; b ^= ( a << 16 ); \ - c -= a; c -= b; c ^= ( b >> 5 ); \ - a -= b; a -= c; a ^= ( c >> 3 ); \ - b -= c; b -= a; b ^= ( a << 10 ); \ - c -= a; c -= b; c ^= ( b >> 15 ); \ -} while (0) - -#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _hj_i,_hj_j,_hj_k; \ - unsigned const char *_hj_key=(unsigned const char*)(key); \ - hashv = 0xfeedbeefu; \ - _hj_i = _hj_j = 0x9e3779b9u; \ - _hj_k = (unsigned)(keylen); \ - while (_hj_k >= 12U) { \ - _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ - + ( (unsigned)_hj_key[2] << 16 ) \ - + ( (unsigned)_hj_key[3] << 24 ) ); \ - _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ - + ( (unsigned)_hj_key[6] << 16 ) \ - + ( (unsigned)_hj_key[7] << 24 ) ); \ - hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ - + ( (unsigned)_hj_key[10] << 16 ) \ - + ( (unsigned)_hj_key[11] << 24 ) ); \ - \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - \ - _hj_key += 12; \ - _hj_k -= 12U; \ - } \ - hashv += (unsigned)(keylen); \ - switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ - case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ - case 1: _hj_i += _hj_key[0]; \ - } \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - bkt = hashv & (num_bkts-1U); \ -} while(0) - -/* The Paul Hsieh hash function */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif -#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned const char *_sfh_key=(unsigned const char*)(key); \ - uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ - \ - unsigned _sfh_rem = _sfh_len & 3U; \ - _sfh_len >>= 2; \ - hashv = 0xcafebabeu; \ - \ - /* Main loop */ \ - for (;_sfh_len > 0U; _sfh_len--) { \ - hashv += get16bits (_sfh_key); \ - _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ - hashv = (hashv << 16) ^ _sfh_tmp; \ - _sfh_key += 2U*sizeof (uint16_t); \ - hashv += hashv >> 11; \ - } \ - \ - /* Handle end cases */ \ - switch (_sfh_rem) { \ - case 3: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 16; \ - hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ - hashv += hashv >> 11; \ - break; \ - case 2: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 11; \ - hashv += hashv >> 17; \ - break; \ - case 1: hashv += *_sfh_key; \ - hashv ^= hashv << 10; \ - hashv += hashv >> 1; \ - } \ - \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ - bkt = hashv & (num_bkts-1U); \ -} while(0) - -#ifdef HASH_USING_NO_STRICT_ALIASING -/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. - * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. - * MurmurHash uses the faster approach only on CPU's where we know it's safe. - * - * Note the preprocessor built-in defines can be emitted using: - * - * gcc -m64 -dM -E - < /dev/null (on gcc) - * cc -## a.c (where a.c is a simple test file) (Sun Studio) - */ -#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) -#define MUR_GETBLOCK(p,i) p[i] -#else /* non intel */ -#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) -#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) -#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) -#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) -#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) -#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) -#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) -#else /* assume little endian non-intel */ -#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) -#endif -#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ - (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ - (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ - MUR_ONE_THREE(p)))) -#endif -#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) -#define MUR_FMIX(_h) \ -do { \ - _h ^= _h >> 16; \ - _h *= 0x85ebca6bu; \ - _h ^= _h >> 13; \ - _h *= 0xc2b2ae35u; \ - _h ^= _h >> 16; \ -} while(0) - -#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ -do { \ - const uint8_t *_mur_data = (const uint8_t*)(key); \ - const int _mur_nblocks = (int)(keylen) / 4; \ - uint32_t _mur_h1 = 0xf88D5353u; \ - uint32_t _mur_c1 = 0xcc9e2d51u; \ - uint32_t _mur_c2 = 0x1b873593u; \ - uint32_t _mur_k1 = 0; \ - const uint8_t *_mur_tail; \ - const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ - int _mur_i; \ - for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ - _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - \ - _mur_h1 ^= _mur_k1; \ - _mur_h1 = MUR_ROTL32(_mur_h1,13); \ - _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ - } \ - _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ - _mur_k1=0; \ - switch((keylen) & 3U) { \ - case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ - case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ - case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - _mur_h1 ^= _mur_k1; \ - } \ - _mur_h1 ^= (uint32_t)(keylen); \ - MUR_FMIX(_mur_h1); \ - hashv = _mur_h1; \ - bkt = hashv & (num_bkts-1U); \ -} while(0) -#endif /* HASH_USING_NO_STRICT_ALIASING */ - -/* key comparison function; return 0 if keys equal */ -#define HASH_KEYCMP(a,b,len) memcmp(a,b,(unsigned long)(len)) - -/* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ -do { \ - if (head.hh_head != NULL) { DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); } \ - else { out=NULL; } \ - while (out != NULL) { \ - if ((out)->hh.keylen == (keylen_in)) { \ - if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) { break; } \ - } \ - if ((out)->hh.hh_next != NULL) { DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); } \ - else { out = NULL; } \ - } \ -} while(0) - -/* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,addhh) \ -do { \ - head.count++; \ - (addhh)->hh_next = head.hh_head; \ - (addhh)->hh_prev = NULL; \ - if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ - (head).hh_head=addhh; \ - if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ - && ((addhh)->tbl->noexpand != 1U)) { \ - HASH_EXPAND_BUCKETS((addhh)->tbl); \ - } \ -} while(0) - -/* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(hh,head,hh_del) \ - (head).count--; \ - if ((head).hh_head == hh_del) { \ - (head).hh_head = hh_del->hh_next; \ - } \ - if (hh_del->hh_prev) { \ - hh_del->hh_prev->hh_next = hh_del->hh_next; \ - } \ - if (hh_del->hh_next) { \ - hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } - -/* Bucket expansion has the effect of doubling the number of buckets - * and redistributing the items into the new buckets. Ideally the - * items will distribute more or less evenly into the new buckets - * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * - * With the items distributed into more buckets, the chain length - * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain - * length is the essence of how a hash provides constant time lookup. - * - * The calculation of tbl->ideal_chain_maxlen below deserves some - * explanation. First, keep in mind that we're calculating the ideal - * maximum chain length based on the *new* (doubled) bucket count. - * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate - * ceil(n/b). We don't depend on floating point arithmetic in this - * hash, so to calculate ceil(n/b) with integers we could write - * - * ceil(n/b) = (n/b) + ((n%b)?1:0) - * - * and in fact a previous version of this hash did just that. - * But now we have improved things a bit by recognizing that b is - * always a power of two. We keep its base 2 log handy (call it lb), - * so now we can write this with a bit shift and logical AND: - * - * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * - */ -#define HASH_EXPAND_BUCKETS(tbl) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ - memset(_he_new_buckets, 0, \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - tbl->ideal_chain_maxlen = \ - (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ - (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ - tbl->nonideal_items = 0; \ - for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ - { \ - _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh != NULL) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ - if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ - tbl->nonideal_items++; \ - _he_newbkt->expand_mult = _he_newbkt->count / \ - tbl->ideal_chain_maxlen; \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ - _he_thh; } \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ - } \ - } \ - uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - tbl->num_buckets *= 2U; \ - tbl->log2_num_buckets++; \ - tbl->buckets = _he_new_buckets; \ - tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ - (tbl->ineff_expands+1U) : 0U; \ - if (tbl->ineff_expands > 1U) { \ - tbl->noexpand=1; \ - uthash_noexpand_fyi(tbl); \ - } \ - uthash_expand_fyi(tbl); \ -} while(0) - - -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. - * HASH_SRT was added to allow the hash handle name to be passed in. */ -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -#define HASH_SRT(hh,head,cmpfcn) \ -do { \ - unsigned _hs_i; \ - unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ - struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ - if (head != NULL) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping != 0U) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p != NULL) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ - _hs_psize++; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - if (! (_hs_q) ) { break; } \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ - if (_hs_psize == 0U) { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else if (( \ - cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ - ) <= 0) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail != NULL ) { \ - _hs_tail->next = ((_hs_e != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - if (_hs_e != NULL) { \ - _hs_e->prev = ((_hs_tail != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ - } \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ - } \ - if (_hs_tail != NULL){ \ - _hs_tail->next = NULL; \ - } \ - if ( _hs_nmerges <= 1U ) { \ - _hs_looping=0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ - } \ - _hs_insize *= 2U; \ - } \ - HASH_FSCK(hh,head); \ - } \ -} while (0) - -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash - * hash handle that must be present in the structure. */ -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -do { \ - unsigned _src_bkt, _dst_bkt; \ - void *_last_elt=NULL, *_elt; \ - UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ - ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if (src != NULL) { \ - for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh != NULL; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ - if (dst == NULL) { \ - DECLTYPE_ASSIGN(dst,_elt); \ - HASH_MAKE_TABLE(hh_dst,dst); \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ - (dst)->hh_dst.tbl->num_items++; \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ - } \ - } \ - } \ - } \ - HASH_FSCK(hh_dst,dst); \ -} while (0) - -#define HASH_CLEAR(hh,head) \ -do { \ - if (head != NULL) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)=NULL; \ - } \ -} while(0) - -#define HASH_OVERHEAD(hh,head) \ - ((head != NULL) ? ( \ - (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ - ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ - sizeof(UT_hash_table) + \ - (HASH_BLOOM_BYTELEN))) : 0U) - -#ifdef NO_DECLTYPE -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#else -#define HASH_ITER(hh,head,el,tmp) \ -for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ - (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) -#endif - -/* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) -#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) - -typedef struct UT_hash_bucket { - struct UT_hash_handle *hh_head; - unsigned count; - - /* expand_mult is normally set to 0. In this situation, the max chain length - * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). - * However, setting expand_mult to a non-zero value delays bucket expansion - * (that would be triggered by additions to this particular bucket) - * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. - * (The multiplier is simply expand_mult+1). The whole idea of this - * multiplier is to reduce bucket expansions, since they are expensive, in - * situations where we know that a particular bucket tends to be overused. - * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. - */ - unsigned expand_mult; - -} UT_hash_bucket; - -/* random signature used only to find hash tables in external analysis */ -#define HASH_SIGNATURE 0xa0111fe1u -#define HASH_BLOOM_SIGNATURE 0xb12220f2u - -typedef struct UT_hash_table { - UT_hash_bucket *buckets; - unsigned num_buckets, log2_num_buckets; - unsigned num_items; - struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ - ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ - - /* in an ideal situation (all buckets used equally), no bucket would have - * more than ceil(#items/#buckets) items. that's the ideal chain length. */ - unsigned ideal_chain_maxlen; - - /* nonideal_items is the number of items in the hash whose chain position - * exceeds the ideal chain maxlen. these items pay the penalty for an uneven - * hash distribution; reaching them in a chain traversal takes >ideal steps */ - unsigned nonideal_items; - - /* ineffective expands occur when a bucket doubling was performed, but - * afterward, more than half the items in the hash had nonideal chain - * positions. If this happens on two consecutive expansions we inhibit any - * further expansion, as it's not helping; this happens when the hash - * function isn't a good fit for the key domain. When expansion is inhibited - * the hash will still work, albeit no longer in constant time. */ - unsigned ineff_expands, noexpand; - - uint32_t signature; /* used only to find hash tables in external analysis */ -#ifdef HASH_BLOOM - uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ - uint8_t *bloom_bv; - uint8_t bloom_nbits; -#endif - -} UT_hash_table; - -typedef struct UT_hash_handle { - struct UT_hash_table *tbl; - void *prev; /* prev element in app order */ - void *next; /* next element in app order */ - struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ - struct UT_hash_handle *hh_next; /* next hh in bucket order */ - void *key; /* ptr to enclosing struct's key */ - unsigned keylen; /* enclosing struct's key len */ - unsigned hashv; /* result of hash-fcn(key) */ -} UT_hash_handle; - -#endif /* UTHASH_H */ diff --git a/src/qmail-qmqpd.c b/src/qmail-qmqpd.c @@ -37,7 +37,7 @@ substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof(ssinbuf)); char ssoutbuf[256]; substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof(ssoutbuf)); -unsigned long bytesleft = 100; +unsigned long long bytesleft = 100; void getbyte(ch) char *ch; @@ -46,9 +46,9 @@ char *ch; substdio_get(&ssin,ch,1); } -unsigned long getlen() +unsigned long long getlen() { - unsigned long len = 0; + unsigned long long len = 0; switch (ns_getlength(&ssin,&len)) { case NS_SUCCESS: return len; break; case NS_TOOMUCH: resources(); break; @@ -110,7 +110,7 @@ char strnum[FMT_ULONG]; int getbuf() { - unsigned long len; + unsigned long long len; int i; len = getlen(); @@ -133,8 +133,8 @@ int main() { char *result; - unsigned long qp; - unsigned long len; + unsigned long long qp; + unsigned long long len; char ch; sig_pipeignore(); diff --git a/src/qmail-queue.c b/src/qmail-queue.c @@ -19,6 +19,7 @@ #include "auto_users.h" #include "date822fmt.h" #include "fmtqfn.h" +#include "error.h" #define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ #define ADDR 1003 @@ -27,6 +28,8 @@ char inbuf[2048]; struct substdio ssin; char outbuf[256]; struct substdio ssout; +char errbuf[256]; +struct substdio sserr; datetime_sec starttime; struct datetime dt; @@ -194,7 +197,11 @@ int main(void) todofn = fnnum("todo/",0); intdfn = fnnum("intd/",0); - if (link(pidfn,messfn) == -1) die(64); + substdio_fdbuf(&sserr,write,2,errbuf,sizeof(errbuf)); + if (link(pidfn,messfn) == -1) { + substdio_putsflush(&sserr, error_str(errno)); + die(64); + } if (unlink(pidfn) == -1) die(63); flagmademess = 1;