xref: /dflybsd-src/libexec/dma/net.c (revision 14dfb9912037c6e7f0ddb9ef1cd77d5e14246639)
1f67bedddSMatthias Schmidt /*
2f67bedddSMatthias Schmidt  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3f67bedddSMatthias Schmidt  *
4f67bedddSMatthias Schmidt  * This code is derived from software contributed to The DragonFly Project
5f67bedddSMatthias Schmidt  * by Matthias Schmidt <matthias@dragonflybsd.org>, University of Marburg,
6f67bedddSMatthias Schmidt  * Germany.
7f67bedddSMatthias Schmidt  *
8f67bedddSMatthias Schmidt  * Redistribution and use in source and binary forms, with or without
9f67bedddSMatthias Schmidt  * modification, are permitted provided that the following conditions
10f67bedddSMatthias Schmidt  * are met:
11f67bedddSMatthias Schmidt  *
12f67bedddSMatthias Schmidt  * 1. Redistributions of source code must retain the above copyright
13f67bedddSMatthias Schmidt  *    notice, this list of conditions and the following disclaimer.
14f67bedddSMatthias Schmidt  * 2. Redistributions in binary form must reproduce the above copyright
15f67bedddSMatthias Schmidt  *    notice, this list of conditions and the following disclaimer in
16f67bedddSMatthias Schmidt  *    the documentation and/or other materials provided with the
17f67bedddSMatthias Schmidt  *    distribution.
18f67bedddSMatthias Schmidt  * 3. Neither the name of The DragonFly Project nor the names of its
19f67bedddSMatthias Schmidt  *    contributors may be used to endorse or promote products derived
20f67bedddSMatthias Schmidt  *    from this software without specific, prior written permission.
21f67bedddSMatthias Schmidt  *
22f67bedddSMatthias Schmidt  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23f67bedddSMatthias Schmidt  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24f67bedddSMatthias Schmidt  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25f67bedddSMatthias Schmidt  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26f67bedddSMatthias Schmidt  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27f67bedddSMatthias Schmidt  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28f67bedddSMatthias Schmidt  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29f67bedddSMatthias Schmidt  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30f67bedddSMatthias Schmidt  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31f67bedddSMatthias Schmidt  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32f67bedddSMatthias Schmidt  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33f67bedddSMatthias Schmidt  * SUCH DAMAGE.
34f67bedddSMatthias Schmidt  */
35f67bedddSMatthias Schmidt 
36c8b07ee5SSascha Wildner #include "dfcompat.h"
37c8b07ee5SSascha Wildner 
38f67bedddSMatthias Schmidt #include <sys/param.h>
39f67bedddSMatthias Schmidt #include <sys/queue.h>
40f67bedddSMatthias Schmidt #include <sys/stat.h>
41f67bedddSMatthias Schmidt #include <sys/types.h>
42f67bedddSMatthias Schmidt #include <sys/socket.h>
43f67bedddSMatthias Schmidt #include <netinet/in.h>
44f67bedddSMatthias Schmidt #include <arpa/inet.h>
45f67bedddSMatthias Schmidt 
46f67bedddSMatthias Schmidt #include <openssl/ssl.h>
47e2c88018SSimon Schubert #include <openssl/err.h>
48f67bedddSMatthias Schmidt 
49c8b07ee5SSascha Wildner #include <ctype.h>
50bc7baf1dSSascha Wildner #include <err.h>
51a5a8a1a4SSimon Schubert #include <errno.h>
52f67bedddSMatthias Schmidt #include <netdb.h>
53f67bedddSMatthias Schmidt #include <setjmp.h>
54f67bedddSMatthias Schmidt #include <signal.h>
55f67bedddSMatthias Schmidt #include <syslog.h>
56f67bedddSMatthias Schmidt #include <unistd.h>
57f67bedddSMatthias Schmidt 
58f67bedddSMatthias Schmidt #include "dma.h"
59f67bedddSMatthias Schmidt 
60c8b07ee5SSascha Wildner char neterr[ERRMSG_SIZE];
61f67bedddSMatthias Schmidt 
62e2c88018SSimon Schubert char *
63e2c88018SSimon Schubert ssl_errstr(void)
64e2c88018SSimon Schubert {
65e2c88018SSimon Schubert 	long oerr, nerr;
66e2c88018SSimon Schubert 
67e2c88018SSimon Schubert 	oerr = 0;
68e2c88018SSimon Schubert 	while ((nerr = ERR_get_error()) != 0)
69e2c88018SSimon Schubert 		oerr = nerr;
70e2c88018SSimon Schubert 
71e2c88018SSimon Schubert 	return (ERR_error_string(oerr, NULL));
72e2c88018SSimon Schubert }
73e2c88018SSimon Schubert 
74f67bedddSMatthias Schmidt ssize_t
75f67bedddSMatthias Schmidt send_remote_command(int fd, const char* fmt, ...)
76f67bedddSMatthias Schmidt {
77f67bedddSMatthias Schmidt 	va_list va;
78f67bedddSMatthias Schmidt 	char cmd[4096];
792922fd2bSSimon Schubert 	size_t len, pos;
802922fd2bSSimon Schubert 	int s;
812922fd2bSSimon Schubert 	ssize_t n;
82f67bedddSMatthias Schmidt 
83f67bedddSMatthias Schmidt 	va_start(va, fmt);
842922fd2bSSimon Schubert 	s = vsnprintf(cmd, sizeof(cmd) - 2, fmt, va);
852922fd2bSSimon Schubert 	va_end(va);
86e2c88018SSimon Schubert 	if (s == sizeof(cmd) - 2 || s < 0) {
87e2c88018SSimon Schubert 		strcpy(neterr, "Internal error: oversized command string");
88e2c88018SSimon Schubert 		return (-1);
89e2c88018SSimon Schubert 	}
90e2c88018SSimon Schubert 
912922fd2bSSimon Schubert 	/* We *know* there are at least two more bytes available */
922922fd2bSSimon Schubert 	strcat(cmd, "\r\n");
932922fd2bSSimon Schubert 	len = strlen(cmd);
94f67bedddSMatthias Schmidt 
95ca259d14SSimon Schubert 	if (((config.features & SECURETRANS) != 0) &&
96ca259d14SSimon Schubert 	    ((config.features & NOSSL) == 0)) {
97ca259d14SSimon Schubert 		while ((s = SSL_write(config.ssl, (const char*)cmd, len)) <= 0) {
98ca259d14SSimon Schubert 			s = SSL_get_error(config.ssl, s);
992922fd2bSSimon Schubert 			if (s != SSL_ERROR_WANT_READ &&
100e2c88018SSimon Schubert 			    s != SSL_ERROR_WANT_WRITE) {
101e2c88018SSimon Schubert 				strncpy(neterr, ssl_errstr(), sizeof(neterr));
1022922fd2bSSimon Schubert 				return (-1);
1032922fd2bSSimon Schubert 			}
104f67bedddSMatthias Schmidt 		}
105e2c88018SSimon Schubert 	}
106f67bedddSMatthias Schmidt 	else {
1072922fd2bSSimon Schubert 		pos = 0;
1082922fd2bSSimon Schubert 		while (pos < len) {
1092922fd2bSSimon Schubert 			n = write(fd, cmd + pos, len - pos);
1102922fd2bSSimon Schubert 			if (n < 0)
1112922fd2bSSimon Schubert 				return (-1);
1122922fd2bSSimon Schubert 			pos += n;
113f67bedddSMatthias Schmidt 		}
1142922fd2bSSimon Schubert 	}
115f67bedddSMatthias Schmidt 
1162922fd2bSSimon Schubert 	return (len);
117f67bedddSMatthias Schmidt }
118f67bedddSMatthias Schmidt 
1196ef9fe01SMatthias Schmidt int
1207b68d8aeSMatthias Schmidt read_remote(int fd, int extbufsize, char *extbuf)
121f67bedddSMatthias Schmidt {
1226ef9fe01SMatthias Schmidt 	ssize_t rlen = 0;
123c8b07ee5SSascha Wildner 	size_t pos, len, copysize;
1246ef9fe01SMatthias Schmidt 	char buff[BUF_SIZE];
125c8b07ee5SSascha Wildner 	int done = 0, status = 0, status_running = 0, extbufpos = 0;
126c8b07ee5SSascha Wildner 	enum { parse_status, parse_spacedash, parse_rest } parsestate;
127f67bedddSMatthias Schmidt 
128c8b07ee5SSascha Wildner 	if (do_timeout(CON_TIMEOUT, 1) != 0) {
129a5a8a1a4SSimon Schubert 		snprintf(neterr, sizeof(neterr), "Timeout reached");
130e2c88018SSimon Schubert 		return (-1);
131f67bedddSMatthias Schmidt 	}
132f67bedddSMatthias Schmidt 
133f67bedddSMatthias Schmidt 	/*
1346ef9fe01SMatthias Schmidt 	 * Remote reading code from femail.c written by Henning Brauer of
1356ef9fe01SMatthias Schmidt 	 * OpenBSD and released under a BSD style license.
136f67bedddSMatthias Schmidt 	 */
137c8b07ee5SSascha Wildner 	len = 0;
138c8b07ee5SSascha Wildner 	pos = 0;
139c8b07ee5SSascha Wildner 	parsestate = parse_status;
140c8b07ee5SSascha Wildner 	neterr[0] = 0;
141c8b07ee5SSascha Wildner 	while (!(done && parsestate == parse_status)) {
1427b68d8aeSMatthias Schmidt 		rlen = 0;
1436ef9fe01SMatthias Schmidt 		if (pos == 0 ||
1446ef9fe01SMatthias Schmidt 		    (pos > 0 && memchr(buff + pos, '\n', len - pos) == NULL)) {
1456ef9fe01SMatthias Schmidt 			memmove(buff, buff + pos, len - pos);
1466ef9fe01SMatthias Schmidt 			len -= pos;
1476ef9fe01SMatthias Schmidt 			pos = 0;
148ca259d14SSimon Schubert 			if (((config.features & SECURETRANS) != 0) &&
149ca259d14SSimon Schubert 			    (config.features & NOSSL) == 0) {
150c8b07ee5SSascha Wildner 				if ((rlen = SSL_read(config.ssl, buff + len, sizeof(buff) - len)) == -1) {
151e2c88018SSimon Schubert 					strncpy(neterr, ssl_errstr(), sizeof(neterr));
152c8b07ee5SSascha Wildner 					goto error;
153e2c88018SSimon Schubert 				}
1546ef9fe01SMatthias Schmidt 			} else {
155e2c88018SSimon Schubert 				if ((rlen = read(fd, buff + len, sizeof(buff) - len)) == -1) {
156e2c88018SSimon Schubert 					strncpy(neterr, strerror(errno), sizeof(neterr));
157c8b07ee5SSascha Wildner 					goto error;
158e2c88018SSimon Schubert 				}
1596ef9fe01SMatthias Schmidt 			}
1606ef9fe01SMatthias Schmidt 			len += rlen;
161c8b07ee5SSascha Wildner 
162c8b07ee5SSascha Wildner 			copysize = sizeof(neterr) - strlen(neterr) - 1;
163c8b07ee5SSascha Wildner 			if (copysize > len)
164c8b07ee5SSascha Wildner 				copysize = len;
165c8b07ee5SSascha Wildner 			strncat(neterr, buff, copysize);
1666ef9fe01SMatthias Schmidt 		}
1677b68d8aeSMatthias Schmidt 		/*
1687b68d8aeSMatthias Schmidt 		 * If there is an external buffer with a size bigger than zero
1697b68d8aeSMatthias Schmidt 		 * and as long as there is space in the external buffer and
1707b68d8aeSMatthias Schmidt 		 * there are new characters read from the mailserver
1717b68d8aeSMatthias Schmidt 		 * copy them to the external buffer
1727b68d8aeSMatthias Schmidt 		 */
173c8b07ee5SSascha Wildner 		if (extbufpos <= (extbufsize - 1) && rlen > 0 && extbufsize > 0 && extbuf != NULL) {
1747b68d8aeSMatthias Schmidt 			/* do not write over the bounds of the buffer */
1757b68d8aeSMatthias Schmidt 			if(extbufpos + rlen > (extbufsize - 1)) {
1767b68d8aeSMatthias Schmidt 				rlen = extbufsize - extbufpos;
1777b68d8aeSMatthias Schmidt 			}
1787b68d8aeSMatthias Schmidt 			memcpy(extbuf + extbufpos, buff + len - rlen, rlen);
1797b68d8aeSMatthias Schmidt 			extbufpos += rlen;
1807b68d8aeSMatthias Schmidt 		}
181f67bedddSMatthias Schmidt 
1826ef9fe01SMatthias Schmidt 		if (pos == len)
183c8b07ee5SSascha Wildner 			continue;
1846ef9fe01SMatthias Schmidt 
185c8b07ee5SSascha Wildner 		switch (parsestate) {
186c8b07ee5SSascha Wildner 		case parse_status:
187c8b07ee5SSascha Wildner 			for (; pos < len; pos++) {
188c8b07ee5SSascha Wildner 				if (isdigit(buff[pos])) {
189c8b07ee5SSascha Wildner 					status_running = status_running * 10 + (buff[pos] - '0');
190c8b07ee5SSascha Wildner 				} else {
191c8b07ee5SSascha Wildner 					status = status_running;
192c8b07ee5SSascha Wildner 					status_running = 0;
193c8b07ee5SSascha Wildner 					parsestate = parse_spacedash;
194c8b07ee5SSascha Wildner 					break;
195c8b07ee5SSascha Wildner 				}
196c8b07ee5SSascha Wildner 			}
197c8b07ee5SSascha Wildner 			continue;
198c8b07ee5SSascha Wildner 
199c8b07ee5SSascha Wildner 		case parse_spacedash:
200c8b07ee5SSascha Wildner 			switch (buff[pos]) {
201c8b07ee5SSascha Wildner 			case ' ':
2026ef9fe01SMatthias Schmidt 				done = 1;
203c8b07ee5SSascha Wildner 				break;
204c8b07ee5SSascha Wildner 
205c8b07ee5SSascha Wildner 			case '-':
206c8b07ee5SSascha Wildner 				/* ignore */
207c8b07ee5SSascha Wildner 				/* XXX read capabilities */
208c8b07ee5SSascha Wildner 				break;
209c8b07ee5SSascha Wildner 
210c8b07ee5SSascha Wildner 			default:
211e2c88018SSimon Schubert 				strcpy(neterr, "invalid syntax in reply from server");
212c8b07ee5SSascha Wildner 				goto error;
213e2c88018SSimon Schubert 			}
2146ef9fe01SMatthias Schmidt 
215c8b07ee5SSascha Wildner 			pos++;
216c8b07ee5SSascha Wildner 			parsestate = parse_rest;
217c8b07ee5SSascha Wildner 			continue;
218c8b07ee5SSascha Wildner 
219c8b07ee5SSascha Wildner 		case parse_rest:
2206ef9fe01SMatthias Schmidt 			/* skip up to \n */
221c8b07ee5SSascha Wildner 			for (; pos < len; pos++) {
222c8b07ee5SSascha Wildner 				if (buff[pos] == '\n') {
223c8b07ee5SSascha Wildner 					pos++;
224c8b07ee5SSascha Wildner 					parsestate = parse_status;
225c8b07ee5SSascha Wildner 					break;
226c8b07ee5SSascha Wildner 				}
227c8b07ee5SSascha Wildner 			}
228c8b07ee5SSascha Wildner 		}
2296ef9fe01SMatthias Schmidt 
2306ef9fe01SMatthias Schmidt 	}
231f67bedddSMatthias Schmidt 
232c8b07ee5SSascha Wildner 	do_timeout(0, 0);
233c8b07ee5SSascha Wildner 
234c8b07ee5SSascha Wildner 	/* chop off trailing newlines */
235c8b07ee5SSascha Wildner 	while (neterr[0] != 0 && strchr("\r\n", neterr[strlen(neterr) - 1]) != 0)
236c8b07ee5SSascha Wildner 		neterr[strlen(neterr) - 1] = 0;
237c8b07ee5SSascha Wildner 
2386ef9fe01SMatthias Schmidt 	return (status/100);
239c8b07ee5SSascha Wildner 
240c8b07ee5SSascha Wildner error:
241c8b07ee5SSascha Wildner 	do_timeout(0, 0);
242c8b07ee5SSascha Wildner 	return (-1);
243f67bedddSMatthias Schmidt }
244f67bedddSMatthias Schmidt 
245f67bedddSMatthias Schmidt /*
246f67bedddSMatthias Schmidt  * Handle SMTP authentication
247f67bedddSMatthias Schmidt  */
248f67bedddSMatthias Schmidt static int
249405f48eeSSimon Schubert smtp_login(int fd, char *login, char* password)
250f67bedddSMatthias Schmidt {
251f67bedddSMatthias Schmidt 	char *temp;
2526ef9fe01SMatthias Schmidt 	int len, res = 0;
253f67bedddSMatthias Schmidt 
254405f48eeSSimon Schubert 	res = smtp_auth_md5(fd, login, password);
2557b68d8aeSMatthias Schmidt 	if (res == 0) {
2567b68d8aeSMatthias Schmidt 		return (0);
2577b68d8aeSMatthias Schmidt 	} else if (res == -2) {
2587b68d8aeSMatthias Schmidt 	/*
2597b68d8aeSMatthias Schmidt 	 * If the return code is -2, then then the login attempt failed,
2607b68d8aeSMatthias Schmidt 	 * do not try other login mechanisms
2617b68d8aeSMatthias Schmidt 	 */
2625d7fe8bbSSimon Schubert 		return (1);
2637b68d8aeSMatthias Schmidt 	}
2647b68d8aeSMatthias Schmidt 
265ca259d14SSimon Schubert 	if ((config.features & INSECURE) != 0 ||
266ca259d14SSimon Schubert 	    (config.features & SECURETRANS) != 0) {
267f67bedddSMatthias Schmidt 		/* Send AUTH command according to RFC 2554 */
268f67bedddSMatthias Schmidt 		send_remote_command(fd, "AUTH LOGIN");
2697b68d8aeSMatthias Schmidt 		if (read_remote(fd, 0, NULL) != 3) {
270405f48eeSSimon Schubert 			syslog(LOG_NOTICE, "remote delivery deferred:"
271a5a8a1a4SSimon Schubert 					" AUTH login not available: %s",
272405f48eeSSimon Schubert 					neterr);
273f67bedddSMatthias Schmidt 			return (1);
274f67bedddSMatthias Schmidt 		}
275f67bedddSMatthias Schmidt 
276f67bedddSMatthias Schmidt 		len = base64_encode(login, strlen(login), &temp);
2775d7fe8bbSSimon Schubert 		if (len < 0) {
2785d7fe8bbSSimon Schubert encerr:
279405f48eeSSimon Schubert 			syslog(LOG_ERR, "can not encode auth reply: %m");
2805d7fe8bbSSimon Schubert 			return (1);
2815d7fe8bbSSimon Schubert 		}
282f67bedddSMatthias Schmidt 
283f67bedddSMatthias Schmidt 		send_remote_command(fd, "%s", temp);
2845d7fe8bbSSimon Schubert 		free(temp);
285e2c88018SSimon Schubert 		res = read_remote(fd, 0, NULL);
286e2c88018SSimon Schubert 		if (res != 3) {
287405f48eeSSimon Schubert 			syslog(LOG_NOTICE, "remote delivery %s: AUTH login failed: %s",
288405f48eeSSimon Schubert 			       res == 5 ? "failed" : "deferred", neterr);
289e2c88018SSimon Schubert 			return (res == 5 ? -1 : 1);
290f67bedddSMatthias Schmidt 		}
291f67bedddSMatthias Schmidt 
292f67bedddSMatthias Schmidt 		len = base64_encode(password, strlen(password), &temp);
2935d7fe8bbSSimon Schubert 		if (len < 0)
2945d7fe8bbSSimon Schubert 			goto encerr;
295f67bedddSMatthias Schmidt 
296f67bedddSMatthias Schmidt 		send_remote_command(fd, "%s", temp);
2975d7fe8bbSSimon Schubert 		free(temp);
2987b68d8aeSMatthias Schmidt 		res = read_remote(fd, 0, NULL);
299e2c88018SSimon Schubert 		if (res != 2) {
300405f48eeSSimon Schubert 			syslog(LOG_NOTICE, "remote delivery %s: Authentication failed: %s",
301405f48eeSSimon Schubert 					res == 5 ? "failed" : "deferred", neterr);
302e2c88018SSimon Schubert 			return (res == 5 ? -1 : 1);
303f67bedddSMatthias Schmidt 		}
3047b68d8aeSMatthias Schmidt 	} else {
305405f48eeSSimon Schubert 		syslog(LOG_WARNING, "non-encrypted SMTP login is disabled in config, so skipping it. ");
3067b68d8aeSMatthias Schmidt 		return (1);
3077b68d8aeSMatthias Schmidt 	}
308f67bedddSMatthias Schmidt 
309f67bedddSMatthias Schmidt 	return (0);
310f67bedddSMatthias Schmidt }
311f67bedddSMatthias Schmidt 
312f67bedddSMatthias Schmidt static int
3133021968aSSimon Schubert open_connection(struct mx_hostentry *h)
314f67bedddSMatthias Schmidt {
3153021968aSSimon Schubert 	int fd;
316f67bedddSMatthias Schmidt 
3173021968aSSimon Schubert 	syslog(LOG_INFO, "trying remote delivery to %s [%s] pref %d",
3183021968aSSimon Schubert 	       h->host, h->addr, h->pref);
319f67bedddSMatthias Schmidt 
3203021968aSSimon Schubert 	fd = socket(h->ai.ai_family, h->ai.ai_socktype, h->ai.ai_protocol);
3213021968aSSimon Schubert 	if (fd < 0) {
3223021968aSSimon Schubert 		syslog(LOG_INFO, "socket for %s [%s] failed: %m",
3233021968aSSimon Schubert 		       h->host, h->addr);
324f67bedddSMatthias Schmidt 		return (-1);
325f67bedddSMatthias Schmidt 	}
3263021968aSSimon Schubert 
327725a35c6SSimon Schubert 	if (connect(fd, (struct sockaddr *)&h->sa, h->ai.ai_addrlen) < 0) {
3283021968aSSimon Schubert 		syslog(LOG_INFO, "connect to %s [%s] failed: %m",
3293021968aSSimon Schubert 		       h->host, h->addr);
330f67bedddSMatthias Schmidt 		close(fd);
331f67bedddSMatthias Schmidt 		return (-1);
332f67bedddSMatthias Schmidt 	}
3333021968aSSimon Schubert 
334f67bedddSMatthias Schmidt 	return (fd);
335f67bedddSMatthias Schmidt }
336f67bedddSMatthias Schmidt 
33710eeb0dfSSimon Schubert static void
33810eeb0dfSSimon Schubert close_connection(int fd)
33910eeb0dfSSimon Schubert {
340de30bd07SMatthias Schmidt 	if (config.ssl != NULL) {
341ca259d14SSimon Schubert 		if (((config.features & SECURETRANS) != 0) &&
342ca259d14SSimon Schubert 		    ((config.features & NOSSL) == 0))
343ca259d14SSimon Schubert 			SSL_shutdown(config.ssl);
344ca259d14SSimon Schubert 		SSL_free(config.ssl);
345de30bd07SMatthias Schmidt 	}
34610eeb0dfSSimon Schubert 
34710eeb0dfSSimon Schubert 	close(fd);
34810eeb0dfSSimon Schubert }
34910eeb0dfSSimon Schubert 
3503021968aSSimon Schubert static int
351c8b07ee5SSascha Wildner deliver_to_host(struct qitem *it, struct mx_hostentry *host)
352f67bedddSMatthias Schmidt {
353f67bedddSMatthias Schmidt 	struct authuser *a;
3543021968aSSimon Schubert 	char line[1000];
355f67bedddSMatthias Schmidt 	size_t linelen;
3563021968aSSimon Schubert 	int fd, error = 0, do_auth = 0, res = 0;
357f67bedddSMatthias Schmidt 
358ebffba26SSimon Schubert 	if (fseek(it->mailf, 0, SEEK_SET) != 0) {
359c8b07ee5SSascha Wildner 		snprintf(errmsg, sizeof(errmsg), "can not seek: %s", strerror(errno));
3607d35694eSSimon Schubert 		return (-1);
3617d35694eSSimon Schubert 	}
3627d35694eSSimon Schubert 
363405f48eeSSimon Schubert 	fd = open_connection(host);
364f67bedddSMatthias Schmidt 	if (fd < 0)
365f67bedddSMatthias Schmidt 		return (1);
366f67bedddSMatthias Schmidt 
3673021968aSSimon Schubert #define READ_REMOTE_CHECK(c, exp)	\
3683021968aSSimon Schubert 	res = read_remote(fd, 0, NULL); \
3693021968aSSimon Schubert 	if (res == 5) { \
3703021968aSSimon Schubert 		syslog(LOG_ERR, "remote delivery to %s [%s] failed after %s: %s", \
3713021968aSSimon Schubert 		       host->host, host->addr, c, neterr); \
372c8b07ee5SSascha Wildner 		snprintf(errmsg, sizeof(errmsg), "%s [%s] did not like our %s:\n%s", \
3733021968aSSimon Schubert 			 host->host, host->addr, c, neterr); \
3743021968aSSimon Schubert 		return (-1); \
3753021968aSSimon Schubert 	} else if (res != exp) { \
3763021968aSSimon Schubert 		syslog(LOG_NOTICE, "remote delivery deferred: %s [%s] failed after %s: %s", \
3773021968aSSimon Schubert 		       host->host, host->addr, c, neterr); \
3783021968aSSimon Schubert 		return (1); \
3793021968aSSimon Schubert 	}
3803021968aSSimon Schubert 
381b8b7d065SSimon Schubert 	/* Check first reply from remote host */
382c8b07ee5SSascha Wildner 	if ((config.features & SECURETRANS) == 0 ||
383c8b07ee5SSascha Wildner 	    (config.features & STARTTLS) != 0) {
384b8b7d065SSimon Schubert 		config.features |= NOSSL;
385b8b7d065SSimon Schubert 		READ_REMOTE_CHECK("connect", 2);
386b8b7d065SSimon Schubert 
387b8b7d065SSimon Schubert 		config.features &= ~NOSSL;
388c8b07ee5SSascha Wildner 	}
389b8b7d065SSimon Schubert 
390b8b7d065SSimon Schubert 	if ((config.features & SECURETRANS) != 0) {
391b8b7d065SSimon Schubert 		error = smtp_init_crypto(fd, config.features);
392655c0566SSimon Schubert 		if (error == 0)
393b8b7d065SSimon Schubert 			syslog(LOG_DEBUG, "SSL initialization successful");
394b8b7d065SSimon Schubert 		else
395b8b7d065SSimon Schubert 			goto out;
396c8b07ee5SSascha Wildner 
397c8b07ee5SSascha Wildner 		if ((config.features & STARTTLS) == 0)
398c8b07ee5SSascha Wildner 			READ_REMOTE_CHECK("connect", 2);
399b8b7d065SSimon Schubert 	}
400b8b7d065SSimon Schubert 
401c21e2cfdSSimon Schubert 	/* XXX allow HELO fallback */
402c21e2cfdSSimon Schubert 	/* XXX record ESMTP keywords */
403f67bedddSMatthias Schmidt 	send_remote_command(fd, "EHLO %s", hostname());
4043021968aSSimon Schubert 	READ_REMOTE_CHECK("EHLO", 2);
405f67bedddSMatthias Schmidt 
406f67bedddSMatthias Schmidt 	/*
407f67bedddSMatthias Schmidt 	 * Use SMTP authentication if the user defined an entry for the remote
408f67bedddSMatthias Schmidt 	 * or smarthost
409f67bedddSMatthias Schmidt 	 */
410f67bedddSMatthias Schmidt 	SLIST_FOREACH(a, &authusers, next) {
4113021968aSSimon Schubert 		if (strcmp(a->host, host->host) == 0) {
412f67bedddSMatthias Schmidt 			do_auth = 1;
413f67bedddSMatthias Schmidt 			break;
414f67bedddSMatthias Schmidt 		}
415f67bedddSMatthias Schmidt 	}
416f67bedddSMatthias Schmidt 
417f67bedddSMatthias Schmidt 	if (do_auth == 1) {
418b558d098SMatthias Schmidt 		/*
419b558d098SMatthias Schmidt 		 * Check if the user wants plain text login without using
420b558d098SMatthias Schmidt 		 * encryption.
421b558d098SMatthias Schmidt 		 */
422ca259d14SSimon Schubert 		syslog(LOG_INFO, "using SMTP authentication for user %s", a->login);
423405f48eeSSimon Schubert 		error = smtp_login(fd, a->login, a->password);
424f67bedddSMatthias Schmidt 		if (error < 0) {
425405f48eeSSimon Schubert 			syslog(LOG_ERR, "remote delivery failed:"
426405f48eeSSimon Schubert 					" SMTP login failed: %m");
427c8b07ee5SSascha Wildner 			snprintf(errmsg, sizeof(errmsg), "SMTP login to %s failed", host->host);
428f67bedddSMatthias Schmidt 			return (-1);
429f67bedddSMatthias Schmidt 		}
430f67bedddSMatthias Schmidt 		/* SMTP login is not available, so try without */
431405f48eeSSimon Schubert 		else if (error > 0) {
432405f48eeSSimon Schubert 			syslog(LOG_WARNING, "SMTP login not available. Trying without.");
433405f48eeSSimon Schubert 		}
434b558d098SMatthias Schmidt 	}
435f67bedddSMatthias Schmidt 
436c21e2cfdSSimon Schubert 	/* XXX send ESMTP ENVID, RET (FULL/HDRS) and 8BITMIME */
4377dfd2fd8SSimon Schubert 	send_remote_command(fd, "MAIL FROM:<%s>", it->sender);
4387dfd2fd8SSimon Schubert 	READ_REMOTE_CHECK("MAIL FROM", 2);
4397dfd2fd8SSimon Schubert 
440c21e2cfdSSimon Schubert 	/* XXX send ESMTP ORCPT */
441f67bedddSMatthias Schmidt 	send_remote_command(fd, "RCPT TO:<%s>", it->addr);
4427dfd2fd8SSimon Schubert 	READ_REMOTE_CHECK("RCPT TO", 2);
443f67bedddSMatthias Schmidt 
444f67bedddSMatthias Schmidt 	send_remote_command(fd, "DATA");
4457dfd2fd8SSimon Schubert 	READ_REMOTE_CHECK("DATA", 3);
446f67bedddSMatthias Schmidt 
4470caaabf6SSimon Schubert 	error = 0;
448f4e61a9fSSimon 'corecode' Schubert 	while (!feof(it->mailf)) {
449f4e61a9fSSimon 'corecode' Schubert 		if (fgets(line, sizeof(line), it->mailf) == NULL)
450f67bedddSMatthias Schmidt 			break;
451f67bedddSMatthias Schmidt 		linelen = strlen(line);
452f67bedddSMatthias Schmidt 		if (linelen == 0 || line[linelen - 1] != '\n') {
453405f48eeSSimon Schubert 			syslog(LOG_CRIT, "remote delivery failed: corrupted queue file");
454c8b07ee5SSascha Wildner 			snprintf(errmsg, sizeof(errmsg), "corrupted queue file");
455f67bedddSMatthias Schmidt 			error = -1;
456f67bedddSMatthias Schmidt 			goto out;
457f67bedddSMatthias Schmidt 		}
458f67bedddSMatthias Schmidt 
459f67bedddSMatthias Schmidt 		/* Remove trailing \n's and escape leading dots */
460f67bedddSMatthias Schmidt 		trim_line(line);
461f67bedddSMatthias Schmidt 
462f67bedddSMatthias Schmidt 		/*
463f67bedddSMatthias Schmidt 		 * If the first character is a dot, we escape it so the line
464f67bedddSMatthias Schmidt 		 * length increases
465f67bedddSMatthias Schmidt 		*/
466f67bedddSMatthias Schmidt 		if (line[0] == '.')
467f67bedddSMatthias Schmidt 			linelen++;
468f67bedddSMatthias Schmidt 
469f67bedddSMatthias Schmidt 		if (send_remote_command(fd, "%s", line) != (ssize_t)linelen+1) {
470405f48eeSSimon Schubert 			syslog(LOG_NOTICE, "remote delivery deferred: write error");
471f67bedddSMatthias Schmidt 			error = 1;
472f67bedddSMatthias Schmidt 			goto out;
473f67bedddSMatthias Schmidt 		}
474f67bedddSMatthias Schmidt 	}
475f67bedddSMatthias Schmidt 
476f67bedddSMatthias Schmidt 	send_remote_command(fd, ".");
4777dfd2fd8SSimon Schubert 	READ_REMOTE_CHECK("final DATA", 2);
478f67bedddSMatthias Schmidt 
479f67bedddSMatthias Schmidt 	send_remote_command(fd, "QUIT");
4806cfc247dSSimon Schubert 	if (read_remote(fd, 0, NULL) != 2)
481405f48eeSSimon Schubert 		syslog(LOG_INFO, "remote delivery succeeded but QUIT failed: %s", neterr);
482f67bedddSMatthias Schmidt out:
483f67bedddSMatthias Schmidt 
48410eeb0dfSSimon Schubert 	close_connection(fd);
485f67bedddSMatthias Schmidt 	return (error);
486f67bedddSMatthias Schmidt }
487f67bedddSMatthias Schmidt 
4883021968aSSimon Schubert int
489c8b07ee5SSascha Wildner deliver_remote(struct qitem *it)
4903021968aSSimon Schubert {
4913021968aSSimon Schubert 	struct mx_hostentry *hosts, *h;
492ca259d14SSimon Schubert 	const char *host;
4933021968aSSimon Schubert 	int port;
4943021968aSSimon Schubert 	int error = 1, smarthost = 0;
4953021968aSSimon Schubert 
496*14dfb991SJoris Giovannangeli 	port = SMTP_PORT;
497*14dfb991SJoris Giovannangeli 
498*14dfb991SJoris Giovannangeli 	/* Smarthost support? */
499*14dfb991SJoris Giovannangeli 	if (config.smarthost != NULL) {
500*14dfb991SJoris Giovannangeli 		host = config.smarthost;
501*14dfb991SJoris Giovannangeli 		port = config.port;
502*14dfb991SJoris Giovannangeli 		syslog(LOG_INFO, "using smarthost (%s:%i)", host, port);
503*14dfb991SJoris Giovannangeli 		smarthost = 1;
504*14dfb991SJoris Giovannangeli 	} else {
5053021968aSSimon Schubert 		host = strrchr(it->addr, '@');
5063021968aSSimon Schubert 		/* Should not happen */
5073021968aSSimon Schubert 		if (host == NULL) {
508c8b07ee5SSascha Wildner 			snprintf(errmsg, sizeof(errmsg), "Internal error: badly formed address %s",
5093021968aSSimon Schubert 				 it->addr);
5103021968aSSimon Schubert 			return(-1);
5113021968aSSimon Schubert 		} else {
5123021968aSSimon Schubert 			/* Step over the @ */
5133021968aSSimon Schubert 			host++;
5143021968aSSimon Schubert 		}
5153021968aSSimon Schubert 	}
5163021968aSSimon Schubert 
5173021968aSSimon Schubert 	error = dns_get_mx_list(host, port, &hosts, smarthost);
5183021968aSSimon Schubert 	if (error) {
519*14dfb991SJoris Giovannangeli 		snprintf(errmsg, sizeof(errmsg), "DNS lookup failure: host %s not found", host);
520*14dfb991SJoris Giovannangeli 		syslog(LOG_NOTICE, "remote delivery %s: DNS lookup failure: host %s not found",
5213021968aSSimon Schubert 		       error < 0 ? "failed" : "deferred",
5223021968aSSimon Schubert 		       host);
5233021968aSSimon Schubert 		return (error);
5243021968aSSimon Schubert 	}
5253021968aSSimon Schubert 
5263021968aSSimon Schubert 	for (h = hosts; *h->host != 0; h++) {
527c8b07ee5SSascha Wildner 		switch (deliver_to_host(it, h)) {
5283021968aSSimon Schubert 		case 0:
5293021968aSSimon Schubert 			/* success */
5303021968aSSimon Schubert 			error = 0;
5313021968aSSimon Schubert 			goto out;
5323021968aSSimon Schubert 		case 1:
5333021968aSSimon Schubert 			/* temp failure */
5343021968aSSimon Schubert 			error = 1;
5353021968aSSimon Schubert 			break;
5363021968aSSimon Schubert 		default:
5373021968aSSimon Schubert 			/* perm failure */
5383021968aSSimon Schubert 			error = -1;
5393021968aSSimon Schubert 			goto out;
5403021968aSSimon Schubert 		}
5413021968aSSimon Schubert 	}
5423021968aSSimon Schubert out:
5433021968aSSimon Schubert 	free(hosts);
5443021968aSSimon Schubert 
5453021968aSSimon Schubert 	return (error);
5463021968aSSimon Schubert }
547