5
/* LMTP client request/response support
17
/* void lmtp_chat_cmd(state, format, ...)
21
/* LMTP_RESP *lmtp_chat_resp(state)
24
/* void lmtp_chat_notify(state)
27
/* void lmtp_chat_reset(state)
30
/* This module implements LMTP client support for request/reply
31
/* conversations, and maintains a limited LMTP transaction log.
33
/* lmtp_chat_cmd() formats a command and sends it to an LMTP server.
34
/* Optionally, the command is logged.
36
/* lmtp_chat_resp() read one LMTP server response. It separates the
37
/* numerical status code from the text, and concatenates multi-line
38
/* responses to one string, using a newline as separator.
39
/* Optionally, the server response is logged.
41
/* lmtp_chat_notify() sends a copy of the LMTP transaction log
42
/* to the postmaster for review. The postmaster notice is sent only
43
/* when delivery is possible immediately. It is an error to call
44
/* lmtp_chat_notify() when no LMTP transaction log exists.
46
/* lmtp_chat_reset() resets the transaction log. This is
47
/* typically done at the beginning or end of an LMTP session,
48
/* or within a session to discard non-error information.
49
/* In addition, lmtp_chat_reset() resets the per-session error
50
/* status bits and flags.
52
/* Fatal errors: memory allocation problem, server response exceeds
53
/* configurable limit.
54
/* All other exceptions are handled by long jumps (see smtp_stream(3)).
56
/* smtp_stream(3) LMTP session I/O support
57
/* msg(3) generic logging interface
61
/* The Secure Mailer license must be distributed with this software.
64
/* IBM T.J. Watson Research
66
/* Yorktown Heights, NY 10598, USA
68
/* Alterations for LMTP by:
69
/* Philip A. Prindeville
73
/* Additional work on LMTP by:
75
/* University of Texas at Dallas
76
/* P.O. Box 830688, MC34
77
/* Richardson, TX 75083, USA
83
#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
89
/* Utility library. */
95
#include <stringops.h>
96
#include <line_wrap.h>
101
#include <recipient_list.h>
102
#include <deliver_request.h>
103
#include <smtp_stream.h>
104
#include <mail_params.h>
105
#include <mail_addr.h>
106
#include <post_mail.h>
107
#include <mail_error.h>
109
/* Application-specific. */
113
#define STR(x) ((char *) vstring_str(x))
114
#define LEN VSTRING_LEN
116
/* lmtp_chat_reset - reset LMTP transaction log */
118
void lmtp_chat_reset(LMTP_STATE *state)
120
if (state->history) {
121
argv_free(state->history);
124
/* What's status without history? */
126
state->error_mask = 0;
129
/* lmtp_chat_append - append record to LMTP transaction log */
131
static void lmtp_chat_append(LMTP_STATE *state, char *direction, char *data)
135
if (state->history == 0)
136
state->history = argv_alloc(10);
137
line = concatenate(direction, data, (char *) 0);
138
argv_add(state->history, line, (char *) 0);
142
/* lmtp_chat_cmd - send an LMTP command */
144
void lmtp_chat_cmd(LMTP_STATE *state, char *fmt,...)
146
LMTP_SESSION *session = state->session;
150
* Format the command, and update the transaction log.
153
vstring_vsprintf(state->buffer, fmt, ap);
155
lmtp_chat_append(state, "Out: ", STR(state->buffer));
158
* Optionally log the command first, so we can see in the log what the
159
* program is trying to do.
162
msg_info("> %s: %s", session->namaddr, STR(state->buffer));
165
* Send the command to the LMTP server.
167
smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream);
170
/* lmtp_chat_resp - read and process LMTP server response */
172
LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
174
LMTP_SESSION *session = state->session;
175
static LMTP_RESP rdata;
180
* Initialize the response data buffer.
183
rdata.buf = vstring_alloc(100);
186
* Censor out non-printable characters in server responses. Concatenate
187
* multi-line server responses. Separate the status code from the text.
188
* Leave further parsing up to the application.
190
VSTRING_RESET(rdata.buf);
192
last_char = smtp_get(state->buffer, session->stream, var_line_limit);
193
printable(STR(state->buffer), '?');
194
if (last_char != '\n')
195
msg_warn("%s: response longer than %d: %.30s...",
196
session->namaddr, var_line_limit, STR(state->buffer));
198
msg_info("< %s: %s", session->namaddr, STR(state->buffer));
201
* Defend against a denial of service attack by limiting the amount
202
* of multi-line text that we are willing to store.
204
if (LEN(rdata.buf) < var_line_limit) {
205
if (VSTRING_LEN(rdata.buf))
206
VSTRING_ADDCH(rdata.buf, '\n');
207
vstring_strcat(rdata.buf, STR(state->buffer));
208
lmtp_chat_append(state, "In: ", STR(state->buffer));
212
* Parse into code and text. Ignore unrecognized garbage. This means
213
* that any character except space (or end of line) will have the
214
* same effect as the '-' line continuation character.
216
for (cp = STR(state->buffer); *cp && ISDIGIT(*cp); cp++)
218
if (cp - STR(state->buffer) == 3) {
221
if (*cp == ' ' || *cp == 0)
224
state->error_mask |= MAIL_ERROR_PROTOCOL;
226
rdata.code = atoi(STR(state->buffer));
227
VSTRING_TERMINATE(rdata.buf);
228
rdata.str = STR(rdata.buf);
232
/* print_line - line_wrap callback */
234
static void print_line(const char *str, int len, int indent, char *context)
236
VSTREAM *notice = (VSTREAM *) context;
238
post_mail_fprintf(notice, " %*s%.*s", indent, "", len, str);
241
/* lmtp_chat_notify - notify postmaster */
243
void lmtp_chat_notify(LMTP_STATE *state)
245
char *myname = "lmtp_chat_notify";
246
LMTP_SESSION *session = state->session;
253
if (state->history == 0)
254
msg_panic("%s: no conversation history", myname);
256
msg_info("%s: notify postmaster", myname);
259
* Construct a message for the postmaster, explaining what this is all
260
* about. This is junk mail: don't send it when the mail posting service
261
* is unavailable, and use the double bounce sender address, to prevent
262
* mail bounce wars. Always prepend one space to message content that we
263
* generate from untrusted data.
265
#define NULL_TRACE_FLAGS 0
269
notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
271
CLEANUP_FLAG_MASK_INTERNAL,
274
msg_warn("postmaster notify: %m");
277
post_mail_fprintf(notice, "From: %s (Mail Delivery System)",
278
mail_addr_mail_daemon());
279
post_mail_fprintf(notice, "To: %s (Postmaster)", var_error_rcpt);
280
post_mail_fprintf(notice, "Subject: %s LMTP client: errors from %s",
281
var_mail_name, session->namaddr);
282
post_mail_fputs(notice, "");
283
post_mail_fprintf(notice, "Unexpected response from %s.", session->namaddr);
284
post_mail_fputs(notice, "");
285
post_mail_fputs(notice, "Transcript of session follows.");
286
post_mail_fputs(notice, "");
287
argv_terminate(state->history);
288
for (cpp = state->history->argv; *cpp; cpp++)
289
line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line,
291
(void) post_mail_fclose(notice);