Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  smtpd_proxy.c

  Sprache: C
 

/*++
/* NAME
/* smtpd_proxy 3
/* SUMMARY
/* SMTP server pass-through proxy client
/* SYNOPSIS
/* #include <smtpd.h>
/* #include <smtpd_proxy.h>
/*
/* typedef struct {
/* .in +4
/* VSTREAM *stream; /* SMTP proxy or replay log */

/* VSTRING *buffer; /* last SMTP proxy response */
/* /* other fields... */
/* .in -4
/* } SMTPD_PROXY;
/*
/* int smtpd_proxy_create(state, flags, service, timeout,
/* ehlo_name, mail_from)
/* SMTPD_STATE *state;
/* int flags;
/* const char *service;
/* int timeout;
/* const char *ehlo_name;
/* const char *mail_from;
/*
/* int proxy->cmd(state, expect, format, ...)
/* SMTPD_PROXY *proxy;
/* SMTPD_STATE *state;
/* int expect;
/* const char *format;
/*
/* void smtpd_proxy_free(state)
/* SMTPD_STATE *state;
/*
/* int smtpd_proxy_parse_opts(param_name, param_val)
/* const char *param_name;
/* const char *param_val;
/* RECORD-LEVEL ROUTINES
/* int proxy->rec_put(proxy->stream, rec_type, data, len)
/* SMTPD_PROXY *proxy;
/* int rec_type;
/* const char *data;
/* ssize_t len;
/*
/* int proxy->rec_fprintf(proxy->stream, rec_type, format, ...)
/* SMTPD_PROXY *proxy;
/* int rec_type;
/* cont char *format;
/* DESCRIPTION
/* The functions in this module implement a pass-through proxy
/* client.
/*
/* In order to minimize the intrusiveness of pass-through
/* proxying, 1) the proxy server must support the same MAIL
/* FROM/RCPT syntax that Postfix supports, 2) the record-level
/* routines for message content proxying have the same interface
/* as the routines that are used for non-proxied mail.
/*
/* smtpd_proxy_create() takes a description of a before-queue
/* filter.  Depending on flags, it either arranges to buffer
/* up commands and message content until the entire message
/* is received, or it immediately connects to the proxy service,
/* sends EHLO, sends client information with the XFORWARD
/* command if possible, sends the MAIL FROM command, and
/* receives the reply.
/* A non-zero result value means trouble: either the proxy is
/* unavailable, or it did not send the expected reply.
/* All results are reported via the proxy->buffer field in a
/* form that can be sent to the SMTP client.  An unexpected
/* 2xx or 3xx proxy server response is replaced by a generic
/* error response to avoid support problems.
/* In case of error, smtpd_proxy_create() updates the
/* state->error_mask and state->err fields, and leaves the
/* SMTPD_PROXY handle in an unconnected state.  Destroy the
/* handle after reporting the error reply in the proxy->buffer
/* field.
/*
/* proxy->cmd() formats and either buffers up the command and
/* expected response until the entire message is received, or
/* it immediately sends the specified command to the proxy
/* server, and receives the proxy server reply.
/* A non-zero result value means trouble: either the proxy is
/* unavailable, or it did not send the expected reply.
/* All results are reported via the proxy->buffer field in a
/* form that can be sent to the SMTP client.  An unexpected
/* 2xx or 3xx proxy server response is replaced by a generic
/* error response to avoid support problems.
/* In case of error, proxy->cmd() updates the state->error_mask
/* and state->err fields.
/*
/* smtpd_proxy_free() destroys a proxy server handle and resets
/* the state->proxy field.
/*
/* smtpd_proxy_parse_opts() parses main.cf processing options.
/*
/* proxy->rec_put() is a rec_put() clone that either buffers
/* up arbitrary message content records until the entire message
/* is received, or that immediately sends it to the proxy
/* server.
/* All data is expected to be in SMTP dot-escaped form.
/* All errors are reported as a REC_TYPE_ERROR result value,
/* with the state->error_mask, state->err and proxy-buffer
/* fields given appropriate values.
/*
/* proxy->rec_fprintf() is a rec_fprintf() clone that formats
/* message content and either buffers up the record until the
/* entire message is received, or that immediately sends it
/* to the proxy server.
/* All data is expected to be in SMTP dot-escaped form.
/* All errors are reported as a REC_TYPE_ERROR result value,
/* with the state->error_mask, state->err and proxy-buffer
/* fields given appropriate values.
/*
/* Arguments:
/* .IP flags
/* Zero or more of the following:
/* .RS
/* .IP SMTPD_PROXY_FLAG_SPEED_ADJUST
/* Buffer up the entire message before contacting a before-queue
/* content filter.
/* Note: when this feature is requested, the before-queue
/* filter MUST use the same 2xx, 4xx or 5xx reply code for all
/* recipients of a multi-recipient message.
/* .IP SMTPD_PROXY_FLAG_REQTLS_HDR
/* Add a "Require-TLS-ESMTP: yes" header if one is not already
/* present.
/* .RE
/* .IP server
/* The SMTP proxy server host:port. The host or host: part is optional.
/* This argument is not duplicated.
/* .IP timeout
/* Time limit for connecting to the proxy server and for
/* sending and receiving proxy server commands and replies.
/* .IP ehlo_name
/* The EHLO Hostname that will be sent to the proxy server.
/* This argument is not duplicated.
/* .IP mail_from
/* The MAIL FROM command. This argument is not duplicated.
/* .IP state
/* SMTP server state.
/* .IP expect
/* Expected proxy server reply status code range. A warning is logged
/* when an unexpected reply is received. Specify one of the following:
/* .RS
/* .IP SMTPD_PROX_WANT_OK
/* The caller expects a reply in the 200 range.
/* .IP SMTPD_PROX_WANT_MORE
/* The caller expects a reply in the 300 range.
/* .IP SMTPD_PROX_WANT_ANY
/* The caller has no expectation. Do not warn for unexpected replies.
/* .IP SMTPD_PROX_WANT_NONE
/* Do not bother waiting for a reply.
/* .RE
/* .IP format
/* A format string.
/* .IP stream
/* Connection to proxy server.
/* .IP data
/* Pointer to the content of one message content record.
/* .IP len
/* The length of a message content record.
/* SEE ALSO
/* smtpd(8) Postfix smtp server
/* DIAGNOSTICS
/* Panic: internal API violations.
/*
/* Fatal errors: memory allocation problem.
/*
/* Warnings: unexpected response from proxy server, unable
/* to connect to proxy server, proxy server read/write error,
/* proxy speed-adjust buffer read/write error.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/


/* System library. */

#include <sys_defs.h>
#include <ctype.h>
#include <unistd.h>

#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif

/* Utility library. */

#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <stringops.h>
#include <connect.h>
#include <name_code.h>
#include <mymalloc.h>

/* Global library. */

#include <mail_error.h>
#include <smtp_stream.h>
#include <cleanup_user.h>
#include <mail_params.h>
#include <rec_type.h>
#include <mail_proto.h>
#include <xtext.h>
#include <record.h>
#include <mail_queue.h>
#include <is_header.h>
#include <header_opts.h>

/* Application-specific. */

#include <smtpd.h>
#include <smtpd_proxy.h>

 /*
  * XFORWARD server features, recognized by the pass-through proxy client.
  */

#define SMTPD_PROXY_XFORWARD_NAME  (1<<0/* client name */
#define SMTPD_PROXY_XFORWARD_ADDR  (1<<1/* client address */
#define SMTPD_PROXY_XFORWARD_PROTO (1<<2/* protocol */
#define SMTPD_PROXY_XFORWARD_HELO  (1<<3/* client helo */
#define SMTPD_PROXY_XFORWARD_IDENT (1<<4/* message identifier */
#define SMTPD_PROXY_XFORWARD_DOMAIN (1<<5/* origin type */
#define SMTPD_PROXY_XFORWARD_PORT  (1<<6/* client port */

 /*
  * Spead-matching: we use an unlinked file for transient storage.
  */

static VSTREAM *smtpd_proxy_replay_stream;

 /*
  * Forward declarations.
  */

static void smtpd_proxy_fake_server_reply(SMTPD_STATE *, int);
static int smtpd_proxy_rdwr_error(SMTPD_STATE *, int);
static int PRINTFLIKE(34) smtpd_proxy_cmd(SMTPD_STATE *, intconst char *,...);
static int smtpd_proxy_rec_put(VSTREAM *, intconst char *, ssize_t);

 /*
  * SLMs.
  */

#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
#define STREQ(x, y) (strcmp((x), (y)) == 0)

/* smtpd_proxy_xforward_flush - flush forwarding information */

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);
}

/* smtpd_proxy_xforward_send - send forwarding information */

static int smtpd_proxy_xforward_send(SMTPD_STATE *state, VSTRING *buf,
                 const char *name,
                 int value_available,
                 const char *value)
{
    size_t  new_len;
    int     ret;

#define CONSTR_LEN(s) (sizeof(s) - 1)
#define PAYLOAD_LIMIT (512 - CONSTR_LEN("250 " XFORWARD_CMD "\r\n"))

    if (!value_available)
 value = XFORWARD_UNAVAILABLE;

    /*
     * Encode the attribute value.
     */

    if (state->expand_buf == 0)
 state->expand_buf = vstring_alloc(100);
    xtext_quote(state->expand_buf, value, "");

    /*
     * How much space does this attribute need? SPACE name = value.
     */

    new_len = strlen(name) + strlen(STR(state->expand_buf)) + 2;
    if (new_len > PAYLOAD_LIMIT)
 msg_warn("%s command payload %s=%.10s... exceeds SMTP protocol limit",
   XFORWARD_CMD, name, value);

    /*
     * Flush the buffer if we need to, and store the attribute.
     */

    if (VSTRING_LEN(buf) > 0 && VSTRING_LEN(buf) + new_len > PAYLOAD_LIMIT)
 if ((ret = smtpd_proxy_xforward_flush(state, buf)) < 0)
     return (ret);
    vstring_sprintf_append(buf, " %s=%s", name, STR(state->expand_buf));

    return (0);
}

/* smtpd_proxy_connect - open proxy connection */

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,
 00,
    };
    int     server_xforward_features;
    int     (*connect_fn) (const char *, intint);
    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,
      CA_VSTREAM_CTL_CONTEXT((void *) state),
      CA_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, "%s""")) {
 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, "\r\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);
}

/* smtpd_proxy_fake_server_reply - produce generic error response */

static void smtpd_proxy_fake_server_reply(SMTPD_STATE *state, int status)
{
    const CLEANUP_STAT_DETAIL *detail;

    /*
     * Either we have no server reply (connection refused), or we have an
     * out-of-protocol server reply, so we make up a generic server error
     * response instead.
     */

    detail = cleanup_stat_detail(status);
    vstring_sprintf(state->proxy->reply,
      "%d %s Error: %s",
      detail->smtp, detail->dsn, detail->text);
}

/* smtpd_proxy_replay_rdwr_error - report replay log I/O error */

static int smtpd_proxy_replay_rdwr_error(SMTPD_STATE *state)
{

    /*
     * Log an appropriate warning message.
     */

    msg_warn("proxy speed-adjust log I/O error: %m");

    /*
     * Set the appropriate flags and server reply.
     */

    state->error_mask |= MAIL_ERROR_RESOURCE;
    /* Update state->err in case we are past the client's DATA command. */
    state->err |= CLEANUP_STAT_PROXY;
    smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
    return (-1);
}

/* smtpd_proxy_rdwr_error - report proxy communication error */

static int smtpd_proxy_rdwr_error(SMTPD_STATE *state, int err)
{
    const char *myname = "smtpd_proxy_rdwr_error";
    SMTPD_PROXY *proxy = state->proxy;

    /*
     * Sanity check.
     */

    if (err != 0 && err != SMTP_ERR_NONE && proxy == 0)
 msg_panic("%s: proxy error %d without proxy handle", myname, err);

    /*
     * Log an appropriate warning message.
     */

    switch (err) {
    case 0:
    case SMTP_ERR_NONE:
 break;
    case SMTP_ERR_EOF:
 msg_warn("lost connection with proxy %s", proxy->service_name);
 break;
    case SMTP_ERR_TIME:
 msg_warn("timeout talking to proxy %s", proxy->service_name);
 break;
    default:
 msg_panic("%s: unknown proxy %s error %d",
    myname, proxy->service_name, err);
    }

    /*
     * Set the appropriate flags and server reply.
     */

    state->error_mask |= MAIL_ERROR_SOFTWARE;
    /* Update state->err in case we are past the client's DATA command. */
    state->err |= CLEANUP_STAT_PROXY;
    smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
    return (-1);
}

/* smtpd_proxy_replay_send - replay saved SMTP session from speed-match log */

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, "%s", 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);
 }
    }
}

/* smtpd_proxy_save_cmd - save SMTP command + expected response to replay log */

static int PRINTFLIKE(34) smtpd_proxy_save_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
{
    va_list ap;

    /*
     * Errors first.
     */

    if (vstream_ferror(smtpd_proxy_replay_stream)
 || vstream_feof(smtpd_proxy_replay_stream))
 return (smtpd_proxy_replay_rdwr_error(state));

    /*
     * Save the expected reply first, so that the replayer can safely
     * overwrite the input buffer with the command.
     */

    rec_fprintf(smtpd_proxy_replay_stream, REC_TYPE_RCPT, "%d", expect);

    /*
     * 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.
     */


    /*
     * Save the command to the replay log, and send it to the before-queue
     * filter after we have received the entire message.
     */

    va_start(ap, fmt);
    rec_vfprintf(smtpd_proxy_replay_stream, REC_TYPE_FROM, fmt, ap);
    va_end(ap);

    /*
     * If we just saved the "." command, replay the log.
     */

    return (strcmp(fmt, ".") ? 0 : smtpd_proxy_replay_send(state));
}

/* smtpd_proxy_cmd - send command to proxy, receive reply */

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));
    }

    /*
     * Format the command.
     */

    va_start(ap, fmt);
    vstring_vsprintf(proxy->request, fmt, ap);
    va_end(ap);

    /*
     * 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 (LEN(proxy->request) > 0) {

 /*
  * 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, LEN(proxy->request) == 0 ?
   "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);
    }
}

/* smtpd_proxy_save_rec_put - save message content to replay log */

static int smtpd_proxy_save_rec_put(VSTREAM *stream, int rec_type,
                const char *data, ssize_t len)
{
    const char *myname = "smtpd_proxy_save_rec_put";
    int     ret;

#define VSTREAM_TO_SMTPD_STATE(s) ((SMTPD_STATE *) vstream_context(s))

    /*
     * Sanity check.
     */

    if (stream == 0)
 msg_panic("%s: attempt to use closed stream", myname);

    /*
     * Send one content record. Errors and results must be as with rec_put().
     */

    if (rec_type == REC_TYPE_NORM || rec_type == REC_TYPE_CONT)
 ret = rec_put(stream, rec_type, data, len);
    else
 msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);

    /*
     * Errors last.
     */

    if (ret != rec_type) {
 (void) smtpd_proxy_replay_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream));
 return (REC_TYPE_ERROR);
    }
    return (rec_type);
}

/* smtpd_proxy_handle_reqtls - propagate or add REQUIRETLS header */

static void smtpd_proxy_handle_reqtls(SMTPD_PROXY *proxy, VSTREAM *stream,
                  const char *data, ssize_t len)
{
    const HEADER_OPTS *hdr_opts;
    const char *cp;

    if (is_header_buf(data, len)) {
 if ((hdr_opts = header_opts_find(data)) != 0
     && hdr_opts->type == HDR_REQTLS_ESMTP) {
     cp = data + strlen(hdr_opts->name) + 1;
     while (cp < data + len && ISSPACE(*cp))
  cp++;
     if (data + len == cp + 3 && strncasecmp(cp, "YES"3) == 0)
  proxy->reqtls_esmtp_hdr_seen += 1;
 }
    } else if (len == 0 || !ISSPACE(data[0])) {
 if (proxy->reqtls_esmtp_hdr_seen == 0)
     smtp_fputs("Require-TLS-ESMTP: yes",
         sizeof("Require-TLS-ESMTP: yes") - 1, stream);
 proxy->flags &= ~SMTPD_PROXY_FLAG_REQTLS_HDR;
    }
}

/* smtpd_proxy_rec_put - send message content, rec_put() clone */

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) {
 SMTPD_PROXY *proxy = VSTREAM_TO_SMTPD_STATE(stream)->proxy;

 if (proxy->flags & SMTPD_PROXY_FLAG_REQTLS_HDR)
     smtpd_proxy_handle_reqtls(proxy, stream, data, len);
 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);
}

/* smtpd_proxy_save_rec_fprintf - save message content to replay log */

static int smtpd_proxy_save_rec_fprintf(VSTREAM *stream, int rec_type,
             const char *fmt,...)
{
    const char *myname = "smtpd_proxy_save_rec_fprintf";
    va_list ap;
    int     ret;

    /*
     * Sanity check.
     */

    if (stream == 0)
 msg_panic("%s: attempt to use closed stream", myname);

    /*
     * Save one content record. Errors and results must be as with
     * rec_fprintf().
     */

    va_start(ap, fmt);
    if (rec_type == REC_TYPE_NORM)
 ret = rec_vfprintf(stream, rec_type, fmt, ap);
    else
 msg_panic("%s: need REC_TYPE_NORM", myname);
    va_end(ap);

    /*
     * Errors last.
     */

    if (ret != rec_type) {
 (void) smtpd_proxy_replay_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream));
 return (REC_TYPE_ERROR);
    }
    return (rec_type);
}

/* smtpd_proxy_rec_fprintf - send message content, rec_fprintf() clone */

static int smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
               const char *fmt,...)
{
    const char *myname = "smtpd_proxy_rec_fprintf";
    va_list ap;
    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_fprintf().
     */

    va_start(ap, fmt);
    if (rec_type == REC_TYPE_NORM)
 smtp_vprintf(stream, fmt, ap);
    else
 msg_panic("%s: need REC_TYPE_NORM", myname);
    va_end(ap);
    return (rec_type);
}

#ifndef NO_TRUNCATE

/* smtpd_proxy_replay_setup - prepare the replay logfile */

static int smtpd_proxy_replay_setup(SMTPD_STATE *state)
{
    const char *myname = "smtpd_proxy_replay_setup";
    off_t   file_offs;

    /*
     * Where possible reuse an existing replay logfile, because creating a
     * file is expensive compared to reading or writing. For security reasons
     * we must truncate the file before reuse. For performance reasons we
     * should truncate the file immediately after the end of a mail
     * transaction. We enforce the security guarantee upon reuse, by
     * requiring that no I/O happened since the file was truncated. This is
     * less expensive than truncating the file redundantly.
     */

    if (smtpd_proxy_replay_stream != 0) {
 /* vstream_ftell() won't invoke the kernel, so all errors are mine. */
 if ((file_offs = vstream_ftell(smtpd_proxy_replay_stream)) != 0)
     msg_panic("%s: bad before-queue filter speed-adjust log offset %lu",
        myname, (unsigned long) file_offs);
 vstream_clearerr(smtpd_proxy_replay_stream);
 if (msg_verbose)
     msg_info("%s: reuse speed-adjust stream fd=%d", myname,
       vstream_fileno(smtpd_proxy_replay_stream));
 /* Here, smtpd_proxy_replay_stream != 0 */
    }

    /*
     * Create a new replay logfile.
     */

    if (smtpd_proxy_replay_stream == 0) {
 smtpd_proxy_replay_stream = mail_queue_enter(MAIL_QUEUE_INCOMING, 0,
           (struct timeval *) 0);
 if (smtpd_proxy_replay_stream == 0)
     return (smtpd_proxy_replay_rdwr_error(state));
 if (unlink(VSTREAM_PATH(smtpd_proxy_replay_stream)) < 0)
     msg_warn("remove before-queue filter speed-adjust log %s: %m",
       VSTREAM_PATH(smtpd_proxy_replay_stream));
 if (msg_verbose)
     msg_info("%s: new speed-adjust stream fd=%d", myname,
       vstream_fileno(smtpd_proxy_replay_stream));
    }

    /*
     * Needed by our DATA-phase record emulation routines.
     */

    vstream_control(smtpd_proxy_replay_stream,
      CA_VSTREAM_CTL_CONTEXT((void *) state),
      CA_VSTREAM_CTL_END);
    return (0);
}

#endif

/* smtpd_proxy_create - set up smtpd proxy handle */

int     smtpd_proxy_create(SMTPD_STATE *state, int flags, const char *service,
              int timeout, const char *ehlo_name,
              const char *mail_from)
{
    SMTPD_PROXY *proxy;

    /*
     * When an operation has many arguments it is safer to use named
     * parameters, and have the compiler enforce the argument count.
     */

#define SMTPD_PROXY_ALLOC(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, \
 a12, a13) \
 ((p) = (SMTPD_PROXY *) mymalloc(sizeof(*(p))), (p)->a1, (p)->a2, \
  (p)->a3, (p)->a4, (p)->a5, (p)->a6, (p)->a7, (p)->a8, (p)->a9, \
  (p)->a10, (p)->a11, (p)->a12, (p)->a13, (p))

    /*
     * Sanity check.
     */

    if (state->proxy != 0)
 msg_panic("smtpd_proxy_create: handle still exists");

    /*
     * Connect to the before-queue filter immediately.
     */

    if ((flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) == 0) {
 state->proxy =
     SMTPD_PROXY_ALLOC(proxy, stream = 0, request = vstring_alloc(10),
         reply = vstring_alloc(10),
         cmd = smtpd_proxy_cmd,
         rec_fprintf = smtpd_proxy_rec_fprintf,
         rec_put = smtpd_proxy_rec_put,
         flags = flags, service_stream = 0,
         service_name = service, timeout = timeout,
         ehlo_name = ehlo_name, mail_from = mail_from,
         reqtls_esmtp_hdr_seen = 0);
 if (smtpd_proxy_connect(state) < 0) {
     /* NOT: smtpd_proxy_free(state); we still need proxy->reply. */
     return (-1);
 }
 proxy->stream = proxy->service_stream;
 return (0);
    }

    /*
     * Connect to the before-queue filter after we receive the entire
     * message. Open the replay logfile early to simplify code. The file is
     * reused for multiple mail transactions, so there is no need to minimize
     * its life time.
     */

    else {
#ifdef NO_TRUNCATE
 msg_panic("smtpd_proxy_create: speed-adjust support is not available");
#else
 if (smtpd_proxy_replay_setup(state) < 0)
     return (-1);
 state->proxy =
     SMTPD_PROXY_ALLOC(proxy, stream = smtpd_proxy_replay_stream,
         request = vstring_alloc(10),
         reply = vstring_alloc(10),
         cmd = smtpd_proxy_save_cmd,
         rec_fprintf = smtpd_proxy_save_rec_fprintf,
         rec_put = smtpd_proxy_save_rec_put,
         flags = flags, service_stream = 0,
         service_name = service, timeout = timeout,
         ehlo_name = ehlo_name, mail_from = mail_from,
         reqtls_esmtp_hdr_seen = 0);
 return (0);
#endif
    }
}

/* smtpd_proxy_close - close proxy connection without destroying handle */

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;
    }
}

/* smtpd_proxy_free - destroy smtpd proxy handle */

void    smtpd_proxy_free(SMTPD_STATE *state)
{
    SMTPD_PROXY *proxy = state->proxy;

    /*
     * Clean up.
     */

    if (proxy->service_stream != 0)
 (void) smtpd_proxy_close(state);
    if (proxy->request != 0)
 vstring_free(proxy->request);
    if (proxy->reply != 0)
 vstring_free(proxy->reply);
    myfree((void *) proxy);
    state->proxy = 0;

    /*
     * Reuse the replay logfile if possible. For security reasons we must
     * truncate the replay logfile before reuse. For performance reasons we
     * should truncate the replay logfile immediately after the end of a mail
     * transaction. We truncate the file here, and enforce the security
     * guarantee by requiring that no I/O happens before the file is reused.
     */

    if (smtpd_proxy_replay_stream == 0)
 return;
    if (vstream_ferror(smtpd_proxy_replay_stream)) {
 /* Errors are already reported. */
 (void) vstream_fclose(smtpd_proxy_replay_stream);
 smtpd_proxy_replay_stream = 0;
 return;
    }
    /* Flush output from aborted transaction before truncating the file!! */
    if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0) {
 msg_warn("seek before-queue filter speed-adjust log: %m");
 (void) vstream_fclose(smtpd_proxy_replay_stream);
 smtpd_proxy_replay_stream = 0;
 return;
    }
    if (ftruncate(vstream_fileno(smtpd_proxy_replay_stream), (off_t) 0) < 0) {
 msg_warn("truncate before-queue filter speed-adjust log: %m");
 (void) vstream_fclose(smtpd_proxy_replay_stream);
 smtpd_proxy_replay_stream = 0;
 return;
    }
}

/* smtpd_proxy_parse_opts - parse main.cf options */

int     smtpd_proxy_parse_opts(const char *param_name, const char *param_val)
{
    static const NAME_MASK proxy_opts_table[] = {
 SMTPD_PROXY_NAME_SPEED_ADJUST, SMTPD_PROXY_FLAG_SPEED_ADJUST,
 00,
    };
    int     flags;

    /*
     * The optional before-filter speed-adjust buffers use disk space.
     * However, we don't know if they compete for storage space with the
     * after-filter queue, so we can't simply bump up the free space
     * requirement to 2.5 * message_size_limit.
     */

    flags = name_mask(param_name, proxy_opts_table, param_val);
    if (flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) {
#ifdef NO_TRUNCATE
 msg_warn("smtpd_proxy %s support is not available",
   SMTPD_PROXY_NAME_SPEED_ADJUST);
 flags &= ~SMTPD_PROXY_FLAG_SPEED_ADJUST;
#endif
    }
    return (flags);
}

Messung V0.5 in Prozent
C=83 H=95 G=88

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142