nightmaremail

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

mxf-remote.c (41863B)


      1 /* src/mxf-remote/mxf-remote.c
      2  * requires skalibs, s6-networking and s6-dns; if not, you must
      3  * use regular qmail-remote with the attendant loss of
      4  * functionality with qmtp, tls and ipv6.
      5  */
      6 #ifndef mxf_remote_c
      7 #define mxf_remote_c
      8 #include "mxf-remote.h"
      9 #include "errs.h"
     10 //#include <s6-dns/skadns.h>
     11 
     12 tain		Deadline, Stamp, Limit;
     13 //protocol_t	protocols[SLICE_LAST];
     14 //TypeAlloc_typedef(protoAlloc,protocol_t,pt,len,a)
     15 //TypeAlloc_readyplus(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus)
     16 //TypeAlloc_append(protoAlloc,protocol_t,pt,len,a,10,protoalloc_readyplus,protoalloc_catp)
     17 protoAlloc	Protocols = TA_ZERO; // genalloc_*(protocol_t
     18 //genalloc	maproutes = GENALLOC_ZERO; // genalloc_*(constmap
     19 char Errbuf[512];
     20 buffer buffer_2_ = BUFFER_INIT(&buffer_write, 2, Errbuf, 512);
     21 //char		Errbufsmall[256]; // no use yet
     22 skadns_t	Dnsres = SKADNS_ZERO;
     23 //char		namechosen[513]; // That's more bytes than we will ever need.
     24 //char		progname[61] = "mxf-remote";
     25 //stralloc	sa	= STRALLOC_ZERO; // ?
     26 int Timeoutconnect = 60; int Mtpfd;
     27 stralloc Host = STRALLOC_ZERO, Sender = STRALLOC_ZERO, Protocolsraw = STRALLOC_ZERO;
     28 stralloc Ucspitlsclient = STRALLOC_ZERO, Tlsclient = STRALLOC_ZERO;
     29 //genalloc recips = GENALLOC_ZERO; // will be filled with strallocs
     30 //struct constmap maproutes[SLICE_LAST];
     31 char PROTOMAPTEXT[] = "qmtps 6209 3 Y N mxf-remote-qmtpc\n" \
     32 	"qmtp 209 1 N N mxf-remote-qmtpc\n" \
     33 	"smtp 25 0 N Y mxf-remote-smtpc\n";
     34 protocol_t		*Slicemap[MAXSLICES];
     35 //genalloc	Descriptors; // of type descriptor_t
     36 //TypeAlloc_typedef(saAlloc,stralloc,sa,len,a)
     37 //TypeAlloc_readyplus(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus)
     38 //TypeAlloc_append(saAlloc,stralloc,sa,len,a,10,saalloc_readyplus,saalloc_catsa)
     39 //TypeAlloc_typedef(pfdAlloc,struct pollfd,pfd,len,a)
     40 //TypeAlloc_readyplus(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus)
     41 //TypeAlloc_append(pfdAlloc,struct pollfd,pfd,len,a,10,pfdalloc_readyplus,pfdalloc_catpfd)
     42 saAlloc			Rcptlist = TA_ZERO;
     43 int				Sigfd;
     44 signed int		ReadyToSend = 0; /*
     45 	* Set this to -1 when the remote domain doesn't accept mail, or 1 when
     46 	* we have a complete (unsorted) picture of how they do.
     47 	*/
     48 
     49 descriptor_t	*Descriptortab = NULL; // NOTES-mxfr - A word on memory management - strong pointers
     50 process_t		*Protegetab = NULL; // NOTES-mxfr - A word on memory management
     51 dnsq_t			*Dnsqtab = NULL; // NOTES-mxfs - A word on memory management
     52 
     53 void getroutes (protocol_t *protocol);
     54 
     55 // in order: pollfd (single entry), opaque (may be NULL), fdcb(descriptor_t*, void*)
     56 int descriptor_add (struct pollfd fd, void* opaque,
     57 		int (*fdcb)(descriptor_t*, short, void*))
     58 {
     59 	descriptor_t	*descriptor = NULL;
     60 
     61 	HASH_FIND_INT(Descriptortab, &(fd.fd), descriptor);
     62 	if (descriptor != NULL) return (errno = ENOENT, FALSE);
     63 	// Otherwise...
     64 	descriptor = malloc(sizeof(descriptor_t)); // strong pointer; if we cannot add this to the hash, we must free it
     65 	descriptor->fd = fd;
     66 	descriptor->opaque = opaque;
     67 	descriptor->fdcb = fdcb;
     68 	HASH_ADD_INT(Descriptortab, fd.fd, descriptor);
     69 	return 1; // if we are returning at all, we have succeeded.
     70 }
     71 
     72 // in order: descriptortab, pollfd (single entry)
     73 // returns true on success, false on nonexistent
     74 int descriptor_del (struct pollfd fd)
     75 {
     76 	descriptor_t	*descriptor = NULL;
     77 
     78 	HASH_FIND_INT(Descriptortab, &(fd.fd), descriptor);
     79 	if (descriptor == NULL) return FALSE;
     80 	// Otherwise...
     81 	HASH_DEL(Descriptortab, descriptor);
     82 	free(descriptor);
     83 	return TRUE; // if we are returning at all, we have succeeded.
     84 }
     85 
     86 // in order: processtab, process ID, opaque to be passed to the report callback, report callback
     87 int protege_add (pid_t pid, void* opaque,
     88 		int (*pdiecb)(process_t*, int, void*))
     89 {
     90 	process_t		*protege = NULL;
     91 
     92 	HASH_FIND(hh, Protegetab, &pid, sizeof(pid_t), protege);
     93 	if (protege != NULL) temp_hasherr();
     94 	// Otherwise...
     95 	protege = malloc(sizeof(descriptor_t)); // strong pointer; if we cannot add this to the hash, we must free it
     96 	protege->pid = pid;
     97 	protege->opaque = opaque;
     98 	protege->pdiecb = pdiecb;
     99 	HASH_ADD(hh, Protegetab, pid, sizeof(pid_t), protege);
    100 	return 1; // if we are returning at all, we have succeeded.
    101 }
    102 // in order: descriptortab, pollfd (single entry)
    103 // returns true on success, false on nonexistent
    104 int protege_del (pid_t pid)
    105 {
    106 	process_t		*protege = NULL;
    107 
    108 	HASH_FIND(hh, Protegetab, &(pid), sizeof(pid_t), protege);
    109 	if (protege == NULL) return (errno = ENOENT, FALSE);
    110 	HASH_DEL(Protegetab, protege);
    111 	free(protege);
    112 	return TRUE;
    113 }
    114 
    115 // in order: dnsqtab, process ID, opaque to be passed to the report callback, report callback
    116 int dnsq_add (uint16_t dnsqid, void* opaque, void* opaque2,
    117 		int (*qcb)(skadns_t*, dnsq_t*, void*, void*))
    118 {
    119 	dnsq_t			*dnsq = NULL;
    120 
    121 	HASH_FIND(hh, Dnsqtab, &dnsqid, sizeof(uint16_t), dnsq);
    122 	if (dnsq != NULL) temp_hasherr();
    123 	// Otherwise...
    124 	dnsq = malloc(sizeof(dnsq_t)); // strong pointer; if we cannot add this to the hash, we must free it
    125 	dnsq->dnsq = dnsqid;
    126 	dnsq->opaque = opaque;
    127 	dnsq->opaque2 = opaque2;
    128 	dnsq->qcb = qcb;
    129 	HASH_ADD(hh, Dnsqtab, dnsq, sizeof(uint16_t), dnsq);
    130 	return TRUE; // if we are returning at all, we have succeeded.
    131 }
    132 // in order: dnsqtab, pollfd (single entry)
    133 // returns true on success, false on nonexistent
    134 int dnsq_del (uint16_t dnsqid)
    135 {
    136 	dnsq_t			*dnsq = NULL;
    137 
    138 	HASH_FIND(hh, Dnsqtab, &(dnsqid), sizeof(uint16_t), dnsq);
    139 	if (dnsq == NULL) return (errno = ENOENT, FALSE);
    140 	HASH_DEL(Dnsqtab, dnsq);
    141 	free(dnsq);
    142 	return TRUE;
    143 }
    144 
    145 int run_fds (tain *deadline, tain *stamp)
    146 {
    147 	pfdAlloc		 fds = TA_ZERO; // pollfd
    148 	descriptor_t	 *des = NULL, *tmp = NULL;
    149 	struct pollfd *pfds = NULL; size_t pfdsiz = 0; size_t i = 0;
    150 	int pollret = 0;
    151 	HASH_ITER(hh, Descriptortab, des, tmp) {
    152 		if (!pfdalloc_catpfd(&fds, (des->fd))) temp_nomem();
    153 		des->fd.revents = 0;
    154 		++pfdsiz;
    155 	}
    156 	if (pfdsiz == 0) temp_runfds();
    157 	des = NULL; tmp = NULL;
    158 	pfds = (struct pollfd *)(fds.pfd);
    159 	pollret = iopause_stamp(pfds, pfdsiz, deadline, stamp);
    160 	// and then we wait for iopause to do its thing
    161 	// and then iopause wakes us up
    162 	switch (pollret) {
    163 		case 0:
    164 			// we literally have nothing to do. we return. this isn't an error state.
    165 			return pollret;
    166 			break;
    167 		case -1:
    168 			// errors AGAIN, FAULT, INTR, INVAL
    169 			switch (errno) {
    170 				case EAGAIN:
    171 					return 0; // we will just get called again
    172 					break;
    173 				case EFAULT:
    174 					temp_fault();
    175 					break;
    176 				case EINTR:
    177 					temp_intr(); // an uncaught signal has come our way
    178 					break;
    179 				case EINVAL:
    180 					temp_bug(); // invalid data
    181 					break;
    182 			}
    183 		default :
    184 			for (i = 0; i < pfdsiz; i++) {
    185 				HASH_FIND_INT(Descriptortab, &(pfds[i].fd), des);
    186 				if (des == NULL) {
    187 					temp_finderr(); // could not find the file descriptor in our tables - what kind of wiseguy do we have on here?
    188 				}
    189 				if (((pfds[i]).revents & POLLNVAL) != 0) {
    190 					// file descriptor is not known to the system; bug
    191 					temp_bug();
    192 				} else if ((pfds[i]).revents != 0) {
    193 					(*(des->fdcb))(des, (pfds[i]).revents, des->opaque);
    194 				}
    195 			}
    196 	}
    197 	// done with fds now? right, we return the number of FDs we processed.
    198 	free((fds.pfd));
    199 	return pollret;
    200 }
    201 
    202 int handledeadprocess ()
    203 {
    204 	process_t		*protege = NULL;
    205 	pid_t			pid = 0;
    206 	int				wstat;
    207 	while ((pid = wait_nohang(&wstat)) > 0) {
    208 		HASH_FIND(hh, Protegetab, &pid, sizeof(pid_t), protege);
    209 		if (protege == NULL) continue; // doesn't really matter
    210 		(*protege->pdiecb)(protege, wstat, protege->opaque);
    211 		protege_del(pid);
    212 	}
    213 	return TRUE;
    214 }
    215 
    216 int checksignals (descriptor_t *descr, short revents, void* unused)
    217 {
    218 	int sig;
    219 	sig = selfpipe_read();
    220 
    221 	switch (sig) {
    222 		case SIGCHLD:
    223 			return handledeadprocess();
    224 			break;
    225 		default :
    226 			return FALSE;// Advanced wiseguy shit
    227 	}
    228 }
    229 
    230 int protocols_init (protoAlloc *protos, char *protomaptext, size_t length)
    231 {
    232 	size_t i = 0;
    233 	char field = 0; /* there are six fields, numbered zero to five
    234 			* their identities are:
    235 			* protoname, port, protoslice, cantls, musttls, executable-filename
    236 			*/
    237 	size_t lpos = 0, fpos = 0; // record the position we are at in the line
    238 	char comment = 0; char s[15]; //true if line is comment
    239 
    240 	for (i = 0; i < MAXSLICES; i++) {
    241 		Slicemap[i] = NULL;
    242 	}
    243 	if (length == 0) {
    244 		protomaptext = PROTOMAPTEXT;
    245 		length = strlen(PROTOMAPTEXT); // that string has no funny business with nulls so we can do this
    246 	}
    247 	protocol_t proto;
    248 	memset(&proto, 0, sizeof(protocol_t));
    249 	proto.slice = -1;
    250 
    251 	for (i = lpos = fpos = 0; i < length; ++i) {
    252 		buffer_put(buffer_2, &(protomaptext[i]), 1);
    253 		switch (protomaptext[i]) {
    254 /*			case '\r': // activate this when we have our own control_readfile; until then it's no use
    255 				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.
    256 			case '\n': // still needed by the default protomap
    257 			case 0:
    258 				switch (fpos) {
    259 					case 0: continue; // it was an empty line
    260 					default : break;
    261 				}
    262 				fpos = -1;
    263 				lpos = 0;
    264 				field = 0;
    265 				comment = 0;
    266 				getroutes(&proto);
    267 				if (!stralloc_0(&(proto.app))) temp_nomem();
    268 				buffer_puts(buffer_2, proto.srvservice[0] != 0 ? "srvservice[0] is not null\n" :"srvservice[0] is null\n");
    269 				if (proto.srvservice[0] != 0) if (!protoalloc_catp(protos, proto)) temp_nomem();
    270 				if (proto.slice > -1 && proto.slice < MAXSLICES) Slicemap[proto.slice] = protos->pt + protos->len-1;
    271 				buffer_puts(buffer_2, "reset\n");
    272 				memset(&proto, 0, sizeof(protocol_t));
    273 				proto.slice = -1;
    274 				break;
    275 			case ' ':
    276 				switch (comment) {
    277 					case 1: continue;
    278 					default : break;
    279 				}
    280 				switch (field) {
    281 					case 5:
    282 						if (!stralloc_catb(&(proto.app), &(protomaptext[i]), 1)) temp_nomem();
    283 						break;
    284 					case 0:
    285 						switch (fpos) {
    286 							case 0:
    287 							++comment;
    288 							break;
    289 							default :
    290 							proto.srvservice[fpos] = 0; // nullcap the srvservice
    291 							fpos = 0;
    292 							++field;
    293 						}
    294 						break;
    295 					case 1:
    296 					case 2:
    297 					case 3:
    298 					case 4:
    299 						fpos = -1;
    300 						++field;
    301 						break;
    302 				}
    303 				break;
    304 			case '\\':
    305 				switch (comment) {
    306 					case 1: continue;
    307 					default : break;
    308 				}
    309 				// The synopsis of \ is: Take next character verbatim into current field, no matter what it is.
    310 				switch (field) {
    311 					case 5:
    312 						if (fpos > PATH_MAX) temp_control(); // malformed
    313 						if (i >= length) temp_control(); // malformed
    314 						if (protomaptext[i] == 0) temp_control(); // malformed
    315 						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.
    316 						break;
    317 					case 0:
    318 						proto.srvservice[fpos] = protomaptext[++i];
    319 					case 1: // It is ignorable in fields 2-5.
    320 					case 2:
    321 					case 3:
    322 					case 4:
    323 						if (i >= length) temp_control(); // malformed
    324 						if (protomaptext[i] == 0) temp_control(); // malformed
    325 						++lpos;
    326 						break;
    327 				}
    328 				break;
    329 			default :
    330 				switch (comment) {
    331 					case 1: continue;
    332 					default : break;
    333 				}
    334 				switch (field) {
    335 					case 0: // protoname
    336 						if (fpos < MAXSRVLEN) proto.srvservice[fpos] = protomaptext[i];
    337 						break;
    338 					case 5: // protoprog (app)
    339 						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.
    340 						break;
    341 					case 1: // defport
    342 						if (protomaptext[i] < '0') temp_control();
    343 						if (protomaptext[i] > '9') temp_control();
    344 						proto.defport = (proto.defport * 10) + (protomaptext[i] - '0');
    345 						break;
    346 					case 2: // MXPS slice, DECIMAL
    347 						buffer_puts(buffer_2, " field:");
    348 						s[fmt_ulong(s, field)] = 0;
    349 						buffer_puts(buffer_2, s);
    350 						if (fpos == 0 && !(protomaptext[i] == '-')) proto.slice = 0; // reset slice to 0 as it is -1
    351 						if (proto.slice != -1) { // a slice of -1 means we are not using a slice and this is a SRV only protocol
    352 							if (!(protomaptext[i] < '0' || protomaptext[i] > '9')) {
    353 								proto.slice = (proto.slice * 10) + (protomaptext[i] - '0');
    354 								s[fmt_ulong(s, proto.slice)] = 0;
    355 								buffer_puts(buffer_2, " slice:");
    356 								buffer_puts(buffer_2, s);
    357 								buffer_puts(buffer_2, " ");
    358 							} else {
    359 								info_dbgs("detected non-numeric character in slice number ");
    360 								out("it was ");
    361 								buffer_put(buffer_2, &protomaptext[i], 1);
    362 								info_dbgs("");
    363 								temp_control();
    364 							}
    365 						}
    366 						break;
    367 					case 3: // is TLS
    368 						switch (fpos) {
    369 							case 0: // we only care about char 1
    370 							if (
    371 								protomaptext[i] == '0'
    372 								|| protomaptext[i] == 'n'
    373 								|| protomaptext[i] == 'N'
    374 								|| protomaptext[i] == 'f'
    375 								|| protomaptext[i] == 'F'
    376 							) proto.tls = 0;
    377 							else if (
    378 								protomaptext[i] == '1'
    379 								|| protomaptext[i] == 'y'
    380 								|| protomaptext[i] == 'Y'
    381 								|| protomaptext[i] == 't'
    382 								|| protomaptext[i] == 'T'
    383 							) proto.tls = 1;
    384 							else temp_control();
    385 							// in default of a valid choice, we have no choice but to blow up.
    386 							default : break;
    387 						}
    388 						break;
    389 					case 4: // can TLS (irrelevant if is TLS)
    390 						switch (fpos + proto.tls) {
    391 							case 0: // we only care about char 1
    392 							if (
    393 								protomaptext[i] == '0'
    394 								|| protomaptext[i] == 'n'
    395 								|| protomaptext[i] == 'N'
    396 								|| protomaptext[i] == 'f'
    397 								|| protomaptext[i] == 'F'
    398 							) proto.tls = 0;
    399 							else if (
    400 								protomaptext[i] == '1'
    401 								|| protomaptext[i] == 'y'
    402 								|| protomaptext[i] == 'Y'
    403 								|| protomaptext[i] == 't'
    404 								|| protomaptext[i] == 'T'
    405 							) proto.tls = 1;
    406 							else temp_control();
    407 							// in default of a valid choice, we have no choice but to blow up.
    408 							default : break;
    409 						}
    410 						break;
    411 				}
    412 		}
    413 		++lpos; ++fpos; // if comment notreached; lpos and fpos don't run if we are in a comment.
    414 		// lpos and fpos only increase 1 for every 2 characters if every first character is a backstroke
    415 	}
    416 	// 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.
    417 	if (proto.app.len > 1) {
    418 		getroutes(&proto);
    419 		if (!protoalloc_catp(protos, proto)) return 0;
    420 	}
    421 	return 1;
    422 }
    423 
    424 
    425 // imported from qmail-remote
    426 // inputs: controlfiles from the filesystem
    427 // outputs: none
    428 // SE: reads control files
    429 
    430 void getcontrols()
    431 {
    432 	if (control_init() == -1) temp_control();
    433 	//if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
    434 	// The client application is responsible for this.
    435 	if (control_readint(&Timeoutconnect,"control/timeoutconnect") == -1)
    436 		temp_control();
    437 //	if (control_rldef(&helohost,"control/helohost",1,NULL) != 1)
    438 //		temp_control();
    439 	if (control_rldef(&Ucspitlsclient,"control/ucspitlsclient",0,UCSPITLSC) != 1)
    440 		temp_control();
    441 	if (control_rldef(&Ucspitlsclient,"control/tlsclient",0,TLSC) != 1)
    442 		temp_control();
    443 	switch(control_readfile(&Protocolsraw,"control/protocols",0)) {
    444 		case -1:
    445 			temp_control();
    446 		case 0:
    447 			if (!protocols_init(&(Protocols),"",0)) temp_nomem(); break;
    448 		case 1:
    449 			if (!protocols_init(&(Protocols),Protocolsraw.s,Protocolsraw.len)) temp_nomem(); break;
    450 	}
    451 	// It strikes me that Protocolsraw is used nowhere else in the file.
    452 }
    453 
    454 void getroutes (protocol_t *protocol)
    455 {
    456 	stralloc ctrl = STRALLOC_ZERO, routes = STRALLOC_ZERO;
    457 	stralloc_copys(&ctrl, "control/");
    458 	stralloc_cats(&ctrl, protocol->srvservice);
    459 	stralloc_cats(&ctrl, "routes");
    460 	stralloc_0(&ctrl);
    461 	switch(control_readfile(&routes,ctrl.s,0)) {
    462 		case -1: temp_control();
    463 		case 0:
    464 			if (!constmap_init(&(protocol->maproutes),"",0,1)) temp_nomem(); break;
    465 		case 1:
    466 			if (!constmap_init(&(protocol->maproutes),routes.s,routes.len,1)) temp_nomem(); break;
    467 	}
    468 	stralloc_free(&ctrl);
    469 }
    470 
    471 int checkdns (descriptor_t *descr, short revents, void* dnsresv)
    472 {
    473 	skadns_t *dnsres = (skadns_t *)dnsresv;
    474 	int upd; size_t dnsqlen = 0, i = 0;
    475 	uint16_t *dnsqs = NULL; dnsq_t *dnsq = NULL;
    476 	upd = skadns_update(dnsres);
    477 	switch (upd) {
    478 		case -1:
    479 			temp_dnsupd();
    480 			return FALSE; // NOTREACHED we should not be here but...
    481 			break;
    482 		default : // there either has, or has not been, a response to collect. let's do that now.
    483 			dnsqs = genalloc_s(uint16_t, &dnsres->list); // have to use genalloc here as that's what Ska uses in skadns
    484 			dnsqlen = genalloc_len(uint16_t, &dnsres->list);
    485 			for (i = 0; i < dnsqlen; ++i) {
    486 				// we must now call the callback and delete the query from the list
    487 				HASH_FIND(hh, Dnsqtab, (dnsqs + i), sizeof(uint16_t), dnsq);
    488 				(*dnsq->qcb)(dnsres, dnsq, (dnsq->opaque), (dnsq->opaque2)); // not exactly sure the use of passing the full query if we'll just delet it
    489 				skadns_release(dnsres, dnsqs[i]);
    490 				dnsq_del(dnsqs[i]);
    491 				dnsq = NULL;
    492 			}
    493 	}
    494 	return TRUE;
    495 }
    496 
    497 void startdns (skadns_t *dnsres) {
    498 	if (!skadns_startf(dnsres, &Deadline, &Stamp)) {
    499 		buffer_puts(buffer_2, "ZDNS resolution startup timeout\n");
    500 		buffer_flush(buffer_2);
    501 		exit(111);
    502 	}
    503 }
    504 
    505 int ipisme (const ipmx_t *ip) {
    506 	// wrapper around ipme and ip6me
    507 	const ip46full	*ix = &(ip->ip);
    508 	struct ip_address	i4;
    509 	struct ip6_address	i6;
    510 	if (ix->is6) {
    511 		memcpy(&(i6.d), &(ix->ip), 16);
    512 		return ip6me_is(i6);
    513 	} else {
    514 		memcpy(&(i4.d), &(ix->ip), 4);
    515 		return ipme_is(i4);
    516 	}
    517 }
    518 
    519 void stopdns (skadns_t *dnsres) {
    520 	skadns_end(dnsres);
    521 }
    522 
    523 /* les noms de ces procs sont misleading.
    524  * They should not be thought of as "we definitely have our response"
    525  * but as "we MAY have our response."
    526  */
    527 int dns_havea (skadns_t *dnsres, dnsq_t *dnsq, void* mxresultp, void* protop)
    528 {
    529 	protocol_t *proto = (protocol_t *)protop;
    530 	mxresult_t *mxresult = (mxresult_t *)mxresultp;
    531 	//mxrAlloc *mxres = &(proto->mxresults);
    532 	//ipmxAlloc *ipmxres = &(mxres->ipmx);
    533 	ipmx_t ipmx = {IP46FULL_ZERO};
    534 	ipmx.ip.is6 = 0;
    535 	stralloc rrs = STRALLOC_ZERO;
    536 	char const *dnsresponse; int dnsresplen, i = 0;
    537 	s6dns_message_header_t dnsmh;
    538 
    539 	if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) {
    540 		proto->lookupdone = 1;
    541 		info_wtfs("skadns_packet in dns_havea");
    542 		return FALSE;
    543 	}
    544 	if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_havea"); // this should succeed if the above didn't fail
    545 	if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_havea less than even a header"); }
    546 	// s6dns_message_header_unpack(dnsresponse, dnsmh);
    547 	switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_a, &rrs)) {
    548 		case -1:
    549 			temp_wtf("s6dns_message_parse in dns_havea - local error");
    550 			break;
    551 		case 0:
    552 			info_wtfs("s6dns_message_parse in dns_havea - rcode - maybe should continue");
    553 			mxresult->lookupdone = 1;
    554 			mxresult->hasresults = 0;
    555 			proto->lookupdone = 1;
    556 			break;
    557 		case 1:
    558 #ifdef    REMOTEDEBUG
    559 			info_dbgs("s6dns_message_parse in dns_havea - success no answer");
    560 #endif // REMOTEDEBUG
    561 			mxresult->lookupdone = 1;
    562 			mxresult->hasresults = 0;
    563 			proto->lookupdone = 1;
    564 			break;
    565 		case 2:
    566 #ifdef    REMOTEDEBUG
    567 			info_dbgs("s6dns_message_parse in dns_havea - success answer");
    568 #endif // REMOTEDEBUG
    569 			mxresult->lookupdone = 1;
    570 			mxresult->hasresults = 1;
    571 			proto->lookupdone = 1;
    572 			proto->hasresults = 1;
    573 			for (i = 0; i < rrs.len/4; ++i) {
    574 				memcpy(&(ipmx.ip.ip), (rrs.s + (i * 4)), 4);
    575 				if (!ipmxalloc_catip(&(mxresult->ipmx), ipmx)) temp_nomem();
    576 				if (ipisme(&ipmx)) mxresult->isme = 1;
    577 				memset(&ipmx, 0, sizeof(ipmx_t)); ipmx.ip.is6 = 0;
    578 			}
    579 			break;
    580 	}
    581 	return TRUE;
    582 }
    583 int dns_haveaaaa (skadns_t *dnsres, dnsq_t *dnsq, void* mxresultp, void* protop)
    584 {
    585 	protocol_t *proto = (protocol_t *)protop;
    586 	mxresult_t *mxresult = (mxresult_t *)mxresultp;
    587 	//mxrAlloc *mxres = &(proto->mxresults);
    588 	//ipmxAlloc *ipmxres = &(mxres->ipmx);
    589 	ipmx_t ipmx = {IP46FULL_ZERO};
    590 	ipmx.ip.is6 = 1;
    591 	stralloc rrs = STRALLOC_ZERO;
    592 	char const *dnsresponse; int dnsresplen, i = 0;
    593 	s6dns_message_header_t dnsmh;
    594 
    595 	if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) {
    596 		info_wtfs("skadns_packet in dns_haveaaaa");
    597 		proto->lookupdone = 1;
    598 		mxresult->lookupdone = 1;
    599 		return FALSE;
    600 	}
    601 	if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_haveaaaa"); // this should succeed if the above didn't fail
    602 	if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_haveaaaa less than even a header"); }
    603 	// s6dns_message_header_unpack(dnsresponse, dnsmh);
    604 	switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_aaaa, &rrs)) {
    605 		case -1:
    606 			temp_wtf("s6dns_message_parse in dns_haveaaaa - local error");
    607 			break;
    608 		case 0:
    609 			info_wtfs("s6dns_message_parse in dns_haveaaaa - rcode - maybe should continue");
    610 			mxresult->lookupdone = 1;
    611 			proto->lookupdone = 1;
    612 			break;
    613 		case 1:
    614 #ifdef    REMOTEDEBUG
    615 			info_dbgs("s6dns_message_parse in dns_haveaaaa - success no answer");
    616 #endif // REMOTEDEBUG
    617 			mxresult->lookupdone = 1;
    618 			proto->lookupdone = 1;
    619 			break;
    620 		case 2:
    621 #ifdef    REMOTEDEBUG
    622 			info_dbgs("s6dns_message_parse in dns_haveaaaa - success answer");
    623 #endif // REMOTEDEBUG
    624 #ifdef    SKALIBS_IPV6_ENABLED
    625 			mxresult->lookupdone = 1;
    626 			mxresult->hasresults = 1;
    627 			proto->lookupdone = 1;
    628 			proto->hasresults = 1;
    629 			for (i = 0; i < rrs.len/16; ++i) {
    630 				memcpy(&(ipmx.ip.ip), (rrs.s + (i * 16)), 16);
    631 				if (!ipmxalloc_catip(&(mxresult->ipmx), ipmx)) temp_nomem();
    632 				if (ipisme(&ipmx)) mxresult->isme = 1;
    633 				memset(&ipmx, 0, sizeof(ipmx_t)); ipmx.ip.is6 = 1;
    634 			}
    635 #endif // SKALIBS_IPV6_ENABLED
    636 			break;
    637 	}
    638 	return TRUE;
    639 }
    640 
    641 int dns_havesrv (skadns_t *dnsres, dnsq_t *dnsq, void* protop, void* unused)
    642 {
    643 	protocol_t *proto = (protocol_t *)protop;
    644 	mxrAlloc *mxres = &(proto->mxresults);
    645 	mxresult_t mxr = MXRESULT_SRV_ZERO;
    646 	genalloc rrs = GENALLOC_ZERO;
    647 	tain shortdl;
    648 	const char *dnsresponse; char ds[256]; int dnsresplen = 0, dsl = 0, i = 0;
    649 	s6dns_message_header_t dnsmh;
    650 	s6dns_message_rr_srv_t *rr = NULL;
    651 	uint16_t qid4, qid6;
    652 
    653 	if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) {
    654 		info_wtfs("skadns_packet in dns_havesrv");
    655 		proto->lookupdone = 1;
    656 		return FALSE;
    657 	}
    658 	if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_havesrv"); // this should succeed if the above didn't fail
    659 	if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_havesrv less than even a header"); }
    660 	// s6dns_message_header_unpack(dnsresponse, dnsmh);
    661 	switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_srv, &rrs)) {
    662 		case -1:
    663 			temp_wtf("s6dns_message_parse in dns_havesrv - local error");
    664 			break;
    665 		case 0:
    666 			info_wtfs("s6dns_message_parse in dns_havesrv - maybe should continue");
    667 			proto->lookupdone = 1;
    668 			break;
    669 		case 1:
    670 #ifdef    REMOTEDEBUG
    671 			info_dbgs("s6dns_message_parse in dns_havesrv - success no answer");
    672 #endif // REMOTEDEBUG
    673 			proto->lookupdone = 1;
    674 			proto->hasresults = 0;
    675 			break;
    676 		case 2:
    677 #ifdef    REMOTEDEBUG
    678 			info_dbgs("s6dns_message_parse in dns_havesrv - success answer");
    679 #endif // REMOTEDEBUG
    680 			proto->lookupdone = 1;
    681 			proto->hasresults = 1;
    682 			for (i = 0; i < genalloc_len(s6dns_message_rr_srv_t, &rrs); ++i) {
    683 //				rr = (s6dns_message_rr_srv_t *)((rrs.s) + (i * sizeof(s6dns_message_rr_srv_t)));
    684 				rr = genalloc_s(s6dns_message_rr_srv_t, &rrs) + i;
    685 				mxr.lookupdone = TRUE;
    686 				mxr.port = rr->port;
    687 				mxr.prio = rr->priority;
    688 				mxr.weight = rr->weight;
    689 				if (!(dsl = s6dns_domain_tostring(ds, 255, &(rr->target)))) temp_wtf("s6dns_domain_tostring in dns_havesrv");
    690 				if (!stralloc_readyplus(&(mxr.name), dsl)) temp_nomem();
    691 				if (!stralloc_copyb(&(mxr.name), ds, dsl)) temp_nomem();
    692 				if (!stralloc_0(&(mxr.name))) temp_nomem();
    693 				if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havesrv (Doctor?)");
    694 				if (!tain_addsec(&shortdl, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havesrv (Doctor?)");
    695 				if (!mxralloc_catmxr(mxres, mxr)) temp_nomem();
    696 				if (mxr.name.len > 1) { // No point if it's just a dot
    697 					if (!skadns_send(&Dnsres, &qid4, &(rr->target), S6DNS_T_A, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havesrv");
    698 					if (!dnsq_add(qid4, &(mxres->mxr[(mxres->len)-1]), proto, dns_havea)) temp_wtf("dnsq_add in dns_havesrv");
    699 					if (!skadns_send(&Dnsres, &qid6, &(rr->target), S6DNS_T_AAAA, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havesrv");
    700 					if (!dnsq_add(qid6, &(mxres->mxr[(mxres->len)-1]), proto, dns_haveaaaa)) temp_wtf("dnsq_add in dns_havesrv");
    701 				}
    702 				memset(&mxr, 0, sizeof(mxresult_t));
    703 			}
    704 			break;
    705 	}
    706 	return TRUE;
    707 }
    708 
    709 int dns_havemx (skadns_t *dnsres, dnsq_t *dnsq, void* protop, void* unused)
    710 {
    711 	// XX - should probably pass the whole protoalloc as protop
    712 	protocol_t *proto = (protocol_t *)protop, *smproto;
    713 	mxrAlloc *mxres = NULL;
    714 	mxresult_t mxr = MXRESULT_MX_ZERO;
    715 	genalloc rrs = GENALLOC_ZERO;
    716 	tain shortdl;
    717 	char const *dnsresponse; char ds[256]; int dnsresplen = 0, dsl = 0, i = 0;
    718 	s6dns_message_header_t dnsmh;
    719 	s6dns_message_rr_mx_t *rr = NULL;
    720 	uint16_t qid4, qid6;
    721 
    722 	if ((dnsresponse = skadns_packet(dnsres, dnsq->dnsq)) == NULL) {
    723 		info_wtfs("skadns_packet in dns_havemx");
    724 		return FALSE;
    725 	}
    726 	if ((dnsresplen = skadns_packetlen(dnsres, dnsq->dnsq)) == -1) temp_wtf("skadns_packetlen in dns_havemx"); // this should succeed if the above didn't fail
    727 	if (dnsresplen < 12) { errno = EINVAL; temp_wtf("skadns_packetlen gave dns_havemx less than even a header"); }
    728 	// s6dns_message_header_unpack(dnsresponse, dnsmh);
    729 	switch (s6dns_message_parse(&dnsmh, dnsresponse, dnsresplen, &s6dns_message_parse_answer_mx, &rrs)) {
    730 		case -1:
    731 			temp_wtf("s6dns_message_parse in dns_havemx - local error");
    732 			break;
    733 		case 0:
    734 			info_wtfs("s6dns_message_parse in dns_havemx - maybe should continue");
    735 			proto->lookupdone = 1;
    736 			break;
    737 		case 1:
    738 #ifdef    REMOTEDEBUG
    739 			buffer_puts(&buffer_2_, "Is6dns_message_parse in dns_havemx - success no answer\n");
    740 #endif // REMOTEDEBUG
    741 			break;
    742 		case 2:
    743 #ifdef    REMOTEDEBUG
    744 			buffer_puts(&buffer_2_, "Is6dns_message_parse in dns_havemx - success answer\n");
    745 #endif // REMOTEDEBUG
    746 			for (i = 0; i < genalloc_len(s6dns_message_rr_mx_t, &rrs); ++i) {
    747 				//rr = (s6dns_message_rr_mx_t *)((rrs.s) + (i * sizeof(s6dns_message_rr_mx_t)));
    748 				rr = genalloc_s(s6dns_message_rr_mx_t, &rrs) + i;
    749 				mxr.lookupdone = TRUE;
    750 				if (_is_mxps(rr->preference)) {
    751 					if ((proto = Slicemap[_mxps_slice(rr->preference)]) == NULL) proto = Slicemap[NOMXPS_SLICE];
    752 					mxr.prio = _mxps_prio(rr->preference);
    753 				} else {
    754 					proto = Slicemap[NOMXPS_SLICE];
    755 					mxr.prio = rr->preference;
    756 				}
    757 				if (proto == NULL) {errno = EINVAL; temp_wtf("dns_havemx - postmanager has not configured a zero MXPS slice, and this is necessary.");}
    758 				proto->lookupdone = 1;
    759 				proto->hasresults = 1;
    760 				mxres = &(proto->mxresults);
    761 				mxr.port = proto->defport;
    762 				mxr.weight = 0;
    763 				if (!(dsl = s6dns_domain_tostring(ds, 255, &(rr->exchange)))) temp_wtf("s6dns_domain_tostring in dns_havemx");
    764 				ds[dsl] = 0;
    765 				if (!stralloc_readyplus(&(mxr.name), dsl+1)) temp_nomem();
    766 				if (!stralloc_copyb(&(mxr.name), ds, dsl)) temp_nomem();
    767 				if (!stralloc_0(&(mxr.name))) temp_nomem();
    768 				if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havemx (Doctor?)");
    769 				if (!tain_addsec(&shortdl, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_havemx (Doctor?)");
    770 				if (!mxralloc_catmxr(mxres, mxr)) temp_nomem();
    771 				if (mxr.name.len > 1) { // No point if it's just a dot, NPI.
    772 					if (!skadns_send(&Dnsres, &qid4, &(rr->exchange), S6DNS_T_A, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havemx");
    773 					if (!dnsq_add(qid4, &(mxres->mxr[(mxres->len)-1]), proto, dns_havea)) temp_wtf("dnsq_add in dns_havemx");
    774 					if (!skadns_send(&Dnsres, &qid6, &(rr->exchange), S6DNS_T_AAAA, &Limit, &shortdl, &Stamp)) temp_wtf("skadns_send in dns_havemx");
    775 					if (!dnsq_add(qid6, &(mxres->mxr[(mxres->len)-1]), proto, dns_haveaaaa)) temp_wtf("dnsq_add in dns_havemx");
    776 				}
    777 				memset(&mxr, 0, sizeof(mxresult_t));
    778 				mxr.ismx = TRUE;
    779 			}
    780 			break;
    781 	}
    782 	return TRUE;
    783 }
    784 /*
    785 int dns_havecname (skadns_t *dnsres, dnsq_t *dnsq, void* protop, void *unused)
    786 {
    787 	// We should only encounter this guy if we are using relayhost.
    788 	protocol_t *proto = (protocol_t *)protop;
    789 	mxrAlloc *mxres = &(proto->mxresults);
    790 	mxresult_t mxr = {0, 0, 0, FALSE, FALSE, STRALLOC_ZERO, TA_ZERO};
    791 	genalloc rrs;
    792 	char const *dnsresponse; int dnsresplen;
    793 	s6dns_message_header_t dnsmh;
    794 }
    795 */
    796 
    797 /*
    798 int protocol_want_srvmx (protocol_t *proto,
    799 int protocol_have_mx (protocol_t *proto,
    800 int protocol_want_ip (protocol_t *proto,
    801 int protocol_have_mxip (protocol_t *proto,
    802 int protocol_wantfd (protocol_t *proto, int mtpfd)
    803 {
    804 	// we'll have to use a selfpipe of some description elsewhere to understand process termination
    805 }
    806  *
    807 int protocol_spin (protocol_t *proto, char stage, unsigned long port,
    808 		char **rrecips, char *relayhost, size_t nprotos)
    809 {
    810 }*/
    811 
    812 // offset is needed in case we're skipping the first protocol for some reason.
    813 // until then, it should always be zero
    814 // nproto is the number of protocols we're doing
    815 // mxralloc is inside the protocol, so
    816 int dns_wantsrv (protocol_t *protos, size_t nproto)
    817 {
    818 	int 			i = 0;
    819 	uint16_t		qtype = S6DNS_T_SRV, id = 0;
    820 	s6dns_domain_t	dnsdomain;
    821 	stralloc		domain = STRALLOC_ZERO;
    822 	//mxresult_t		mxr = {0, 0, 0, STRALLOC_ZERO, TA_ZERO};
    823 	//mxrAlloc		*mxrs;
    824 	//tain			limit, dl;
    825 
    826 	for (i = 0; i < nproto; ++i) {
    827 		//mxrs = protos[i].mxresults.mxr;
    828 		if (!stralloc_copys(&domain, "_")) temp_nomem();
    829 		if (!stralloc_cats(&domain, protos[i].srvservice)) temp_nomem();
    830 		if (!stralloc_cats(&domain, "._tcp.")) temp_nomem();
    831 		if (!stralloc_cats(&domain, Host.s)) temp_nomem();
    832 		//if (!stralloc_0(&domain)) temp_nomem();
    833 		if (!s6dns_domain_fromstring_noqualify_encode(&dnsdomain, domain.s, domain.len)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_wantsrv");
    834 		if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_wantsrv (Doctor?)");
    835 		if (!tain_addsec(&Deadline, &Stamp, 1)) temp_wtf("tain_addsec in dns_wantsrv (Doctor?)");
    836 		if (!skadns_send(&Dnsres, &id, &dnsdomain, qtype, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantsrv");
    837 		if (!dnsq_add(id, (protos + i), NULL, dns_havesrv)) temp_wtf("dnsq_add in dns_wantsrv");
    838 	}
    839 	stralloc_free(&domain);
    840 	return TRUE; // not used
    841 }
    842 
    843 int dns_wantmx (protocol_t *protos, size_t nproto)
    844 {
    845 	int 			i = 0;
    846 	uint16_t		qtype = S6DNS_T_MX, id = 0;
    847 	s6dns_domain_t	dnsdomain;
    848 	stralloc		domain = STRALLOC_ZERO;
    849 	//tain 			limit, dl;
    850 	//	mxresult_t mxr = {0, 0, 0, FALSE, FALSE, TRUE, STRALLOC_ZERO, TA_ZERO};
    851 
    852 	if (!stralloc_cats(&domain, Host.s)) temp_nomem();
    853 	//if (!stralloc_0(&domain)) temp_nomem();
    854 	if (!s6dns_domain_fromstring_noqualify_encode(&dnsdomain, domain.s, domain.len)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_wantmx");
    855 	if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_wantmx (Doctor?)");
    856 	if (!tain_addsec(&Deadline, &Stamp, 1)) temp_wtf("tain_addsec in dns_wantmx (Doctor?)");
    857 	if (!skadns_send(&Dnsres, &id, &dnsdomain, qtype, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantmx");
    858 	if (!dnsq_add(id, protos, NULL, dns_havemx)) temp_wtf("dnsq_add in dns_wantmx");
    859 	stralloc_free(&domain);
    860 	return TRUE; // not used
    861 }
    862 
    863 // the only entrypoint to this is a DNS relayhost
    864 int dns_wantip (protocol_t *proto, uint16_t port, char *relayhost)
    865 {
    866 	int 			i = 0;
    867 	uint16_t		qid4 = 0, qid6 = 0;
    868 	s6dns_domain_t	dnsdomain;
    869 	stralloc		domain = STRALLOC_ZERO;
    870 	//tain 			limit, dl; &(mxres->mxr[(mxres->len)-1])
    871 	mxresult_t mxr = MXRESULT_SRV_ZERO;
    872 	mxrAlloc *mxres = &(proto->mxresults);
    873 
    874 	mxr.port = port;
    875 
    876 	if (!stralloc_cats(&domain, relayhost)) temp_nomem();
    877 	//if (!stralloc_0(&domain)) temp_nomem();
    878 	if (!s6dns_domain_fromstring_noqualify_encode(&dnsdomain, domain.s, domain.len)) temp_wtf("s6dns_domain_fromstring_noqualify_encode in dns_wantip");
    879 	if (!tain_addsec(&Limit, &Stamp, Timeoutconnect)) temp_wtf("tain_addsec in dns_wantip (Doctor?)");
    880 	if (!tain_addsec(&Deadline, &Stamp, 1)) temp_wtf("tain_addsec in dns_wantip (Doctor?)");
    881 	if (!mxralloc_catmxr(mxres, mxr)) temp_nomem();
    882 	if (!skadns_send(&Dnsres, &qid6, &dnsdomain, S6DNS_T_AAAA, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantip");
    883 	if (!dnsq_add(qid6, &(mxres->mxr[(mxres->len)-1]), NULL, dns_haveaaaa)) temp_wtf("dnsq_add in dns_wantip");
    884 	if (!skadns_send(&Dnsres, &qid4, &dnsdomain, S6DNS_T_A, &Limit, &Deadline, &Stamp)) temp_wtf("skadns_send in dns_wantip");
    885 	if (!dnsq_add(qid4, &(mxres->mxr[(mxres->len)-1]), NULL, dns_havea)) temp_wtf("dnsq_add in dns_wantip");
    886 	stralloc_free(&domain);
    887 	return TRUE; // not used
    888 }
    889 
    890 int lookupsdone (protocol_t *proto) {
    891 	//protocol_t *proto = NULL;
    892 	mxresult_t *mxr = NULL;
    893 	int i = 0;
    894 	if (proto->lookupdone == 0) return FALSE;
    895 	for (i = 0; i < proto->mxresults.len; ++i) {
    896 		mxr = (mxresult_t *)((proto->mxresults.mxr) + (i * sizeof(mxresult_t)));
    897 		if (mxr->lookupdone == 0) return FALSE;
    898 	}
    899 	return TRUE; // if we have not returned false by now, all lookups are done
    900 }
    901 
    902 // comparator for qsort
    903 int mxworse (void* mx1p, void* mx2p) {
    904 	// These get sorted in ascending order.
    905 	// If mx1 is less preferable than mx2, it is greater (TRUE).
    906 	// If it is equally preferable, it is equal (FALSE).
    907 	// If it is more preferable, it is lesser (-1).
    908 	mxresult_t *mx1 = (mxresult_t *)mx1p;
    909 	mxresult_t *mx2 = (mxresult_t *)mx2p;
    910 
    911 	if ((mx1->ismx) && !(mx2->ismx)) {
    912 		// literally need do nothing else, we prefer mx2
    913 		return TRUE;
    914 	}
    915 
    916 	if (mx1->prio > mx2->prio) {
    917 		// mx1 prio is higher (less prior) than mx2 prio, so true.
    918 		return TRUE;
    919 	}
    920 
    921 	if (mx1->prio == mx2->prio) {
    922 		// mx1 prio is equal to mx2 prio, so false, unless weight higher.
    923 		if (mx1->weight > mx2->weight) return -1; // mx1 is heavier, so more pref (better)
    924 		if (mx1->weight < mx2->weight) return TRUE; // mx1 is lighter, so less pref (worse)
    925 		return FALSE;
    926 	}
    927 
    928 	if (mx1->prio < mx2->prio) {
    929 		// mx1 prio is lower (less prior) than mx2 prio, so true.
    930 		return -1;
    931 	}
    932 	return FALSE; // Not reached.
    933 }
    934 
    935 void printprotos () {
    936 	protocol_t *proto = NULL;
    937 	mxresult_t *mxr = NULL;
    938 	ipmx_t *ipmx = NULL;
    939 	char s[48]; int i = 0, j = 0; unsigned long l = 0;
    940 	memset(&s, 0, 48);
    941 	out("I3.0.0 ");
    942 	out("Protocol element count: ");
    943 	s[fmt_ulong(s, Protocols.len)] = 0;
    944 	out(s);
    945 	out(" Protocol element allocation count: ");
    946 	s[fmt_ulong(s, Protocols.a)] = 0;
    947 	out(s);
    948 	out("\n");
    949 	if (Protocols.len < 1) {
    950 		info_dbgs("3.3.0 No protocols on the protocol array!");
    951 	} else {
    952 		for (i = 0; i < Protocols.len; ++i) {
    953 			proto = (protocol_t *)&(Protocols.pt[i]);
    954 			// + (i * sizeof(protocol_t)));
    955 			out("I3.0.0 ");
    956 			out("Protocol element #");
    957 			s[fmt_ulong(s, i)] = 0;
    958 			out(s);
    959 			out(": \n srvservice: ");
    960 			out(proto->srvservice);
    961 			out("\n defport: ");
    962 			s[fmt_uint(s, proto->defport)] = 0;
    963 			out(s);
    964 			if (proto->slice < 0)
    965 				out("\n no MXPS slice for this protocol");
    966 			else {
    967 				l = proto->slice;
    968 				out("\n slice: ");
    969 				s[fmt_uint(s, l)] = 0;
    970 				out(s);
    971 			}
    972 			switch (proto->tls) {
    973 				case 1:
    974 					out("\n will use TLS");
    975 					break;
    976 				case 0:
    977 					switch (proto->starttls) {
    978 						case 1:
    979 							out("\n can use TLS");
    980 							break;
    981 						case 0:
    982 							out("\n cannot use TLS");
    983 							break;
    984 					}
    985 			}
    986 			if (proto->maproutes.num != 0) {
    987 				out("\n has ");
    988 				out(proto->srvservice);
    989 				out("routes");
    990 			} else {
    991 				out("\n does not have ");
    992 				out(proto->srvservice);
    993 				out("routes");
    994 			}
    995 			switch (proto->lookupdone) {
    996 				case 0:
    997 					out("\n DNS lookups have not been done");
    998 					break;
    999 				case 1:
   1000 					switch (proto->hasresults) {
   1001 						case 3:
   1002 						case 2:
   1003 							out("\n direct A results exist");
   1004 							break;
   1005 						case 1:
   1006 							out("\n MX or SRVmail results exist");
   1007 							for (j = 0; j < proto->mxresults.len; ++j) {
   1008 								out("\n  port: ");
   1009 								s[fmt_uint(s, proto->mxresults.mxr[j].port)] = 0;
   1010 								out(s);
   1011 								out("  prio: ");
   1012 								s[fmt_uint(s, proto->mxresults.mxr[j].prio)] = 0;
   1013 								out(s);
   1014 								out("  weight: ");
   1015 								s[fmt_uint(s, proto->mxresults.mxr[j].weight)] = 0;
   1016 								out(s);
   1017 								out("  SRV/MX: ");
   1018 								out(proto->mxresults.mxr[j].ismx ? "MX" : "SRV");
   1019 								out("  lookupdone: ");
   1020 								out(proto->mxresults.mxr[j].lookupdone == 1 ? "yes" : "no");
   1021 								out("  has results: ");
   1022 								out(proto->mxresults.mxr[j].hasresults == 1 ? "yes" : proto->mxresults.mxr[j].hasresults > 1 ? "literal IP " : "no");
   1023 								out("  name: ");
   1024 								buffer_put(buffer_2, proto->mxresults.mxr[j].name.s, proto->mxresults.mxr[j].name.len);
   1025 								if (proto->mxresults.mxr[j].ipmx.len > 0) {
   1026 									out("\n   first IP result: ");
   1027 									s[ip46full_fmt(s, &(proto->mxresults.mxr[j].ipmx.ipmx[0].ip))] = 0;
   1028 									out(s);
   1029 								}
   1030 							}
   1031 							break;
   1032 						case 0:
   1033 							out("\n MX or SRVmail results do not exist");
   1034 							break;
   1035 					}
   1036 			}
   1037 			out("\n app: ");
   1038 			buffer_put(buffer_2, proto->app.s, proto->app.len);
   1039 			out("\n\n");
   1040 			buffer_flush(buffer_2);
   1041 		}
   1042 		buffer_flush(buffer_2);
   1043 	}
   1044 }
   1045 
   1046 int lookupsdone_all (protocol_t *protos, int nprotos)
   1047 {
   1048 	// protos should be a pointer to the beginning of the protocol list as we are using it.
   1049 	int i = 0;
   1050 	for (; i < nprotos; ++i) {
   1051 		if (!lookupsdone((protos + i))) return FALSE;
   1052 	}
   1053 	return TRUE;
   1054 }
   1055 
   1056 // 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
   1057 // we could also, if we wanted, be tcpclient ourselves, and just launch things under ucspitlsclient as relevant
   1058 
   1059 int main (int argcount, char **args)
   1060 {
   1061 	char **rrecips, *relayhost;
   1062 	int i = 0, offset = 0, nprotos = 0; unsigned long port; char skipmx = 0, fullresults = 0;
   1063 	protocol_t *routesproto = NULL, *proto = NULL; ipmx_t ipmx = {IP46FULL_ZERO};
   1064 	sigset_t sigset; mxresult_t rhmxr = MXRESULT_SLIP_ZERO; mxrAlloc mxres = TA_ZERO; // mxresu is unsorted
   1065 	tain shortdl;
   1066 
   1067 	PROG	= "mxf-remote";
   1068 
   1069 	tain_now(&Stamp);
   1070 	tain_addsec(&Deadline, &Stamp, 2); // eh, the manpages at skarnet's website said to do this so I will
   1071 
   1072 	// check basic configuration
   1073 	if (argcount < 4) perm_usage();
   1074 	if (chdir(auto_qmail) == -1) temp_chdir();
   1075 	if (!stralloc_copys(&Host,args[1])) temp_nomem();
   1076 	//if (!stralloc_0(&Host)) temp_nomem();
   1077 	if (!stralloc_copys(&Sender,args[2])) temp_nomem();
   1078 	//if (!stralloc_0(&Sender)) temp_nomem();
   1079 
   1080 	getcontrols();
   1081 	ipme_init();
   1082 	ip6me_init();
   1083 
   1084 	// trap signals
   1085 	sig_ignore(SIGPIPE);
   1086 	Sigfd = selfpipe_init(); // selfpipe.h
   1087 	sigemptyset(&sigset);
   1088 	sigaddset(&sigset, SIGCHLD);
   1089 	if (0 == selfpipe_trapset(&sigset)) temp_oserr();
   1090 	struct pollfd sigpfd;
   1091 	sigpfd.fd = Sigfd;
   1092 	sigpfd.events = POLLIN;
   1093 	sigpfd.revents = 0;
   1094 	descriptor_add(sigpfd, (void *)&Protegetab, &checksignals);
   1095 
   1096 	printprotos();
   1097 	/* Of interest here is that ... NOTES-mxfr #0010 */
   1098 
   1099 	/* So at this stage we know what the protocols are and we can decide
   1100 	 * whether we want to skip SRV and MX.
   1101 	 *
   1102 	 * If we do, it's because we have a <protocol>routes file. protocol->maproutes
   1103 	 * will have the scoop.
   1104 	 * ga_foreach doesn't support non-pointer structs, so we make a pointer here.
   1105 	 */
   1106 	startdns(&Dnsres);
   1107 	struct pollfd dnspfd;
   1108 	dnspfd.fd = skadns_fd(&Dnsres);
   1109 	dnspfd.events = POLLIN;
   1110 	dnspfd.revents = 0;
   1111 	descriptor_add(dnspfd, (void *)&Dnsres, &checkdns);
   1112 	rrecips = args + 3;
   1113 
   1114 	for (i = 0; i < Protocols.len; i++) {
   1115 	//	proto = (protocol_t *)((Protocols.pt) + (offset = (i * sizeof(protocol_t))) ); // fallback version
   1116 		proto = (protocol_t *)&(Protocols.pt[i]);
   1117 		// This is lifted from qmail-remote.c:333 with minimal changes
   1118 		// to fit the new system.
   1119 		// I do not purport to understand it.
   1120 		for (i = 0;i <= Host.len;++i) {
   1121 			if ((i == 0) || (i == Host.len) || (Host.s[i] == '.'))
   1122 				if ((relayhost = constmap(&(proto->maproutes),Host.s + i,Host.len - i))) {
   1123 					skipmx = 1;
   1124 					routesproto = proto;
   1125 					nprotos = 1;
   1126 				}
   1127 		}
   1128 		if (relayhost && !*relayhost) relayhost = 0;
   1129 
   1130 		if (relayhost) {
   1131 			i = str_chr(relayhost,':');
   1132 			if (relayhost[i]) {
   1133 				scan_ulong(relayhost + i + 1,&port);
   1134 				relayhost[i] = 0;
   1135 			}
   1136 			if (!stralloc_copys(&Host,relayhost)) temp_nomem();
   1137 		}
   1138 
   1139 		if (skipmx) {
   1140 			if (!port) port = routesproto->defport;
   1141 			break;
   1142 		}
   1143 	}
   1144 
   1145 	// fire off the SRV/MX DNS volley; wait Timeoutconnect seconds from when
   1146 	// we're finished bowling to when we give up on run_fds
   1147 	// also break if a foreach loop of lookupsdone(Protocols.pt[i]) does not return false
   1148 	if (!skipmx) {
   1149 		proto = (protocol_t *)((Protocols.pt) + (offset = 0)); // reset the proto pointer
   1150 		nprotos = Protocols.len; // it's 1 if we are skipping MX and SRVmail
   1151 	}
   1152 
   1153 	if (relayhost) {
   1154 		if (!ip46_scan(relayhost, &(ipmx.ip))) {
   1155 			dns_wantip(routesproto, port, relayhost);
   1156 		} else {
   1157 			routesproto->hasresults = 1;
   1158 			routesproto->lookupdone = 1;
   1159 			// I guess we're building an mxr then...
   1160 			rhmxr.port = port;
   1161 			rhmxr.hasresults = 2;
   1162 			// prio/wt dealt with
   1163 			if (!stralloc_copys(&(rhmxr.name), relayhost)) temp_nomem();
   1164 			if (!stralloc_0(&(rhmxr.name))) temp_nomem();
   1165 			if (!ipmxalloc_catip(&(rhmxr.ipmx), ipmx)) temp_nomem();
   1166 		}
   1167 	} else {
   1168 		// our DNS volley
   1169 		dns_wantsrv(proto, nprotos);
   1170 		dns_wantmx(proto, nprotos);
   1171 		out("regular DNS...\n");
   1172 	}
   1173 
   1174 	tain_now(&Stamp);
   1175 	tain_addsec(&Deadline, &Stamp, Timeoutconnect);
   1176 	tain_addsec(&shortdl, &Stamp, 3);
   1177 
   1178 	while (!lookupsdone_all(proto, nprotos)) {
   1179 		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.
   1180 		tain_addsec(&shortdl, &Stamp, 3);
   1181 		if (!tain_less(&Stamp, &Deadline)) {
   1182 			errno = ETIMEDOUT;
   1183 			skadns_end(&Dnsres);
   1184 			info_wtfs("DNS lookup event loop took too long - mostly orderly shutdown executed"); // cut...
   1185 			break;
   1186 		}
   1187 	} // should not run if lookupsdone_all already (SLIP relayhost)
   1188 
   1189 	printprotos();
   1190 	/* TODO: actually send the letter */
   1191 
   1192 }
   1193 #endif