Bug Summary

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