static int smtpd_proxy_rec_put(VSTREAM *stream, int rec_type, const char *data, ssize_t len) { const char *myname = "smtpd_proxy_rec_put"; int err = 0; /* * Errors first. */ if (vstream_ferror(stream) || vstream_feof(stream) || (err = vstream_setjmp(stream)) != 0) { (void) smtpd_proxy_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream), err); return (REC_TYPE_ERROR); } /* * Send one content record. Errors and results must be as with rec_put(). */ if (rec_type == REC_TYPE_NORM) smtp_fputs(data, len, stream); else if (rec_type == REC_TYPE_CONT) smtp_fwrite(data, len, stream); else msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname); return (rec_type); }
void smtp_chat_cmd(SMTP_SESSION *session, const char *fmt,...) { va_list ap; /* * Format the command, and update the transaction log. */ va_start(ap, fmt); vstring_vsprintf(session->buffer, fmt, ap); va_end(ap); smtp_chat_append(session, "Out: ", STR(session->buffer)); /* * Optionally log the command first, so we can see in the log what the * program is trying to do. */ if (msg_verbose) msg_info("> %s: %s", session->namaddrport, STR(session->buffer)); /* * Send the command to the SMTP server. */ smtp_fputs(STR(session->buffer), LEN(session->buffer), session->stream); /* * Force flushing of output does not belong here. It is done in the * smtp_loop() main protocol loop when reading the server response, and * in smtp_helo() when reading the EHLO response after sending the EHLO * command. * * If we do forced flush here, then we must longjmp() on error, and a * matching "prepare for disaster" error handler must be set up before * every smtp_chat_cmd() call. */ #if 0 /* * Flush unsent data to avoid timeouts after slow DNS lookups. */ if (time((time_t *) 0) - vstream_ftime(session->stream) > 10) vstream_fflush(session->stream); /* * Abort immediately if the connection is broken. */ if (vstream_ftimeout(session->stream)) vstream_longjmp(session->stream, SMTP_ERR_TIME); if (vstream_ferror(session->stream)) vstream_longjmp(session->stream, SMTP_ERR_EOF); #endif }
void smtpd_chat_reply(SMTPD_STATE *state, char *format,...) { va_list ap; int delay = 0; va_start(ap, format); vstring_vsprintf(state->buffer, format, ap); va_end(ap); if (var_soft_bounce && STR(state->buffer)[0] == '5') STR(state->buffer)[0] = '4'; smtp_chat_append(state, "Out: "); if (msg_verbose) msg_info("> %s[%s]: %s", state->name, state->addr, STR(state->buffer)); /* * Slow down clients that make errors. Sleep-on-anything slows down * clients that make an excessive number of errors within a session. */ if (state->error_count >= var_smtpd_soft_erlim) sleep(delay = var_smtpd_err_sleep); smtp_fputs(STR(state->buffer), LEN(state->buffer), state->client); /* * Flush unsent output if no I/O happened for a while. This avoids * timeouts with pipelined SMTP sessions that have lots of server-side * delays (tarpit delays or DNS lookups for UCE restrictions). */ if (delay || time((time_t *) 0) - vstream_ftime(state->client) > 10) vstream_fflush(state->client); /* * Abort immediately if the connection is broken. */ if (vstream_ftimeout(state->client)) vstream_longjmp(state->client, SMTP_ERR_TIME); if (vstream_ferror(state->client)) vstream_longjmp(state->client, SMTP_ERR_EOF); }
static void data_done(int unused, char *context) { SESSION *session = (SESSION *) context; RESPONSE *resp; int except; static const char *mydate; static int mypid; /* * Get response to DATA command. */ if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending DATA command", exception_text(except)); if ((resp = response(session->stream, buffer))->code == 354) { /* see below */ ; } else if (allow_reject) { msg_warn("data rejected: %d %s", resp->code, resp->str); if (resp->code == 421 || resp->code == 521) { close_session(session); return; } send_rset(unused, context); return; } else { msg_fatal("data rejected: %d %s", resp->code, resp->str); } /* * Send basic header to keep mailers that bother to examine them happy. */ if (send_headers) { if (mydate == 0) { mydate = mail_date(time((time_t *) 0)); mypid = getpid(); } smtp_printf(session->stream, "From: <%s>", sender); smtp_printf(session->stream, "To: <%s>", recipient); smtp_printf(session->stream, "Date: %s", mydate); smtp_printf(session->stream, "Message-Id: <%04x.%04x.%04x@%s>", mypid, vstream_fileno(session->stream), message_count, var_myhostname); if (subject) smtp_printf(session->stream, "Subject: %s", subject); smtp_fputs("", 0, session->stream); } /* * Send some garbage. */ if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending message", exception_text(except)); if (message_length == 0) { smtp_fputs("La de da de da 1.", 17, session->stream); smtp_fputs("La de da de da 2.", 17, session->stream); smtp_fputs("La de da de da 3.", 17, session->stream); smtp_fputs("La de da de da 4.", 17, session->stream); } else { /* * XXX This may cause the process to block with message content * larger than VSTREAM_BUFIZ bytes. */ smtp_fputs(message_data, message_length, session->stream); } /* * Send end of message and process the server response. */ command(session->stream, "."); /* * Update the running counter. */ if (count) { counter++; vstream_printf("%d\r", counter); vstream_fflush(VSTREAM_OUT); } /* * Prepare for the next event. */ event_enable_read(vstream_fileno(session->stream), dot_done, (char *) session); }
static int smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...) { SMTPD_PROXY *proxy = state->proxy; va_list ap; char *cp; int last_char; int err = 0; static VSTRING *buffer = 0; /* * Errors first. Be prepared for delayed errors from the DATA phase. */ if (vstream_ferror(proxy->service_stream) || vstream_feof(proxy->service_stream) || (err = vstream_setjmp(proxy->service_stream)) != 0) { return (smtpd_proxy_rdwr_error(state, err)); } /* * The command can be omitted at the start of an SMTP session. This is * not documented as part of the official interface because it is used * only internally to this module. */ if (fmt != SMTPD_PROXY_CONN_FMT) { /* * Format the command. */ va_start(ap, fmt); vstring_vsprintf(proxy->request, fmt, ap); va_end(ap); /* * Optionally log the command first, so that we can see in the log * what the program is trying to do. */ if (msg_verbose) msg_info("> %s: %s", proxy->service_name, STR(proxy->request)); /* * Send the command to the proxy server. Since we're going to read a * reply immediately, there is no need to flush buffers. */ smtp_fputs(STR(proxy->request), LEN(proxy->request), proxy->service_stream); } /* * Early return if we don't want to wait for a server reply (such as * after sending QUIT). */ if (expect == SMTPD_PROX_WANT_NONE) return (0); /* * Censor out non-printable characters in server responses and save * complete multi-line responses if possible. * * We can't parse or store input that exceeds var_line_limit, so we just * skip over it to simplify the remainder of the code below. */ VSTRING_RESET(proxy->reply); if (buffer == 0) buffer = vstring_alloc(10); for (;;) { last_char = smtp_get(buffer, proxy->service_stream, var_line_limit, SMTP_GET_FLAG_SKIP); printable(STR(buffer), '?'); if (last_char != '\n') msg_warn("%s: response longer than %d: %.30s...", proxy->service_name, var_line_limit, STR(buffer)); if (msg_verbose) msg_info("< %s: %.100s", proxy->service_name, STR(buffer)); /* * Defend against a denial of service attack by limiting the amount * of multi-line text that we are willing to store. */ if (LEN(proxy->reply) < var_line_limit) { if (VSTRING_LEN(proxy->reply)) vstring_strcat(proxy->reply, "\r\n"); vstring_strcat(proxy->reply, STR(buffer)); } /* * Parse the response into code and text. Ignore unrecognized * garbage. This means that any character except space (or end of * line) will have the same effect as the '-' line continuation * character. */ for (cp = STR(buffer); *cp && ISDIGIT(*cp); cp++) /* void */ ; if (cp - STR(buffer) == 3) { if (*cp == '-') continue; if (*cp == ' ' || *cp == 0) break; } msg_warn("received garbage from proxy %s: %.100s", proxy->service_name, STR(buffer)); } /* * Log a warning in case the proxy does not send the expected response. * Silently accept any response when the client expressed no expectation. * * Starting with Postfix 2.6 we don't pass through unexpected 2xx or 3xx * proxy replies. They are a source of support problems, so we replace * them by generic server error replies. */ if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(proxy->reply)) { msg_warn("proxy %s rejected \"%s\": \"%s\"", proxy->service_name, fmt == SMTPD_PROXY_CONN_FMT ? "connection request" : STR(proxy->request), STR(proxy->reply)); if (*STR(proxy->reply) == SMTPD_PROX_WANT_OK || *STR(proxy->reply) == SMTPD_PROX_WANT_MORE) { smtpd_proxy_rdwr_error(state, 0); } return (-1); } else { return (0); } }