Example #1
0
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);
}
Example #2
0
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
}
Example #3
0
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);
}
Example #4
0
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);
}
Example #5
0
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);
    }
}