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