Exemple #1
0
static int smtpd_proxy_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
{
    int     ret;

    if (VSTRING_LEN(buf) > 0) {
	ret = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK,
			      XFORWARD_CMD "%s", STR(buf));
	VSTRING_RESET(buf);
	return (ret);
    }
    return (0);
}
Exemple #2
0
void    smtpd_proxy_close(SMTPD_STATE *state)
{
    SMTPD_PROXY *proxy = state->proxy;

    /*
     * Specify SMTPD_PROX_WANT_NONE so that the server reply will not clobber
     * the END-OF-DATA reply.
     */
    if (proxy->service_stream != 0) {
	if (vstream_feof(proxy->service_stream) == 0
	    && vstream_ferror(proxy->service_stream) == 0)
	    (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE,
				   SMTPD_CMD_QUIT);
	(void) vstream_fclose(proxy->service_stream);
	if (proxy->stream == proxy->service_stream)
	    proxy->stream = 0;
	proxy->service_stream = 0;
    }
}
Exemple #3
0
void    smtpd_proxy_close(SMTPD_STATE *state)
{
    SMTPD_PROXY *proxy = state->proxy;

    /*
     * XXX We can't send QUIT if the stream is still good, because that would
     * overwrite the last server reply in proxy->buffer. We probably should
     * just bite the bullet and allocate separate buffers for sending and
     * receiving.
     */
    if (proxy->service_stream != 0) {
#if 0
	if (vstream_feof(proxy->service_stream) == 0
	    && vstream_ferror(proxy->service_stream) == 0)
	    (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE,
				   SMTPD_CMD_QUIT);
#endif
	(void) vstream_fclose(proxy->service_stream);
	if (proxy->stream == proxy->service_stream)
	    proxy->stream = 0;
	proxy->service_stream = 0;
    }
}
Exemple #4
0
static int smtpd_proxy_replay_send(SMTPD_STATE *state)
{
    const char *myname = "smtpd_proxy_replay_send";
    static VSTRING *replay_buf = 0;
    SMTPD_PROXY *proxy = state->proxy;
    int     rec_type;
    int     expect = SMTPD_PROX_WANT_BAD;

    /*
     * Sanity check.
     */
    if (smtpd_proxy_replay_stream == 0)
	msg_panic("%s: no before-queue filter speed-adjust log", myname);

    /*
     * Errors first.
     */
    if (vstream_ferror(smtpd_proxy_replay_stream)
	|| vstream_feof(smtpd_proxy_replay_stream)
	|| rec_put(smtpd_proxy_replay_stream, REC_TYPE_END, "", 0) != REC_TYPE_END
	|| vstream_fflush(smtpd_proxy_replay_stream))
	/* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */
	return (smtpd_proxy_replay_rdwr_error(state));

    /*
     * Delayed connection to the before-queue filter.
     */
    if (smtpd_proxy_connect(state) < 0)
	return (-1);

    /*
     * Replay the speed-match log. We do sanity check record content, but we
     * don't implement a protocol state engine here, since we are reading
     * from a file that we just wrote ourselves.
     * 
     * This is different than the MailChannels patented solution that
     * multiplexes a large number of slowed-down inbound connections over a
     * small number of fast connections to a local MTA.
     * 
     * - MailChannels receives mail directly from the Internet. It uses one
     * connection to the local MTA to reject invalid recipients before
     * receiving the entire email message at reduced bit rates, and then uses
     * a different connection to quickly deliver the message to the local
     * MTA.
     * 
     * - Postfix receives mail directly from the Internet. The Postfix SMTP
     * server rejects invalid recipients before receiving the entire message
     * over the Internet, and then delivers the message quickly to a local
     * SMTP-based content filter.
     */
    if (replay_buf == 0)
	replay_buf = vstring_alloc(100);
    if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0)
	return (smtpd_proxy_replay_rdwr_error(state));

    for (;;) {
	switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf,
				   REC_FLAG_NONE)) {

	    /*
	     * Message content.
	     */
	case REC_TYPE_NORM:
	case REC_TYPE_CONT:
	    if (smtpd_proxy_rec_put(proxy->service_stream, rec_type,
				    STR(replay_buf), LEN(replay_buf)) < 0)
		return (-1);
	    break;

	    /*
	     * Expected server reply type.
	     */
	case REC_TYPE_RCPT:
	    if (!alldig(STR(replay_buf))
		|| (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD)
		msg_panic("%s: malformed server reply type: %s",
			  myname, STR(replay_buf));
	    break;

	    /*
	     * Client command, or void. Bail out on the first negative proxy
	     * response. This is OK, because the filter must use the same
	     * reply code for all recipients of a multi-recipient message.
	     */
	case REC_TYPE_FROM:
	    if (expect == SMTPD_PROX_WANT_BAD)
		msg_panic("%s: missing server reply type", myname);
	    if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" :
				SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0)
		return (-1);
	    expect = SMTPD_PROX_WANT_BAD;
	    break;

	    /*
	     * Explicit end marker, instead of implicit EOF.
	     */
	case REC_TYPE_END:
	    return (0);

	    /*
	     * Errors.
	     */
	case REC_TYPE_ERROR:
	    return (smtpd_proxy_replay_rdwr_error(state));
	default:
	    msg_panic("%s: unexpected record type; %d", myname, rec_type);
	}
    }
}
Exemple #5
0
static int smtpd_proxy_connect(SMTPD_STATE *state)
{
    SMTPD_PROXY *proxy = state->proxy;
    int     fd;
    char   *lines;
    char   *words;
    VSTRING *buf;
    int     bad;
    char   *word;
    static const NAME_CODE known_xforward_features[] = {
	XFORWARD_NAME, SMTPD_PROXY_XFORWARD_NAME,
	XFORWARD_ADDR, SMTPD_PROXY_XFORWARD_ADDR,
	XFORWARD_PORT, SMTPD_PROXY_XFORWARD_PORT,
	XFORWARD_PROTO, SMTPD_PROXY_XFORWARD_PROTO,
	XFORWARD_HELO, SMTPD_PROXY_XFORWARD_HELO,
	XFORWARD_IDENT, SMTPD_PROXY_XFORWARD_IDENT,
	XFORWARD_DOMAIN, SMTPD_PROXY_XFORWARD_DOMAIN,
	0, 0,
    };
    int     server_xforward_features;
    int     (*connect_fn) (const char *, int, int);
    const char *endpoint;

    /*
     * Find connection method (default inet)
     */
    if (strncasecmp("unix:", proxy->service_name, 5) == 0) {
	endpoint = proxy->service_name + 5;
	connect_fn = unix_connect;
    } else {
	if (strncasecmp("inet:", proxy->service_name, 5) == 0)
	    endpoint = proxy->service_name + 5;
	else
	    endpoint = proxy->service_name;
	connect_fn = inet_connect;
    }

    /*
     * Connect to proxy.
     */
    if ((fd = connect_fn(endpoint, BLOCKING, proxy->timeout)) < 0) {
	msg_warn("connect to proxy filter %s: %m", proxy->service_name);
	return (smtpd_proxy_rdwr_error(state, 0));
    }
    proxy->service_stream = vstream_fdopen(fd, O_RDWR);
    /* Needed by our DATA-phase record emulation routines. */
    vstream_control(proxy->service_stream, VSTREAM_CTL_CONTEXT,
		    (char *) state, VSTREAM_CTL_END);
    /* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */
    if (connect_fn == inet_connect)
	vstream_tweak_tcp(proxy->service_stream);
    smtp_timeout_setup(proxy->service_stream, proxy->timeout);

    /*
     * Get server greeting banner.
     * 
     * If this fails then we have a problem because the proxy should always
     * accept our connection. Make up our own response instead of passing
     * back a negative greeting banner: the proxy open is delayed to the
     * point that the client expects a MAIL FROM or RCPT TO reply.
     */
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, SMTPD_PROXY_CONN_FMT)) {
	smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
	smtpd_proxy_close(state);
	return (-1);
    }

    /*
     * Send our own EHLO command. If this fails then we have a problem
     * because the proxy should always accept our EHLO command. Make up our
     * own response instead of passing back a negative EHLO reply: the proxy
     * open is delayed to the point that the remote SMTP client expects a
     * MAIL FROM or RCPT TO reply.
     */
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "EHLO %s",
			proxy->ehlo_name)) {
	smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
	smtpd_proxy_close(state);
	return (-1);
    }

    /*
     * Parse the EHLO reply and see if we can forward logging information.
     */
    server_xforward_features = 0;
    lines = STR(proxy->reply);
    while ((words = mystrtok(&lines, "\n")) != 0) {
	if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
	    if (strcasecmp(word, XFORWARD_CMD) == 0)
		while ((word = mystrtok(&words, " \t")) != 0)
		    server_xforward_features |=
			name_code(known_xforward_features,
				  NAME_CODE_FLAG_NONE, word);
	}
    }

    /*
     * Send XFORWARD attributes. For robustness, explicitly specify what SMTP
     * session attributes are known and unknown. Make up our own response
     * instead of passing back a negative XFORWARD reply: the proxy open is
     * delayed to the point that the remote SMTP client expects a MAIL FROM
     * or RCPT TO reply.
     */
    if (server_xforward_features) {
	buf = vstring_alloc(100);
	bad =
	    (((server_xforward_features & SMTPD_PROXY_XFORWARD_NAME)
	      && smtpd_proxy_xforward_send(state, buf, XFORWARD_NAME,
				  IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)),
					   FORWARD_NAME(state)))
	     || ((server_xforward_features & SMTPD_PROXY_XFORWARD_ADDR)
		 && smtpd_proxy_xforward_send(state, buf, XFORWARD_ADDR,
				  IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
					      FORWARD_ADDR(state)))
	     || ((server_xforward_features & SMTPD_PROXY_XFORWARD_PORT)
		 && smtpd_proxy_xforward_send(state, buf, XFORWARD_PORT,
				  IS_AVAIL_CLIENT_PORT(FORWARD_PORT(state)),
					      FORWARD_PORT(state)))
	     || ((server_xforward_features & SMTPD_PROXY_XFORWARD_HELO)
		 && smtpd_proxy_xforward_send(state, buf, XFORWARD_HELO,
				  IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)),
					      FORWARD_HELO(state)))
	     || ((server_xforward_features & SMTPD_PROXY_XFORWARD_IDENT)
		 && smtpd_proxy_xforward_send(state, buf, XFORWARD_IDENT,
				IS_AVAIL_CLIENT_IDENT(FORWARD_IDENT(state)),
					      FORWARD_IDENT(state)))
	     || ((server_xforward_features & SMTPD_PROXY_XFORWARD_PROTO)
		 && smtpd_proxy_xforward_send(state, buf, XFORWARD_PROTO,
				IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)),
					      FORWARD_PROTO(state)))
	     || ((server_xforward_features & SMTPD_PROXY_XFORWARD_DOMAIN)
		 && smtpd_proxy_xforward_send(state, buf, XFORWARD_DOMAIN, 1,
			 STREQ(FORWARD_DOMAIN(state), MAIL_ATTR_RWR_LOCAL) ?
				  XFORWARD_DOM_LOCAL : XFORWARD_DOM_REMOTE))
	     || smtpd_proxy_xforward_flush(state, buf));
	vstring_free(buf);
	if (bad) {
	    smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
	    smtpd_proxy_close(state);
	    return (-1);
	}
    }

    /*
     * Pass-through the remote SMTP client's MAIL FROM command. If this
     * fails, then we have a problem because the proxy should always accept
     * any MAIL FROM command that was accepted by us.
     */
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "%s",
			proxy->mail_from) != 0) {
	/* NOT: smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY); */
	smtpd_proxy_close(state);
	return (-1);
    }
    return (0);
}
Exemple #6
0
static int smtpd_proxy_replay_send(SMTPD_STATE *state)
{
    const char *myname = "smtpd_proxy_replay_send";
    static VSTRING *replay_buf = 0;
    SMTPD_PROXY *proxy = state->proxy;
    int     rec_type;
    int     expect = SMTPD_PROX_WANT_BAD;

    /*
     * Sanity check.
     */
    if (smtpd_proxy_replay_stream == 0)
	msg_panic("%s: no before-queue filter speed-adjust log", myname);

    /*
     * Errors first.
     */
    if (vstream_ferror(smtpd_proxy_replay_stream)
	|| vstream_feof(smtpd_proxy_replay_stream)
	|| rec_put(smtpd_proxy_replay_stream, REC_TYPE_END, "", 0) != REC_TYPE_END
	|| vstream_fflush(smtpd_proxy_replay_stream))
	/* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */
	return (smtpd_proxy_replay_rdwr_error(state));

    /*
     * Delayed connection to the before-queue filter.
     */
    if (smtpd_proxy_connect(state) < 0)
	return (-1);

    /*
     * Replay the speed-match log. We do sanity check record content, but we
     * don't implement a protocol state engine here, since we are reading
     * from a file that we just wrote ourselves.
     */
    if (replay_buf == 0)
	replay_buf = vstring_alloc(100);
    if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0)
	return (smtpd_proxy_replay_rdwr_error(state));

    for (;;) {
	switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf,
				   REC_FLAG_NONE)) {

	    /*
	     * Message content.
	     */
	case REC_TYPE_NORM:
	case REC_TYPE_CONT:
	    if (smtpd_proxy_rec_put(proxy->service_stream, rec_type,
				    STR(replay_buf), LEN(replay_buf)) < 0)
		return (-1);
	    break;

	    /*
	     * Expected server reply type.
	     */
	case REC_TYPE_RCPT:
	    if (!alldig(STR(replay_buf))
		|| (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD)
		msg_panic("%s: malformed server reply type: %s",
			  myname, STR(replay_buf));
	    break;

	    /*
	     * Client command, or void. Bail out on the first negative proxy
	     * response. This is OK, because the filter must use the same
	     * reply code for all recipients of a multi-recipient message.
	     */
	case REC_TYPE_FROM:
	    if (expect == SMTPD_PROX_WANT_BAD)
		msg_panic("%s: missing server reply type", myname);
	    if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" :
				SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0)
		return (-1);
	    expect = SMTPD_PROX_WANT_BAD;
	    break;

	    /*
	     * Explicit end marker, instead of implicit EOF.
	     */
	case REC_TYPE_END:
	    return (0);

	    /*
	     * Errors.
	     */
	case REC_TYPE_ERROR:
	    return (smtpd_proxy_replay_rdwr_error(state));
	default:
	    msg_panic("%s: unexpected record type; %d", myname, rec_type);
	}
    }
}