Bug Summary

File:src/usr.sbin/smtpd/smtpd/../mda.c
Warning:line 140, column 11
The left operand of '!=' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name mda.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/smtpd/smtpd/.. -D IO_TLS -D QUEUE_PROFILING -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/smtpd/smtpd/../mda.c
1/* $OpenBSD: mda.c,v 1.143 2021/06/14 17:58:15 eric Exp $ */
2
3/*
4 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
5 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22#include <ctype.h>
23#include <inttypes.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sysexits.h>
27#include <unistd.h>
28#include <vis.h>
29
30#include "smtpd.h"
31#include "log.h"
32
33#define MDA_HIWAT65536 65536
34
35struct mda_envelope {
36 TAILQ_ENTRY(mda_envelope)struct { struct mda_envelope *tqe_next; struct mda_envelope *
*tqe_prev; }
entry;
37 uint64_t session_id;
38 uint64_t id;
39 time_t creation;
40 char *sender;
41 char *rcpt;
42 char *dest;
43 char *user;
44 char *dispatcher;
45 char *mda_subaddress;
46 char *mda_exec;
47};
48
49#define USER_WAITINFO0x01 0x01
50#define USER_RUNNABLE0x02 0x02
51#define USER_ONHOLD0x04 0x04
52#define USER_HOLDQ0x08 0x08
53
54struct mda_user {
55 uint64_t id;
56 TAILQ_ENTRY(mda_user)struct { struct mda_user *tqe_next; struct mda_user **tqe_prev
; }
entry;
57 TAILQ_ENTRY(mda_user)struct { struct mda_user *tqe_next; struct mda_user **tqe_prev
; }
entry_runnable;
58 char name[LOGIN_NAME_MAX32];
59 char usertable[PATH_MAX1024];
60 size_t evpcount;
61 TAILQ_HEAD(, mda_envelope)struct { struct mda_envelope *tqh_first; struct mda_envelope *
*tqh_last; }
envelopes;
62 int flags;
63 size_t running;
64 struct userinfo userinfo;
65};
66
67struct mda_session {
68 uint64_t id;
69 struct mda_user *user;
70 struct mda_envelope *evp;
71 struct io *io;
72 FILE *datafp;
73};
74
75static void mda_io(struct io *, int, void *);
76static int mda_check_loop(FILE *, struct mda_envelope *);
77static int mda_getlastline(int, char *, size_t);
78static void mda_done(struct mda_session *);
79static void mda_fail(struct mda_user *, int, const char *,
80 enum enhanced_status_code);
81static void mda_drain(void);
82static void mda_log(const struct mda_envelope *, const char *, const char *);
83static void mda_queue_ok(uint64_t);
84static void mda_queue_tempfail(uint64_t, const char *,
85 enum enhanced_status_code);
86static void mda_queue_permfail(uint64_t, const char *, enum enhanced_status_code);
87static void mda_queue_loop(uint64_t);
88static struct mda_user *mda_user(const struct envelope *);
89static void mda_user_free(struct mda_user *);
90static const char *mda_user_to_text(const struct mda_user *);
91static struct mda_envelope *mda_envelope(uint64_t, const struct envelope *);
92static void mda_envelope_free(struct mda_envelope *);
93static struct mda_session * mda_session(struct mda_user *);
94static const char *mda_sysexit_to_str(int);
95
96static struct tree sessions;
97static struct tree users;
98
99static TAILQ_HEAD(, mda_user)struct { struct mda_user *tqh_first; struct mda_user **tqh_last
; }
runnable;
100
101void
102mda_imsg(struct mproc *p, struct imsg *imsg)
103{
104 struct mda_session *s;
105 struct mda_user *u;
106 struct mda_envelope *e;
107 struct envelope evp;
108 struct deliver deliver;
109 struct msg m;
110 const void *data;
111 const char *error, *parent_error, *syserror;
112 uint64_t reqid;
113 size_t sz;
1
'sz' declared without an initial value
114 char out[256], buf[LINE_MAX2048];
115 int n;
116 enum lka_resp_status status;
117 enum mda_resp_status mda_status;
118 int mda_sysexit;
119
120 switch (imsg->hdr.type) {
2
Control jumps to 'case IMSG_MDA_LOOKUP_USERINFO:' at line 121
121 case IMSG_MDA_LOOKUP_USERINFO:
122 m_msg(&m, imsg);
123 m_get_id(&m, &reqid);
124 m_get_int(&m, (int *)&status);
125 if (status == LKA_OK)
3
Assuming 'status' is not equal to LKA_OK
4
Taking false branch
126 m_get_data(&m, &data, &sz);
127 m_end(&m);
128
129 u = tree_xget(&users, reqid);
130
131 if (status == LKA_TEMPFAIL)
5
Assuming 'status' is not equal to LKA_TEMPFAIL
6
Taking false branch
132 mda_fail(u, 0,
133 "Temporary failure in user lookup",
134 ESC_OTHER_ADDRESS_STATUS);
135 else if (status == LKA_PERMFAIL)
7
Assuming 'status' is not equal to LKA_PERMFAIL
8
Taking false branch
136 mda_fail(u, 1,
137 "Permanent failure in user lookup",
138 ESC_DESTINATION_MAILBOX_HAS_MOVED);
139 else {
140 if (sz != sizeof(u->userinfo))
9
The left operand of '!=' is a garbage value
141 fatalx("mda: userinfo size mismatch");
142 memmove(&u->userinfo, data, sz);
143 u->flags &= ~USER_WAITINFO0x01;
144 u->flags |= USER_RUNNABLE0x02;
145 TAILQ_INSERT_TAIL(&runnable, u, entry_runnable)do { (u)->entry_runnable.tqe_next = ((void *)0); (u)->entry_runnable
.tqe_prev = (&runnable)->tqh_last; *(&runnable)->
tqh_last = (u); (&runnable)->tqh_last = &(u)->entry_runnable
.tqe_next; } while (0)
;
146 mda_drain();
147 }
148 return;
149
150 case IMSG_QUEUE_DELIVER:
151 m_msg(&m, imsg);
152 m_get_envelope(&m, &evp);
153 m_end(&m);
154
155 u = mda_user(&evp);
156
157 if (u->evpcount >= env->sc_mda_task_hiwat) {
158 if (!(u->flags & USER_ONHOLD0x04)) {
159 log_debug("debug: mda: hiwat reached for "
160 "user \"%s\": holding envelopes",
161 mda_user_to_text(u));
162 u->flags |= USER_ONHOLD0x04;
163 }
164 }
165
166 if (u->flags & USER_ONHOLD0x04) {
167 u->flags |= USER_HOLDQ0x08;
168 m_create(p_queue, IMSG_MDA_DELIVERY_HOLD,
169 0, 0, -1);
170 m_add_evpid(p_queue, evp.id);
171 m_add_id(p_queue, u->id);
172 m_close(p_queue);
173 return;
174 }
175
176 e = mda_envelope(u->id, &evp);
177 TAILQ_INSERT_TAIL(&u->envelopes, e, entry)do { (e)->entry.tqe_next = ((void *)0); (e)->entry.tqe_prev
= (&u->envelopes)->tqh_last; *(&u->envelopes
)->tqh_last = (e); (&u->envelopes)->tqh_last = &
(e)->entry.tqe_next; } while (0)
;
178 u->evpcount += 1;
179 stat_increment("mda.pending", 1);
180
181 if (!(u->flags & USER_RUNNABLE0x02) &&
182 !(u->flags & USER_WAITINFO0x01)) {
183 u->flags |= USER_RUNNABLE0x02;
184 TAILQ_INSERT_TAIL(&runnable, u, entry_runnable)do { (u)->entry_runnable.tqe_next = ((void *)0); (u)->entry_runnable
.tqe_prev = (&runnable)->tqh_last; *(&runnable)->
tqh_last = (u); (&runnable)->tqh_last = &(u)->entry_runnable
.tqe_next; } while (0)
;
185 }
186
187 mda_drain();
188 return;
189
190 case IMSG_MDA_OPEN_MESSAGE:
191 m_msg(&m, imsg);
192 m_get_id(&m, &reqid);
193 m_end(&m);
194
195 s = tree_xget(&sessions, reqid);
196 e = s->evp;
197
198 if (imsg->fd == -1) {
199 log_debug("debug: mda: cannot get message fd");
200 mda_queue_tempfail(e->id,
201 "Cannot get message fd",
202 ESC_OTHER_MAIL_SYSTEM_STATUS);
203 mda_log(e, "TempFail", "Cannot get message fd");
204 mda_done(s);
205 return;
206 }
207
208 log_debug("debug: mda: got message fd %d "
209 "for session %016"PRIx64"llx" " evpid %016"PRIx64"llx",
210 imsg->fd, s->id, e->id);
211
212 if ((s->datafp = fdopen(imsg->fd, "r")) == NULL((void *)0)) {
213 log_warn("warn: mda: fdopen");
214 close(imsg->fd);
215 mda_queue_tempfail(e->id, "fdopen failed",
216 ESC_OTHER_MAIL_SYSTEM_STATUS);
217 mda_log(e, "TempFail", "fdopen failed");
218 mda_done(s);
219 return;
220 }
221
222 /* check delivery loop */
223 if (mda_check_loop(s->datafp, e)) {
224 log_debug("debug: mda: loop detected");
225 mda_queue_loop(e->id);
226 mda_log(e, "PermFail", "Loop detected");
227 mda_done(s);
228 return;
229 }
230
231 /* start queueing delivery headers */
232 if (e->sender[0])
233 /*
234 * XXX: remove existing Return-Path,
235 * if any
236 */
237 n = io_printf(s->io,
238 "Return-Path: <%s>\n"
239 "Delivered-To: %s\n",
240 e->sender,
241 e->rcpt ? e->rcpt : e->dest);
242 else
243 n = io_printf(s->io,
244 "Delivered-To: %s\n",
245 e->rcpt ? e->rcpt : e->dest);
246 if (n == -1) {
247 log_warn("warn: mda: "
248 "fail to write delivery info");
249 mda_queue_tempfail(e->id, "Out of memory",
250 ESC_OTHER_MAIL_SYSTEM_STATUS);
251 mda_log(e, "TempFail", "Out of memory");
252 mda_done(s);
253 return;
254 }
255
256 /* request parent to fork a helper process */
257 memset(&deliver, 0, sizeof deliver);
258 (void)text_to_mailaddr(&deliver.sender, s->evp->sender);
259 (void)text_to_mailaddr(&deliver.rcpt, s->evp->rcpt);
260 (void)text_to_mailaddr(&deliver.dest, s->evp->dest);
261 if (s->evp->mda_exec)
262 (void)strlcpy(deliver.mda_exec, s->evp->mda_exec, sizeof deliver.mda_exec);
263 if (s->evp->mda_subaddress)
264 (void)strlcpy(deliver.mda_subaddress, s->evp->mda_subaddress, sizeof deliver.mda_subaddress);
265 (void)strlcpy(deliver.dispatcher, s->evp->dispatcher, sizeof deliver.dispatcher);
266 deliver.userinfo = s->user->userinfo;
267
268 log_debug("debug: mda: querying mda fd "
269 "for session %016"PRIx64"llx" " evpid %016"PRIx64"llx",
270 s->id, s->evp->id);
271
272 m_create(p_parent, IMSG_MDA_FORK, 0, 0, -1);
273 m_add_id(p_parent, reqid);
274 m_add_data(p_parent, &deliver, sizeof(deliver));
275 m_close(p_parent);
276 return;
277
278 case IMSG_MDA_FORK:
279 m_msg(&m, imsg);
280 m_get_id(&m, &reqid);
281 m_end(&m);
282
283 s = tree_xget(&sessions, reqid);
284 e = s->evp;
285 if (imsg->fd == -1) {
286 log_warn("warn: mda: fail to retrieve mda fd");
287 mda_queue_tempfail(e->id, "Cannot get mda fd",
288 ESC_OTHER_MAIL_SYSTEM_STATUS);
289 mda_log(e, "TempFail", "Cannot get mda fd");
290 mda_done(s);
291 return;
292 }
293
294 log_debug("debug: mda: got mda fd %d "
295 "for session %016"PRIx64"llx" " evpid %016"PRIx64"llx",
296 imsg->fd, s->id, s->evp->id);
297
298 io_set_nonblocking(imsg->fd);
299 io_set_fd(s->io, imsg->fd);
300 io_set_write(s->io);
301 return;
302
303 case IMSG_MDA_DONE:
304 m_msg(&m, imsg);
305 m_get_id(&m, &reqid);
306 m_get_int(&m, (int *)&mda_status);
307 m_get_int(&m, (int *)&mda_sysexit);
308 m_get_string(&m, &parent_error);
309 m_end(&m);
310
311 s = tree_xget(&sessions, reqid);
312 e = s->evp;
313 /*
314 * Grab last line of mda stdout/stderr if available.
315 */
316 out[0] = '\0';
317 if (imsg->fd != -1)
318 mda_getlastline(imsg->fd, out, sizeof(out));
319
320 /*
321 * Choose between parent's description of error and
322 * child's output, the latter having preference over
323 * the former.
324 */
325 error = NULL((void *)0);
326 if (mda_status == MDA_OK) {
327 if (s->datafp || (s->io && io_queued(s->io))) {
328 error = "mda exited prematurely";
329 mda_status = MDA_TEMPFAIL;
330 }
331 } else
332 error = out[0] ? out : parent_error;
333
334 syserror = NULL((void *)0);
335 if (mda_sysexit)
336 syserror = mda_sysexit_to_str(mda_sysexit);
337
338 /* update queue entry */
339 switch (mda_status) {
340 case MDA_TEMPFAIL:
341 mda_queue_tempfail(e->id, error,
342 ESC_OTHER_MAIL_SYSTEM_STATUS);
343 (void)snprintf(buf, sizeof buf,
344 "Error (%s%s%s)",
345 syserror ? syserror : "",
346 syserror ? ": " : "",
347 error);
348 mda_log(e, "TempFail", buf);
349 break;
350 case MDA_PERMFAIL:
351 mda_queue_permfail(e->id, error,
352 ESC_OTHER_MAIL_SYSTEM_STATUS);
353 (void)snprintf(buf, sizeof buf,
354 "Error (%s%s%s)",
355 syserror ? syserror : "",
356 syserror ? ": " : "",
357 error);
358 mda_log(e, "PermFail", buf);
359 break;
360 case MDA_OK:
361 mda_queue_ok(e->id);
362 mda_log(e, "Ok", "Delivered");
363 break;
364 }
365 mda_done(s);
366 return;
367 }
368
369 fatalx("mda_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
370}
371
372void
373mda_postfork()
374{
375}
376
377void
378mda_postprivdrop()
379{
380 tree_init(&sessions)do { do { (&((&sessions)->tree))->sph_root = ((
void *)0); } while (0); (&sessions)->count = 0; } while
(0)
;
381 tree_init(&users)do { do { (&((&users)->tree))->sph_root = ((void
*)0); } while (0); (&users)->count = 0; } while(0)
;
382 TAILQ_INIT(&runnable)do { (&runnable)->tqh_first = ((void *)0); (&runnable
)->tqh_last = &(&runnable)->tqh_first; } while (
0)
;
383}
384
385static void
386mda_io(struct io *io, int evt, void *arg)
387{
388 struct mda_session *s = arg;
389 char *ln = NULL((void *)0);
390 size_t sz = 0;
391 ssize_t len;
392
393 log_trace(TRACE_IO, "mda: %p: %s %s", s, io_strevent(evt),do { if (tracing & (0x0004)) log_trace0("mda: %p: %s %s",
s, io_strevent(evt), io_strio(io)); } while (0)
394 io_strio(io))do { if (tracing & (0x0004)) log_trace0("mda: %p: %s %s",
s, io_strevent(evt), io_strio(io)); } while (0)
;
395
396 switch (evt) {
397 case IO_LOWAT:
398
399 /* done */
400 done:
401 if (s->datafp == NULL((void *)0)) {
402 log_debug("debug: mda: all data sent for session"
403 " %016"PRIx64"llx" " evpid %016"PRIx64"llx",
404 s->id, s->evp->id);
405 io_free(io);
406 s->io = NULL((void *)0);
407 return;
408 }
409
410 while (io_queued(s->io) < MDA_HIWAT65536) {
411 if ((len = getline(&ln, &sz, s->datafp)) == -1)
412 break;
413 if (io_write(s->io, ln, len) == -1) {
414 m_create(p_parent, IMSG_MDA_KILL,
415 0, 0, -1);
416 m_add_id(p_parent, s->id);
417 m_add_string(p_parent, "Out of memory");
418 m_close(p_parent);
419 io_pause(io, IO_OUT0x02);
420 free(ln);
421 return;
422 }
423 }
424
425 free(ln);
426 ln = NULL((void *)0);
427 if (ferror(s->datafp)(!__isthreaded ? (((s->datafp)->_flags & 0x0040) !=
0) : (ferror)(s->datafp))
) {
428 log_debug("debug: mda: ferror on session %016"PRIx64"llx",
429 s->id);
430 m_create(p_parent, IMSG_MDA_KILL, 0, 0, -1);
431 m_add_id(p_parent, s->id);
432 m_add_string(p_parent, "Error reading body");
433 m_close(p_parent);
434 io_pause(io, IO_OUT0x02);
435 return;
436 }
437
438 if (feof(s->datafp)(!__isthreaded ? (((s->datafp)->_flags & 0x0020) !=
0) : (feof)(s->datafp))
) {
439 log_debug("debug: mda: end-of-file for session"
440 " %016"PRIx64"llx" " evpid %016"PRIx64"llx",
441 s->id, s->evp->id);
442 fclose(s->datafp);
443 s->datafp = NULL((void *)0);
444 if (io_queued(s->io) == 0)
445 goto done;
446 }
447 return;
448
449 case IO_TIMEOUT:
450 log_debug("debug: mda: timeout on session %016"PRIx64"llx", s->id);
451 io_pause(io, IO_OUT0x02);
452 return;
453
454 case IO_ERROR:
455 log_debug("debug: mda: io error on session %016"PRIx64"llx"": %s",
456 s->id, io_error(io));
457 io_pause(io, IO_OUT0x02);
458 return;
459
460 case IO_DISCONNECTED:
461 log_debug("debug: mda: io disconnected on session %016"PRIx64"llx",
462 s->id);
463 io_pause(io, IO_OUT0x02);
464 return;
465
466 default:
467 log_debug("debug: mda: unexpected event on session %016"PRIx64"llx",
468 s->id);
469 io_pause(io, IO_OUT0x02);
470 return;
471 }
472}
473
474static int
475mda_check_loop(FILE *fp, struct mda_envelope *e)
476{
477 char *buf = NULL((void *)0);
478 size_t sz = 0;
479 ssize_t len;
480 int ret = 0;
481
482 while ((len = getline(&buf, &sz, fp)) != -1) {
483 if (buf[len - 1] == '\n')
484 buf[len - 1] = '\0';
485
486 if (strchr(buf, ':') == NULL((void *)0) && !isspace((unsigned char)*buf))
487 break;
488
489 if (strncasecmp("Delivered-To: ", buf, 14) == 0) {
490 if (strcasecmp(buf + 14, e->dest) == 0) {
491 ret = 1;
492 break;
493 }
494 }
495 }
496
497 free(buf);
498 fseek(fp, SEEK_SET0, 0);
499 return (ret);
500}
501
502static int
503mda_getlastline(int fd, char *dst, size_t dstsz)
504{
505 FILE *fp;
506 char *ln = NULL((void *)0);
507 size_t sz = 0;
508 ssize_t len;
509 int out = 0;
510
511 if (lseek(fd, 0, SEEK_SET0) == -1) {
512 log_warn("warn: mda: lseek");
513 close(fd);
514 return (-1);
515 }
516 fp = fdopen(fd, "r");
517 if (fp == NULL((void *)0)) {
518 log_warn("warn: mda: fdopen");
519 close(fd);
520 return (-1);
521 }
522 while ((len = getline(&ln, &sz, fp)) != -1) {
523 if (ln[len - 1] == '\n')
524 ln[len - 1] = '\0';
525 out = 1;
526 }
527 fclose(fp);
528
529 if (out) {
530 (void)strlcpy(dst, "\"", dstsz);
531 (void)strnvis(dst + 1, ln, dstsz - 2, VIS_SAFE0x20 | VIS_CSTYLE0x02 | VIS_NL0x10);
532 (void)strlcat(dst, "\"", dstsz);
533 }
534
535 free(ln);
536 return (0);
537}
538
539static void
540mda_fail(struct mda_user *user, int permfail, const char *error,
541 enum enhanced_status_code code)
542{
543 struct mda_envelope *e;
544
545 while ((e = TAILQ_FIRST(&user->envelopes)((&user->envelopes)->tqh_first))) {
546 TAILQ_REMOVE(&user->envelopes, e, entry)do { if (((e)->entry.tqe_next) != ((void *)0)) (e)->entry
.tqe_next->entry.tqe_prev = (e)->entry.tqe_prev; else (
&user->envelopes)->tqh_last = (e)->entry.tqe_prev
; *(e)->entry.tqe_prev = (e)->entry.tqe_next; ; ; } while
(0)
;
547 if (permfail) {
548 mda_log(e, "PermFail", error);
549 mda_queue_permfail(e->id, error, code);
550 }
551 else {
552 mda_log(e, "TempFail", error);
553 mda_queue_tempfail(e->id, error, code);
554 }
555 mda_envelope_free(e);
556 }
557
558 mda_user_free(user);
559}
560
561static void
562mda_drain(void)
563{
564 struct mda_user *u;
565
566 while ((u = (TAILQ_FIRST(&runnable)((&runnable)->tqh_first)))) {
567
568 TAILQ_REMOVE(&runnable, u, entry_runnable)do { if (((u)->entry_runnable.tqe_next) != ((void *)0)) (u
)->entry_runnable.tqe_next->entry_runnable.tqe_prev = (
u)->entry_runnable.tqe_prev; else (&runnable)->tqh_last
= (u)->entry_runnable.tqe_prev; *(u)->entry_runnable.tqe_prev
= (u)->entry_runnable.tqe_next; ; ; } while (0)
;
569
570 if (u->evpcount == 0 && u->running == 0) {
571 log_debug("debug: mda: all done for user \"%s\"",
572 mda_user_to_text(u));
573 mda_user_free(u);
574 continue;
575 }
576
577 if (u->evpcount == 0) {
578 log_debug("debug: mda: no more envelope for \"%s\"",
579 mda_user_to_text(u));
580 u->flags &= ~USER_RUNNABLE0x02;
581 continue;
582 }
583
584 if (u->running >= env->sc_mda_max_user_session) {
585 log_debug("debug: mda: "
586 "maximum number of session reached for user \"%s\"",
587 mda_user_to_text(u));
588 u->flags &= ~USER_RUNNABLE0x02;
589 continue;
590 }
591
592 if (tree_count(&sessions)((&sessions)->count) >= env->sc_mda_max_session) {
593 log_debug("debug: mda: "
594 "maximum number of session reached");
595 TAILQ_INSERT_HEAD(&runnable, u, entry_runnable)do { if (((u)->entry_runnable.tqe_next = (&runnable)->
tqh_first) != ((void *)0)) (&runnable)->tqh_first->
entry_runnable.tqe_prev = &(u)->entry_runnable.tqe_next
; else (&runnable)->tqh_last = &(u)->entry_runnable
.tqe_next; (&runnable)->tqh_first = (u); (u)->entry_runnable
.tqe_prev = &(&runnable)->tqh_first; } while (0)
;
596 return;
597 }
598
599 mda_session(u);
600
601 if (u->evpcount == env->sc_mda_task_lowat) {
602 if (u->flags & USER_ONHOLD0x04) {
603 log_debug("debug: mda: down to lowat for user "
604 "\"%s\": releasing",
605 mda_user_to_text(u));
606 u->flags &= ~USER_ONHOLD0x04;
607 }
608 if (u->flags & USER_HOLDQ0x08) {
609 m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE,
610 0, 0, -1);
611 m_add_id(p_queue, u->id);
612 m_add_int(p_queue, env->sc_mda_task_release);
613 m_close(p_queue);
614 }
615 }
616
617 /* re-add the user at the tail of the queue */
618 TAILQ_INSERT_TAIL(&runnable, u, entry_runnable)do { (u)->entry_runnable.tqe_next = ((void *)0); (u)->entry_runnable
.tqe_prev = (&runnable)->tqh_last; *(&runnable)->
tqh_last = (u); (&runnable)->tqh_last = &(u)->entry_runnable
.tqe_next; } while (0)
;
619 }
620}
621
622static void
623mda_done(struct mda_session *s)
624{
625 log_debug("debug: mda: session %016" PRIx64"llx" " done", s->id);
626
627 tree_xpop(&sessions, s->id);
628
629 mda_envelope_free(s->evp);
630
631 s->user->running--;
632 if (!(s->user->flags & USER_RUNNABLE0x02)) {
633 log_debug("debug: mda: user \"%s\" becomes runnable",
634 s->user->name);
635 TAILQ_INSERT_TAIL(&runnable, s->user, entry_runnable)do { (s->user)->entry_runnable.tqe_next = ((void *)0); (
s->user)->entry_runnable.tqe_prev = (&runnable)->
tqh_last; *(&runnable)->tqh_last = (s->user); (&
runnable)->tqh_last = &(s->user)->entry_runnable
.tqe_next; } while (0)
;
636 s->user->flags |= USER_RUNNABLE0x02;
637 }
638
639 if (s->datafp)
640 fclose(s->datafp);
641 if (s->io)
642 io_free(s->io);
643
644 free(s);
645
646 stat_decrement("mda.running", 1);
647
648 mda_drain();
649}
650
651static void
652mda_log(const struct mda_envelope *evp, const char *prefix, const char *status)
653{
654 char rcpt[LINE_MAX2048];
655
656 rcpt[0] = '\0';
657 if (evp->rcpt)
658 (void)snprintf(rcpt, sizeof rcpt, "rcpt=<%s> ", evp->rcpt);
659
660 log_info("%016"PRIx64"llx"" mda delivery evpid=%016" PRIx64"llx" " from=<%s> to=<%s> "
661 "%suser=%s delay=%s result=%s stat=%s",
662 evp->session_id,
663 evp->id,
664 evp->sender ? evp->sender : "",
665 evp->dest,
666 rcpt,
667 evp->user,
668 duration_to_text(time(NULL((void *)0)) - evp->creation),
669 prefix,
670 status);
671}
672
673static void
674mda_queue_ok(uint64_t evpid)
675{
676 m_create(p_queue, IMSG_MDA_DELIVERY_OK, 0, 0, -1);
677 m_add_evpid(p_queue, evpid);
678 m_close(p_queue);
679}
680
681static void
682mda_queue_tempfail(uint64_t evpid, const char *reason,
683 enum enhanced_status_code code)
684{
685 m_create(p_queue, IMSG_MDA_DELIVERY_TEMPFAIL, 0, 0, -1);
686 m_add_evpid(p_queue, evpid);
687 m_add_string(p_queue, reason);
688 m_add_int(p_queue, (int)code);
689 m_close(p_queue);
690}
691
692static void
693mda_queue_permfail(uint64_t evpid, const char *reason,
694 enum enhanced_status_code code)
695{
696 m_create(p_queue, IMSG_MDA_DELIVERY_PERMFAIL, 0, 0, -1);
697 m_add_evpid(p_queue, evpid);
698 m_add_string(p_queue, reason);
699 m_add_int(p_queue, (int)code);
700 m_close(p_queue);
701}
702
703static void
704mda_queue_loop(uint64_t evpid)
705{
706 m_create(p_queue, IMSG_MDA_DELIVERY_LOOP, 0, 0, -1);
707 m_add_evpid(p_queue, evpid);
708 m_close(p_queue);
709}
710
711static struct mda_user *
712mda_user(const struct envelope *evp)
713{
714 struct dispatcher *dsp;
715 struct mda_user *u;
716 void *i;
717
718 i = NULL((void *)0);
719 dsp = dict_xget(env->sc_dispatchers, evp->dispatcher);
720 while (tree_iter(&users, &i, NULL((void *)0), (void**)(&u))) {
721 if (!strcmp(evp->mda_user, u->name) &&
722 !strcmp(dsp->u.local.table_userbase, u->usertable))
723 return (u);
724 }
725
726 u = xcalloc(1, sizeof *u);
727 u->id = generate_uid();
728 TAILQ_INIT(&u->envelopes)do { (&u->envelopes)->tqh_first = ((void *)0); (&
u->envelopes)->tqh_last = &(&u->envelopes)->
tqh_first; } while (0)
;
729 (void)strlcpy(u->name, evp->mda_user, sizeof(u->name));
730 (void)strlcpy(u->usertable, dsp->u.local.table_userbase,
731 sizeof(u->usertable));
732
733 tree_xset(&users, u->id, u);
734
735 m_create(p_lka, IMSG_MDA_LOOKUP_USERINFO, 0, 0, -1);
736 m_add_id(p_lka, u->id);
737 m_add_string(p_lka, dsp->u.local.table_userbase);
738 m_add_string(p_lka, evp->mda_user);
739 m_close(p_lka);
740 u->flags |= USER_WAITINFO0x01;
741
742 stat_increment("mda.user", 1);
743
744 if (dsp->u.local.user)
745 log_debug("mda: new user %016" PRIx64"llx"
746 " for \"%s\" delivering as \"%s\"",
747 u->id, mda_user_to_text(u), dsp->u.local.user);
748 else
749 log_debug("mda: new user %016" PRIx64"llx"
750 " for \"%s\"", u->id, mda_user_to_text(u));
751
752 return (u);
753}
754
755static void
756mda_user_free(struct mda_user *u)
757{
758 tree_xpop(&users, u->id);
759
760 if (u->flags & USER_HOLDQ0x08) {
761 m_create(p_queue, IMSG_MDA_HOLDQ_RELEASE, 0, 0, -1);
762 m_add_id(p_queue, u->id);
763 m_add_int(p_queue, 0);
764 m_close(p_queue);
765 }
766
767 free(u);
768 stat_decrement("mda.user", 1);
769}
770
771static const char *
772mda_user_to_text(const struct mda_user *u)
773{
774 static char buf[1024];
775
776 (void)snprintf(buf, sizeof(buf), "%s:%s", u->usertable, u->name);
777
778 return (buf);
779}
780
781static struct mda_envelope *
782mda_envelope(uint64_t session_id, const struct envelope *evp)
783{
784 struct mda_envelope *e;
785 char buf[LINE_MAX2048];
786
787 e = xcalloc(1, sizeof *e);
788 e->session_id = session_id;
789 e->id = evp->id;
790 e->creation = evp->creation;
791 buf[0] = '\0';
792 if (evp->sender.user[0] && evp->sender.domain[0])
793 (void)snprintf(buf, sizeof buf, "%s@%s",
794 evp->sender.user, evp->sender.domain);
795 e->sender = xstrdup(buf);
796 (void)snprintf(buf, sizeof buf, "%s@%s", evp->dest.user,
797 evp->dest.domain);
798 e->dest = xstrdup(buf);
799 (void)snprintf(buf, sizeof buf, "%s@%s", evp->rcpt.user,
800 evp->rcpt.domain);
801 e->rcpt = xstrdup(buf);
802 e->user = evp->mda_user[0] ?
803 xstrdup(evp->mda_user) : xstrdup(evp->dest.user);
804 e->dispatcher = xstrdup(evp->dispatcher);
805 if (evp->mda_exec[0])
806 e->mda_exec = xstrdup(evp->mda_exec);
807 if (evp->mda_subaddress[0])
808 e->mda_subaddress = xstrdup(evp->mda_subaddress);
809 stat_increment("mda.envelope", 1);
810 return (e);
811}
812
813static void
814mda_envelope_free(struct mda_envelope *e)
815{
816 free(e->sender);
817 free(e->dest);
818 free(e->rcpt);
819 free(e->user);
820 free(e->mda_exec);
821 free(e);
822
823 stat_decrement("mda.envelope", 1);
824}
825
826static struct mda_session *
827mda_session(struct mda_user * u)
828{
829 struct mda_session *s;
830
831 s = xcalloc(1, sizeof *s);
832 s->id = generate_uid();
833 s->user = u;
834 s->io = io_new();
835 io_set_callback(s->io, mda_io, s);
836
837 tree_xset(&sessions, s->id, s);
838
839 s->evp = TAILQ_FIRST(&u->envelopes)((&u->envelopes)->tqh_first);
840 TAILQ_REMOVE(&u->envelopes, s->evp, entry)do { if (((s->evp)->entry.tqe_next) != ((void *)0)) (s->
evp)->entry.tqe_next->entry.tqe_prev = (s->evp)->
entry.tqe_prev; else (&u->envelopes)->tqh_last = (s
->evp)->entry.tqe_prev; *(s->evp)->entry.tqe_prev
= (s->evp)->entry.tqe_next; ; ; } while (0)
;
841 u->evpcount--;
842 u->running++;
843
844 stat_decrement("mda.pending", 1);
845 stat_increment("mda.running", 1);
846
847 log_debug("debug: mda: new session %016" PRIx64"llx"
848 " for user \"%s\" evpid %016" PRIx64"llx", s->id,
849 mda_user_to_text(u), s->evp->id);
850
851 m_create(p_queue, IMSG_MDA_OPEN_MESSAGE, 0, 0, -1);
852 m_add_id(p_queue, s->id);
853 m_add_msgid(p_queue, evpid_to_msgid(s->evp->id));
854 m_close(p_queue);
855
856 return (s);
857}
858
859static const char *
860mda_sysexit_to_str(int sysexit)
861{
862 switch (sysexit) {
863 case EX_USAGE64:
864 return "command line usage error";
865 case EX_DATAERR65:
866 return "data format error";
867 case EX_NOINPUT66:
868 return "cannot open input";
869 case EX_NOUSER67:
870 return "user unknown";
871 case EX_NOHOST68:
872 return "host name unknown";
873 case EX_UNAVAILABLE69:
874 return "service unavailable";
875 case EX_SOFTWARE70:
876 return "internal software error";
877 case EX_OSERR71:
878 return "system resource problem";
879 case EX_OSFILE72:
880 return "critical OS file missing";
881 case EX_CANTCREAT73:
882 return "can't create user output file";
883 case EX_IOERR74:
884 return "input/output error";
885 case EX_TEMPFAIL75:
886 return "temporary failure";
887 case EX_PROTOCOL76:
888 return "remote error in protocol";
889 case EX_NOPERM77:
890 return "permission denied";
891 case EX_CONFIG78:
892 return "local configuration error";
893 default:
894 break;
895 }
896 return NULL((void *)0);
897}
898