7 /* SMTP server request/response support
10 /* #include <smtpd_chat.h>
12 /* void smtpd_chat_query(state)
13 /* SMTPD_STATE *state;
15 /* void smtpd_chat_reply(state, format, ...)
16 /* SMTPD_STATE *state;
19 /* void smtpd_chat_notify(state)
20 /* SMTPD_STATE *state;
22 /* void smtpd_chat_reset(state)
23 /* SMTPD_STATE *state;
25 /* This module implements SMTP server support for request/reply
26 /* conversations, and maintains a limited SMTP transaction log.
28 /* smtpd_chat_query() receives a client request and appends a copy
29 /* to the SMTP transaction log.
31 /* smtpd_chat_reply() formats a server reply, sends it to the
32 /* client, and appends a copy to the SMTP transaction log.
33 /* When soft_bounce is enabled, all 5xx (reject) reponses are
34 /* replaced by 4xx (try again). In case of a 421 reply the
35 /* SMTPD_FLAG_HANGUP flag is set for orderly disconnect.
37 /* smtpd_chat_notify() sends a copy of the SMTP transaction log
38 /* to the postmaster for review. The postmaster notice is sent only
39 /* when delivery is possible immediately. It is an error to call
40 /* smtpd_chat_notify() when no SMTP transaction log exists.
42 /* smtpd_chat_reset() resets the transaction log. This is
43 /* typically done at the beginning of an SMTP session, or
44 /* within a session to discard non-error information.
46 /* Panic: interface violations. Fatal errors: out of memory.
47 /* internal protocol errors.
51 /* The Secure Mailer license must be distributed with this software.
54 /* IBM T.J. Watson Research
56 /* Yorktown Heights, NY 10598, USA
65 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
68 /* Utility library. */
74 #include <stringops.h>
75 #include <line_wrap.h>
80 #include <smtp_stream.h>
83 #include <mail_proto.h>
84 #include <mail_params.h>
85 #include <mail_addr.h>
86 #include <post_mail.h>
87 #include <mail_error.h>
89 /* Application-specific. */
92 #include "smtpd_chat.h"
94 #define STR vstring_str
95 #define LEN VSTRING_LEN
97 /* smtp_chat_reset - reset SMTP transaction log */
99 void smtpd_chat_reset(SMTPD_STATE
*state
)
101 if (state
->history
) {
102 argv_free(state
->history
);
107 /* smtp_chat_append - append record to SMTP transaction log */
109 static void smtp_chat_append(SMTPD_STATE
*state
, char *direction
,
114 if (state
->notify_mask
== 0)
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);
124 /* smtpd_chat_query - receive and record an SMTP request */
126 void smtpd_chat_query(SMTPD_STATE
*state
)
130 last_char
= smtp_get(state
->buffer
, state
->client
, var_line_limit
);
131 smtp_chat_append(state
, "In: ", STR(state
->buffer
));
132 if (last_char
!= '\n')
133 msg_warn("%s: request longer than %d: %.30s...",
134 state
->namaddr
, var_line_limit
,
135 printable(STR(state
->buffer
), '?'));
138 msg_info("< %s: %s", state
->namaddr
, STR(state
->buffer
));
141 /* smtpd_chat_reply - format, send and record an SMTP response */
143 void smtpd_chat_reply(SMTPD_STATE
*state
, const char *format
,...)
152 * Slow down clients that make errors. Sleep-on-anything slows down
153 * clients that make an excessive number of errors within a session.
155 if (state
->error_count
>= var_smtpd_soft_erlim
)
156 sleep(delay
= var_smtpd_err_sleep
);
158 va_start(ap
, format
);
159 vstring_vsprintf(state
->buffer
, format
, ap
);
161 /* All 5xx replies must have a 5.xx.xx detail code. */
162 for (cp
= STR(state
->buffer
), end
= cp
+ strlen(STR(state
->buffer
));;) {
163 if (var_soft_bounce
) {
170 /* This is why we use strlen() above instead of VSTRING_LEN(). */
171 if ((next
= strstr(cp
, "\r\n")) != 0) {
176 smtp_chat_append(state
, "Out: ", cp
);
179 msg_info("> %s: %s", state
->namaddr
, cp
);
181 smtp_fputs(cp
, next
- cp
, state
->client
);
189 * Flush unsent output if no I/O happened for a while. This avoids
190 * timeouts with pipelined SMTP sessions that have lots of server-side
191 * delays (tarpit delays or DNS lookups for UCE restrictions).
193 if (delay
|| time((time_t *) 0) - vstream_ftime(state
->client
) > 10)
194 vstream_fflush(state
->client
);
197 * Abort immediately if the connection is broken.
199 if (vstream_ftimeout(state
->client
))
200 vstream_longjmp(state
->client
, SMTP_ERR_TIME
);
201 if (vstream_ferror(state
->client
))
202 vstream_longjmp(state
->client
, SMTP_ERR_EOF
);
205 * Orderly disconnect in case of 421 or 521 reply.
207 if (strncmp(STR(state
->buffer
), "421", 3) == 0
208 || strncmp(STR(state
->buffer
), "521", 3) == 0)
209 state
->flags
|= SMTPD_FLAG_HANGUP
;
212 /* print_line - line_wrap callback */
214 static void print_line(const char *str
, int len
, int indent
, char *context
)
216 VSTREAM
*notice
= (VSTREAM
*) context
;
218 post_mail_fprintf(notice
, " %*s%.*s", indent
, "", len
, str
);
221 /* smtpd_chat_notify - notify postmaster */
223 void smtpd_chat_notify(SMTPD_STATE
*state
)
225 const char *myname
= "smtpd_chat_notify";
232 if (state
->history
== 0)
233 msg_panic("%s: no conversation history", myname
);
235 msg_info("%s: notify postmaster", myname
);
238 * Construct a message for the postmaster, explaining what this is all
239 * about. This is junk mail: don't send it when the mail posting service
240 * is unavailable, and use the double bounce sender address to prevent
241 * mail bounce wars. Always prepend one space to message content that we
242 * generate from untrusted data.
244 #define NULL_TRACE_FLAGS 0
245 #define NO_QUEUE_ID ((VSTRING *) 0)
249 notice
= post_mail_fopen_nowait(mail_addr_double_bounce(),
251 INT_FILT_MASK_NOTIFY
,
252 NULL_TRACE_FLAGS
, NO_QUEUE_ID
);
254 msg_warn("postmaster notify: %m");
257 post_mail_fprintf(notice
, "From: %s (Mail Delivery System)",
258 mail_addr_mail_daemon());
259 post_mail_fprintf(notice
, "To: %s (Postmaster)", var_error_rcpt
);
260 post_mail_fprintf(notice
, "Subject: %s SMTP server: errors from %s",
261 var_mail_name
, state
->namaddr
);
262 post_mail_fputs(notice
, "");
263 post_mail_fputs(notice
, "Transcript of session follows.");
264 post_mail_fputs(notice
, "");
265 argv_terminate(state
->history
);
266 for (cpp
= state
->history
->argv
; *cpp
; cpp
++)
267 line_wrap(printable(*cpp
, '?'), LENGTH
, INDENT
, print_line
,
269 post_mail_fputs(notice
, "");
271 post_mail_fprintf(notice
, "Session aborted, reason: %s", state
->reason
);
272 post_mail_fputs(notice
, "");
273 post_mail_fprintf(notice
, "For other details, see the local mail logfile");
274 (void) post_mail_fclose(notice
);