Postfix3.3.1
bounce_notify_util.c
[詳解]
1 /*++
2 /* NAME
3 /* bounce_notify_util 3
4 /* SUMMARY
5 /* send non-delivery report to sender, server side
6 /* SYNOPSIS
7 /* #include "bounce_service.h"
8 /*
9 /* typedef struct {
10 /* .in +4
11 /* /* All private members... */
12 /* .in -4
13 /* } BOUNCE_INFO;
14 /*
15 /* BOUNCE_INFO *bounce_mail_init(service, queue_name, queue_id, encoding,
16 /* smtputf8, dsn_envid, template)
17 /* const char *service;
18 /* const char *queue_name;
19 /* const char *queue_id;
20 /* const char *encoding;
21 /* int smtputf8;
22 /* const char *dsn_envid;
23 /* const BOUNCE_TEMPLATE *template;
24 /*
25 /* BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding,
26 /* smtputf8, dsn_envid, dsn_notify,
27 /* rcpt_buf, dsn_buf, template)
28 /* const char *queue_name;
29 /* const char *queue_id;
30 /* const char *encoding;
31 /* int smtputf8;
32 /* int dsn_notify;
33 /* const char *dsn_envid;
34 /* RCPT_BUF *rcpt_buf;
35 /* DSN_BUF *dsn_buf;
36 /* const BOUNCE_TEMPLATE *template;
37 /*
38 /* void bounce_mail_free(bounce_info)
39 /* BOUNCE_INFO *bounce_info;
40 /*
41 /* int bounce_header(fp, bounce_info, recipient, postmaster_copy)
42 /* VSTREAM *fp;
43 /* BOUNCE_INFO *bounce_info;
44 /* const char *recipient;
45 /* int postmaster_copy;
46 /*
47 /* int bounce_boilerplate(fp, bounce_info)
48 /* VSTREAM *fp;
49 /* BOUNCE_INFO *bounce_info;
50 /*
51 /* int bounce_recipient_log(fp, bounce_info)
52 /* VSTREAM *fp;
53 /* BOUNCE_INFO *bounce_info;
54 /*
55 /* int bounce_diagnostic_log(fp, bounce_info, notify_filter)
56 /* VSTREAM *fp;
57 /* BOUNCE_INFO *bounce_info;
58 /* int notify_filter;
59 /*
60 /* int bounce_header_dsn(fp, bounce_info)
61 /* VSTREAM *fp;
62 /* BOUNCE_INFO *bounce_info;
63 /*
64 /* int bounce_recipient_dsn(fp, bounce_info)
65 /* VSTREAM *fp;
66 /* BOUNCE_INFO *bounce_info;
67 /*
68 /* int bounce_diagnostic_dsn(fp, bounce_info, notify_filter)
69 /* VSTREAM *fp;
70 /* BOUNCE_INFO *bounce_info;
71 /* int notify_filter;
72 /*
73 /* int bounce_original(fp, bounce_info, headers_only)
74 /* VSTREAM *fp;
75 /* BOUNCE_INFO *bounce_info;
76 /* int headers_only;
77 /*
78 /* void bounce_delrcpt(bounce_info)
79 /* BOUNCE_INFO *bounce_info;
80 /*
81 /* void bounce_delrcpt_one(bounce_info)
82 /* BOUNCE_INFO *bounce_info;
83 /* DESCRIPTION
84 /* This module implements the grunt work of sending a non-delivery
85 /* notification. A bounce is sent in a form that satisfies RFC 1894
86 /* (delivery status notifications).
87 /*
88 /* bounce_mail_init() bundles up its argument and attempts to
89 /* open the corresponding logfile and message file. A BOUNCE_INFO
90 /* structure contains all the necessary information about an
91 /* undeliverable message.
92 /*
93 /* bounce_mail_one_init() provides the same function for only
94 /* one recipient that is not read from bounce logfile.
95 /*
96 /* bounce_mail_free() releases memory allocated by bounce_mail_init()
97 /* and closes any files opened by bounce_mail_init().
98 /*
99 /* bounce_header() produces a standard mail header with the specified
100 /* recipient and starts a text/plain message segment for the
101 /* human-readable problem description. postmaster_copy is either
102 /* POSTMASTER_COPY or NO_POSTMASTER_COPY.
103 /*
104 /* bounce_boilerplate() produces the standard "sorry" text that
105 /* creates the illusion that mail systems are civilized.
106 /*
107 /* bounce_recipient_log() sends a human-readable representation of
108 /* logfile information for one recipient, with the recipient address
109 /* and with the text why the recipient was undeliverable.
110 /*
111 /* bounce_diagnostic_log() sends a human-readable representation of
112 /* logfile information for all undeliverable recipients. The
113 /* notify_filter specifies what recipient status records should be
114 /* reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
115 /* In the absence of DSN NOTIFY information all records are reported.
116 /* The result value is -1 in case of error, the number of reported
117 /* recipients in case of success.
118 /*
119 /* bounce_header_dsn() starts a message/delivery-status message
120 /* segment and sends the machine-readable information that identifies
121 /* the reporting MTA.
122 /*
123 /* bounce_recipient_dsn() sends a machine-readable representation of
124 /* logfile information for one recipient, with the recipient address
125 /* and with the text why the recipient was undeliverable.
126 /*
127 /* bounce_diagnostic_dsn() sends a machine-readable representation of
128 /* logfile information for all undeliverable recipients. The
129 /* notify_filter specifies what recipient status records should be
130 /* reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
131 /* In the absence of DSN NOTIFY information all records are reported.
132 /* The result value is -1 in case of error, the number of reported
133 /* recipients in case of success.
134 /*
135 /* bounce_original() starts a message/rfc822 or text/rfc822-headers
136 /* message segment and sends the original message, either full
137 /* (DSN_RET_FULL) or message headers only (DSN_RET_HDRS).
138 /*
139 /* bounce_delrcpt() deletes recipients in the logfile from the original
140 /* queue file.
141 /*
142 /* bounce_delrcpt_one() deletes one recipient from the original
143 /* queue file.
144 /* DIAGNOSTICS
145 /* Fatal error: error opening existing file.
146 /* BUGS
147 /* SEE ALSO
148 /* bounce(3) basic bounce service client interface
149 /* LICENSE
150 /* .ad
151 /* .fi
152 /* The Secure Mailer license must be distributed with this software.
153 /* AUTHOR(S)
154 /* Wietse Venema
155 /* IBM T.J. Watson Research
156 /* P.O. Box 704
157 /* Yorktown Heights, NY 10598, USA
158 /*
159 /* Wietse Venema
160 /* Google, Inc.
161 /* 111 8th Avenue
162 /* New York, NY 10011, USA
163 /*--*/
164 
165 /* System library. */
166 
167 #include <sys_defs.h>
168 #include <sys/stat.h>
169 #include <stdlib.h>
170 #include <stdio.h> /* sscanf() */
171 #include <unistd.h>
172 #include <errno.h>
173 #include <string.h>
174 #include <ctype.h>
175 
176 #ifdef STRCASECMP_IN_STRINGS_H
177 #include <strings.h>
178 #endif
179 
180 /* Utility library. */
181 
182 #include <msg.h>
183 #include <mymalloc.h>
184 #include <events.h>
185 #include <vstring.h>
186 #include <vstream.h>
187 #include <line_wrap.h>
188 #include <stringops.h>
189 #include <myflock.h>
190 
191 /* Global library. */
192 
193 #include <mail_queue.h>
194 #include <quote_822_local.h>
195 #include <mail_params.h>
196 #include <is_header.h>
197 #include <record.h>
198 #include <rec_type.h>
199 #include <post_mail.h>
200 #include <mail_addr.h>
201 #include <mail_error.h>
202 #include <bounce_log.h>
203 #include <mail_date.h>
204 #include <mail_proto.h>
205 #include <lex_822.h>
206 #include <deliver_completed.h>
207 #include <dsn_mask.h>
208 #include <smtputf8.h>
209 
210 /* Application-specific. */
211 
212 #include "bounce_service.h"
213 
214 #define STR vstring_str
215 
216 /* bounce_mail_alloc - initialize */
217 
218 static BOUNCE_INFO *bounce_mail_alloc(const char *service,
219  const char *queue_name,
220  const char *queue_id,
221  const char *encoding,
222  int smtputf8,
223  const char *dsn_envid,
224  RCPT_BUF *rcpt_buf,
225  DSN_BUF *dsn_buf,
226  BOUNCE_TEMPLATE *template,
227  BOUNCE_LOG *log_handle)
228 {
229  BOUNCE_INFO *bounce_info;
230  int rec_type;
231 
232  /*
233  * Bundle up a bunch of parameters and initialize information that will
234  * be discovered on the fly.
235  *
236  * XXX Instead of overriding the returned-message MIME encoding, separate
237  * the returned-message MIME encoding from the (boiler plate, delivery
238  * status) MIME encoding.
239  */
240  bounce_info = (BOUNCE_INFO *) mymalloc(sizeof(*bounce_info));
241  bounce_info->service = service;
242  bounce_info->queue_name = queue_name;
243  bounce_info->queue_id = queue_id;
244  bounce_info->smtputf8 = smtputf8;
245  /* Fix 20140708: override MIME encoding: addresses may be 8bit. */
246  /* Fix 20140718: override MIME encoding: 8bit $myhostname expansion. */
247  if (var_smtputf8_enable /* was: bounce_info->smtputf8 */ ) {
248  bounce_info->mime_encoding = "8bit";
249  } else if (strcmp(encoding, MAIL_ATTR_ENC_8BIT) == 0) {
250  bounce_info->mime_encoding = "8bit";
251  } else if (strcmp(encoding, MAIL_ATTR_ENC_7BIT) == 0) {
252  bounce_info->mime_encoding = "7bit";
253  } else {
254  if (strcmp(encoding, MAIL_ATTR_ENC_NONE) != 0)
255  msg_warn("%s: unknown encoding: %.200s",
256  bounce_info->queue_id, encoding);
257  bounce_info->mime_encoding = 0;
258  }
259  if (dsn_envid && *dsn_envid)
260  bounce_info->dsn_envid = dsn_envid;
261  else
262  bounce_info->dsn_envid = 0;
263  bounce_info->template = template;
264  bounce_info->buf = vstring_alloc(100);
265  bounce_info->sender = vstring_alloc(100);
266  bounce_info->arrival_time = 0;
267  bounce_info->orig_offs = 0;
268  bounce_info->message_size = 0;
269  bounce_info->rcpt_buf = rcpt_buf;
270  bounce_info->dsn_buf = dsn_buf;
271  bounce_info->log_handle = log_handle;
272 
273  /*
274  * RFC 1894: diagnostic-type is an RFC 822 atom. We use X-$mail_name and
275  * must ensure it is valid.
276  */
277  bounce_info->mail_name = mystrdup(var_mail_name);
278  translit(bounce_info->mail_name, " \t\r\n()<>@,;:\\\".[]",
279  "-----------------");
280 
281  /*
282  * Compute a supposedly unique boundary string. This assumes that a queue
283  * ID and a hostname contain acceptable characters for a boundary string,
284  * but the assumption is not verified.
285  */
286  vstring_sprintf(bounce_info->buf, "%s.%lu/%s",
287  queue_id, (unsigned long) event_time(), var_myhostname);
288  bounce_info->mime_boundary = mystrdup(STR(bounce_info->buf));
289 
290  /*
291  * If the original message cannot be found, do not raise a run-time
292  * error. There is nothing we can do about the error, and all we are
293  * doing is to inform the sender of a delivery problem. Bouncing a
294  * message does not have to be a perfect job. But if the system IS
295  * running out of resources, raise a fatal run-time error and force a
296  * backoff.
297  */
298  if ((bounce_info->orig_fp = mail_queue_open(queue_name, queue_id,
299  O_RDWR, 0)) == 0
300  && errno != ENOENT)
301  msg_fatal("open %s %s: %m", service, queue_id);
302 
303  /*
304  * Get time/size/sender information from the original message envelope
305  * records. If the envelope is corrupted just send whatever we can
306  * (remember this is a best effort, it does not have to be perfect).
307  *
308  * Lock the file for shared use, so that queue manager leaves it alone after
309  * restarting.
310  */
311 #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT)
312 
313  if (bounce_info->orig_fp != 0) {
314  if (myflock(vstream_fileno(bounce_info->orig_fp), INTERNAL_LOCK,
315  DELIVER_LOCK_MODE) < 0)
316  msg_fatal("cannot get shared lock on %s: %m",
317  VSTREAM_PATH(bounce_info->orig_fp));
318  while ((rec_type = rec_get(bounce_info->orig_fp,
319  bounce_info->buf, 0)) > 0) {
320 
321  /*
322  * Postfix version dependent: data offset in SIZE record.
323  */
324  if (rec_type == REC_TYPE_SIZE) {
325  if (bounce_info->message_size == 0)
326  sscanf(STR(bounce_info->buf), "%ld %ld",
327  &bounce_info->message_size,
328  &bounce_info->orig_offs);
329  if (bounce_info->message_size < 0)
330  bounce_info->message_size = 0;
331  if (bounce_info->orig_offs < 0)
332  bounce_info->orig_offs = 0;
333  }
334 
335  /*
336  * Information for the Arrival-Date: attribute.
337  */
338  else if (rec_type == REC_TYPE_TIME) {
339  if (bounce_info->arrival_time == 0
340  && (bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
341  bounce_info->arrival_time = 0;
342  }
343 
344  /*
345  * Information for the X-Postfix-Sender: attribute.
346  */
347  else if (rec_type == REC_TYPE_FROM) {
348  quote_822_local_flags(bounce_info->sender,
349  VSTRING_LEN(bounce_info->buf) ?
350  STR(bounce_info->buf) :
351  mail_addr_mail_daemon(), 0);
352  }
353 
354  /*
355  * Backwards compatibility: no data offset in SIZE record.
356  */
357  else if (rec_type == REC_TYPE_MESG) {
358  /* XXX Future: sender+recipient after message content. */
359  if (VSTRING_LEN(bounce_info->sender) == 0)
360  msg_warn("%s: no sender before message content record",
361  bounce_info->queue_id);
362  bounce_info->orig_offs = vstream_ftell(bounce_info->orig_fp);
363  break;
364  }
365  if (bounce_info->orig_offs > 0
366  && bounce_info->arrival_time > 0
367  && VSTRING_LEN(bounce_info->sender) > 0)
368  break;
369  }
370  }
371  return (bounce_info);
372 }
373 
374 /* bounce_mail_init - initialize */
375 
376 BOUNCE_INFO *bounce_mail_init(const char *service,
377  const char *queue_name,
378  const char *queue_id,
379  const char *encoding,
380  int smtputf8,
381  const char *dsn_envid,
382  BOUNCE_TEMPLATE *template)
383 {
384  BOUNCE_INFO *bounce_info;
385  BOUNCE_LOG *log_handle;
386  RCPT_BUF *rcpt_buf;
387  DSN_BUF *dsn_buf;
388 
389  /*
390  * Initialize the bounce_info structure. If the bounce log cannot be
391  * found, do not raise a fatal run-time error. There is nothing we can do
392  * about the error, and all we are doing is to inform the sender of a
393  * delivery problem, Bouncing a message does not have to be a perfect
394  * job. But if the system IS running out of resources, raise a fatal
395  * run-time error and force a backoff.
396  */
397  if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0) {
398  if (errno != ENOENT)
399  msg_fatal("open %s %s: %m", service, queue_id);
400  rcpt_buf = 0;
401  dsn_buf = 0;
402  } else {
403  rcpt_buf = rcpb_create();
404  dsn_buf = dsb_create();
405  }
406  bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding,
407  smtputf8, dsn_envid, rcpt_buf, dsn_buf,
408  template, log_handle);
409  return (bounce_info);
410 }
411 
412 /* bounce_mail_one_init - initialize */
413 
414 BOUNCE_INFO *bounce_mail_one_init(const char *queue_name,
415  const char *queue_id,
416  const char *encoding,
417  int smtputf8,
418  const char *dsn_envid,
419  RCPT_BUF *rcpt_buf,
420  DSN_BUF *dsn_buf,
421  BOUNCE_TEMPLATE *template)
422 {
423  BOUNCE_INFO *bounce_info;
424 
425  /*
426  * Initialize the bounce_info structure for just one recipient.
427  */
428  bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding,
429  smtputf8, dsn_envid, rcpt_buf, dsn_buf,
430  template, (BOUNCE_LOG *) 0);
431  return (bounce_info);
432 }
433 
434 /* bounce_mail_free - undo bounce_mail_init */
435 
436 void bounce_mail_free(BOUNCE_INFO *bounce_info)
437 {
438  if (bounce_info->log_handle) {
439  if (bounce_log_close(bounce_info->log_handle))
440  msg_warn("%s: read bounce log %s: %m",
441  bounce_info->queue_id, bounce_info->queue_id);
442  rcpb_free(bounce_info->rcpt_buf);
443  dsb_free(bounce_info->dsn_buf);
444  }
445  if (bounce_info->orig_fp && vstream_fclose(bounce_info->orig_fp))
446  msg_warn("%s: read message file %s %s: %m",
447  bounce_info->queue_id, bounce_info->queue_name,
448  bounce_info->queue_id);
449  vstring_free(bounce_info->buf);
450  vstring_free(bounce_info->sender);
451  myfree(bounce_info->mail_name);
452  myfree((void *) bounce_info->mime_boundary);
453  myfree((void *) bounce_info);
454 }
455 
456 /* bounce_header - generate bounce message header */
457 
458 int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
459  const char *dest, int postmaster_copy)
460 {
461  BOUNCE_TEMPLATE *template = bounce_info->template;
462 
463  /*
464  * Print a minimal bounce header. The cleanup service will add other
465  * headers and will make all addresses fully qualified.
466  */
467 #define STREQ(a, b) (strcasecmp((a), (b)) == 0)
468 #define STRNE(a, b) (strcasecmp((a), (b)) != 0)
469 
470  /*
471  * Generic headers.
472  */
473  bounce_template_headers(post_mail_fprintf, bounce, template,
474  STR(quote_822_local(bounce_info->buf, dest)),
475  postmaster_copy);
476 
477  /*
478  * Auto-Submitted header, as per RFC 3834.
479  */
480  post_mail_fprintf(bounce, "Auto-Submitted: %s", postmaster_copy ?
481  "auto-generated" : "auto-replied");
482 
483  /*
484  * MIME header. Use 8bit encoding when either the bounced message or the
485  * template requires it.
486  */
487  post_mail_fprintf(bounce, "MIME-Version: 1.0");
488  post_mail_fprintf(bounce, "Content-Type: %s; report-type=%s;",
489  "multipart/report", "delivery-status");
490  post_mail_fprintf(bounce, "\tboundary=\"%s\"", bounce_info->mime_boundary);
491  if (bounce_info->mime_encoding)
492  post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
493  STREQ(bounce_info->mime_encoding, MAIL_ATTR_ENC_7BIT) ?
494  bounce_template_encoding(template) :
495  bounce_info->mime_encoding);
496  post_mail_fputs(bounce, "");
497  post_mail_fputs(bounce, "This is a MIME-encapsulated message.");
498 
499  /*
500  * MIME header.
501  */
502 #define NOT_US_ASCII(tp) \
503  STRNE(bounce_template_charset(template), "us-ascii")
504 
505 #define NOT_7BIT_MIME(bp) \
506  (bp->mime_encoding && STRNE(bp->mime_encoding, MAIL_ATTR_ENC_7BIT))
507 
508  post_mail_fputs(bounce, "");
509  post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
510  post_mail_fprintf(bounce, "Content-Description: %s", "Notification");
511  /* Fix 20140718: UTF-8 address or $myhostname expansion. */
512  post_mail_fprintf(bounce, "Content-Type: %s; charset=%s",
513  "text/plain", NOT_US_ASCII(template) ?
514  bounce_template_charset(template) :
515  NOT_7BIT_MIME(bounce_info) ?
516  "utf-8" : "us-ascii");
517  /* Fix 20140709: addresses may be 8bit. */
518  if (NOT_7BIT_MIME(bounce_info))
519  post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
520  bounce_info->mime_encoding);
521  post_mail_fputs(bounce, "");
522 
523  return (vstream_ferror(bounce));
524 }
525 
526 /* bounce_boilerplate - generate boiler-plate text */
527 
528 int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
529 {
530 
531  /*
532  * Print the boiler-plate text.
533  */
534  bounce_template_expand(post_mail_fputs, bounce, bounce_info->template);
535  return (vstream_ferror(bounce));
536 }
537 
538 /* bounce_print - line_wrap callback */
539 
540 static void bounce_print(const char *str, int len, int indent, void *context)
541 {
542  VSTREAM *bounce = (VSTREAM *) context;
543 
544  post_mail_fprintf(bounce, "%*s%.*s", indent, "", len, str);
545 }
546 
547 /* bounce_print_wrap - print and wrap a line */
548 
549 static void bounce_print_wrap(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
550  const char *format,...)
551 {
552  va_list ap;
553 
554 #define LENGTH 79
555 #define INDENT 4
556 
557  va_start(ap, format);
558  vstring_vsprintf(bounce_info->buf, format, ap);
559  va_end(ap);
560  line_wrap(STR(bounce_info->buf), LENGTH, INDENT,
561  bounce_print, (void *) bounce);
562 }
563 
564 /* bounce_recipient_log - send one bounce log report entry */
565 
566 int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
567 {
568  RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
569  DSN *dsn = &bounce_info->dsn_buf->dsn;
570 
571  /*
572  * Mask control and non-ASCII characters (done in bounce_log_read()),
573  * wrap long lines and prepend one blank, so this data can safely be
574  * piped into other programs. Sort of like TCP Wrapper's safe_finger
575  * program.
576  */
577 #define NON_NULL_EMPTY(s) ((s) && *(s))
578 
579  post_mail_fputs(bounce, "");
580  if (NON_NULL_EMPTY(rcpt->orig_addr)) {
581  bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s",
582  rcpt->address, rcpt->orig_addr, dsn->reason);
583  } else {
584  bounce_print_wrap(bounce, bounce_info, "<%s>: %s",
585  rcpt->address, dsn->reason);
586  }
587  return (vstream_ferror(bounce));
588 }
589 
590 /* bounce_diagnostic_log - send bounce log report */
591 
592 int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
593  int notify_filter)
594 {
595  RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
596  int count = 0;
597 
598  /*
599  * Append a human-readable copy of the delivery error log. We're doing a
600  * best effort, so there is no point raising a fatal run-time error in
601  * case of a logfile read error.
602  *
603  * XXX DSN If the logfile with failed recipients is unavailable, pretend
604  * that we found something anyway, so that this notification will not be
605  * canceled.
606  */
607  if (bounce_info->log_handle == 0
608  || bounce_log_rewind(bounce_info->log_handle)) {
609  if (IS_FAILURE_TEMPLATE(bounce_info->template)) {
610  post_mail_fputs(bounce, "");
611  post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
612  count = 1; /* XXX don't abort */
613  }
614  } else {
615  while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
616  bounce_info->dsn_buf) != 0) {
617  if (rcpt->dsn_notify == 0 /* compat */
618  || (rcpt->dsn_notify & notify_filter)) {
619  count++;
620  if (bounce_recipient_log(bounce, bounce_info) != 0)
621  break;
622  }
623  }
624  }
625  return (vstream_ferror(bounce) ? -1 : count);
626 }
627 
628 /* bounce_header_dsn - send per-MTA bounce DSN records */
629 
630 int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
631 {
632 
633  /*
634  * MIME header.
635  */
636  post_mail_fputs(bounce, "");
637  post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
638  post_mail_fprintf(bounce, "Content-Description: %s",
639  "Delivery report");
640  /* Generate *global* only if the original requested SMTPUTF8 support. */
641  post_mail_fprintf(bounce, "Content-Type: message/%sdelivery-status",
642  (bounce_info->smtputf8 & SMTPUTF8_FLAG_REQUESTED) ?
643  "global-" : "");
644  /* Fix 20140709: addresses may be 8bit. */
645  if (NOT_7BIT_MIME(bounce_info)
646  /* BC Fix 20170610: prevent MIME downgrade of message/delivery-status. */
647  && (bounce_info->smtputf8 & SMTPUTF8_FLAG_REQUESTED))
648  post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
649  bounce_info->mime_encoding);
650 
651  /*
652  * According to RFC 1894: The body of a message/delivery-status consists
653  * of one or more "fields" formatted according to the ABNF of RFC 822
654  * header "fields" (see [6]). The per-message fields appear first,
655  * followed by a blank line.
656  */
657  post_mail_fputs(bounce, "");
658  post_mail_fprintf(bounce, "Reporting-MTA: dns; %s", var_myhostname);
659 #if 0
660  post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
661 #endif
662  if (NON_NULL_EMPTY(bounce_info->dsn_envid)) {
663  post_mail_fprintf(bounce, "Original-Envelope-Id: %s",
664  bounce_info->dsn_envid);
665  }
666  post_mail_fprintf(bounce, "X-%s-Queue-ID: %s",
667  bounce_info->mail_name, bounce_info->queue_id);
668 
669 #define IS_UTF8_ADDRESS(str, len) \
670  ((str)[0] != 0 && !allascii(str) && valid_utf8_string((str), (len)))
671 
672  /* Fix 20140708: use "utf-8" or "rfc822" as appropriate. */
673  if (VSTRING_LEN(bounce_info->sender) > 0)
674  post_mail_fprintf(bounce, "X-%s-Sender: %s; %s",
675  bounce_info->mail_name, bounce_info->smtputf8
676  && IS_UTF8_ADDRESS(STR(bounce_info->sender),
677  VSTRING_LEN(bounce_info->sender)) ?
678  "utf-8" : "rfc822", STR(bounce_info->sender));
679  if (bounce_info->arrival_time > 0)
680  post_mail_fprintf(bounce, "Arrival-Date: %s",
681  mail_date(bounce_info->arrival_time));
682  return (vstream_ferror(bounce));
683 }
684 
685 /* bounce_recipient_dsn - send per-recipient DSN records */
686 
687 int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
688 {
689  RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
690  DSN *dsn = &bounce_info->dsn_buf->dsn;
691 
692  post_mail_fputs(bounce, "");
693  /* Fix 20140708: Don't send "utf-8" type with non-UTF8 address. */
694  post_mail_fprintf(bounce, "Final-Recipient: %s; %s",
695  bounce_info->smtputf8
696  && IS_UTF8_ADDRESS(rcpt->address,
697  strlen(rcpt->address)) ?
698  "utf-8" : "rfc822", rcpt->address);
699 
700  /*
701  * XXX DSN
702  *
703  * RFC 3464 section 6.3.d: "If no ORCPT parameter was provided for this
704  * recipient, the Original-Recipient field MUST NOT appear."
705  *
706  * This is inconsistent with section 5.2.1.d: "If no ORCPT parameter was
707  * present in the RCPT command when the message was received, an ORCPT
708  * parameter MAY be added to the RCPT command when the message is
709  * relayed.". Postfix adds an ORCPT parameter under these conditions.
710  *
711  * Therefore, all down-stream MTAs will send DSNs with Original-Recipient
712  * field ontaining this same ORCPT value. When a down-stream MTA can use
713  * that information in their DSNs, it makes no sense that an up-stream
714  * MTA can't use that same information in its own DSNs.
715  *
716  * Postfix always reports an Original-Recipient field, because it is more
717  * more useful and more consistent.
718  */
719  if (NON_NULL_EMPTY(rcpt->dsn_orcpt)) {
720  post_mail_fprintf(bounce, "Original-Recipient: %s", rcpt->dsn_orcpt);
721  } else if (NON_NULL_EMPTY(rcpt->orig_addr)) {
722  /* Fix 20140708: Don't send "utf-8" type with non-UTF8 address. */
723  post_mail_fprintf(bounce, "Original-Recipient: %s; %s",
724  bounce_info->smtputf8
725  && IS_UTF8_ADDRESS(rcpt->orig_addr,
726  strlen(rcpt->orig_addr)) ?
727  "utf-8" : "rfc822", rcpt->orig_addr);
728  }
729  post_mail_fprintf(bounce, "Action: %s",
730  IS_FAILURE_TEMPLATE(bounce_info->template) ?
731  "failed" : dsn->action);
732  post_mail_fprintf(bounce, "Status: %s", dsn->status);
733  if (NON_NULL_EMPTY(dsn->mtype) && NON_NULL_EMPTY(dsn->mname))
734  bounce_print_wrap(bounce, bounce_info, "Remote-MTA: %s; %s",
735  dsn->mtype, dsn->mname);
736  if (NON_NULL_EMPTY(dsn->dtype) && NON_NULL_EMPTY(dsn->dtext))
737  bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: %s; %s",
738  dsn->dtype, dsn->dtext);
739  else
740  bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
741  bounce_info->mail_name, dsn->reason);
742 #if 0
743  if (dsn->time > 0)
744  post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
745  mail_date(dsn->time));
746 #endif
747  if (IS_DELAY_TEMPLATE(bounce_info->template))
748  post_mail_fprintf(bounce, "Will-Retry-Until: %s",
749  mail_date(bounce_info->arrival_time + var_max_queue_time));
750  return (vstream_ferror(bounce));
751 }
752 
753 /* bounce_diagnostic_dsn - send bounce log report, machine readable form */
754 
755 int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
756  int notify_filter)
757 {
758  RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
759  int count = 0;
760 
761  /*
762  * Append a machine-readable copy of the delivery error log. We're doing
763  * a best effort, so there is no point raising a fatal run-time error in
764  * case of a logfile read error.
765  *
766  * XXX DSN If the logfile with failed recipients is unavailable, pretend
767  * that we found something anyway, so that this notification will not be
768  * canceled.
769  */
770  if (bounce_info->log_handle == 0
771  || bounce_log_rewind(bounce_info->log_handle)) {
772  if (IS_FAILURE_TEMPLATE(bounce_info->template))
773  count = 1; /* XXX don't abort */
774  } else {
775  while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
776  bounce_info->dsn_buf) != 0) {
777  if (rcpt->dsn_notify == 0 /* compat */
778  || (rcpt->dsn_notify & notify_filter)) {
779  count++;
780  if (bounce_recipient_dsn(bounce, bounce_info) != 0)
781  break;
782  }
783  }
784  }
785  return (vstream_ferror(bounce) ? -1 : count);
786 }
787 
788 /* bounce_original - send a copy of the original to the victim */
789 
790 int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
791  int headers_only)
792 {
793  int status = 0;
794  int rec_type = 0;
795 
796  /*
797  * When truncating a large message, don't damage the MIME structure: send
798  * the message headers only.
799  */
800  if (var_bounce_limit > 0
801  && bounce_info->orig_fp
802  && (bounce_info->message_size <= 0
803  || bounce_info->message_size > var_bounce_limit))
804  headers_only = DSN_RET_HDRS;
805 
806  /*
807  * MIME headers.
808  */
809 #define IS_UNDELIVERED_TEMPLATE(template) \
810  (IS_FAILURE_TEMPLATE(template) || IS_DELAY_TEMPLATE(template))
811 
812  post_mail_fputs(bounce, "");
813  post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
814  post_mail_fprintf(bounce, "Content-Description: %s%s",
815  IS_UNDELIVERED_TEMPLATE(bounce_info->template) ?
816  "Undelivered " : "",
817  headers_only == DSN_RET_HDRS ?
818  "Message Headers" : "Message");
819  /* Generate *global* only if the original requested SMTPUTF8 support. */
820  if (bounce_info->smtputf8 & SMTPUTF8_FLAG_REQUESTED)
821  post_mail_fprintf(bounce, "Content-Type: message/%s",
822  headers_only == DSN_RET_HDRS ?
823  "global-headers" : "global");
824  else
825  post_mail_fprintf(bounce, "Content-Type: %s",
826  headers_only == DSN_RET_HDRS ?
827  "text/rfc822-headers" : "message/rfc822");
828  if (NOT_7BIT_MIME(bounce_info))
829  post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
830  bounce_info->mime_encoding);
831  post_mail_fputs(bounce, "");
832 
833  /*
834  * Send place holder if original is unavailable.
835  */
836  if (bounce_info->orig_offs == 0 || vstream_fseek(bounce_info->orig_fp,
837  bounce_info->orig_offs, SEEK_SET) < 0) {
838  post_mail_fputs(bounce, "\t--- Undelivered message unavailable ---");
839  return (vstream_ferror(bounce));
840  }
841 
842  /*
843  * XXX The cleanup server removes Return-Path: headers. This should be
844  * done only with mail that enters via a non-SMTP channel, but changing
845  * this now could break other software. Removing Return-Path: could break
846  * digital signatures, though this is unlikely. In any case,
847  * header_checks are more effective when the Return-Path: header is
848  * present, so we prepend one to the bounce message.
849  */
850  post_mail_fprintf(bounce, "Return-Path: <%s>", STR(bounce_info->sender));
851 
852  /*
853  * Copy the original message contents. We're doing raw record output here
854  * so that we don't throw away binary transparency yet.
855  */
856 #define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s))
857 
858  while (status == 0 && (rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) {
859  if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
860  break;
861  if (headers_only == DSN_RET_HDRS
862  && !IS_HEADER(vstring_str(bounce_info->buf)))
863  break;
864  status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
865  }
866 
867  /*
868  * Final MIME headers. These require -- at the end of the boundary
869  * string.
870  *
871  * XXX This should be a separate bounce_terminate() entry so we can be
872  * assured that the terminator will always be sent.
873  */
874  post_mail_fputs(bounce, "");
875  post_mail_fprintf(bounce, "--%s--", bounce_info->mime_boundary);
876 
877  return (status);
878 }
879 
880 /* bounce_delrcpt - delete recipients from original queue file */
881 
882 void bounce_delrcpt(BOUNCE_INFO *bounce_info)
883 {
884  RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
885 
886  if (bounce_info->orig_fp != 0
887  && bounce_info->log_handle != 0
888  && bounce_log_rewind(bounce_info->log_handle) == 0)
889  while (bounce_log_read(bounce_info->log_handle, bounce_info->rcpt_buf,
890  bounce_info->dsn_buf) != 0)
891  if (rcpt->offset > 0)
892  deliver_completed(bounce_info->orig_fp, rcpt->offset);
893 }
894 
895 /* bounce_delrcpt_one - delete one recipient from original queue file */
896 
897 void bounce_delrcpt_one(BOUNCE_INFO *bounce_info)
898 {
899  RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;
900 
901  if (bounce_info->orig_fp != 0 && rcpt->offset > 0)
902  deliver_completed(bounce_info->orig_fp, rcpt->offset);
903 }
const char * orig_addr
#define STREQ(a, b)
char * var_mail_name
Definition: mail_params.c:230
void myfree(void *ptr)
Definition: mymalloc.c:207
const char * dtext
Definition: dsn.h:22
int post_mail_fprintf(VSTREAM *cleanup, const char *format,...)
Definition: post_mail.c:418
#define INDENT
DSN_BUF * dsb_create(void)
Definition: dsn_buf.c:169
char * mystrdup(const char *str)
Definition: mymalloc.c:225
const char * mime_encoding
const char * address
const char * mail_date(time_t when)
Definition: mail_date.c:54
int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info, int headers_only)
void line_wrap(const char *str, int len, int indent, LINE_WRAP_FN output_fn, void *context)
Definition: line_wrap.c:76
const char * action
Definition: dsn.h:19
const char * reason
Definition: dsn.h:20
RCPT_BUF * rcpt_buf
#define vstring_str(vp)
Definition: vstring.h:71
VSTRING * buf
off_t vstream_ftell(VSTREAM *stream)
Definition: vstream.c:1157
#define IS_UTF8_ADDRESS(str, len)
#define DSN_RET_HDRS
Definition: dsn_mask.h:18
void deliver_completed(VSTREAM *stream, long offset)
time_t arrival_time
#define REC_TYPE_SIZE
Definition: rec_type.h:37
int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, const char *dest, int postmaster_copy)
const char * queue_name
const char * mail_addr_mail_daemon(void)
Definition: mail_addr.c:88
void dsb_free(DSN_BUF *dsb)
Definition: dsn_buf.c:190
char * mail_name
DSN dsn
Definition: dsn_buf.h:28
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
BOUNCE_TEMPLATE * template
#define LENGTH
int var_smtputf8_enable
Definition: mail_params.c:343
#define REC_TYPE_FROM
Definition: rec_type.h:43
char * translit(char *, const char *, const char *)
Definition: translit.c:40
#define IS_UNDELIVERED_TEMPLATE(template)
#define VSTRING_LEN(vp)
Definition: vstring.h:72
int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info, int notify_filter)
RECIPIENT rcpt
Definition: rcpt_buf.h:30
VSTRING * quote_822_local_flags(VSTRING *dst, const char *mbox, int flags)
#define SMTPUTF8_FLAG_REQUESTED
Definition: smtputf8.h:97
#define REC_PUT_BUF(v, t, b)
Definition: record.h:43
RCPT_BUF * rcpb_create(void)
Definition: rcpt_buf.c:80
VSTRING * vstring_vsprintf(VSTRING *vp, const char *format, va_list ap)
Definition: vstring.c:614
#define MAIL_ATTR_ENC_NONE
Definition: mail_proto.h:205
void rcpb_free(RCPT_BUF *rcpt)
Definition: rcpt_buf.c:108
#define REC_TYPE_CONT
Definition: rec_type.h:58
int var_bounce_limit
Definition: bounce.c:190
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
VSTREAM * mail_queue_open(const char *queue_name, const char *queue_id, int flags, mode_t mode)
Definition: mail_queue.c:424
#define IS_HEADER(s)
#define REC_TYPE_MESG
Definition: rec_type.h:56
#define bounce_template_encoding(t)
int bounce_log_close(BOUNCE_LOG *bp)
Definition: bounce_log.c:305
#define DELIVER_LOCK_MODE
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int myflock(int fd, int lock_style, int operation)
Definition: myflock.c:87
int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
#define NOT_7BIT_MIME(bp)
const char * mime_boundary
#define bounce_log_rewind(bp)
Definition: bounce_log.h:42
const char * mname
Definition: dsn.h:24
#define bounce_template_charset(t)
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
void bounce_template_expand(BOUNCE_XP_PUT_FN out_fn, VSTREAM *fp, BOUNCE_TEMPLATE *tp)
#define IS_DELAY_TEMPLATE(t)
int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
#define NOT_US_ASCII(tp)
#define IS_FAILURE_TEMPLATE(t)
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
DSN_BUF * dsn_buf
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
Definition: vstream.c:1093
int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info, int notify_filter)
const char * status
Definition: dsn.h:18
int post_mail_fputs(VSTREAM *cleanup, const char *str)
Definition: post_mail.c:439
time_t event_time(void)
Definition: events.c:647
#define STR
const char * dsn_envid
const char * service
BOUNCE_INFO * bounce_mail_init(const char *service, const char *queue_name, const char *queue_id, const char *encoding, int smtputf8, const char *dsn_envid, BOUNCE_TEMPLATE *template)
#define quote_822_local(dst, src)
int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
const char * dsn_orcpt
Definition: dsn.h:17
BOUNCE_LOG * log_handle
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
VSTRING * sender
#define vstream_fileno(vp)
Definition: vstream.h:115
const char * mtype
Definition: dsn.h:23
#define REC_TYPE_NORM
Definition: rec_type.h:59
#define REC_TYPE_TIME
Definition: rec_type.h:38
BOUNCE_LOG * bounce_log_open(const char *queue_name, const char *queue_id, int flags, mode_t mode)
Definition: bounce_log.c:114
const char * queue_id
char * var_myhostname
Definition: mail_params.c:223
void bounce_mail_free(BOUNCE_INFO *bounce_info)
BOUNCE_INFO * bounce_mail_one_init(const char *queue_name, const char *queue_id, const char *encoding, int smtputf8, const char *dsn_envid, RCPT_BUF *rcpt_buf, DSN_BUF *dsn_buf, BOUNCE_TEMPLATE *template)
#define MAIL_ATTR_ENC_8BIT
Definition: mail_proto.h:203
#define rec_get(fp, buf, limit)
Definition: record.h:56
#define vstream_ferror(vp)
Definition: vstream.h:120
int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
const char * dtype
Definition: dsn.h:21
void bounce_template_headers(BOUNCE_XP_PRN_FN out_fn, VSTREAM *fp, BOUNCE_TEMPLATE *tp, const char *rcpt, int postmaster_copy)
void bounce_delrcpt_one(BOUNCE_INFO *bounce_info)
#define NON_NULL_EMPTY(s)
BOUNCE_LOG * bounce_log_read(BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf, DSN_BUF *dsn_buf)
Definition: bounce_log.c:147
#define MAIL_ATTR_ENC_7BIT
Definition: mail_proto.h:204
int var_max_queue_time
Definition: bounce.c:191
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void bounce_delrcpt(BOUNCE_INFO *bounce_info)
VSTREAM * orig_fp