xref: /openbsd-src/usr.sbin/smtpd/enqueue.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: enqueue.c,v 1.11 2009/04/05 16:10:42 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/tree.h>
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <event.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "smtpd.h"
40 
41 extern struct imsgbuf	*ibuf;
42 
43 __dead void	usage(void);
44 int		enqueue(int, char **);
45 int		enqueue_init(struct message *);
46 int		enqueue_add_recipient(struct message *, char *);
47 int		enqueue_messagefd(struct message *);
48 int		enqueue_write_message(FILE *, FILE *);
49 int		enqueue_commit(struct message *);
50 
51 int
52 enqueue(int argc, char *argv[])
53 {
54 	int		ch;
55 	int		fd;
56 	FILE		*fpout;
57 	struct message	message;
58 	char		sender[MAX_PATH_SIZE];
59 
60 	uid_t uid;
61 	char *username;
62 	char hostname[MAXHOSTNAMELEN];
63 	struct passwd *pw;
64 
65 	uid = getuid();
66 	pw = safe_getpwuid(uid);
67 	if (pw == NULL)
68 		errx(1, "you don't exist, go away.");
69 
70 	username = pw->pw_name;
71 	gethostname(hostname, sizeof(hostname));
72 
73 	if (! bsnprintf(sender, sizeof(sender), "%s@%s", username, hostname))
74 		errx(1, "sender address too long.");
75 
76 	while ((ch = getopt(argc, argv, "f:i")) != -1) {
77 		switch (ch) {
78 		case 'f':
79 			if (strlcpy(sender, optarg, sizeof(sender))
80 			    >= sizeof(sender))
81 				errx(1, "sender address too long.");
82 			break;
83 		case 'i': /* ignore, interface compatibility */
84 		case 'o':
85 			break;
86 		default:
87 			usage();
88 		}
89 	}
90 
91 	argc -= optind;
92 	argv += optind;
93 
94 	bzero(&message, sizeof(struct message));
95 
96 	strlcpy(message.session_helo, "localhost",
97 	    sizeof(message.session_helo));
98 	strlcpy(message.session_hostname, hostname,
99 	    sizeof(message.session_hostname));
100 
101 	/* build sender */
102 	if (! recipient_to_path(&message.sender, sender))
103 		errx(1, "invalid sender address.");
104 
105 	if (! enqueue_init(&message))
106 		errx(1, "failed to initialize enqueue message.");
107 
108 	if (argc == 0)
109 		errx(1, "no recipient.");
110 
111 	while (argc--) {
112 		if (! enqueue_add_recipient(&message, *argv))
113 			errx(1, "invalid recipient.");
114 		++argv;
115 	}
116 
117 	fd = enqueue_messagefd(&message);
118 	if (fd == -1 || (fpout = fdopen(fd, "w")) == NULL)
119 		errx(1, "failed to open message file for writing.");
120 
121 	if (! enqueue_write_message(stdin, fpout))
122 		errx(1, "failed to write message to message file.");
123 
124 	if (! safe_fclose(fpout))
125 		errx(1, "error while writing to message file.");
126 
127 	if (! enqueue_commit(&message))
128 		errx(1, "failed to commit message to queue.");
129 
130 	return 0;
131 }
132 
133 int
134 enqueue_add_recipient(struct message *messagep, char *recipient)
135 {
136 	char buffer[MAX_PATH_SIZE];
137 	struct message_recipient mr;
138 	struct sockaddr_in6 *ssin6;
139 	struct sockaddr_in *ssin;
140 	struct message message;
141 	int done = 0;
142 	int n;
143 	struct imsg imsg;
144 
145 	bzero(&mr, sizeof(mr));
146 
147 	message = *messagep;
148 
149 	if (strlcpy(buffer, recipient, sizeof(buffer)) >= sizeof(buffer))
150 		errx(1, "recipient address too long.");
151 
152 	if (strchr(buffer, '@') == NULL) {
153 		if (! bsnprintf(buffer, sizeof(buffer), "%s@%s",
154 			buffer, messagep->sender.domain))
155 			errx(1, "recipient address too long.");
156 	}
157 
158 	if (! recipient_to_path(&message.recipient, buffer))
159 		errx(1, "invalid recipient address.");
160 
161 	message.session_rcpt = message.recipient;
162 
163 	mr.ss.ss_family = AF_INET6;
164 	mr.ss.ss_len = sizeof(*ssin6);
165 	ssin6 = (struct sockaddr_in6 *)&mr.ss;
166 	if (inet_pton(AF_INET6, "::1", &ssin6->sin6_addr) != 1) {
167 		mr.ss.ss_family = AF_INET;
168 		mr.ss.ss_len = sizeof(*ssin);
169 		ssin = (struct sockaddr_in *)&mr.ss;
170 		if (inet_pton(AF_INET, "127.0.0.1", &ssin->sin_addr) != 1)
171 			return 0;
172 	}
173 	message.session_ss = mr.ss;
174 
175 	mr.path = message.recipient;
176 	mr.id = message.session_id;
177 	mr.msg = message;
178 	mr.msg.flags |= F_MESSAGE_ENQUEUED;
179 
180 	imsg_compose(ibuf, IMSG_MFA_RCPT, 0, 0, -1, &mr, sizeof (mr));
181 	while (ibuf->w.queued)
182 		if (msgbuf_write(&ibuf->w) < 0)
183 			err(1, "write error");
184 
185 	while (!done) {
186 		if ((n = imsg_read(ibuf)) == -1)
187 			errx(1, "imsg_read error");
188 		if (n == 0)
189 			errx(1, "pipe closed");
190 
191 		if ((n = imsg_get(ibuf, &imsg)) == -1)
192 			errx(1, "imsg_get error");
193 
194 		if (n == 0)
195 			continue;
196 
197 		done = 1;
198 		switch (imsg.hdr.type) {
199 		case IMSG_CTL_OK: {
200 			return 1;
201 		}
202 		case IMSG_CTL_FAIL:
203 			return 0;
204 		default:
205 			errx(1, "unexpected reply (%d)", imsg.hdr.type);
206 		}
207 		imsg_free(&imsg);
208 	}
209 
210 	return 1;
211 }
212 
213 int
214 enqueue_write_message(FILE *fpin, FILE *fpout)
215 {
216 	char *buf, *lbuf;
217 	size_t len;
218 
219 	lbuf = NULL;
220 	while ((buf = fgetln(fpin, &len))) {
221 		if (buf[len - 1] == '\n') {
222 			buf[len - 1] = '\0';
223 			len--;
224 		}
225 		else {
226 			/* EOF without EOL, copy and add the NUL */
227 			if ((lbuf = malloc(len + 1)) == NULL)
228 				err(1, NULL);
229 			memcpy(lbuf, buf, len);
230 			lbuf[len] = '\0';
231 			buf = lbuf;
232 		}
233 		if (fprintf(fpout, "%s\n", buf) != (int)len + 1)
234 			return 0;
235 	}
236 	free(lbuf);
237 	return 1;
238 }
239 
240 int
241 enqueue_init(struct message *messagep)
242 {
243 	int done = 0;
244 	int n;
245 	struct imsg imsg;
246 
247 	imsg_compose(ibuf, IMSG_QUEUE_CREATE_MESSAGE, 0, 0, -1, messagep, sizeof(*messagep));
248 	while (ibuf->w.queued)
249 		if (msgbuf_write(&ibuf->w) < 0)
250 			err(1, "write error");
251 
252 	while (!done) {
253 		if ((n = imsg_read(ibuf)) == -1)
254 			errx(1, "imsg_read error");
255 		if (n == 0)
256 			errx(1, "pipe closed");
257 
258 		if ((n = imsg_get(ibuf, &imsg)) == -1)
259 			errx(1, "imsg_get error");
260 
261 		if (n == 0)
262 			continue;
263 
264 		done = 1;
265 		switch (imsg.hdr.type) {
266 		case IMSG_CTL_OK: {
267 			struct message *mp;
268 
269 			mp = imsg.data;
270 			messagep->session_id = mp->session_id;
271 			strlcpy(messagep->message_id, mp->message_id,
272 			    sizeof(messagep->message_id));
273 
274 			return 1;
275 		}
276 		case IMSG_CTL_FAIL:
277 			return 0;
278 		default:
279 			err(1, "unexpected reply (%d)", imsg.hdr.type);
280 		}
281 		imsg_free(&imsg);
282 	}
283 
284 	return 0;
285 }
286 
287 int
288 enqueue_messagefd(struct message *messagep)
289 {
290 	int done = 0;
291 	int n;
292 	struct imsg imsg;
293 
294 	imsg_compose(ibuf, IMSG_QUEUE_MESSAGE_FILE, 0, 0, -1, messagep, sizeof(*messagep));
295 	while (ibuf->w.queued)
296 		if (msgbuf_write(&ibuf->w) < 0)
297 			err(1, "write error");
298 
299 	while (!done) {
300 		if ((n = imsg_read(ibuf)) == -1)
301 			errx(1, "imsg_read error");
302 		if (n == 0)
303 			errx(1, "pipe closed");
304 
305 		if ((n = imsg_get(ibuf, &imsg)) == -1)
306 			errx(1, "imsg_get error");
307 
308 		if (n == 0)
309 			continue;
310 
311 		done = 1;
312 		switch (imsg.hdr.type) {
313 		case IMSG_CTL_OK:
314 			return imsg_get_fd(ibuf, &imsg);
315 		case IMSG_CTL_FAIL:
316 			return -1;
317 		default:
318 			err(1, "unexpected reply (%d)", imsg.hdr.type);
319 		}
320 		imsg_free(&imsg);
321 	}
322 
323 	return -1;
324 }
325 
326 
327 int
328 enqueue_commit(struct message *messagep)
329 {
330 	int done = 0;
331 	int n;
332 	struct imsg imsg;
333 
334 	imsg_compose(ibuf, IMSG_QUEUE_COMMIT_MESSAGE, 0, 0, -1, messagep, sizeof(*messagep));
335 	while (ibuf->w.queued)
336 		if (msgbuf_write(&ibuf->w) < 0)
337 			err(1, "write error");
338 
339 	while (!done) {
340 		if ((n = imsg_read(ibuf)) == -1)
341 			errx(1, "imsg_read error");
342 		if (n == 0)
343 			errx(1, "pipe closed");
344 
345 		if ((n = imsg_get(ibuf, &imsg)) == -1)
346 			errx(1, "imsg_get error");
347 
348 		if (n == 0)
349 			continue;
350 
351 		done = 1;
352 		switch (imsg.hdr.type) {
353 		case IMSG_CTL_OK: {
354 			return 1;
355 		}
356 		case IMSG_CTL_FAIL: {
357 			return 0;
358 		}
359 		default:
360 			err(1, "unexpected reply (%d)", imsg.hdr.type);
361 		}
362 		imsg_free(&imsg);
363 	}
364 
365 	return 0;
366 }
367