1 |
|
|
/* $OpenBSD: bounce.c,v 1.77 2016/11/30 11:52:48 eric Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> |
5 |
|
|
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> |
6 |
|
|
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org> |
7 |
|
|
* |
8 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
9 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
10 |
|
|
* copyright notice and this permission notice appear in all copies. |
11 |
|
|
* |
12 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
13 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
15 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
18 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include <sys/types.h> |
22 |
|
|
#include <sys/queue.h> |
23 |
|
|
#include <sys/tree.h> |
24 |
|
|
#include <sys/socket.h> |
25 |
|
|
|
26 |
|
|
#include <err.h> |
27 |
|
|
#include <errno.h> |
28 |
|
|
#include <event.h> |
29 |
|
|
#include <imsg.h> |
30 |
|
|
#include <inttypes.h> |
31 |
|
|
#include <pwd.h> |
32 |
|
|
#include <signal.h> |
33 |
|
|
#include <stdio.h> |
34 |
|
|
#include <stdlib.h> |
35 |
|
|
#include <string.h> |
36 |
|
|
#include <time.h> |
37 |
|
|
#include <unistd.h> |
38 |
|
|
#include <limits.h> |
39 |
|
|
|
40 |
|
|
#include "smtpd.h" |
41 |
|
|
#include "log.h" |
42 |
|
|
|
43 |
|
|
#define BOUNCE_MAXRUN 2 |
44 |
|
|
#define BOUNCE_HIWAT 65535 |
45 |
|
|
|
46 |
|
|
enum { |
47 |
|
|
BOUNCE_EHLO, |
48 |
|
|
BOUNCE_MAIL, |
49 |
|
|
BOUNCE_RCPT, |
50 |
|
|
BOUNCE_DATA, |
51 |
|
|
BOUNCE_DATA_NOTICE, |
52 |
|
|
BOUNCE_DATA_MESSAGE, |
53 |
|
|
BOUNCE_DATA_END, |
54 |
|
|
BOUNCE_QUIT, |
55 |
|
|
BOUNCE_CLOSE, |
56 |
|
|
}; |
57 |
|
|
|
58 |
|
|
struct bounce_envelope { |
59 |
|
|
TAILQ_ENTRY(bounce_envelope) entry; |
60 |
|
|
uint64_t id; |
61 |
|
|
struct mailaddr dest; |
62 |
|
|
char *report; |
63 |
|
|
uint8_t esc_class; |
64 |
|
|
uint8_t esc_code; |
65 |
|
|
}; |
66 |
|
|
|
67 |
|
|
struct bounce_message { |
68 |
|
|
SPLAY_ENTRY(bounce_message) sp_entry; |
69 |
|
|
TAILQ_ENTRY(bounce_message) entry; |
70 |
|
|
uint32_t msgid; |
71 |
|
|
struct delivery_bounce bounce; |
72 |
|
|
char *smtpname; |
73 |
|
|
char *to; |
74 |
|
|
time_t timeout; |
75 |
|
|
TAILQ_HEAD(, bounce_envelope) envelopes; |
76 |
|
|
}; |
77 |
|
|
|
78 |
|
|
struct bounce_session { |
79 |
|
|
char *smtpname; |
80 |
|
|
struct bounce_message *msg; |
81 |
|
|
FILE *msgfp; |
82 |
|
|
int state; |
83 |
|
|
struct io *io; |
84 |
|
|
uint64_t boundary; |
85 |
|
|
}; |
86 |
|
|
|
87 |
|
|
SPLAY_HEAD(bounce_message_tree, bounce_message); |
88 |
|
|
static int bounce_message_cmp(const struct bounce_message *, |
89 |
|
|
const struct bounce_message *); |
90 |
|
|
SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry, |
91 |
|
|
bounce_message_cmp); |
92 |
|
|
|
93 |
|
|
static void bounce_drain(void); |
94 |
|
|
static void bounce_send(struct bounce_session *, const char *, ...); |
95 |
|
|
static int bounce_next_message(struct bounce_session *); |
96 |
|
|
static int bounce_next(struct bounce_session *); |
97 |
|
|
static void bounce_delivery(struct bounce_message *, int, const char *); |
98 |
|
|
static void bounce_status(struct bounce_session *, const char *, ...); |
99 |
|
|
static void bounce_io(struct io *, int, void *); |
100 |
|
|
static void bounce_timeout(int, short, void *); |
101 |
|
|
static void bounce_free(struct bounce_session *); |
102 |
|
|
static const char *action_str(const struct delivery_bounce *); |
103 |
|
|
|
104 |
|
|
static struct tree wait_fd; |
105 |
|
|
static struct bounce_message_tree messages; |
106 |
|
|
static TAILQ_HEAD(, bounce_message) pending; |
107 |
|
|
|
108 |
|
|
static int nmessage = 0; |
109 |
|
|
static int running = 0; |
110 |
|
|
static struct event ev_timer; |
111 |
|
|
|
112 |
|
|
static void |
113 |
|
|
bounce_init(void) |
114 |
|
|
{ |
115 |
|
|
static int init = 0; |
116 |
|
|
|
117 |
|
|
if (init == 0) { |
118 |
|
|
TAILQ_INIT(&pending); |
119 |
|
|
SPLAY_INIT(&messages); |
120 |
|
|
tree_init(&wait_fd); |
121 |
|
|
evtimer_set(&ev_timer, bounce_timeout, NULL); |
122 |
|
|
init = 1; |
123 |
|
|
} |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
void |
127 |
|
|
bounce_add(uint64_t evpid) |
128 |
|
|
{ |
129 |
|
|
char buf[LINE_MAX], *line; |
130 |
|
|
struct envelope evp; |
131 |
|
|
struct bounce_message key, *msg; |
132 |
|
|
struct bounce_envelope *be; |
133 |
|
|
|
134 |
|
|
bounce_init(); |
135 |
|
|
|
136 |
|
|
if (queue_envelope_load(evpid, &evp) == 0) { |
137 |
|
|
m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1); |
138 |
|
|
m_add_evpid(p_scheduler, evpid); |
139 |
|
|
m_close(p_scheduler); |
140 |
|
|
return; |
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
if (evp.type != D_BOUNCE) |
144 |
|
|
errx(1, "bounce: evp:%016" PRIx64 " is not of type D_BOUNCE!", |
145 |
|
|
evp.id); |
146 |
|
|
|
147 |
|
|
key.msgid = evpid_to_msgid(evpid); |
148 |
|
|
key.bounce = evp.agent.bounce; |
149 |
|
|
key.smtpname = evp.smtpname; |
150 |
|
|
|
151 |
|
|
switch (evp.esc_class) { |
152 |
|
|
case ESC_STATUS_OK: |
153 |
|
|
key.bounce.type = B_DSN; |
154 |
|
|
break; |
155 |
|
|
case ESC_STATUS_TEMPFAIL: |
156 |
|
|
key.bounce.type = B_WARNING; |
157 |
|
|
break; |
158 |
|
|
default: |
159 |
|
|
key.bounce.type = B_ERROR; |
160 |
|
|
} |
161 |
|
|
|
162 |
|
|
key.bounce.dsn_ret = evp.dsn_ret; |
163 |
|
|
key.bounce.expire = evp.expire; |
164 |
|
|
msg = SPLAY_FIND(bounce_message_tree, &messages, &key); |
165 |
|
|
if (msg == NULL) { |
166 |
|
|
msg = xcalloc(1, sizeof(*msg), "bounce_add"); |
167 |
|
|
msg->msgid = key.msgid; |
168 |
|
|
msg->bounce = key.bounce; |
169 |
|
|
|
170 |
|
|
TAILQ_INIT(&msg->envelopes); |
171 |
|
|
|
172 |
|
|
msg->smtpname = xstrdup(evp.smtpname, "bounce_add"); |
173 |
|
|
(void)snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user, |
174 |
|
|
evp.sender.domain); |
175 |
|
|
msg->to = xstrdup(buf, "bounce_add"); |
176 |
|
|
nmessage += 1; |
177 |
|
|
SPLAY_INSERT(bounce_message_tree, &messages, msg); |
178 |
|
|
log_debug("debug: bounce: new message %08" PRIx32, |
179 |
|
|
msg->msgid); |
180 |
|
|
stat_increment("bounce.message", 1); |
181 |
|
|
} else |
182 |
|
|
TAILQ_REMOVE(&pending, msg, entry); |
183 |
|
|
|
184 |
|
|
line = evp.errorline; |
185 |
|
|
if (strlen(line) > 4 && (*line == '1' || *line == '6')) |
186 |
|
|
line += 4; |
187 |
|
|
(void)snprintf(buf, sizeof(buf), "%s@%s: %s\n", evp.dest.user, |
188 |
|
|
evp.dest.domain, line); |
189 |
|
|
|
190 |
|
|
be = xmalloc(sizeof *be, "bounce_add"); |
191 |
|
|
be->id = evpid; |
192 |
|
|
be->report = xstrdup(buf, "bounce_add"); |
193 |
|
|
(void)strlcpy(be->dest.user, evp.dest.user, sizeof(be->dest.user)); |
194 |
|
|
(void)strlcpy(be->dest.domain, evp.dest.domain, |
195 |
|
|
sizeof(be->dest.domain)); |
196 |
|
|
be->esc_class = evp.esc_class; |
197 |
|
|
be->esc_code = evp.esc_code; |
198 |
|
|
TAILQ_INSERT_TAIL(&msg->envelopes, be, entry); |
199 |
|
|
buf[strcspn(buf, "\n")] = '\0'; |
200 |
|
|
log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, buf); |
201 |
|
|
|
202 |
|
|
msg->timeout = time(NULL) + 1; |
203 |
|
|
TAILQ_INSERT_TAIL(&pending, msg, entry); |
204 |
|
|
|
205 |
|
|
stat_increment("bounce.envelope", 1); |
206 |
|
|
bounce_drain(); |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
void |
210 |
|
|
bounce_fd(int fd) |
211 |
|
|
{ |
212 |
|
|
struct bounce_session *s; |
213 |
|
|
struct bounce_message *msg; |
214 |
|
|
|
215 |
|
|
log_debug("debug: bounce: got enqueue socket %d", fd); |
216 |
|
|
|
217 |
|
|
if (fd == -1 || TAILQ_EMPTY(&pending)) { |
218 |
|
|
log_debug("debug: bounce: cancelling"); |
219 |
|
|
if (fd != -1) |
220 |
|
|
close(fd); |
221 |
|
|
running -= 1; |
222 |
|
|
bounce_drain(); |
223 |
|
|
return; |
224 |
|
|
} |
225 |
|
|
|
226 |
|
|
msg = TAILQ_FIRST(&pending); |
227 |
|
|
|
228 |
|
|
s = xcalloc(1, sizeof(*s), "bounce_fd"); |
229 |
|
|
s->smtpname = xstrdup(msg->smtpname, "bounce_fd"); |
230 |
|
|
s->state = BOUNCE_EHLO; |
231 |
|
|
s->io = io_new(); |
232 |
|
|
io_set_callback(s->io, bounce_io, s); |
233 |
|
|
io_set_fd(s->io, fd); |
234 |
|
|
io_set_timeout(s->io, 30000); |
235 |
|
|
io_set_read(s->io); |
236 |
|
|
s->boundary = generate_uid(); |
237 |
|
|
|
238 |
|
|
log_debug("debug: bounce: new session %p", s); |
239 |
|
|
stat_increment("bounce.session", 1); |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
static void |
243 |
|
|
bounce_timeout(int fd, short ev, void *arg) |
244 |
|
|
{ |
245 |
|
|
log_debug("debug: bounce: timeout"); |
246 |
|
|
|
247 |
|
|
bounce_drain(); |
248 |
|
|
} |
249 |
|
|
|
250 |
|
|
static void |
251 |
|
|
bounce_drain() |
252 |
|
|
{ |
253 |
|
|
struct bounce_message *msg; |
254 |
|
|
struct timeval tv; |
255 |
|
|
time_t t; |
256 |
|
|
|
257 |
|
|
log_debug("debug: bounce: drain: nmessage=%d running=%d", |
258 |
|
|
nmessage, running); |
259 |
|
|
|
260 |
|
|
while (1) { |
261 |
|
|
if (running >= BOUNCE_MAXRUN) { |
262 |
|
|
log_debug("debug: bounce: max session reached"); |
263 |
|
|
return; |
264 |
|
|
} |
265 |
|
|
|
266 |
|
|
if (nmessage == 0) { |
267 |
|
|
log_debug("debug: bounce: no more messages"); |
268 |
|
|
return; |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
if (running >= nmessage) { |
272 |
|
|
log_debug("debug: bounce: enough sessions running"); |
273 |
|
|
return; |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
if ((msg = TAILQ_FIRST(&pending)) == NULL) { |
277 |
|
|
log_debug("debug: bounce: no more pending messages"); |
278 |
|
|
return; |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
t = time(NULL); |
282 |
|
|
if (msg->timeout > t) { |
283 |
|
|
log_debug("debug: bounce: next message not ready yet"); |
284 |
|
|
if (!evtimer_pending(&ev_timer, NULL)) { |
285 |
|
|
log_debug("debug: bounce: setting timer"); |
286 |
|
|
tv.tv_sec = msg->timeout - t; |
287 |
|
|
tv.tv_usec = 0; |
288 |
|
|
evtimer_add(&ev_timer, &tv); |
289 |
|
|
} |
290 |
|
|
return; |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
log_debug("debug: bounce: requesting new enqueue socket..."); |
294 |
|
|
m_compose(p_pony, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL, 0); |
295 |
|
|
|
296 |
|
|
running += 1; |
297 |
|
|
} |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
static void |
301 |
|
|
bounce_send(struct bounce_session *s, const char *fmt, ...) |
302 |
|
|
{ |
303 |
|
|
va_list ap; |
304 |
|
|
char *p; |
305 |
|
|
int len; |
306 |
|
|
|
307 |
|
|
va_start(ap, fmt); |
308 |
|
|
if ((len = vasprintf(&p, fmt, ap)) == -1) |
309 |
|
|
fatal("bounce: vasprintf"); |
310 |
|
|
va_end(ap); |
311 |
|
|
|
312 |
|
|
log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p); |
313 |
|
|
|
314 |
|
|
io_xprintf(s->io, "%s\n", p); |
315 |
|
|
|
316 |
|
|
free(p); |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
static const char * |
320 |
|
|
bounce_duration(long long int d) |
321 |
|
|
{ |
322 |
|
|
static char buf[32]; |
323 |
|
|
|
324 |
|
|
if (d < 60) { |
325 |
|
|
(void)snprintf(buf, sizeof buf, "%lld second%s", d, |
326 |
|
|
(d == 1) ? "" : "s"); |
327 |
|
|
} else if (d < 3600) { |
328 |
|
|
d = d / 60; |
329 |
|
|
(void)snprintf(buf, sizeof buf, "%lld minute%s", d, |
330 |
|
|
(d == 1) ? "" : "s"); |
331 |
|
|
} |
332 |
|
|
else if (d < 3600 * 24) { |
333 |
|
|
d = d / 3600; |
334 |
|
|
(void)snprintf(buf, sizeof buf, "%lld hour%s", d, |
335 |
|
|
(d == 1) ? "" : "s"); |
336 |
|
|
} |
337 |
|
|
else { |
338 |
|
|
d = d / (3600 * 24); |
339 |
|
|
(void)snprintf(buf, sizeof buf, "%lld day%s", d, |
340 |
|
|
(d == 1) ? "" : "s"); |
341 |
|
|
} |
342 |
|
|
return (buf); |
343 |
|
|
} |
344 |
|
|
|
345 |
|
|
#define NOTICE_INTRO \ |
346 |
|
|
" Hi!\n\n" \ |
347 |
|
|
" This is the MAILER-DAEMON, please DO NOT REPLY to this email.\n" |
348 |
|
|
|
349 |
|
|
const char *notice_error = |
350 |
|
|
" An error has occurred while attempting to deliver a message for\n" |
351 |
|
|
" the following list of recipients:\n\n"; |
352 |
|
|
|
353 |
|
|
const char *notice_warning = |
354 |
|
|
" A message is delayed for more than %s for the following\n" |
355 |
|
|
" list of recipients:\n\n"; |
356 |
|
|
|
357 |
|
|
const char *notice_warning2 = |
358 |
|
|
" Please note that this is only a temporary failure report.\n" |
359 |
|
|
" The message is kept in the queue for up to %s.\n" |
360 |
|
|
" You DO NOT NEED to re-send the message to these recipients.\n\n"; |
361 |
|
|
|
362 |
|
|
const char *notice_success = |
363 |
|
|
" Your message was successfully delivered to these recipients.\n\n"; |
364 |
|
|
|
365 |
|
|
const char *notice_relay = |
366 |
|
|
" Your message was relayed to these recipients.\n\n"; |
367 |
|
|
|
368 |
|
|
static int |
369 |
|
|
bounce_next_message(struct bounce_session *s) |
370 |
|
|
{ |
371 |
|
|
struct bounce_message *msg; |
372 |
|
|
char buf[LINE_MAX]; |
373 |
|
|
int fd; |
374 |
|
|
time_t now; |
375 |
|
|
|
376 |
|
|
again: |
377 |
|
|
|
378 |
|
|
now = time(NULL); |
379 |
|
|
|
380 |
|
|
TAILQ_FOREACH(msg, &pending, entry) { |
381 |
|
|
if (msg->timeout > now) |
382 |
|
|
continue; |
383 |
|
|
if (strcmp(msg->smtpname, s->smtpname)) |
384 |
|
|
continue; |
385 |
|
|
break; |
386 |
|
|
} |
387 |
|
|
if (msg == NULL) |
388 |
|
|
return (0); |
389 |
|
|
|
390 |
|
|
TAILQ_REMOVE(&pending, msg, entry); |
391 |
|
|
SPLAY_REMOVE(bounce_message_tree, &messages, msg); |
392 |
|
|
|
393 |
|
|
if ((fd = queue_message_fd_r(msg->msgid)) == -1) { |
394 |
|
|
bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, |
395 |
|
|
"Could not open message fd"); |
396 |
|
|
goto again; |
397 |
|
|
} |
398 |
|
|
|
399 |
|
|
if ((s->msgfp = fdopen(fd, "r")) == NULL) { |
400 |
|
|
(void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno)); |
401 |
|
|
log_warn("warn: bounce: fdopen"); |
402 |
|
|
close(fd); |
403 |
|
|
bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf); |
404 |
|
|
goto again; |
405 |
|
|
} |
406 |
|
|
|
407 |
|
|
s->msg = msg; |
408 |
|
|
return (1); |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
static int |
412 |
|
|
bounce_next(struct bounce_session *s) |
413 |
|
|
{ |
414 |
|
|
struct bounce_envelope *evp; |
415 |
|
|
char *line = NULL; |
416 |
|
|
size_t n, sz = 0; |
417 |
|
|
ssize_t len; |
418 |
|
|
|
419 |
|
|
switch (s->state) { |
420 |
|
|
case BOUNCE_EHLO: |
421 |
|
|
bounce_send(s, "EHLO %s", s->smtpname); |
422 |
|
|
s->state = BOUNCE_MAIL; |
423 |
|
|
break; |
424 |
|
|
|
425 |
|
|
case BOUNCE_MAIL: |
426 |
|
|
case BOUNCE_DATA_END: |
427 |
|
|
log_debug("debug: bounce: %p: getting next message...", s); |
428 |
|
|
if (bounce_next_message(s) == 0) { |
429 |
|
|
log_debug("debug: bounce: %p: no more messages", s); |
430 |
|
|
bounce_send(s, "QUIT"); |
431 |
|
|
s->state = BOUNCE_CLOSE; |
432 |
|
|
break; |
433 |
|
|
} |
434 |
|
|
log_debug("debug: bounce: %p: found message %08"PRIx32, |
435 |
|
|
s, s->msg->msgid); |
436 |
|
|
bounce_send(s, "MAIL FROM: <>"); |
437 |
|
|
s->state = BOUNCE_RCPT; |
438 |
|
|
break; |
439 |
|
|
|
440 |
|
|
case BOUNCE_RCPT: |
441 |
|
|
bounce_send(s, "RCPT TO: <%s>", s->msg->to); |
442 |
|
|
s->state = BOUNCE_DATA; |
443 |
|
|
break; |
444 |
|
|
|
445 |
|
|
case BOUNCE_DATA: |
446 |
|
|
bounce_send(s, "DATA"); |
447 |
|
|
s->state = BOUNCE_DATA_NOTICE; |
448 |
|
|
break; |
449 |
|
|
|
450 |
|
|
case BOUNCE_DATA_NOTICE: |
451 |
|
|
/* Construct an appropriate notice. */ |
452 |
|
|
|
453 |
|
|
io_xprintf(s->io, |
454 |
|
|
"Subject: Delivery status notification: %s\n" |
455 |
|
|
"From: Mailer Daemon <MAILER-DAEMON@%s>\n" |
456 |
|
|
"To: %s\n" |
457 |
|
|
"Date: %s\n" |
458 |
|
|
"MIME-Version: 1.0\n" |
459 |
|
|
"Content-Type: multipart/mixed;" |
460 |
|
|
"boundary=\"%16" PRIu64 "/%s\"\n" |
461 |
|
|
"\n" |
462 |
|
|
"This is a MIME-encapsulated message.\n" |
463 |
|
|
"\n", |
464 |
|
|
action_str(&s->msg->bounce), |
465 |
|
|
s->smtpname, |
466 |
|
|
s->msg->to, |
467 |
|
|
time_to_text(time(NULL)), |
468 |
|
|
s->boundary, |
469 |
|
|
s->smtpname); |
470 |
|
|
|
471 |
|
|
io_xprintf(s->io, |
472 |
|
|
"--%16" PRIu64 "/%s\n" |
473 |
|
|
"Content-Description: Notification\n" |
474 |
|
|
"Content-Type: text/plain; charset=us-ascii\n" |
475 |
|
|
"\n" |
476 |
|
|
NOTICE_INTRO |
477 |
|
|
"\n", |
478 |
|
|
s->boundary, s->smtpname); |
479 |
|
|
|
480 |
|
|
switch (s->msg->bounce.type) { |
481 |
|
|
case B_ERROR: |
482 |
|
|
io_xprint(s->io, notice_error); |
483 |
|
|
break; |
484 |
|
|
case B_WARNING: |
485 |
|
|
io_xprintf(s->io, notice_warning, |
486 |
|
|
bounce_duration(s->msg->bounce.delay)); |
487 |
|
|
break; |
488 |
|
|
case B_DSN: |
489 |
|
|
io_xprint(s->io, s->msg->bounce.mta_without_dsn ? |
490 |
|
|
notice_relay : notice_success); |
491 |
|
|
break; |
492 |
|
|
default: |
493 |
|
|
log_warn("warn: bounce: unknown bounce_type"); |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { |
497 |
|
|
io_xprint(s->io, evp->report); |
498 |
|
|
} |
499 |
|
|
io_xprint(s->io, "\n"); |
500 |
|
|
|
501 |
|
|
if (s->msg->bounce.type == B_WARNING) |
502 |
|
|
io_xprintf(s->io, notice_warning2, |
503 |
|
|
bounce_duration(s->msg->bounce.expire)); |
504 |
|
|
|
505 |
|
|
io_xprintf(s->io, |
506 |
|
|
" Below is a copy of the original message:\n" |
507 |
|
|
"\n"); |
508 |
|
|
|
509 |
|
|
io_xprintf(s->io, |
510 |
|
|
"--%16" PRIu64 "/%s\n" |
511 |
|
|
"Content-Description: Delivery Report\n" |
512 |
|
|
"Content-Type: message/delivery-status\n" |
513 |
|
|
"\n", |
514 |
|
|
s->boundary, s->smtpname); |
515 |
|
|
|
516 |
|
|
io_xprintf(s->io, |
517 |
|
|
"Reporting-MTA: dns; %s\n" |
518 |
|
|
"\n", |
519 |
|
|
s->smtpname); |
520 |
|
|
|
521 |
|
|
TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { |
522 |
|
|
io_xprintf(s->io, |
523 |
|
|
"Final-Recipient: rfc822; %s@%s\n" |
524 |
|
|
"Action: %s\n" |
525 |
|
|
"Status: %s\n" |
526 |
|
|
"\n", |
527 |
|
|
evp->dest.user, |
528 |
|
|
evp->dest.domain, |
529 |
|
|
action_str(&s->msg->bounce), |
530 |
|
|
esc_code(evp->esc_class, evp->esc_code)); |
531 |
|
|
} |
532 |
|
|
|
533 |
|
|
log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", |
534 |
|
|
s, io_queued(s->io)); |
535 |
|
|
|
536 |
|
|
s->state = BOUNCE_DATA_MESSAGE; |
537 |
|
|
break; |
538 |
|
|
|
539 |
|
|
case BOUNCE_DATA_MESSAGE: |
540 |
|
|
io_xprintf(s->io, |
541 |
|
|
"--%16" PRIu64 "/%s\n" |
542 |
|
|
"Content-Description: Message headers\n" |
543 |
|
|
"Content-Type: text/rfc822-headers\n" |
544 |
|
|
"\n", |
545 |
|
|
s->boundary, s->smtpname); |
546 |
|
|
|
547 |
|
|
n = io_queued(s->io); |
548 |
|
|
while (io_queued(s->io) < BOUNCE_HIWAT) { |
549 |
|
|
if ((len = getline(&line, &sz, s->msgfp)) == -1) |
550 |
|
|
break; |
551 |
|
|
if (len == 1 && line[0] == '\n' && /* end of headers */ |
552 |
|
|
s->msg->bounce.type == B_DSN && |
553 |
|
|
s->msg->bounce.dsn_ret == DSN_RETHDRS) { |
554 |
|
|
free(line); |
555 |
|
|
fclose(s->msgfp); |
556 |
|
|
s->msgfp = NULL; |
557 |
|
|
io_xprintf(s->io, |
558 |
|
|
"\n--%16" PRIu64 "/%s--\n", s->boundary, |
559 |
|
|
s->smtpname); |
560 |
|
|
bounce_send(s, "."); |
561 |
|
|
s->state = BOUNCE_DATA_END; |
562 |
|
|
return (0); |
563 |
|
|
} |
564 |
|
|
line[len - 1] = '\0'; |
565 |
|
|
io_xprintf(s->io, "%s%s\n", |
566 |
|
|
(len == 2 && line[0] == '.') ? "." : "", line); |
567 |
|
|
} |
568 |
|
|
free(line); |
569 |
|
|
|
570 |
|
|
if (ferror(s->msgfp)) { |
571 |
|
|
fclose(s->msgfp); |
572 |
|
|
s->msgfp = NULL; |
573 |
|
|
bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, |
574 |
|
|
"Error reading message"); |
575 |
|
|
s->msg = NULL; |
576 |
|
|
return (-1); |
577 |
|
|
} |
578 |
|
|
|
579 |
|
|
io_xprintf(s->io, |
580 |
|
|
"\n--%16" PRIu64 "/%s--\n", s->boundary, s->smtpname); |
581 |
|
|
|
582 |
|
|
log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", |
583 |
|
|
s, io_queued(s->io) - n); |
584 |
|
|
|
585 |
|
|
if (feof(s->msgfp)) { |
586 |
|
|
fclose(s->msgfp); |
587 |
|
|
s->msgfp = NULL; |
588 |
|
|
bounce_send(s, "."); |
589 |
|
|
s->state = BOUNCE_DATA_END; |
590 |
|
|
} |
591 |
|
|
break; |
592 |
|
|
|
593 |
|
|
case BOUNCE_QUIT: |
594 |
|
|
bounce_send(s, "QUIT"); |
595 |
|
|
s->state = BOUNCE_CLOSE; |
596 |
|
|
break; |
597 |
|
|
|
598 |
|
|
default: |
599 |
|
|
fatalx("bounce: bad state"); |
600 |
|
|
} |
601 |
|
|
|
602 |
|
|
return (0); |
603 |
|
|
} |
604 |
|
|
|
605 |
|
|
|
606 |
|
|
static void |
607 |
|
|
bounce_delivery(struct bounce_message *msg, int delivery, const char *status) |
608 |
|
|
{ |
609 |
|
|
struct bounce_envelope *be; |
610 |
|
|
struct envelope evp; |
611 |
|
|
size_t n; |
612 |
|
|
const char *f; |
613 |
|
|
|
614 |
|
|
n = 0; |
615 |
|
|
while ((be = TAILQ_FIRST(&msg->envelopes))) { |
616 |
|
|
if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) { |
617 |
|
|
if (queue_envelope_load(be->id, &evp) == 0) { |
618 |
|
|
fatalx("could not reload envelope!"); |
619 |
|
|
} |
620 |
|
|
evp.retry++; |
621 |
|
|
evp.lasttry = msg->timeout; |
622 |
|
|
envelope_set_errormsg(&evp, "%s", status); |
623 |
|
|
queue_envelope_update(&evp); |
624 |
|
|
m_create(p_scheduler, delivery, 0, 0, -1); |
625 |
|
|
m_add_envelope(p_scheduler, &evp); |
626 |
|
|
m_close(p_scheduler); |
627 |
|
|
} else { |
628 |
|
|
m_create(p_scheduler, delivery, 0, 0, -1); |
629 |
|
|
m_add_evpid(p_scheduler, be->id); |
630 |
|
|
m_close(p_scheduler); |
631 |
|
|
queue_envelope_delete(be->id); |
632 |
|
|
} |
633 |
|
|
TAILQ_REMOVE(&msg->envelopes, be, entry); |
634 |
|
|
free(be->report); |
635 |
|
|
free(be); |
636 |
|
|
n += 1; |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
|
640 |
|
|
if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) |
641 |
|
|
f = "TempFail"; |
642 |
|
|
else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL) |
643 |
|
|
f = "PermFail"; |
644 |
|
|
else |
645 |
|
|
f = NULL; |
646 |
|
|
|
647 |
|
|
if (f) |
648 |
|
|
log_warnx("warn: %s injecting failure report on message %08" |
649 |
|
|
PRIx32 " to <%s> for %zu envelope%s: %s", |
650 |
|
|
f, msg->msgid, msg->to, n, n > 1 ? "s":"", status); |
651 |
|
|
|
652 |
|
|
nmessage -= 1; |
653 |
|
|
stat_decrement("bounce.message", 1); |
654 |
|
|
stat_decrement("bounce.envelope", n); |
655 |
|
|
free(msg->smtpname); |
656 |
|
|
free(msg->to); |
657 |
|
|
free(msg); |
658 |
|
|
} |
659 |
|
|
|
660 |
|
|
static void |
661 |
|
|
bounce_status(struct bounce_session *s, const char *fmt, ...) |
662 |
|
|
{ |
663 |
|
|
va_list ap; |
664 |
|
|
char *status; |
665 |
|
|
int len, delivery; |
666 |
|
|
|
667 |
|
|
/* Ignore if there is no message */ |
668 |
|
|
if (s->msg == NULL) |
669 |
|
|
return; |
670 |
|
|
|
671 |
|
|
va_start(ap, fmt); |
672 |
|
|
if ((len = vasprintf(&status, fmt, ap)) == -1) |
673 |
|
|
fatal("bounce: vasprintf"); |
674 |
|
|
va_end(ap); |
675 |
|
|
|
676 |
|
|
if (*status == '2') |
677 |
|
|
delivery = IMSG_QUEUE_DELIVERY_OK; |
678 |
|
|
else if (*status == '5' || *status == '6') |
679 |
|
|
delivery = IMSG_QUEUE_DELIVERY_PERMFAIL; |
680 |
|
|
else |
681 |
|
|
delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL; |
682 |
|
|
|
683 |
|
|
bounce_delivery(s->msg, delivery, status); |
684 |
|
|
s->msg = NULL; |
685 |
|
|
if (s->msgfp) |
686 |
|
|
fclose(s->msgfp); |
687 |
|
|
|
688 |
|
|
free(status); |
689 |
|
|
} |
690 |
|
|
|
691 |
|
|
static void |
692 |
|
|
bounce_free(struct bounce_session *s) |
693 |
|
|
{ |
694 |
|
|
log_debug("debug: bounce: %p: deleting session", s); |
695 |
|
|
|
696 |
|
|
io_free(s->io); |
697 |
|
|
|
698 |
|
|
free(s->smtpname); |
699 |
|
|
free(s); |
700 |
|
|
|
701 |
|
|
running -= 1; |
702 |
|
|
stat_decrement("bounce.session", 1); |
703 |
|
|
bounce_drain(); |
704 |
|
|
} |
705 |
|
|
|
706 |
|
|
static void |
707 |
|
|
bounce_io(struct io *io, int evt, void *arg) |
708 |
|
|
{ |
709 |
|
|
struct bounce_session *s = arg; |
710 |
|
|
const char *error; |
711 |
|
|
char *line, *msg; |
712 |
|
|
int cont; |
713 |
|
|
size_t len; |
714 |
|
|
|
715 |
|
|
log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt), |
716 |
|
|
io_strio(io)); |
717 |
|
|
|
718 |
|
|
switch (evt) { |
719 |
|
|
case IO_DATAIN: |
720 |
|
|
nextline: |
721 |
|
|
line = io_getline(s->io, &len); |
722 |
|
|
if (line == NULL && io_datalen(s->io) >= LINE_MAX) { |
723 |
|
|
bounce_status(s, "Input too long"); |
724 |
|
|
bounce_free(s); |
725 |
|
|
return; |
726 |
|
|
} |
727 |
|
|
|
728 |
|
|
if (line == NULL) |
729 |
|
|
break; |
730 |
|
|
|
731 |
|
|
log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line); |
732 |
|
|
|
733 |
|
|
if ((error = parse_smtp_response(line, len, &msg, &cont))) { |
734 |
|
|
bounce_status(s, "Bad response: %s", error); |
735 |
|
|
bounce_free(s); |
736 |
|
|
return; |
737 |
|
|
} |
738 |
|
|
if (cont) |
739 |
|
|
goto nextline; |
740 |
|
|
|
741 |
|
|
if (s->state == BOUNCE_CLOSE) { |
742 |
|
|
bounce_free(s); |
743 |
|
|
return; |
744 |
|
|
} |
745 |
|
|
|
746 |
|
|
if (line[0] != '2' && line[0] != '3') { /* fail */ |
747 |
|
|
bounce_status(s, "%s", line); |
748 |
|
|
s->state = BOUNCE_QUIT; |
749 |
|
|
} else if (s->state == BOUNCE_DATA_END) { /* accepted */ |
750 |
|
|
bounce_status(s, "%s", line); |
751 |
|
|
} |
752 |
|
|
|
753 |
|
|
if (bounce_next(s) == -1) { |
754 |
|
|
bounce_free(s); |
755 |
|
|
return; |
756 |
|
|
} |
757 |
|
|
|
758 |
|
|
io_set_write(io); |
759 |
|
|
break; |
760 |
|
|
|
761 |
|
|
case IO_LOWAT: |
762 |
|
|
if (s->state == BOUNCE_DATA_MESSAGE) |
763 |
|
|
if (bounce_next(s) == -1) { |
764 |
|
|
bounce_free(s); |
765 |
|
|
return; |
766 |
|
|
} |
767 |
|
|
if (io_queued(s->io) == 0) |
768 |
|
|
io_set_read(io); |
769 |
|
|
break; |
770 |
|
|
|
771 |
|
|
default: |
772 |
|
|
bounce_status(s, "442 i/o error %d", evt); |
773 |
|
|
bounce_free(s); |
774 |
|
|
break; |
775 |
|
|
} |
776 |
|
|
} |
777 |
|
|
|
778 |
|
|
static int |
779 |
|
|
bounce_message_cmp(const struct bounce_message *a, |
780 |
|
|
const struct bounce_message *b) |
781 |
|
|
{ |
782 |
|
|
int r; |
783 |
|
|
|
784 |
|
|
if (a->msgid < b->msgid) |
785 |
|
|
return (-1); |
786 |
|
|
if (a->msgid > b->msgid) |
787 |
|
|
return (1); |
788 |
|
|
if ((r = strcmp(a->smtpname, b->smtpname))) |
789 |
|
|
return (r); |
790 |
|
|
|
791 |
|
|
return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce)); |
792 |
|
|
} |
793 |
|
|
|
794 |
|
|
static const char * |
795 |
|
|
action_str(const struct delivery_bounce *b) |
796 |
|
|
{ |
797 |
|
|
switch (b->type) { |
798 |
|
|
case B_ERROR: |
799 |
|
|
return ("error"); |
800 |
|
|
case B_WARNING: |
801 |
|
|
return ("delayed"); |
802 |
|
|
case B_DSN: |
803 |
|
|
if (b->mta_without_dsn) |
804 |
|
|
return ("relayed"); |
805 |
|
|
|
806 |
|
|
return ("success"); |
807 |
|
|
default: |
808 |
|
|
log_warn("warn: bounce: unknown bounce_type"); |
809 |
|
|
return (""); |
810 |
|
|
} |
811 |
|
|
} |
812 |
|
|
|
813 |
|
|
SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry, |
814 |
|
|
bounce_message_cmp); |