1 /* This file is part of Mailfromd.
2    Copyright (C) 2005-2021 Sergey Poznyakoff
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <syslog.h>
27 #include <signal.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <ctype.h>
35 
36 #include <mailutils/mailutils.h>
37 #include <mailutils/daemon.h>
38 
39 #include "libmf.h"
40 #include "srvcfg.h"
41 #include "callout.h"
42 #include "callout-dbgmod.h"
43 
44 #define SMTP_MAJOR(c) ((c)/100)
45 
46 #define SMTP_PARSEHLO 0x01
47 #define CAPA_VRFY     0x02
48 
49 struct smtp_io_data {
50 	char *id;            /* I/O id */
51 	char *email;
52 	char *ehlo;
53 	char *mailfrom;
54 
55 	time_t timeout[SMTP_NUM_TIMEOUT];
56 	smtp_io_callback_t callback;
57 	void *callback_closure;
58 	mu_stream_t stream;  /* I/O stream */
59 	mu_opool_t pool;     /* Opool for keeping commands/replies */
60 	char *command;       /* Last issued command */
61 	char *reply;         /* Last received reply */
62 	char *start;         /* First line of the reply, if it was multiline */
63 	size_t nlines;       /* Number of lines in the reply */
64 	char buf[128];       /* Input buffer */
65 	size_t level;        /* Number of bytes in buf */
66 	int code;            /* Reply code */
67 	int esmtp_capa;
68 };
69 
70 struct smtp_io_data *
smtp_io_create(const char * id,time_t timeout[],smtp_io_callback_t callback,void * closure)71 smtp_io_create(const char *id, time_t timeout[], smtp_io_callback_t callback,
72 	       void *closure)
73 {
74 	struct smtp_io_data *iop = mu_zalloc(sizeof(*iop));
75 
76 	if (!id)
77 		iop->id = mu_strdup("null");
78 	else {
79 		size_t len;
80 		iop->id = mu_strdup(id);
81 		len = strlen(iop->id);
82 		if (len > 2 && strcmp(iop->id + len - 2, ": ") == 0)
83 			iop->id[len - 2] = 0;
84 	}
85 	memcpy(&iop->timeout, timeout, sizeof(iop->timeout));
86 	iop->callback = callback;
87 	iop->callback_closure = closure;
88 	mu_opool_create(&iop->pool, MU_OPOOL_ENOMEMABRT);
89 	iop->start = iop->command = iop->reply = NULL;
90 	iop->nlines = 0;
91 	iop->level = 0;
92 	return iop;
93 }
94 
95 void
smtp_io_set_timeouts(struct smtp_io_data * iop,time_t * to)96 smtp_io_set_timeouts(struct smtp_io_data *iop, time_t *to)
97 {
98 	memcpy(&iop->timeout, to, sizeof(iop->timeout));
99 }
100 
101 static void
replstr(char ** pdst,const char * str)102 replstr(char **pdst, const char *str)
103 {
104 	if (*pdst)
105 		free(*pdst);
106 	*pdst = mu_strdup(str);
107 }
108 
109 
110 void
smtp_io_init(struct smtp_io_data * iop)111 smtp_io_init(struct smtp_io_data *iop)
112 {
113 	iop->start = iop->command = iop->reply = NULL;
114 	iop->level = 0;
115 }
116 
117 void
smtp_io_setup_callout(struct smtp_io_data * iop,const char * email,const char * ehlo,const char * mailfrom)118 smtp_io_setup_callout(struct smtp_io_data *iop,
119 		      const char *email,
120 		      const char *ehlo,
121 		      const char *mailfrom)
122 {
123 	replstr(&iop->email, email);
124 	replstr(&iop->ehlo, ehlo);
125 	if (!mailfrom || !*mailfrom)
126 		mailfrom = "<>";
127 	replstr(&iop->mailfrom, mailfrom);
128 }
129 
130 void
smtp_io_free(struct smtp_io_data * iop)131 smtp_io_free(struct smtp_io_data *iop)
132 {
133 	if (iop->stream) {
134 		mu_stream_close (iop->stream);
135 		mu_stream_destroy(&iop->stream);
136 	}
137 	mu_opool_destroy(&iop->pool);
138 	free(iop->id);
139 	free(iop->email);
140 	free(iop->ehlo);
141 	free(iop->mailfrom);
142 	free(iop);
143 }
144 
145 
146 static int
smtp_wait(struct smtp_io_data * iop,int flags,struct timeout_ctl * tctl)147 smtp_wait(struct smtp_io_data *iop, int flags, struct timeout_ctl *tctl)
148 {
149 	return mf_stream_wait(iop->stream, flags, tctl);
150 }
151 
152 static int
smtp_send(struct smtp_io_data * iop,const char * command)153 smtp_send(struct smtp_io_data *iop, const char *command)
154 {
155 	size_t len = strlen(command);
156 	struct timeout_ctl tctl;
157 
158 	init_timeout_ctl (&tctl, io_timeout);
159 
160 	iop->reply = NULL; /* Clear reply for logging purposes */
161 	do {
162 		size_t nb;
163 		int rc;
164 
165 		UPDATE_TTW(tctl);
166 
167 		rc = mu_stream_write(iop->stream, command, len, &nb);
168 		if (rc == 0) {
169 			if (nb == 0) {
170 				mu_error(_("%s: stream_write: wrote 0 bytes"),
171                                          iop->id);
172 				return -1;
173 			}
174 			len -= nb;
175 			command += nb;
176 		} else if (rc == EAGAIN) {
177 			rc = smtp_wait(iop, MU_STREAM_READY_WR, &tctl);
178 			if (rc) {
179 				mu_error(_("%s: smtp_wait failed: %s"),
180 					 iop->id, mu_strerror(rc));
181 				return -1;
182 			}
183 			continue;
184 		} else {
185 			mu_error("%s: mu_stream_write: %s",
186 				  iop->id, mu_strerror (rc));
187 			return -1;
188 		}
189 	} while (len > 0);
190 	return 0;
191 }
192 
193 static int
smtp_send2(struct smtp_io_data * iop,const char * command,const char * arg)194 smtp_send2(struct smtp_io_data *iop, const char *command, const char *arg)
195 {
196 	mu_opool_appendz(iop->pool, command);
197 	if (arg)
198 		mu_opool_appendz(iop->pool, arg);
199 	mu_opool_appendz(iop->pool, "\r\n");
200 	mu_opool_append_char(iop->pool, 0);
201 	iop->command = mu_opool_finish(iop->pool, NULL);
202 
203 	return smtp_send(iop, iop->command);
204 }
205 
206 static int
smtp_send3(struct smtp_io_data * iop,const char * command,const char * arg1,const char * arg2)207 smtp_send3(struct smtp_io_data *iop, const char *command,
208 	   const char *arg1, const char *arg2)
209 {
210 	mu_opool_appendz(iop->pool, command);
211 	mu_opool_appendz(iop->pool, arg1);
212 	mu_opool_appendz(iop->pool, arg2);
213 	mu_opool_appendz(iop->pool, "\r\n");
214 	mu_opool_append_char(iop->pool, 0);
215 	iop->command = mu_opool_finish(iop->pool, NULL);
216 
217 	return smtp_send(iop, iop->command);
218 }
219 
220 static int
smtp_recvline(struct smtp_io_data * iop,enum smtp_timeout to)221 smtp_recvline(struct smtp_io_data *iop, enum smtp_timeout to)
222 {
223 	struct timeout_ctl tctl;
224 
225 	init_timeout_ctl(&tctl, iop->timeout[to]);
226 	for (;;) {
227 		char *p;
228 
229 		UPDATE_TTW(tctl);
230 
231 		if (iop->level == 0) {
232 			int rc = mu_stream_read(iop->stream,
233 						iop->buf, sizeof iop->buf,
234 						&iop->level);
235 			if (rc == 0) {
236 				if (iop->level == 0) {
237 					mu_error(_("%s: stream_read: read 0 bytes"), iop->id);
238 					return -1;
239 				}
240 			} else if (rc == EAGAIN) {
241 				rc = smtp_wait(iop,
242 					       MU_STREAM_READY_RD, &tctl);
243 				if (rc) {
244 					mu_error(_("%s: smtp_wait failed: %s"),
245 						 iop->id, mu_strerror(rc));
246 					return -1;
247 				}
248 				continue;
249 			} else {
250 				mu_error("%s: mu_stream_read: %s",
251 					  iop->id, mu_strerror (rc));
252 				return -1;
253 			}
254 		}
255 
256 		p = memchr(iop->buf, '\n', iop->level);
257 		if (!p) {
258 			mu_opool_append(iop->pool, iop->buf, iop->level);
259 			iop->level = 0;
260 			continue;
261 		} else {
262 			size_t len = p - iop->buf + 1;
263 			mu_opool_append(iop->pool, iop->buf, len);
264 			mu_opool_append_char(iop->pool, 0);
265 			iop->reply = mu_opool_finish(iop->pool, NULL);
266 			iop->level -= len;
267 			memmove(iop->buf, iop->buf + len, iop->level);
268 			break;
269 		}
270 	}
271 	return 0;
272 }
273 
274 static int
smtp_recv(struct smtp_io_data * iop,enum smtp_timeout to)275 smtp_recv(struct smtp_io_data *iop, enum smtp_timeout to)
276 {
277 	char *p;
278 	iop->start = NULL;
279 	iop->nlines = 0;
280 	do {
281 		int code;
282 		int rc = smtp_recvline(iop, to);
283 		if (rc)
284 			return -1;
285 		code = strtoul(iop->reply, &p, 0);
286 		if (p - iop->reply != 3 || (*p != '-' && *p != ' ')) {
287 			mu_error(_("%s: unexpected reply from server: %s"),
288 				 iop->id, iop->reply);
289 			return -1;
290 		} else if (!iop->start) {
291 			iop->start = iop->reply;
292 			iop->code = code;
293 		} else if (iop->code != code) {
294 			mu_error(_("%s: unexpected reply code from server: %d"),
295 				 iop->id, code);
296 			return -1;
297 		}
298 		iop->nlines++;
299 		if ((iop->esmtp_capa & SMTP_PARSEHLO) && iop->nlines > 1) {
300 			if (strncmp(iop->reply + 4, "VRFY", 4) == 0 &&
301 			    (iop->reply[8] == '\r' || iop->reply[8] == '\n')) {
302 				iop->esmtp_capa |= CAPA_VRFY;
303 				iop->esmtp_capa &= ~SMTP_PARSEHLO;
304 			}
305 		}
306 	} while (*p == '-');
307 	iop->esmtp_capa &= ~SMTP_PARSEHLO;
308 	mu_opool_clear(iop->pool);
309 	return 0;
310 }
311 
312 /* Return the first line (terminated by \n or \r\n) from STR,
313    or the word "nothing" if STR is NULL.
314    Desctructive version: modifies STR. */
315 static char *
first_line_of(char * str)316 first_line_of(char *str)
317 {
318 	if (str) {
319 		size_t len = strcspn(str, "\r\n");
320 		str[len] = 0;
321 		return str;
322 	}
323 	return "nothing";
324 }
325 
326 const char *
smtp_last_sent(struct smtp_io_data * iop)327 smtp_last_sent(struct smtp_io_data *iop)
328 {
329 	return first_line_of(iop->command);
330 }
331 
332 const char *
smtp_last_received(struct smtp_io_data * iop)333 smtp_last_received(struct smtp_io_data *iop)
334 {
335 	return first_line_of(iop->start);
336 }
337 
338 const char *
smtp_io_id(struct smtp_io_data * iop)339 smtp_io_id(struct smtp_io_data *iop)
340 {
341 	return iop->id;
342 }
343 
344 const char *
smtp_io_email(struct smtp_io_data * iop)345 smtp_io_email(struct smtp_io_data *iop)
346 {
347 	return iop->email;
348 }
349 
350 
351 /* Milter-specific functions */
352 
353 static mf_status
reset(struct smtp_io_data * io)354 reset(struct smtp_io_data *io)
355 {
356 	smtp_send2(io, "RSET", NULL);
357 	if (smtp_recv(io, smtp_timeout_rset))
358 		return mf_timeout;
359 	else if (SMTP_MAJOR(io->code) != 2)
360 		return SMTP_MAJOR(io->code) == 4 ?
361 			mf_temp_failure : mf_failure;
362 	return mf_success;
363 }
364 
365 static mf_status
esmtp_vrfy(struct smtp_io_data * io)366 esmtp_vrfy(struct smtp_io_data *io)
367 {
368 	smtp_send2(io, "VRFY ", io->email);
369 	if (smtp_recv(io, smtp_timeout_rcpt))
370 		/* FIXME: Need a separate timeout? */
371 		return mf_timeout;
372 	if (io->code / 10 == 25)
373 		return mf_success;
374 	else if (SMTP_MAJOR(io->code) == 5)
375 		return mf_not_found;
376 	return mf_failure;
377 }
378 
379 static mf_status
callout_io(struct smtp_io_data * io,const char * hostname,mu_address_t addr)380 callout_io(struct smtp_io_data *io, const char *hostname, mu_address_t addr)
381 {
382 	size_t i;
383 	size_t mailcount;
384 	mf_status status;
385 
386 	if (io->callback)
387 		io->callback(io->callback_closure, "INIT", hostname);
388 
389 	mu_address_get_count(addr, &mailcount);
390 
391 	if (smtp_recv(io, smtp_timeout_initial))
392 		return mf_timeout;
393 
394 	if (io->callback)
395 		io->callback(io->callback_closure, "GRTNG",
396 			     smtp_last_received(io));
397 
398 	if (SMTP_MAJOR(io->code) != 2)
399 		return SMTP_MAJOR(io->code) == 4 ?
400 			mf_temp_failure : mf_not_found;
401 
402 	smtp_send2(io, "EHLO ", io->ehlo);
403 	if (enable_vrfy)
404 		io->esmtp_capa |= SMTP_PARSEHLO;
405 	if (smtp_recv(io, smtp_timeout_helo))
406 		return mf_timeout;
407 
408 	if (SMTP_MAJOR(io->code) == 5) {
409 		/* Let's try HELO, then */
410 		smtp_send2(io, "HELO ", io->ehlo);
411 		if (smtp_recv(io, smtp_timeout_helo))
412 			return mf_not_found;
413 	}
414 
415 	if (io->callback)
416 		io->callback(io->callback_closure,
417 			     "HELO", smtp_last_received(io));
418 
419 	if (SMTP_MAJOR(io->code) != 2)
420 		return SMTP_MAJOR(io->code) == 4 ?
421 			mf_temp_failure : mf_not_found;
422 
423 	if (io->esmtp_capa & CAPA_VRFY) {
424 		status = esmtp_vrfy(io);
425 		if (mf_resolved(status))
426 			return status;
427 	}
428 
429 	status = mf_success;
430 	for (i = 1; i <= mailcount; i++) {
431 		const char *fromaddr;
432 
433 		mu_address_sget_email(addr, i, &fromaddr);
434 
435 		smtp_send3(io, "MAIL FROM:<", fromaddr, ">");
436 
437 		if (smtp_recv(io, smtp_timeout_mail))
438 			return mf_timeout;
439 		else if (SMTP_MAJOR(io->code) != 2) {
440 			if (SMTP_MAJOR(io->code) == 4) {
441 				if (reset(io) != mf_success) {
442 					/* RSET must always return 250
443 					   If it does not, there's no
444 					   use talking to this host
445 					   any more */
446 					return mf_failure;
447 				} else
448 					status = mf_temp_failure;
449 			} else
450 				status = mf_not_found;
451 		} else {
452 			status = mf_success;
453 			break;
454 		}
455 	}
456 	if (status != mf_success)
457 		return status;
458 
459 	smtp_send3(io, "RCPT TO:<", io->email, ">");
460 	if (smtp_recv(io, smtp_timeout_rcpt))
461 		return mf_timeout;
462 	else if (SMTP_MAJOR(io->code) != 2)
463 		return SMTP_MAJOR(io->code) == 4 ?
464 				mf_temp_failure : mf_not_found;
465 	return mf_success;
466 }
467 
468 static int
create_transcript_stream(mu_stream_t * pstream,struct smtp_io_data * io)469 create_transcript_stream (mu_stream_t *pstream, struct smtp_io_data *io)
470 {
471 	int rc;
472 	mu_stream_t stream = *pstream;
473 	mu_stream_t dstr, xstr;
474 	char *fltargs[3] = { "INLINE-COMMENT", };
475 
476 	rc = mu_dbgstream_create (&dstr, MU_DIAG_DEBUG);
477 	if (rc) {
478 		mu_error (_("cannot create debug stream: %s; "
479 			    "transcript disabled"),
480 			  mu_strerror (rc));
481 		return rc;
482 	}
483 
484 	mu_asprintf (&fltargs[1], "%s: ", io->id);
485 	fltargs[2] = NULL;
486 	rc = mu_filter_create_args (&xstr, dstr,
487 				    "INLINE-COMMENT",
488 				    2, (const char**)fltargs,
489 				    MU_FILTER_ENCODE, MU_STREAM_WRITE);
490 	free (fltargs[1]);
491 
492 	if (rc == 0) {
493 		mu_stream_unref(dstr);
494 		dstr = xstr;
495 		mu_stream_set_buffer (dstr, mu_buffer_line, 0);
496 	} else
497 		mu_error (_("cannot create transcript filter"
498 			    "stream: %s"), mu_strerror (rc));
499 
500 	rc = mu_xscript_stream_create (&xstr, stream, dstr, NULL);
501 	if (rc)
502 		mu_error (_("cannot create transcript stream: %s; "
503 			    "transcript disabled"),
504 			  mu_strerror (rc));
505 	else {
506 		mu_stream_unref (stream);
507 		*pstream = xstr;
508 	}
509 	return rc;
510 }
511 
512 
513 mf_status
smtp_io_open(struct smtp_io_data * io,const char * hostname)514 smtp_io_open(struct smtp_io_data *io, const char *hostname)
515 {
516 	int rc;
517 	mu_stream_t stream;
518 	struct timeout_ctl tctl;
519 	struct mu_sockaddr_hints hints;
520 	struct mu_sockaddr *address, *srcaddr;
521 
522 	memset(&hints, 0, sizeof hints);
523 	hints.family = AF_INET;
524 	hints.socktype = SOCK_STREAM;
525 	hints.protocol = IPPROTO_TCP;
526 	hints.port = 25;
527 	rc = mu_sockaddr_from_node(&address, hostname, NULL, &hints);
528 	if (rc) {
529 		mu_error(_("cannot convert %s to sockaddr: %s"),
530 			 hostname, mu_strerror(rc));
531 		return mf_failure;
532 	}
533 
534 	mu_sockaddr_copy (&srcaddr, source_address);
535 	rc = mu_tcp_stream_create_from_sa(&stream, address, source_address,
536 					  MU_STREAM_NONBLOCK);
537 
538 	if (rc && !(rc == EAGAIN || rc == EINPROGRESS)) {
539 		mu_error(_("%s: cannot connect to `%s': %s"),
540 			 io->id, hostname,
541 			 mu_strerror(rc));
542 		mu_sockaddr_free(srcaddr);
543 		return mf_failure;
544 	}
545 	mu_stream_set_buffer (stream, mu_buffer_line, 0);
546 
547 	init_timeout_ctl(&tctl, io->timeout[smtp_timeout_connect]);
548 	while (rc) {
549 		if ((rc == EAGAIN || rc == EINPROGRESS) && tctl.timeout) {
550 			rc = mf_stream_wait(stream, MU_STREAM_READY_WR,
551 					    &tctl);
552 			if (rc == 0) {
553 				UPDATE_TTW(tctl);
554 				rc = mu_stream_open(stream);
555 				continue;
556 			}
557 		}
558 		mu_error("%s: stream_open(%s): %s",
559 			 io->id, hostname, mu_strerror(rc));
560 		mu_stream_destroy(&stream);
561 		return mf_timeout;
562 	}
563 	mu_debug(MF_SOURCE_CALLOUT, MU_DEBUG_TRACE9, ("stream opened"));
564 	if (smtp_transcript)
565 		create_transcript_stream(&stream, io);
566 
567 	io->stream = stream;
568 	return mf_success;
569 }
570 
571 void
smtp_io_close(struct smtp_io_data * io)572 smtp_io_close(struct smtp_io_data *io)
573 {
574 	if (io->stream) {
575 		mu_stream_close(io->stream);
576 		mu_stream_destroy(&io->stream);
577 	}
578 }
579 
580 mf_status
callout_host(struct smtp_io_data * io,const char * hostname)581 callout_host(struct smtp_io_data *io, const char *hostname)
582 {
583 	int rc;
584 	mf_status status = mf_success;
585 	mu_address_t addr;
586 	const char *mailfrom;
587 
588 	mu_debug(MF_SOURCE_CALLOUT, MU_DEBUG_TRACE5,
589               ("email = %s, hostname = %s",
590                io->email, hostname));
591 
592 	smtp_io_init(io);
593 	status = smtp_io_open(io, hostname);
594 	if (status != mf_success)
595 		return status;
596 
597 	/* FIXME-MU: compensate for mailutils deficiency */
598 	mailfrom = (io->mailfrom[0] == 0) ? "<>" : io->mailfrom;
599 	rc = mu_address_create(&addr, mailfrom);
600 	if (rc) {
601 		mu_error(_("%s: cannot create address `%s': %s"),
602 			 io->id, mailfrom, mu_strerror(rc));
603 		return mf_timeout;
604 	}
605 
606 	status = callout_io(io, hostname, addr);
607 
608 	mu_address_destroy(&addr);
609 
610 	if (io->callback) {
611 		io->callback(io->callback_closure,
612 			     "SENT", smtp_last_sent(io));
613 		io->callback(io->callback_closure,
614 			     "RECV", smtp_last_received(io));
615 	}
616 
617         mu_debug(MF_SOURCE_CALLOUT, MU_DEBUG_TRACE0,
618                  ("%s: verification of <%s> finished with status: %s; sent \"%s\", got \"%s\"",
619                  io->id,
620                  smtp_io_email(io),
621                  mf_status_str(status),
622                  smtp_last_sent(io),
623                  smtp_last_received(io)));
624 
625 	smtp_send2(io, "QUIT", NULL);
626 	smtp_recv(io, smtp_timeout_quit);
627 
628 	smtp_io_close(io);
629 
630 	return status;
631 }
632 
633 mf_status
callout_mx(struct smtp_io_data * iop,const char * hostname,int * pcount)634 callout_mx(struct smtp_io_data *iop, const char *hostname, int *pcount)
635 {
636 	int i;
637 	struct dns_reply reply;
638 	mf_status rc, mxstat;
639 
640 	mxstat = dns_to_mf_status(mx_lookup(hostname, 0, &reply));
641 
642 	if (pcount)
643 		*pcount = 0;
644 	switch (mxstat) {
645 	case mf_success:
646 		mu_debug(MF_SOURCE_CALLOUT, MU_DEBUG_TRACE1,
647 			 ("Checking MX servers for %s", iop->email));
648 		rc = mf_not_found;
649 		for (i = 0; i < reply.count; i++) {
650 			rc = callout_host(iop, reply.data.str[i]);
651 			if (mf_resolved(rc))
652 				break;
653 		}
654 		if (pcount)
655 			*pcount = reply.count;
656 		dns_reply_free(&reply);
657 		break;
658 
659 	default:
660 		rc = mxstat;
661 		break;
662 	}
663 	return rc;
664 }
665 
666 /* Method "strict". Verifies whether EMAIL is understood either by
667    host CLIENT_ADDR or one of MX servers of its domain */
668 mf_status
callout_strict(struct smtp_io_data * iop,const char * hostname)669 callout_strict(struct smtp_io_data *iop, const char *hostname)
670 {
671 	mf_status rc;
672 
673 	rc = callout_host(iop, hostname);
674 	if (!mf_resolved(rc)) {
675 		int mxcount;
676 		mf_status mx_stat;
677 		mx_stat = callout_mx(iop, hostname, &mxcount);
678 		if (!(mx_stat == mf_not_found && mxcount == 0)
679 		    && (mf_resolved(mx_stat)
680 			|| mx_stat == mf_timeout
681 			|| mx_stat == mf_temp_failure))
682 			rc = mx_stat;
683 	}
684 	return rc;
685 }
686 
687 mf_status
callout_standard(struct smtp_io_data * iop)688 callout_standard(struct smtp_io_data *iop)
689 {
690 	int rc;
691 
692 	char *p = strchr(iop->email, '@');
693 	if (p == NULL) {
694 		mu_error(_("%s: invalid address: %s"), iop->id, iop->email);
695 		rc = mf_not_found;
696 	} else {
697 		int mxcount;
698 		p++;
699 		rc = callout_mx(iop, p, &mxcount);
700 		if (rc != mf_success && mxcount == 0) {
701 			mf_status host_stat;
702 			host_stat = callout_host(iop, p);
703 			if (mf_resolved(host_stat)
704 			    || host_stat == mf_timeout
705 			    || host_stat == mf_temp_failure)
706 				rc = host_stat;
707 		}
708 	}
709 	return rc;
710 }
711 
712 static char *modnames[] = {
713 #define __DBGMOD_C_ARRAY
714 # include "callout-dbgmod.h"
715 #undef __DBGMOD_C_ARRAY
716 	NULL
717 };
718 
719 mu_debug_handle_t callout_debug_handle;
720 
721 void
libcallout_init()722 libcallout_init()
723 {
724 	int i;
725 
726 	callout_debug_handle = mu_debug_register_category (modnames[0]);
727 	for (i = 1; modnames[i]; i++)
728 		mu_debug_register_category (modnames[i]);
729 }
730 
731