Postfix3.3.1
smtpd_chat.c
[詳解]
1 /*++
2 /* NAME
3 /* smtpd_chat 3
4 /* SUMMARY
5 /* SMTP server request/response support
6 /* SYNOPSIS
7 /* #include <smtpd.h>
8 /* #include <smtpd_chat.h>
9 /*
10 /* void smtpd_chat_query(state)
11 /* SMTPD_STATE *state;
12 /*
13 /* void smtpd_chat_reply(state, format, ...)
14 /* SMTPD_STATE *state;
15 /* char *format;
16 /*
17 /* void smtpd_chat_notify(state)
18 /* SMTPD_STATE *state;
19 /*
20 /* void smtpd_chat_reset(state)
21 /* SMTPD_STATE *state;
22 /* DESCRIPTION
23 /* This module implements SMTP server support for request/reply
24 /* conversations, and maintains a limited SMTP transaction log.
25 /*
26 /* smtpd_chat_query() receives a client request and appends a copy
27 /* to the SMTP transaction log.
28 /*
29 /* smtpd_chat_reply() formats a server reply, sends it to the
30 /* client, and appends a copy to the SMTP transaction log.
31 /* When soft_bounce is enabled, all 5xx (reject) responses are
32 /* replaced by 4xx (try again). In case of a 421 reply the
33 /* SMTPD_FLAG_HANGUP flag is set for orderly disconnect.
34 /*
35 /* smtpd_chat_notify() sends a copy of the SMTP transaction log
36 /* to the postmaster for review. The postmaster notice is sent only
37 /* when delivery is possible immediately. It is an error to call
38 /* smtpd_chat_notify() when no SMTP transaction log exists.
39 /*
40 /* smtpd_chat_reset() resets the transaction log. This is
41 /* typically done at the beginning of an SMTP session, or
42 /* within a session to discard non-error information.
43 /* DIAGNOSTICS
44 /* Panic: interface violations. Fatal errors: out of memory.
45 /* internal protocol errors.
46 /* LICENSE
47 /* .ad
48 /* .fi
49 /* The Secure Mailer license must be distributed with this software.
50 /* AUTHOR(S)
51 /* Wietse Venema
52 /* IBM T.J. Watson Research
53 /* P.O. Box 704
54 /* Yorktown Heights, NY 10598, USA
55 /*--*/
56 
57 /* System library. */
58 
59 #include <sys_defs.h>
60 #include <setjmp.h>
61 #include <unistd.h>
62 #include <time.h>
63 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
64 #include <stdarg.h>
65 
66 /* Utility library. */
67 
68 #include <msg.h>
69 #include <argv.h>
70 #include <vstring.h>
71 #include <vstream.h>
72 #include <stringops.h>
73 #include <line_wrap.h>
74 #include <mymalloc.h>
75 
76 /* Global library. */
77 
78 #include <smtp_stream.h>
79 #include <record.h>
80 #include <rec_type.h>
81 #include <mail_proto.h>
82 #include <mail_params.h>
83 #include <mail_addr.h>
84 #include <post_mail.h>
85 #include <mail_error.h>
86 #include <smtp_reply_footer.h>
87 
88 /* Application-specific. */
89 
90 #include "smtpd.h"
91 #include "smtpd_expand.h"
92 #include "smtpd_chat.h"
93 
94 #define STR vstring_str
95 #define LEN VSTRING_LEN
96 
97 /* smtp_chat_reset - reset SMTP transaction log */
98 
100 {
101  if (state->history) {
102  argv_free(state->history);
103  state->history = 0;
104  }
105 }
106 
107 /* smtp_chat_append - append record to SMTP transaction log */
108 
109 static void smtp_chat_append(SMTPD_STATE *state, char *direction,
110  const char *text)
111 {
112  char *line;
113 
114  if (state->notify_mask == 0)
115  return;
116 
117  if (state->history == 0)
118  state->history = argv_alloc(10);
119  line = concatenate(direction, text, (char *) 0);
120  argv_add(state->history, line, (char *) 0);
121  myfree(line);
122 }
123 
124 /* smtpd_chat_query - receive and record an SMTP request */
125 
127 {
128  int last_char;
129 
130  /*
131  * We can't parse or store input that exceeds var_line_limit, so we skip
132  * over it to avoid loss of synchronization.
133  */
134  last_char = smtp_get(state->buffer, state->client, var_line_limit,
136  smtp_chat_append(state, "In: ", STR(state->buffer));
137  if (last_char != '\n')
138  msg_warn("%s: request longer than %d: %.30s...",
139  state->namaddr, var_line_limit,
140  printable(STR(state->buffer), '?'));
141 
142  if (msg_verbose)
143  msg_info("< %s: %s", state->namaddr, STR(state->buffer));
144 }
145 
146 /* smtpd_chat_reply - format, send and record an SMTP response */
147 
148 void smtpd_chat_reply(SMTPD_STATE *state, const char *format,...)
149 {
150  va_list ap;
151  int delay = 0;
152  char *cp;
153  char *next;
154  char *end;
155 
156  /*
157  * Slow down clients that make errors. Sleep-on-anything slows down
158  * clients that make an excessive number of errors within a session.
159  */
160  if (state->error_count >= var_smtpd_soft_erlim)
161  sleep(delay = var_smtpd_err_sleep);
162 
163  va_start(ap, format);
164  vstring_vsprintf(state->buffer, format, ap);
165  va_end(ap);
166 
168  && (*(cp = STR(state->buffer)) == '4' || *cp == '5'))
171  (void *) state);
172 
173  /* All 5xx replies must have a 5.xx.xx detail code. */
174  for (cp = STR(state->buffer), end = cp + strlen(STR(state->buffer));;) {
175  if (var_soft_bounce) {
176  if (cp[0] == '5') {
177  cp[0] = '4';
178  if (cp[4] == '5')
179  cp[4] = '4';
180  }
181  }
182  /* This is why we use strlen() above instead of VSTRING_LEN(). */
183  if ((next = strstr(cp, "\r\n")) != 0) {
184  *next = 0;
185  if (next[2] != 0)
186  cp[3] = '-'; /* contact footer kludge */
187  else
188  next = end; /* strip trailing \r\n */
189  } else {
190  next = end;
191  }
192  smtp_chat_append(state, "Out: ", cp);
193 
194  if (msg_verbose)
195  msg_info("> %s: %s", state->namaddr, cp);
196 
197  smtp_fputs(cp, next - cp, state->client);
198  if (next < end)
199  cp = next + 2;
200  else
201  break;
202  }
203 
204  /*
205  * Flush unsent output if no I/O happened for a while. This avoids
206  * timeouts with pipelined SMTP sessions that have lots of server-side
207  * delays (tarpit delays or DNS lookups for UCE restrictions).
208  */
209  if (delay || time((time_t *) 0) - vstream_ftime(state->client) > 10)
210  vstream_fflush(state->client);
211 
212  /*
213  * Abort immediately if the connection is broken.
214  */
215  if (vstream_ftimeout(state->client))
217  if (vstream_ferror(state->client))
219 
220  /*
221  * Orderly disconnect in case of 421 or 521 reply.
222  */
223  if (strncmp(STR(state->buffer), "421", 3) == 0
224  || strncmp(STR(state->buffer), "521", 3) == 0)
225  state->flags |= SMTPD_FLAG_HANGUP;
226 }
227 
228 /* print_line - line_wrap callback */
229 
230 static void print_line(const char *str, int len, int indent, void *context)
231 {
232  VSTREAM *notice = (VSTREAM *) context;
233 
234  post_mail_fprintf(notice, " %*s%.*s", indent, "", len, str);
235 }
236 
237 /* smtpd_chat_notify - notify postmaster */
238 
240 {
241  const char *myname = "smtpd_chat_notify";
242  VSTREAM *notice;
243  char **cpp;
244 
245  /*
246  * Sanity checks.
247  */
248  if (state->history == 0)
249  msg_panic("%s: no conversation history", myname);
250  if (msg_verbose)
251  msg_info("%s: notify postmaster", myname);
252 
253  /*
254  * Construct a message for the postmaster, explaining what this is all
255  * about. This is junk mail: don't send it when the mail posting service
256  * is unavailable, and use the double bounce sender address to prevent
257  * mail bounce wars. Always prepend one space to message content that we
258  * generate from untrusted data.
259  */
260 #define NULL_TRACE_FLAGS 0
261 #define NO_QUEUE_ID ((VSTRING *) 0)
262 #define LENGTH 78
263 #define INDENT 4
264 
269  if (notice == 0) {
270  msg_warn("postmaster notify: %m");
271  return;
272  }
273  post_mail_fprintf(notice, "From: %s (Mail Delivery System)",
275  post_mail_fprintf(notice, "To: %s (Postmaster)", var_error_rcpt);
276  post_mail_fprintf(notice, "Subject: %s SMTP server: errors from %s",
277  var_mail_name, state->namaddr);
278  post_mail_fputs(notice, "");
279  post_mail_fputs(notice, "Transcript of session follows.");
280  post_mail_fputs(notice, "");
281  argv_terminate(state->history);
282  for (cpp = state->history->argv; *cpp; cpp++)
283  line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line,
284  (void *) notice);
285  post_mail_fputs(notice, "");
286  if (state->reason)
287  post_mail_fprintf(notice, "Session aborted, reason: %s", state->reason);
288  post_mail_fputs(notice, "");
289  post_mail_fprintf(notice, "For other details, see the local mail logfile");
290  (void) post_mail_fclose(notice);
291 }
int msg_verbose
Definition: msg.c:177
#define LENGTH
char * var_mail_name
Definition: mail_params.c:230
void myfree(void *ptr)
Definition: mymalloc.c:207
int post_mail_fprintf(VSTREAM *cleanup, const char *format,...)
Definition: post_mail.c:418
VSTRING * buffer
Definition: smtpd.h:71
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
#define SMTPUTF8_FLAG_NONE
Definition: smtputf8.h:96
void line_wrap(const char *str, int len, int indent, LINE_WRAP_FN output_fn, void *context)
Definition: line_wrap.c:76
int var_soft_bounce
Definition: mail_params.c:281
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
const char * smtpd_expand_lookup(const char *name, int unused_mode, void *context)
Definition: smtpd_expand.c:174
#define STR
Definition: smtpd_chat.c:94
#define SMTP_ERR_TIME
Definition: smtp_stream.h:31
const char * mail_addr_mail_daemon(void)
Definition: mail_addr.c:88
#define INDENT
#define vstream_longjmp(stream, val)
Definition: vstream.h:249
char ** argv
Definition: argv.h:20
#define MAIL_SRC_MASK_NOTIFY
Definition: mail_proto.h:82
void argv_add(ARGV *argvp,...)
Definition: argv.c:197
int smtp_get(VSTRING *vp, VSTREAM *stream, ssize_t bound, int flags)
Definition: smtp_stream.c:305
int var_smtpd_soft_erlim
Definition: smtpd.c:1231
void smtpd_chat_reset(SMTPD_STATE *state)
Definition: smtpd_chat.c:99
ARGV * argv_alloc(ssize_t len)
Definition: argv.c:149
#define vstream_ftime(vp)
Definition: vstream.h:127
void smtpd_chat_notify(SMTPD_STATE *state)
Definition: smtpd_chat.c:239
char * reason
Definition: smtpd.h:102
VSTREAM * client
Definition: smtpd.h:70
VSTRING * vstring_vsprintf(VSTRING *vp, const char *format, va_list ap)
Definition: vstring.c:614
VSTREAM * post_mail_fopen_nowait(const char *sender, const char *recipient, int source_class, int trace_flags, int utf8_flags, VSTRING *queue_id)
Definition: post_mail.c:293
#define SMTP_ERR_EOF
Definition: smtp_stream.h:30
#define vstream_ftimeout(vp)
Definition: vstream.h:124
int notify_mask
Definition: smtpd.h:94
void msg_warn(const char *fmt,...)
Definition: msg.c:215
void smtp_fputs(const char *cp, ssize_t todo, VSTREAM *stream)
Definition: smtp_stream.c:383
int post_mail_fclose(VSTREAM *cleanup)
Definition: post_mail.c:449
void smtpd_chat_reply(SMTPD_STATE *state, const char *format,...)
Definition: smtpd_chat.c:148
char * var_error_rcpt
Definition: smtp.c:857
int var_line_limit
Definition: mail_params.c:263
void smtpd_chat_query(SMTPD_STATE *state)
Definition: smtpd_chat.c:126
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
int post_mail_fputs(VSTREAM *cleanup, const char *str)
Definition: post_mail.c:439
char * concatenate(const char *arg0,...)
Definition: concatenate.c:42
const char * mail_addr_double_bounce(void)
Definition: mail_addr.c:64
char * var_smtpd_rej_footer
Definition: postscreen.c:511
int var_smtpd_err_sleep
Definition: smtpd.c:1257
VSTRING * smtpd_expand_filter
Definition: smtpd_expand.c:80
#define NO_QUEUE_ID
int flags
Definition: smtpd.h:68
#define SMTP_GET_FLAG_SKIP
Definition: smtp_stream.h:51
char * printable(char *string, int replacement)
Definition: printable.c:49
#define vstream_ferror(vp)
Definition: vstream.h:120
int error_count
Definition: smtpd.h:92
char * namaddr
Definition: smtpd.h:79
ARGV * history
Definition: smtpd.h:101
void argv_terminate(ARGV *argvp)
Definition: argv.c:242
void msg_info(const char *fmt,...)
Definition: msg.c:199
#define SMTPD_FLAG_HANGUP
Definition: smtpd.h:194
#define NULL_TRACE_FLAGS