~ubuntu-branches/ubuntu/lucid/postfix/lucid-security

« back to all changes in this revision

Viewing changes to src/smtpd/smtpd_proxy.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2010-02-17 21:53:13 UTC
  • mfrom: (1.1.25 upstream)
  • Revision ID: james.westby@ubuntu.com-20100217215313-91q95mj9qx1mz2y4
Tags: 2.7.0-1~build1
upload to lucid

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
/*
10
10
/*      typedef struct {
11
11
/* .in +4
12
 
/*              /* other fields... */
13
 
/*              VSTREAM *proxy;         /* connection to SMTP proxy */
14
 
/*              VSTRING *proxy_buffer;  /* last SMTP proxy response */
 
12
/*              VSTREAM *stream;        /* SMTP proxy or replay log */
 
13
/*              VSTRING *buffer;        /* last SMTP proxy response */
15
14
/*              /* other fields... */
16
15
/* .in -4
17
 
/*      } SMTPD_STATE;
 
16
/*      } SMTPD_PROXY;
18
17
/*
19
 
/*      int     smtpd_proxy_open(state, service, timeout, ehlo_name, mail_from)
 
18
/*      int     smtpd_proxy_create(state, flags, service, timeout,
 
19
/*                                      ehlo_name, mail_from)
20
20
/*      SMTPD_STATE *state;
 
21
/*      int     flags;
21
22
/*      const char *service;
22
23
/*      int     timeout;
23
24
/*      const char *ehlo_name;
24
25
/*      const char *mail_from;
25
26
/*
26
 
/*      int     smtpd_proxy_cmd(state, expect, format, ...)
 
27
/*      int     proxy->cmd(state, expect, format, ...)
 
28
/*      SMTPD_PROXY *proxy;
27
29
/*      SMTPD_STATE *state;
28
30
/*      int     expect;
29
 
/*      cont char *format;
30
 
/*
31
 
/*      void    smtpd_proxy_close(state)
32
 
/*      SMTPD_STATE *state;
 
31
/*      const char *format;
 
32
/*
 
33
/*      void    smtpd_proxy_disconnect(state)
 
34
/*      SMTPD_STATE *state;
 
35
/*
 
36
/*      void    smtpd_proxy_free(state)
 
37
/*      SMTPD_STATE *state;
 
38
/*
 
39
/*      int     smtpd_proxy_parse_opts(param_name, param_val)
 
40
/*      const char *param_name;
 
41
/*      const char *param_val;
33
42
/* RECORD-LEVEL ROUTINES
34
 
/*      int     smtpd_proxy_rec_put(stream, rec_type, data, len)
35
 
/*      VSTREAM *stream;
 
43
/*      int     proxy->rec_put(proxy->stream, rec_type, data, len)
 
44
/*      SMTPD_PROXY *proxy;
36
45
/*      int     rec_type;
37
46
/*      const char *data;
38
47
/*      ssize_t len;
39
48
/*
40
 
/*      int     smtpd_proxy_rec_fprintf(stream, rec_type, format, ...)
41
 
/*      VSTREAM *stream;
 
49
/*      int     proxy->rec_fprintf(proxy->stream, rec_type, format, ...)
 
50
/*      SMTPD_PROXY *proxy;
42
51
/*      int     rec_type;
43
52
/*      cont char *format;
44
53
/* DESCRIPTION
45
54
/*      The functions in this module implement a pass-through proxy
46
55
/*      client.
47
56
/*
48
 
/*      In order to minimize the intrusiveness of pass-through proxying, 1) the
49
 
/*      proxy server must support the same MAIL FROM/RCPT syntax that Postfix
50
 
/*      supports, 2) the record-level routines for message content proxying
51
 
/*      have the same interface as the routines that are used for non-proxied
52
 
/*      mail.
53
 
/*
54
 
/*      smtpd_proxy_open() connects to the proxy service, sends EHLO, sends
55
 
/*      client information with the XFORWARD command if possible, sends
56
 
/*      the MAIL FROM command, and receives the reply. A non-zero result means
57
 
/*      trouble: either the proxy is unavailable, or it did not send the
58
 
/*      expected reply.
59
 
/*      The result is reported via the state->proxy_buffer field in a form
60
 
/*      that can be sent to the SMTP client. In case of error, the
61
 
/*      state->error_mask and state->err fields are updated.
62
 
/*      A state->proxy_buffer field is created automatically; this field
63
 
/*      persists beyond the end of a proxy session.
64
 
/*
65
 
/*      smtpd_proxy_cmd() formats and sends the specified command to the
66
 
/*      proxy server, and receives the proxy server reply. A non-zero result
67
 
/*      means trouble: either the proxy is unavailable, or it did not send the
68
 
/*      expected reply.
69
 
/*      All results are reported via the state->proxy_buffer field in a form
70
 
/*      that can be sent to the SMTP client. In case of error, the
71
 
/*      state->error_mask and state->err fields are updated.
72
 
/*
73
 
/*      smtpd_proxy_close() disconnects from a proxy server and resets
74
 
/*      the state->proxy field. The last proxy server reply or error
75
 
/*      description remains available via state->proxy-reply.
76
 
/*
77
 
/*      smtpd_proxy_rec_put() is a rec_put() clone that passes arbitrary
78
 
/*      message content records to the proxy server. The data is expected
79
 
/*      to be in SMTP dot-escaped form. All errors are reported as a
80
 
/*      REC_TYPE_ERROR result value.
81
 
/*
82
 
/*      smtpd_proxy_rec_fprintf() is a rec_fprintf() clone that formats
83
 
/*      message content and sends it to the proxy server. Leading dots are
84
 
/*      not escaped. All errors are reported as a REC_TYPE_ERROR result
85
 
/*      value.
 
57
/*      In order to minimize the intrusiveness of pass-through
 
58
/*      proxying, 1) the proxy server must support the same MAIL
 
59
/*      FROM/RCPT syntax that Postfix supports, 2) the record-level
 
60
/*      routines for message content proxying have the same interface
 
61
/*      as the routines that are used for non-proxied mail.
 
62
/*
 
63
/*      smtpd_proxy_create() takes a description of a before-queue
 
64
/*      filter.  Depending on flags, it either arranges to buffer
 
65
/*      up commands and message content until the entire message
 
66
/*      is received, or it immediately connects to the proxy service,
 
67
/*      sends EHLO, sends client information with the XFORWARD
 
68
/*      command if possible, sends the MAIL FROM command, and
 
69
/*      receives the reply.
 
70
/*      A non-zero result value means trouble: either the proxy is
 
71
/*      unavailable, or it did not send the expected reply.
 
72
/*      All results are reported via the proxy->buffer field in a
 
73
/*      form that can be sent to the SMTP client.  An unexpected
 
74
/*      2xx or 3xx proxy server response is replaced by a generic
 
75
/*      error response to avoid support problems.
 
76
/*      In case of error, smtpd_proxy_create() updates the
 
77
/*      state->error_mask and state->err fields, and leaves the
 
78
/*      SMTPD_PROXY handle in an unconnected state.  Destroy the
 
79
/*      handle after reporting the error reply in the proxy->buffer
 
80
/*      field.
 
81
/*
 
82
/*      proxy->cmd() formats and either buffers up the command and
 
83
/*      expected response until the entire message is received, or
 
84
/*      it immediately sends the specified command to the proxy
 
85
/*      server, and receives the proxy server reply.
 
86
/*      A non-zero result value means trouble: either the proxy is
 
87
/*      unavailable, or it did not send the expected reply.
 
88
/*      All results are reported via the proxy->buffer field in a
 
89
/*      form that can be sent to the SMTP client.  An unexpected
 
90
/*      2xx or 3xx proxy server response is replaced by a generic
 
91
/*      error response to avoid support problems.
 
92
/*      In case of error, proxy->cmd() updates the state->error_mask
 
93
/*      and state->err fields.
 
94
/*
 
95
/*      smtpd_proxy_disconnect() disconnects from a proxy server.
 
96
/*      The last proxy server reply or error description remains
 
97
/*      available via the proxy->buffer field.
 
98
/*
 
99
/*      smtpd_proxy_free() destroys a proxy server handle and resets
 
100
/*      the state->proxy field.
 
101
/*
 
102
/*      smtpd_proxy_parse_opts() parses main.cf processing options.
 
103
/*
 
104
/*      proxy->rec_put() is a rec_put() clone that either buffers
 
105
/*      up arbitrary message content records until the entire message
 
106
/*      is received, or that immediately sends it to the proxy
 
107
/*      server.
 
108
/*      All data is expected to be in SMTP dot-escaped form.
 
109
/*      All errors are reported as a REC_TYPE_ERROR result value,
 
110
/*      with the state->error_mask, state->err and proxy-buffer
 
111
/*      fields given appropriate values.
 
112
/*
 
113
/*      proxy->rec_fprintf() is a rec_fprintf() clone that formats
 
114
/*      message content and either buffers up the record until the
 
115
/*      entire message is received, or that immediately sends it
 
116
/*      to the proxy server.
 
117
/*      All data is expected to be in SMTP dot-escaped form.
 
118
/*      All errors are reported as a REC_TYPE_ERROR result value,
 
119
/*      with the state->error_mask, state->err and proxy-buffer
 
120
/*      fields given appropriate values.
86
121
/*
87
122
/* Arguments:
 
123
/* .IP flags
 
124
/*      Zero, or SMTPD_PROXY_FLAG_SPEED_ADJUST to buffer up the entire
 
125
/*      message before contacting a before-queue content filter.
 
126
/*      Note: when this feature is requested, the before-queue
 
127
/*      filter MUST use the same 2xx, 4xx or 5xx reply code for all
 
128
/*      recipients of a multi-recipient message.
88
129
/* .IP server
89
130
/*      The SMTP proxy server host:port. The host or host: part is optional.
 
131
/*      This argument is not duplicated.
90
132
/* .IP timeout
91
133
/*      Time limit for connecting to the proxy server and for
92
134
/*      sending and receiving proxy server commands and replies.
93
135
/* .IP ehlo_name
94
136
/*      The EHLO Hostname that will be sent to the proxy server.
 
137
/*      This argument is not duplicated.
95
138
/* .IP mail_from
96
 
/*      The MAIL FROM command.
 
139
/*      The MAIL FROM command. This argument is not duplicated.
97
140
/* .IP state
98
141
/*      SMTP server state.
99
142
/* .IP expect
120
163
/* SEE ALSO
121
164
/*      smtpd(8) Postfix smtp server
122
165
/* DIAGNOSTICS
 
166
/*      Panic: internal API violations.
 
167
/*
123
168
/*      Fatal errors: memory allocation problem.
124
169
/*
125
170
/*      Warnings: unexpected response from proxy server, unable
126
 
/*      to connect to proxy server, proxy server read/write error.
 
171
/*      to connect to proxy server, proxy server read/write error,
 
172
/*      proxy speed-adjust buffer read/write error.
127
173
/* LICENSE
128
174
/* .ad
129
175
/* .fi
139
185
 
140
186
#include <sys_defs.h>
141
187
#include <ctype.h>
 
188
#include <unistd.h>
142
189
 
143
190
#ifdef STRCASECMP_IN_STRINGS_H
144
191
#include <strings.h>
152
199
#include <stringops.h>
153
200
#include <connect.h>
154
201
#include <name_code.h>
 
202
#include <mymalloc.h>
155
203
 
156
204
/* Global library. */
157
205
 
163
211
#include <mail_proto.h>
164
212
#include <mail_params.h>                /* null_format_string */
165
213
#include <xtext.h>
 
214
#include <record.h>
 
215
#include <mail_queue.h>
166
216
 
167
217
/* Application-specific. */
168
218
 
181
231
#define SMTPD_PROXY_XFORWARD_PORT  (1<<6)       /* client port */
182
232
 
183
233
 /*
 
234
  * Spead-matching: we use an unlinked file for transient storage.
 
235
  */
 
236
static VSTREAM *smtpd_proxy_replay_stream;
 
237
 
 
238
 /*
 
239
  * Forward declarations.
 
240
  */
 
241
static void smtpd_proxy_fake_server_reply(SMTPD_STATE *, int);
 
242
static int smtpd_proxy_rdwr_error(SMTPD_STATE *, int);
 
243
static int smtpd_proxy_cmd(SMTPD_STATE *, int, const char *,...);
 
244
static int smtpd_proxy_rec_put(VSTREAM *, int, const char *, ssize_t);
 
245
 
 
246
 /*
184
247
  * SLMs.
185
248
  */
186
249
#define STR(x)  vstring_str(x)
187
250
#define LEN(x)  VSTRING_LEN(x)
188
 
#define SMTPD_PROXY_CONNECT null_format_string
 
251
#define SMTPD_PROXY_CONN_FMT null_format_string
189
252
#define STREQ(x, y)     (strcmp((x), (y)) == 0)
190
253
 
191
 
/* smtpd_xforward_flush - flush forwarding information */
 
254
/* smtpd_proxy_xforward_flush - flush forwarding information */
192
255
 
193
 
static int smtpd_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
 
256
static int smtpd_proxy_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
194
257
{
195
258
    int     ret;
196
259
 
203
266
    return (0);
204
267
}
205
268
 
206
 
/* smtpd_xforward - send forwarding information */
 
269
/* smtpd_proxy_xforward_send - send forwarding information */
207
270
 
208
 
static int smtpd_xforward(SMTPD_STATE *state, VSTRING *buf, const char *name,
209
 
                                  int value_available, const char *value)
 
271
static int smtpd_proxy_xforward_send(SMTPD_STATE *state, VSTRING *buf,
 
272
                                             const char *name,
 
273
                                             int value_available,
 
274
                                             const char *value)
210
275
{
211
276
    size_t  new_len;
212
277
    int     ret;
236
301
     * Flush the buffer if we need to, and store the attribute.
237
302
     */
238
303
    if (VSTRING_LEN(buf) > 0 && VSTRING_LEN(buf) + new_len > PAYLOAD_LIMIT)
239
 
        if ((ret = smtpd_xforward_flush(state, buf)) < 0)
 
304
        if ((ret = smtpd_proxy_xforward_flush(state, buf)) < 0)
240
305
            return (ret);
241
306
    vstring_sprintf_append(buf, " %s=%s", name, STR(state->expand_buf));
242
307
 
243
308
    return (0);
244
309
}
245
310
 
246
 
/* smtpd_proxy_open - open proxy connection */
 
311
/* smtpd_proxy_connect - open proxy connection */
247
312
 
248
 
int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
249
 
                                 int timeout, const char *ehlo_name,
250
 
                                 const char *mail_from)
 
313
static int smtpd_proxy_connect(SMTPD_STATE *state)
251
314
{
 
315
    SMTPD_PROXY *proxy = state->proxy;
252
316
    int     fd;
253
317
    char   *lines;
254
318
    char   *words;
255
319
    VSTRING *buf;
256
320
    int     bad;
257
321
    char   *word;
258
 
    static const NAME_CODE xforward_features[] = {
 
322
    static const NAME_CODE known_xforward_features[] = {
259
323
        XFORWARD_NAME, SMTPD_PROXY_XFORWARD_NAME,
260
324
        XFORWARD_ADDR, SMTPD_PROXY_XFORWARD_ADDR,
261
325
        XFORWARD_PORT, SMTPD_PROXY_XFORWARD_PORT,
264
328
        XFORWARD_DOMAIN, SMTPD_PROXY_XFORWARD_DOMAIN,
265
329
        0, 0,
266
330
    };
267
 
    const CLEANUP_STAT_DETAIL *detail;
 
331
    int     server_xforward_features;
268
332
    int     (*connect_fn) (const char *, int, int);
269
333
    const char *endpoint;
270
334
 
271
335
    /*
272
 
     * This buffer persists beyond the end of a proxy session so we can
273
 
     * inspect the last command's reply.
274
 
     */
275
 
    if (state->proxy_buffer == 0)
276
 
        state->proxy_buffer = vstring_alloc(10);
277
 
 
278
 
    /*
279
336
     * Find connection method (default inet)
280
337
     */
281
 
    if (strncasecmp("unix:", service, 5) == 0) {
282
 
        endpoint = service + 5;
 
338
    if (strncasecmp("unix:", proxy->service_name, 5) == 0) {
 
339
        endpoint = proxy->service_name + 5;
283
340
        connect_fn = unix_connect;
284
341
    } else {
285
 
        if (strncasecmp("inet:", service, 5) == 0)
286
 
            endpoint = service + 5;
 
342
        if (strncasecmp("inet:", proxy->service_name, 5) == 0)
 
343
            endpoint = proxy->service_name + 5;
287
344
        else
288
 
            endpoint = service;
 
345
            endpoint = proxy->service_name;
289
346
        connect_fn = inet_connect;
290
347
    }
291
348
 
292
349
    /*
293
350
     * Connect to proxy.
294
351
     */
295
 
    if ((fd = connect_fn(endpoint, BLOCKING, timeout)) < 0) {
296
 
        state->error_mask |= MAIL_ERROR_SOFTWARE;
297
 
        state->err |= CLEANUP_STAT_PROXY;
298
 
        msg_warn("connect to proxy service %s: %m", service);
299
 
        detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
300
 
        vstring_sprintf(state->proxy_buffer,
301
 
                        "%d %s Error: %s",
302
 
                        detail->smtp, detail->dsn, detail->text);
303
 
        return (-1);
304
 
    }
305
 
    state->proxy = vstream_fdopen(fd, O_RDWR);
306
 
    vstream_control(state->proxy, VSTREAM_CTL_PATH, service, VSTREAM_CTL_END);
307
 
    smtp_timeout_setup(state->proxy, timeout);
 
352
    if ((fd = connect_fn(endpoint, BLOCKING, proxy->timeout)) < 0)
 
353
        return (smtpd_proxy_rdwr_error(state, 0));
 
354
    proxy->service_stream = vstream_fdopen(fd, O_RDWR);
 
355
    /* Needed by our DATA-phase record emulation routines. */
 
356
    vstream_control(proxy->service_stream, VSTREAM_CTL_CONTEXT,
 
357
                    (char *) state, VSTREAM_CTL_END);
 
358
    smtp_timeout_setup(proxy->service_stream, proxy->timeout);
308
359
 
309
360
    /*
310
361
     * Get server greeting banner.
311
362
     * 
312
363
     * If this fails then we have a problem because the proxy should always
313
364
     * accept our connection. Make up our own response instead of passing
314
 
     * back the greeting banner: the proxy open might be delayed to the point
315
 
     * that the client expects a MAIL FROM or RCPT TO reply.
 
365
     * back a negative greeting banner: the proxy open is delayed to the
 
366
     * point that the client expects a MAIL FROM or RCPT TO reply.
316
367
     */
317
 
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, SMTPD_PROXY_CONNECT) != 0) {
318
 
        detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
319
 
        vstring_sprintf(state->proxy_buffer,
320
 
                        "%d %s Error: %s",
321
 
                        detail->smtp, detail->dsn, detail->text);
 
368
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, SMTPD_PROXY_CONN_FMT)) {
 
369
        smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
322
370
        smtpd_proxy_close(state);
323
371
        return (-1);
324
372
    }
326
374
    /*
327
375
     * Send our own EHLO command. If this fails then we have a problem
328
376
     * because the proxy should always accept our EHLO command. Make up our
329
 
     * own response instead of passing back the EHLO reply: the proxy open
330
 
     * might be delayed to the point that the client expects a MAIL FROM or
331
 
     * RCPT TO reply.
 
377
     * own response instead of passing back a negative EHLO reply: the proxy
 
378
     * open is delayed to the point that the remote SMTP client expects a
 
379
     * MAIL FROM or RCPT TO reply.
332
380
     */
333
 
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "EHLO %s", ehlo_name) != 0) {
334
 
        detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
335
 
        vstring_sprintf(state->proxy_buffer,
336
 
                        "%d %s Error: %s",
337
 
                        detail->smtp, detail->dsn, detail->text);
 
381
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "EHLO %s",
 
382
                        proxy->ehlo_name)) {
 
383
        smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
338
384
        smtpd_proxy_close(state);
339
385
        return (-1);
340
386
    }
342
388
    /*
343
389
     * Parse the EHLO reply and see if we can forward logging information.
344
390
     */
345
 
    state->proxy_xforward_features = 0;
346
 
    lines = STR(state->proxy_buffer);
 
391
    server_xforward_features = 0;
 
392
    lines = STR(proxy->buffer);
347
393
    while ((words = mystrtok(&lines, "\n")) != 0) {
348
394
        if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
349
395
            if (strcasecmp(word, XFORWARD_CMD) == 0)
350
396
                while ((word = mystrtok(&words, " \t")) != 0)
351
 
                    state->proxy_xforward_features |=
352
 
                        name_code(xforward_features, NAME_CODE_FLAG_NONE, word);
 
397
                    server_xforward_features |=
 
398
                        name_code(known_xforward_features,
 
399
                                  NAME_CODE_FLAG_NONE, word);
353
400
        }
354
401
    }
355
402
 
356
403
    /*
357
404
     * Send XFORWARD attributes. For robustness, explicitly specify what SMTP
358
 
     * session attributes are known and unknown.
 
405
     * session attributes are known and unknown. Make up our own response
 
406
     * instead of passing back a negative XFORWARD reply: the proxy open is
 
407
     * delayed to the point that the remote SMTP client expects a MAIL FROM
 
408
     * or RCPT TO reply.
359
409
     */
360
 
    if (state->proxy_xforward_features) {
 
410
    if (server_xforward_features) {
361
411
        buf = vstring_alloc(100);
362
 
        bad = 0;
363
 
        if (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_NAME)
364
 
            bad = smtpd_xforward(state, buf, XFORWARD_NAME,
365
 
                                 IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)),
366
 
                                 FORWARD_NAME(state));
367
 
        if (bad == 0
368
 
            && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_ADDR))
369
 
            bad = smtpd_xforward(state, buf, XFORWARD_ADDR,
370
 
                                 IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
371
 
                                 FORWARD_ADDR(state));
372
 
        if (bad == 0
373
 
            && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_PORT))
374
 
            bad = smtpd_xforward(state, buf, XFORWARD_PORT,
375
 
                                 IS_AVAIL_CLIENT_PORT(FORWARD_PORT(state)),
376
 
                                 FORWARD_PORT(state));
377
 
        if (bad == 0
378
 
            && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_HELO))
379
 
            bad = smtpd_xforward(state, buf, XFORWARD_HELO,
380
 
                                 IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)),
381
 
                                 FORWARD_HELO(state));
382
 
        if (bad == 0
383
 
            && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_PROTO))
384
 
            bad = smtpd_xforward(state, buf, XFORWARD_PROTO,
385
 
                                 IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)),
386
 
                                 FORWARD_PROTO(state));
387
 
        if (bad == 0
388
 
          && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_DOMAIN))
389
 
            bad = smtpd_xforward(state, buf, XFORWARD_DOMAIN, 1,
 
412
        bad =
 
413
            (((server_xforward_features & SMTPD_PROXY_XFORWARD_NAME)
 
414
              && smtpd_proxy_xforward_send(state, buf, XFORWARD_NAME,
 
415
                                  IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)),
 
416
                                           FORWARD_NAME(state)))
 
417
             || ((server_xforward_features & SMTPD_PROXY_XFORWARD_ADDR)
 
418
                 && smtpd_proxy_xforward_send(state, buf, XFORWARD_ADDR,
 
419
                                  IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
 
420
                                              FORWARD_ADDR(state)))
 
421
             || ((server_xforward_features & SMTPD_PROXY_XFORWARD_PORT)
 
422
                 && smtpd_proxy_xforward_send(state, buf, XFORWARD_PORT,
 
423
                                  IS_AVAIL_CLIENT_PORT(FORWARD_PORT(state)),
 
424
                                              FORWARD_PORT(state)))
 
425
             || ((server_xforward_features & SMTPD_PROXY_XFORWARD_HELO)
 
426
                 && smtpd_proxy_xforward_send(state, buf, XFORWARD_HELO,
 
427
                                  IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)),
 
428
                                              FORWARD_HELO(state)))
 
429
             || ((server_xforward_features & SMTPD_PROXY_XFORWARD_PROTO)
 
430
                 && smtpd_proxy_xforward_send(state, buf, XFORWARD_PROTO,
 
431
                                IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)),
 
432
                                              FORWARD_PROTO(state)))
 
433
             || ((server_xforward_features & SMTPD_PROXY_XFORWARD_DOMAIN)
 
434
                 && smtpd_proxy_xforward_send(state, buf, XFORWARD_DOMAIN, 1,
390
435
                         STREQ(FORWARD_DOMAIN(state), MAIL_ATTR_RWR_LOCAL) ?
391
 
                                 XFORWARD_DOM_LOCAL : XFORWARD_DOM_REMOTE);
392
 
        if (bad == 0)
393
 
            bad = smtpd_xforward_flush(state, buf);
 
436
                                  XFORWARD_DOM_LOCAL : XFORWARD_DOM_REMOTE))
 
437
             || smtpd_proxy_xforward_flush(state, buf));
394
438
        vstring_free(buf);
395
439
        if (bad) {
396
 
            detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
397
 
            vstring_sprintf(state->proxy_buffer,
398
 
                            "%d %s Error: %s",
399
 
                            detail->smtp, detail->dsn, detail->text);
 
440
            smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
400
441
            smtpd_proxy_close(state);
401
442
            return (-1);
402
443
        }
403
444
    }
404
445
 
405
446
    /*
406
 
     * Pass-through the client's MAIL FROM command. If this fails, then we
407
 
     * have a problem because the proxy should always accept any MAIL FROM
408
 
     * command that was accepted by us.
 
447
     * Pass-through the remote SMTP client's MAIL FROM command. If this
 
448
     * fails, then we have a problem because the proxy should always accept
 
449
     * any MAIL FROM command that was accepted by us.
409
450
     */
410
 
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "%s", mail_from) != 0) {
 
451
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "%s",
 
452
                        proxy->mail_from) != 0) {
 
453
        /* NOT: smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY); */
411
454
        smtpd_proxy_close(state);
412
455
        return (-1);
413
456
    }
414
457
    return (0);
415
458
}
416
459
 
 
460
/* smtpd_proxy_fake_server_reply - produce generic error response */
 
461
 
 
462
static void smtpd_proxy_fake_server_reply(SMTPD_STATE *state, int status)
 
463
{
 
464
    const CLEANUP_STAT_DETAIL *detail;
 
465
 
 
466
    /*
 
467
     * Either we have no server reply (connection refused), or we have an
 
468
     * out-of-protocol server reply, so we make up a generic server error
 
469
     * response instead.
 
470
     */
 
471
    detail = cleanup_stat_detail(status);
 
472
    vstring_sprintf(state->proxy->buffer,
 
473
                    "%d %s Error: %s",
 
474
                    detail->smtp, detail->dsn, detail->text);
 
475
}
 
476
 
 
477
/* smtpd_proxy_replay_rdwr_error - report replay log I/O error */
 
478
 
 
479
static int smtpd_proxy_replay_rdwr_error(SMTPD_STATE *state)
 
480
{
 
481
 
 
482
    /*
 
483
     * Log an appropriate warning message.
 
484
     */
 
485
    msg_warn("proxy speed-adjust log I/O error: %m");
 
486
 
 
487
    /*
 
488
     * Set the appropriate flags and server reply.
 
489
     */
 
490
    state->error_mask |= MAIL_ERROR_RESOURCE;
 
491
    /* Update state->err in case we are past the client's DATA command. */
 
492
    state->err |= CLEANUP_STAT_PROXY;
 
493
    smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
 
494
    return (-1);
 
495
}
 
496
 
417
497
/* smtpd_proxy_rdwr_error - report proxy communication error */
418
498
 
419
 
static int smtpd_proxy_rdwr_error(VSTREAM *stream, int err)
 
499
static int smtpd_proxy_rdwr_error(SMTPD_STATE *state, int err)
420
500
{
 
501
    const char *myname = "smtpd_proxy_rdwr_error";
 
502
    SMTPD_PROXY *proxy = state->proxy;
 
503
 
 
504
    /*
 
505
     * Sanity check.
 
506
     */
 
507
    if (err != 0 && err != SMTP_ERR_NONE && proxy == 0)
 
508
        msg_panic("%s: proxy error %d without proxy handle", myname, err);
 
509
 
 
510
    /*
 
511
     * Log an appropriate warning message.
 
512
     */
421
513
    switch (err) {
422
 
        case SMTP_ERR_EOF:
423
 
        msg_warn("lost connection with proxy %s", VSTREAM_PATH(stream));
424
 
        return (err);
 
514
    case 0:
 
515
    case SMTP_ERR_NONE:
 
516
        break;
 
517
    case SMTP_ERR_EOF:
 
518
        msg_warn("lost connection with proxy %s", proxy->service_name);
 
519
        break;
425
520
    case SMTP_ERR_TIME:
426
 
        msg_warn("timeout talking to proxy %s", VSTREAM_PATH(stream));
427
 
        return (err);
 
521
        msg_warn("timeout talking to proxy %s", proxy->service_name);
 
522
        break;
428
523
    default:
429
 
        msg_panic("smtpd_proxy_rdwr_error: unknown proxy %s stream error %d",
430
 
                  VSTREAM_PATH(stream), err);
431
 
    }
432
 
}
433
 
 
434
 
/* smtpd_proxy_cmd_error - report unexpected proxy reply */
435
 
 
436
 
static void smtpd_proxy_cmd_error(SMTPD_STATE *state, const char *fmt,
437
 
                                          va_list ap)
438
 
{
 
524
        msg_panic("%s: unknown proxy %s error %d",
 
525
                  myname, proxy->service_name, err);
 
526
    }
 
527
 
 
528
    /*
 
529
     * Set the appropriate flags and server reply.
 
530
     */
 
531
    state->error_mask |= MAIL_ERROR_SOFTWARE;
 
532
    /* Update state->err in case we are past the client's DATA command. */
 
533
    state->err |= CLEANUP_STAT_PROXY;
 
534
    smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
 
535
    return (-1);
 
536
}
 
537
 
 
538
/* smtpd_proxy_replay_send - replay saved SMTP session from speed-match log */
 
539
 
 
540
static int smtpd_proxy_replay_send(SMTPD_STATE *state)
 
541
{
 
542
    const char *myname = "smtpd_proxy_replay_send";
 
543
    static VSTRING *replay_buf = 0;
 
544
    SMTPD_PROXY *proxy = state->proxy;
 
545
    int     rec_type;
 
546
    int     expect = SMTPD_PROX_WANT_BAD;
 
547
 
 
548
    /*
 
549
     * Sanity check.
 
550
     */
 
551
    if (smtpd_proxy_replay_stream == 0)
 
552
        msg_panic("%s: no before-queue filter speed-adjust log", myname);
 
553
 
 
554
    /*
 
555
     * Errors first.
 
556
     */
 
557
    if (vstream_ferror(smtpd_proxy_replay_stream)
 
558
        || vstream_feof(smtpd_proxy_replay_stream)
 
559
        || rec_put(smtpd_proxy_replay_stream, REC_TYPE_END, "", 0) != REC_TYPE_END
 
560
        || vstream_fflush(smtpd_proxy_replay_stream))
 
561
        /* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */
 
562
        return (smtpd_proxy_replay_rdwr_error(state));
 
563
 
 
564
    /*
 
565
     * Delayed connection to the before-queue filter.
 
566
     */
 
567
    if (smtpd_proxy_connect(state) < 0)
 
568
        return (-1);
 
569
 
 
570
    /*
 
571
     * Replay the speed-match log. We do sanity check record content, but we
 
572
     * don't implement a protocol state engine here, since we are reading
 
573
     * from a file that we just wrote ourselves.
 
574
     */
 
575
    if (replay_buf == 0)
 
576
        replay_buf = vstring_alloc(100);
 
577
    if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0)
 
578
        return (smtpd_proxy_replay_rdwr_error(state));
 
579
 
 
580
    for (;;) {
 
581
        switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf,
 
582
                                   REC_FLAG_NONE)) {
 
583
 
 
584
            /*
 
585
             * Message content.
 
586
             */
 
587
        case REC_TYPE_NORM:
 
588
        case REC_TYPE_CONT:
 
589
            if (smtpd_proxy_rec_put(proxy->service_stream, rec_type,
 
590
                                    STR(replay_buf), LEN(replay_buf)) < 0)
 
591
                return (-1);
 
592
            break;
 
593
 
 
594
            /*
 
595
             * Expected server reply type.
 
596
             */
 
597
        case REC_TYPE_RCPT:
 
598
            if (!alldig(STR(replay_buf))
 
599
                || (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD)
 
600
                msg_panic("%s: malformed server reply type: %s",
 
601
                          myname, STR(replay_buf));
 
602
            break;
 
603
 
 
604
            /*
 
605
             * Client command, or void. Bail out on the first negative proxy
 
606
             * response. This is OK, because the filter must use the same
 
607
             * reply code for all recipients of a multi-recipient message.
 
608
             */
 
609
        case REC_TYPE_FROM:
 
610
            if (expect == SMTPD_PROX_WANT_BAD)
 
611
                msg_panic("%s: missing server reply type", myname);
 
612
            if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" :
 
613
                                SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0)
 
614
                return (-1);
 
615
            expect = SMTPD_PROX_WANT_BAD;
 
616
            break;
 
617
 
 
618
            /*
 
619
             * Explicit end marker, instead of implicit EOF.
 
620
             */
 
621
        case REC_TYPE_END:
 
622
            return (0);
 
623
 
 
624
            /*
 
625
             * Errors.
 
626
             */
 
627
        case REC_TYPE_ERROR:
 
628
            return (smtpd_proxy_replay_rdwr_error(state));
 
629
        default:
 
630
            msg_panic("%s: unexpected record type; %d", myname, rec_type);
 
631
        }
 
632
    }
 
633
}
 
634
 
 
635
/* smtpd_proxy_save_cmd - save SMTP command + expected response to replay log */
 
636
 
 
637
static int smtpd_proxy_save_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
 
638
{
 
639
    va_list ap;
 
640
 
 
641
    /*
 
642
     * Errors first.
 
643
     */
 
644
    if (vstream_ferror(smtpd_proxy_replay_stream)
 
645
        || vstream_feof(smtpd_proxy_replay_stream))
 
646
        return (smtpd_proxy_replay_rdwr_error(state));
 
647
 
 
648
    /*
 
649
     * Save the expected reply first, so that the replayer can safely
 
650
     * overwrite the input buffer with the command.
 
651
     */
 
652
    rec_fprintf(smtpd_proxy_replay_stream, REC_TYPE_RCPT, "%d", expect);
 
653
 
 
654
    /*
 
655
     * The command can be omitted at the start of an SMTP session. This is
 
656
     * not documented as part of the official interface because it is used
 
657
     * only internally to this module. Use an explicit null string in case
 
658
     * the SMTPD_PROXY_CONN_FMT implementation details change.
 
659
     */
 
660
    if (fmt == SMTPD_PROXY_CONN_FMT)
 
661
        fmt = "";
 
662
 
 
663
    /*
 
664
     * Save the command to the replay log, and send it to the before-queue
 
665
     * filter after we have received the entire message.
 
666
     */
 
667
    va_start(ap, fmt);
 
668
    rec_vfprintf(smtpd_proxy_replay_stream, REC_TYPE_FROM, fmt, ap);
 
669
    va_end(ap);
 
670
 
 
671
    /*
 
672
     * If we just saved the "." command, replay the log.
 
673
     */
 
674
    return (strcmp(fmt, ".") ? 0 : smtpd_proxy_replay_send(state));
 
675
}
 
676
 
 
677
/* smtpd_proxy_cmd_warn - report unexpected proxy reply */
 
678
 
 
679
static void smtpd_proxy_cmd_warn(SMTPD_STATE *state, const char *fmt,
 
680
                                         va_list ap)
 
681
{
 
682
    SMTPD_PROXY *proxy = state->proxy;
439
683
    VSTRING *buf;
440
684
 
441
685
    /*
444
688
     * because it is used only internally to this module.
445
689
     */
446
690
    buf = vstring_alloc(100);
447
 
    vstring_vsprintf(buf, fmt == SMTPD_PROXY_CONNECT ?
 
691
    vstring_vsprintf(buf, fmt == SMTPD_PROXY_CONN_FMT ?
448
692
                     "connection request" : fmt, ap);
449
 
    msg_warn("proxy %s rejected \"%s\": \"%s\"", VSTREAM_PATH(state->proxy),
450
 
             STR(buf), STR(state->proxy_buffer));
 
693
    msg_warn("proxy %s rejected \"%s\": \"%s\"",
 
694
             proxy->service_name, STR(buf), STR(proxy->buffer));
451
695
    vstring_free(buf);
452
696
}
453
697
 
454
698
/* smtpd_proxy_cmd - send command to proxy, receive reply */
455
699
 
456
 
int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
 
700
static int smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
457
701
{
 
702
    SMTPD_PROXY *proxy = state->proxy;
458
703
    va_list ap;
459
704
    char   *cp;
460
705
    int     last_char;
461
706
    int     err = 0;
462
707
    static VSTRING *buffer = 0;
463
 
    const CLEANUP_STAT_DETAIL *detail;
464
708
 
465
709
    /*
466
710
     * Errors first. Be prepared for delayed errors from the DATA phase.
467
711
     */
468
 
    if (vstream_ferror(state->proxy)
469
 
        || vstream_feof(state->proxy)
470
 
        || ((err = vstream_setjmp(state->proxy)) != 0
471
 
            && smtpd_proxy_rdwr_error(state->proxy, err))) {
472
 
        state->error_mask |= MAIL_ERROR_SOFTWARE;
473
 
        state->err |= CLEANUP_STAT_PROXY;
474
 
        detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
475
 
        vstring_sprintf(state->proxy_buffer,
476
 
                        "%d %s Error: %s",
477
 
                        detail->smtp, detail->dsn, detail->text);
478
 
        return (-1);
 
712
    if (vstream_ferror(proxy->service_stream)
 
713
        || vstream_feof(proxy->service_stream)
 
714
        || (err = vstream_setjmp(proxy->service_stream)) != 0) {
 
715
        return (smtpd_proxy_rdwr_error(state, err));
479
716
    }
480
717
 
481
718
    /*
483
720
     * not documented as part of the official interface because it is used
484
721
     * only internally to this module.
485
722
     */
486
 
    if (fmt != SMTPD_PROXY_CONNECT) {
 
723
    if (fmt != SMTPD_PROXY_CONN_FMT) {
487
724
 
488
725
        /*
489
726
         * Format the command.
490
727
         */
491
728
        va_start(ap, fmt);
492
 
        vstring_vsprintf(state->proxy_buffer, fmt, ap);
 
729
        vstring_vsprintf(proxy->buffer, fmt, ap);
493
730
        va_end(ap);
494
731
 
495
732
        /*
497
734
         * what the program is trying to do.
498
735
         */
499
736
        if (msg_verbose)
500
 
            msg_info("> %s: %s", VSTREAM_PATH(state->proxy),
501
 
                     STR(state->proxy_buffer));
 
737
            msg_info("> %s: %s", proxy->service_name, STR(proxy->buffer));
502
738
 
503
739
        /*
504
740
         * Send the command to the proxy server. Since we're going to read a
505
741
         * reply immediately, there is no need to flush buffers.
506
742
         */
507
 
        smtp_fputs(STR(state->proxy_buffer), LEN(state->proxy_buffer),
508
 
                   state->proxy);
 
743
        smtp_fputs(STR(proxy->buffer), LEN(proxy->buffer),
 
744
                   proxy->service_stream);
509
745
    }
510
746
 
511
747
    /*
512
748
     * Early return if we don't want to wait for a server reply (such as
513
 
     * after sending QUIT.
 
749
     * after sending QUIT).
514
750
     */
515
751
    if (expect == SMTPD_PROX_WANT_NONE)
516
752
        return (0);
519
755
     * Censor out non-printable characters in server responses and save
520
756
     * complete multi-line responses if possible.
521
757
     */
522
 
    VSTRING_RESET(state->proxy_buffer);
 
758
    VSTRING_RESET(proxy->buffer);
523
759
    if (buffer == 0)
524
760
        buffer = vstring_alloc(10);
525
761
    for (;;) {
526
 
        last_char = smtp_get(buffer, state->proxy, var_line_limit);
 
762
        last_char = smtp_get(buffer, proxy->service_stream, var_line_limit);
527
763
        printable(STR(buffer), '?');
528
764
        if (last_char != '\n')
529
765
            msg_warn("%s: response longer than %d: %.30s...",
530
 
                     VSTREAM_PATH(state->proxy), var_line_limit,
 
766
                     proxy->service_name, var_line_limit,
531
767
                     STR(buffer));
532
768
        if (msg_verbose)
533
 
            msg_info("< %s: %.100s", VSTREAM_PATH(state->proxy),
534
 
                     STR(buffer));
 
769
            msg_info("< %s: %.100s", proxy->service_name, STR(buffer));
535
770
 
536
771
        /*
537
772
         * Defend against a denial of service attack by limiting the amount
538
773
         * of multi-line text that we are willing to store.
539
774
         */
540
 
        if (LEN(state->proxy_buffer) < var_line_limit) {
541
 
            if (VSTRING_LEN(state->proxy_buffer))
542
 
                VSTRING_ADDCH(state->proxy_buffer, '\n');
543
 
            vstring_strcat(state->proxy_buffer, STR(buffer));
 
775
        if (LEN(proxy->buffer) < var_line_limit) {
 
776
            if (VSTRING_LEN(proxy->buffer))
 
777
                VSTRING_ADDCH(proxy->buffer, '\n');
 
778
            vstring_strcat(proxy->buffer, STR(buffer));
544
779
        }
545
780
 
546
781
        /*
558
793
                break;
559
794
        }
560
795
        msg_warn("received garbage from proxy %s: %.100s",
561
 
                 VSTREAM_PATH(state->proxy), STR(buffer));
 
796
                 proxy->service_name, STR(buffer));
562
797
    }
563
798
 
564
799
    /*
565
800
     * Log a warning in case the proxy does not send the expected response.
566
801
     * Silently accept any response when the client expressed no expectation.
567
802
     * 
568
 
     * Don't pass through misleading 2xx replies. it confuses naive users and
569
 
     * SMTP clients, and creates support problems.
 
803
     * Starting with Postfix 2.6 we don't pass through unexpected 2xx or 3xx
 
804
     * proxy replies. They are a source of support problems, so we replace
 
805
     * them by generic server error replies.
570
806
     */
571
 
    if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(state->proxy_buffer)) {
 
807
    if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(proxy->buffer)) {
572
808
        va_start(ap, fmt);
573
 
        smtpd_proxy_cmd_error(state, fmt, ap);
 
809
        smtpd_proxy_cmd_warn(state, fmt, ap);
574
810
        va_end(ap);
575
 
        if (*STR(state->proxy_buffer) == SMTPD_PROX_WANT_OK
576
 
            || *STR(state->proxy_buffer) == SMTPD_PROX_WANT_MORE) {
577
 
            state->error_mask |= MAIL_ERROR_SOFTWARE;
578
 
            state->err |= CLEANUP_STAT_PROXY;
579
 
            detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
580
 
            vstring_sprintf(state->proxy_buffer,
581
 
                            "%d %s Error: %s",
582
 
                            detail->smtp, detail->dsn, detail->text);
 
811
        if (*STR(proxy->buffer) == SMTPD_PROX_WANT_OK
 
812
            || *STR(proxy->buffer) == SMTPD_PROX_WANT_MORE) {
 
813
            smtpd_proxy_rdwr_error(state, 0);
583
814
        }
584
815
        return (-1);
585
816
    } else {
587
818
    }
588
819
}
589
820
 
 
821
/* smtpd_proxy_save_rec_put - save message content to replay log */
 
822
 
 
823
static int smtpd_proxy_save_rec_put(VSTREAM *stream, int rec_type,
 
824
                                            const char *data, ssize_t len)
 
825
{
 
826
    const char *myname = "smtpd_proxy_save_rec_put";
 
827
    int     ret;
 
828
 
 
829
#define VSTREAM_TO_SMTPD_STATE(s) ((SMTPD_STATE *) vstream_context(s))
 
830
 
 
831
    /*
 
832
     * Sanity check.
 
833
     */
 
834
    if (stream == 0)
 
835
        msg_panic("%s: attempt to use closed stream", myname);
 
836
 
 
837
    /*
 
838
     * Send one content record. Errors and results must be as with rec_put().
 
839
     */
 
840
    if (rec_type == REC_TYPE_NORM || rec_type == REC_TYPE_CONT)
 
841
        ret = rec_put(stream, rec_type, data, len);
 
842
    else
 
843
        msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);
 
844
 
 
845
    /*
 
846
     * Errors last.
 
847
     */
 
848
    if (ret != rec_type) {
 
849
        (void) smtpd_proxy_replay_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream));
 
850
        return (REC_TYPE_ERROR);
 
851
    }
 
852
    return (rec_type);
 
853
}
 
854
 
590
855
/* smtpd_proxy_rec_put - send message content, rec_put() clone */
591
856
 
592
 
int     smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
593
 
                                    const char *data, ssize_t len)
 
857
static int smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
 
858
                                       const char *data, ssize_t len)
594
859
{
595
 
    int     err;
 
860
    const char *myname = "smtpd_proxy_rec_put";
 
861
    int     err = 0;
596
862
 
597
863
    /*
598
864
     * Errors first.
599
865
     */
600
 
    if (vstream_ferror(stream) || vstream_feof(stream))
 
866
    if (vstream_ferror(stream) || vstream_feof(stream)
 
867
        || (err = vstream_setjmp(stream)) != 0) {
 
868
        (void) smtpd_proxy_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream), err);
601
869
        return (REC_TYPE_ERROR);
602
 
    if ((err = vstream_setjmp(stream)) != 0)
603
 
        return (smtpd_proxy_rdwr_error(stream, err), REC_TYPE_ERROR);
 
870
    }
604
871
 
605
872
    /*
606
873
     * Send one content record. Errors and results must be as with rec_put().
610
877
    else if (rec_type == REC_TYPE_CONT)
611
878
        smtp_fwrite(data, len, stream);
612
879
    else
613
 
        msg_panic("smtpd_proxy_rec_put: need REC_TYPE_NORM or REC_TYPE_CONT");
 
880
        msg_panic("%s: need REC_TYPE_NORM or REC_TYPE_CONT", myname);
 
881
    return (rec_type);
 
882
}
 
883
 
 
884
/* smtpd_proxy_save_rec_fprintf - save message content to replay log */
 
885
 
 
886
static int smtpd_proxy_save_rec_fprintf(VSTREAM *stream, int rec_type,
 
887
                                                const char *fmt,...)
 
888
{
 
889
    const char *myname = "smtpd_proxy_save_rec_fprintf";
 
890
    va_list ap;
 
891
    int     ret;
 
892
 
 
893
    /*
 
894
     * Sanity check.
 
895
     */
 
896
    if (stream == 0)
 
897
        msg_panic("%s: attempt to use closed stream", myname);
 
898
 
 
899
    /*
 
900
     * Save one content record. Errors and results must be as with
 
901
     * rec_fprintf().
 
902
     */
 
903
    va_start(ap, fmt);
 
904
    if (rec_type == REC_TYPE_NORM)
 
905
        ret = rec_vfprintf(stream, rec_type, fmt, ap);
 
906
    else
 
907
        msg_panic("%s: need REC_TYPE_NORM", myname);
 
908
    va_end(ap);
 
909
 
 
910
    /*
 
911
     * Errors last.
 
912
     */
 
913
    if (ret != rec_type) {
 
914
        (void) smtpd_proxy_replay_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream));
 
915
        return (REC_TYPE_ERROR);
 
916
    }
614
917
    return (rec_type);
615
918
}
616
919
 
617
920
/* smtpd_proxy_rec_fprintf - send message content, rec_fprintf() clone */
618
921
 
619
 
int     smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
620
 
                                        const char *fmt,...)
 
922
static int smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
 
923
                                           const char *fmt,...)
621
924
{
 
925
    const char *myname = "smtpd_proxy_rec_fprintf";
622
926
    va_list ap;
623
 
    int     err;
 
927
    int     err = 0;
624
928
 
625
929
    /*
626
930
     * Errors first.
627
931
     */
628
 
    if (vstream_ferror(stream) || vstream_feof(stream))
 
932
    if (vstream_ferror(stream) || vstream_feof(stream)
 
933
        || (err = vstream_setjmp(stream)) != 0) {
 
934
        (void) smtpd_proxy_rdwr_error(VSTREAM_TO_SMTPD_STATE(stream), err);
629
935
        return (REC_TYPE_ERROR);
630
 
    if ((err = vstream_setjmp(stream)) != 0)
631
 
        return (smtpd_proxy_rdwr_error(stream, err), REC_TYPE_ERROR);
 
936
    }
632
937
 
633
938
    /*
634
939
     * Send one content record. Errors and results must be as with
638
943
    if (rec_type == REC_TYPE_NORM)
639
944
        smtp_vprintf(stream, fmt, ap);
640
945
    else
641
 
        msg_panic("smtpd_proxy_rec_fprintf: need REC_TYPE_NORM");
 
946
        msg_panic("%s: need REC_TYPE_NORM", myname);
642
947
    va_end(ap);
643
948
    return (rec_type);
644
949
}
645
950
 
646
 
/* smtpd_proxy_close - close proxy connection */
 
951
#ifndef NO_TRUNCATE
 
952
 
 
953
/* smtpd_proxy_replay_setup - prepare the replay logfile */
 
954
 
 
955
static int smtpd_proxy_replay_setup(SMTPD_STATE *state)
 
956
{
 
957
    const char *myname = "smtpd_proxy_replay_setup";
 
958
    off_t   file_offs;
 
959
 
 
960
    /*
 
961
     * Where possible reuse an existing replay logfile, because creating a
 
962
     * file is expensive compared to reading or writing. For security reasons
 
963
     * we must truncate the file before reuse. For performance reasons we
 
964
     * should truncate the file immediately after the end of a mail
 
965
     * transaction. We enforce the security guarantee upon reuse, by
 
966
     * requiring that no I/O happened since the file was truncated. This is
 
967
     * less expensive than truncating the file redundantly.
 
968
     */
 
969
    if (smtpd_proxy_replay_stream != 0) {
 
970
        /* vstream_ftell() won't invoke the kernel, so all errors are mine. */
 
971
        if ((file_offs = vstream_ftell(smtpd_proxy_replay_stream)) != 0)
 
972
            msg_panic("%s: bad before-queue filter speed-adjust log offset %lu",
 
973
                      myname, (unsigned long) file_offs);
 
974
        vstream_clearerr(smtpd_proxy_replay_stream);
 
975
        if (msg_verbose)
 
976
            msg_info("%s: reuse speed-adjust stream fd=%d", myname,
 
977
                     vstream_fileno(smtpd_proxy_replay_stream));
 
978
        /* Here, smtpd_proxy_replay_stream != 0 */
 
979
    }
 
980
 
 
981
    /*
 
982
     * Create a new replay logfile.
 
983
     */
 
984
    if (smtpd_proxy_replay_stream == 0) {
 
985
        smtpd_proxy_replay_stream = mail_queue_enter(MAIL_QUEUE_INCOMING, 0,
 
986
                                                     (struct timeval *) 0);
 
987
        if (smtpd_proxy_replay_stream == 0)
 
988
            return (smtpd_proxy_replay_rdwr_error(state));
 
989
        if (unlink(VSTREAM_PATH(smtpd_proxy_replay_stream)) < 0)
 
990
            msg_warn("remove before-queue filter speed-adjust log %s: %m",
 
991
                     VSTREAM_PATH(smtpd_proxy_replay_stream));
 
992
        if (msg_verbose)
 
993
            msg_info("%s: new speed-adjust stream fd=%d", myname,
 
994
                     vstream_fileno(smtpd_proxy_replay_stream));
 
995
    }
 
996
 
 
997
    /*
 
998
     * Needed by our DATA-phase record emulation routines.
 
999
     */
 
1000
    vstream_control(smtpd_proxy_replay_stream, VSTREAM_CTL_CONTEXT,
 
1001
                    (char *) state, VSTREAM_CTL_END);
 
1002
    return (0);
 
1003
}
 
1004
 
 
1005
#endif
 
1006
 
 
1007
/* smtpd_proxy_create - set up smtpd proxy handle */
 
1008
 
 
1009
int     smtpd_proxy_create(SMTPD_STATE *state, int flags, const char *service,
 
1010
                                   int timeout, const char *ehlo_name,
 
1011
                                   const char *mail_from)
 
1012
{
 
1013
    SMTPD_PROXY *proxy;
 
1014
 
 
1015
    /*
 
1016
     * When an operation has many arguments it is safer to use named
 
1017
     * parameters, and have the compiler enforce the argument count.
 
1018
     */
 
1019
#define SMTPD_PROXY_ALLOC(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) \
 
1020
        ((p) = (SMTPD_PROXY *) mymalloc(sizeof(*(p))), (p)->a1, (p)->a2, \
 
1021
         (p)->a3, (p)->a4, (p)->a5, (p)->a6, (p)->a7, (p)->a8, (p)->a9, \
 
1022
         (p)->a10, (p)->a11, (p))
 
1023
 
 
1024
    /*
 
1025
     * Sanity check.
 
1026
     */
 
1027
    if (state->proxy != 0)
 
1028
        msg_panic("smtpd_proxy_create: handle still exists");
 
1029
 
 
1030
    /*
 
1031
     * Connect to the before-queue filter immediately.
 
1032
     */
 
1033
    if ((flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) == 0) {
 
1034
        state->proxy =
 
1035
            SMTPD_PROXY_ALLOC(proxy, stream = 0, buffer = vstring_alloc(10),
 
1036
                              cmd = smtpd_proxy_cmd,
 
1037
                              rec_fprintf = smtpd_proxy_rec_fprintf,
 
1038
                              rec_put = smtpd_proxy_rec_put,
 
1039
                              flags = flags, service_stream = 0,
 
1040
                              service_name = service, timeout = timeout,
 
1041
                              ehlo_name = ehlo_name, mail_from = mail_from);
 
1042
        if (smtpd_proxy_connect(state) < 0) {
 
1043
            /* NOT: smtpd_proxy_free(state); we still need proxy->buffer. */
 
1044
            return (-1);
 
1045
        }
 
1046
        proxy->stream = proxy->service_stream;
 
1047
        return (0);
 
1048
    }
 
1049
 
 
1050
    /*
 
1051
     * Connect to the before-queue filter after we receive the entire
 
1052
     * message. Open the replay logfile early to simplify code. The file is
 
1053
     * reused for multiple mail transactions, so there is no need to minimize
 
1054
     * its life time.
 
1055
     */
 
1056
    else {
 
1057
#ifdef NO_TRUNCATE
 
1058
        msg_panic("smtpd_proxy_create: speed-adjust support is not available");
 
1059
#else
 
1060
        if (smtpd_proxy_replay_setup(state) < 0)
 
1061
            return (-1);
 
1062
        state->proxy =
 
1063
            SMTPD_PROXY_ALLOC(proxy, stream = smtpd_proxy_replay_stream,
 
1064
                              buffer = vstring_alloc(10),
 
1065
                              cmd = smtpd_proxy_save_cmd,
 
1066
                              rec_fprintf = smtpd_proxy_save_rec_fprintf,
 
1067
                              rec_put = smtpd_proxy_save_rec_put,
 
1068
                              flags = flags, service_stream = 0,
 
1069
                              service_name = service, timeout = timeout,
 
1070
                              ehlo_name = ehlo_name, mail_from = mail_from);
 
1071
        return (0);
 
1072
#endif
 
1073
    }
 
1074
}
 
1075
 
 
1076
/* smtpd_proxy_close - close proxy connection without destroying handle */
647
1077
 
648
1078
void    smtpd_proxy_close(SMTPD_STATE *state)
649
1079
{
650
 
    (void) vstream_fclose(state->proxy);
 
1080
    SMTPD_PROXY *proxy = state->proxy;
 
1081
 
 
1082
    /*
 
1083
     * XXX We can't send QUIT if the stream is still good, because that would
 
1084
     * overwrite the last server reply in proxy->buffer. We probably should
 
1085
     * just bite the bullet and allocate separate buffers for sending and
 
1086
     * receiving.
 
1087
     */
 
1088
    if (proxy->service_stream != 0) {
 
1089
#if 0
 
1090
        if (vstream_feof(proxy->service_stream) == 0
 
1091
            && vstream_ferror(proxy->service_stream) == 0)
 
1092
            (void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE,
 
1093
                                   SMTPD_CMD_QUIT);
 
1094
#endif
 
1095
        (void) vstream_fclose(proxy->service_stream);
 
1096
        if (proxy->stream == proxy->service_stream)
 
1097
            proxy->stream = 0;
 
1098
        proxy->service_stream = 0;
 
1099
    }
 
1100
}
 
1101
 
 
1102
/* smtpd_proxy_free - destroy smtpd proxy handle */
 
1103
 
 
1104
void    smtpd_proxy_free(SMTPD_STATE *state)
 
1105
{
 
1106
    SMTPD_PROXY *proxy = state->proxy;
 
1107
 
 
1108
    /*
 
1109
     * Clean up.
 
1110
     */
 
1111
    if (proxy->service_stream != 0)
 
1112
        (void) smtpd_proxy_close(state);
 
1113
    if (proxy->buffer != 0)
 
1114
        vstring_free(proxy->buffer);
 
1115
    myfree((char *) proxy);
651
1116
    state->proxy = 0;
 
1117
 
 
1118
    /*
 
1119
     * Reuse the replay logfile if possible. For security reasons we must
 
1120
     * truncate the replay logfile before reuse. For performance reasons we
 
1121
     * should truncate the replay logfile immediately after the end of a mail
 
1122
     * transaction. We truncate the file here, and enforce the security
 
1123
     * guarantee by requiring that no I/O happens before the file is reused.
 
1124
     */
 
1125
    if (smtpd_proxy_replay_stream == 0)
 
1126
        return;
 
1127
    if (vstream_ferror(smtpd_proxy_replay_stream)) {
 
1128
        /* Errors are already reported. */
 
1129
        (void) vstream_fclose(smtpd_proxy_replay_stream);
 
1130
        smtpd_proxy_replay_stream = 0;
 
1131
        return;
 
1132
    }
 
1133
    /* Flush output from aborted transaction before truncating the file!! */
 
1134
    if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0) {
 
1135
        msg_warn("seek before-queue filter speed-adjust log: %m");
 
1136
        (void) vstream_fclose(smtpd_proxy_replay_stream);
 
1137
        smtpd_proxy_replay_stream = 0;
 
1138
        return;
 
1139
    }
 
1140
    if (ftruncate(vstream_fileno(smtpd_proxy_replay_stream), (off_t) 0) < 0) {
 
1141
        msg_warn("truncate before-queue filter speed-adjust log: %m");
 
1142
        (void) vstream_fclose(smtpd_proxy_replay_stream);
 
1143
        smtpd_proxy_replay_stream = 0;
 
1144
        return;
 
1145
    }
 
1146
}
 
1147
 
 
1148
/* smtpd_proxy_parse_opts - parse main.cf options */
 
1149
 
 
1150
int     smtpd_proxy_parse_opts(const char *param_name, const char *param_val)
 
1151
{
 
1152
    static const NAME_MASK proxy_opts_table[] = {
 
1153
        SMTPD_PROXY_NAME_SPEED_ADJUST, SMTPD_PROXY_FLAG_SPEED_ADJUST,
 
1154
        0, 0,
 
1155
    };
 
1156
    int     flags;
 
1157
 
 
1158
    /*
 
1159
     * The optional before-filter speed-adjust buffers use disk space.
 
1160
     * However, we don't know if they compete for storage space with the
 
1161
     * after-filter queue, so we can't simply bump up the free space
 
1162
     * requirement to 2.5 * message_size_limit.
 
1163
     */
 
1164
    flags = name_mask(param_name, proxy_opts_table, param_val);
 
1165
    if (flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) {
 
1166
#ifdef NO_TRUNCATE
 
1167
        msg_warn("smtpd_proxy %s support is not available",
 
1168
                 SMTPD_PROXY_NAME_SPEED_ADJUST);
 
1169
        flags &= ~SMTPD_PROXY_FLAG_SPEED_ADJUST;
 
1170
#endif
 
1171
    }
 
1172
    return (flags);
652
1173
}