10
10
/* typedef struct {
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... */
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
22
/* const char *service;
23
24
/* const char *ehlo_name;
24
25
/* const char *mail_from;
26
/* int smtpd_proxy_cmd(state, expect, format, ...)
27
/* int proxy->cmd(state, expect, format, ...)
28
/* SMTPD_PROXY *proxy;
27
29
/* SMTPD_STATE *state;
31
/* void smtpd_proxy_close(state)
32
/* SMTPD_STATE *state;
31
/* const char *format;
33
/* void smtpd_proxy_disconnect(state)
34
/* SMTPD_STATE *state;
36
/* void smtpd_proxy_free(state)
37
/* SMTPD_STATE *state;
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)
43
/* int proxy->rec_put(proxy->stream, rec_type, data, len)
44
/* SMTPD_PROXY *proxy;
37
46
/* const char *data;
40
/* int smtpd_proxy_rec_fprintf(stream, rec_type, format, ...)
49
/* int proxy->rec_fprintf(proxy->stream, rec_type, format, ...)
50
/* SMTPD_PROXY *proxy;
43
52
/* cont char *format;
45
54
/* The functions in this module implement a pass-through proxy
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
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
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.
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
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.
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.
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.
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
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.
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
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.
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.
99
/* smtpd_proxy_free() destroys a proxy server handle and resets
100
/* the state->proxy field.
102
/* smtpd_proxy_parse_opts() parses main.cf processing options.
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
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.
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.
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.
89
130
/* The SMTP proxy server host:port. The host or host: part is optional.
131
/* This argument is not duplicated.
91
133
/* Time limit for connecting to the proxy server and for
92
134
/* sending and receiving proxy server commands and replies.
94
136
/* The EHLO Hostname that will be sent to the proxy server.
137
/* This argument is not duplicated.
96
/* The MAIL FROM command.
139
/* The MAIL FROM command. This argument is not duplicated.
98
141
/* SMTP server state.
264
328
XFORWARD_DOMAIN, SMTPD_PROXY_XFORWARD_DOMAIN,
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;
272
* This buffer persists beyond the end of a proxy session so we can
273
* inspect the last command's reply.
275
if (state->proxy_buffer == 0)
276
state->proxy_buffer = vstring_alloc(10);
279
336
* Find connection method (default inet)
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;
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;
345
endpoint = proxy->service_name;
289
346
connect_fn = inet_connect;
293
350
* Connect to proxy.
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,
302
detail->smtp, detail->dsn, detail->text);
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);
310
361
* Get server greeting banner.
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.
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,
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);
343
389
* Parse the EHLO reply and see if we can forward logging information.
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);
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
360
if (state->proxy_xforward_features) {
410
if (server_xforward_features) {
361
411
buf = vstring_alloc(100);
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));
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));
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));
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));
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));
388
&& (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_DOMAIN))
389
bad = smtpd_xforward(state, buf, XFORWARD_DOMAIN, 1,
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);
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);
396
detail = cleanup_stat_detail(CLEANUP_STAT_PROXY);
397
vstring_sprintf(state->proxy_buffer,
399
detail->smtp, detail->dsn, detail->text);
440
smtpd_proxy_fake_server_reply(state, CLEANUP_STAT_PROXY);
400
441
smtpd_proxy_close(state);
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.
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);
460
/* smtpd_proxy_fake_server_reply - produce generic error response */
462
static void smtpd_proxy_fake_server_reply(SMTPD_STATE *state, int status)
464
const CLEANUP_STAT_DETAIL *detail;
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
471
detail = cleanup_stat_detail(status);
472
vstring_sprintf(state->proxy->buffer,
474
detail->smtp, detail->dsn, detail->text);
477
/* smtpd_proxy_replay_rdwr_error - report replay log I/O error */
479
static int smtpd_proxy_replay_rdwr_error(SMTPD_STATE *state)
483
* Log an appropriate warning message.
485
msg_warn("proxy speed-adjust log I/O error: %m");
488
* Set the appropriate flags and server reply.
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);
417
497
/* smtpd_proxy_rdwr_error - report proxy communication error */
419
static int smtpd_proxy_rdwr_error(VSTREAM *stream, int err)
499
static int smtpd_proxy_rdwr_error(SMTPD_STATE *state, int err)
501
const char *myname = "smtpd_proxy_rdwr_error";
502
SMTPD_PROXY *proxy = state->proxy;
507
if (err != 0 && err != SMTP_ERR_NONE && proxy == 0)
508
msg_panic("%s: proxy error %d without proxy handle", myname, err);
511
* Log an appropriate warning message.
423
msg_warn("lost connection with proxy %s", VSTREAM_PATH(stream));
518
msg_warn("lost connection with proxy %s", proxy->service_name);
425
520
case SMTP_ERR_TIME:
426
msg_warn("timeout talking to proxy %s", VSTREAM_PATH(stream));
521
msg_warn("timeout talking to proxy %s", proxy->service_name);
429
msg_panic("smtpd_proxy_rdwr_error: unknown proxy %s stream error %d",
430
VSTREAM_PATH(stream), err);
434
/* smtpd_proxy_cmd_error - report unexpected proxy reply */
436
static void smtpd_proxy_cmd_error(SMTPD_STATE *state, const char *fmt,
524
msg_panic("%s: unknown proxy %s error %d",
525
myname, proxy->service_name, err);
529
* Set the appropriate flags and server reply.
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);
538
/* smtpd_proxy_replay_send - replay saved SMTP session from speed-match log */
540
static int smtpd_proxy_replay_send(SMTPD_STATE *state)
542
const char *myname = "smtpd_proxy_replay_send";
543
static VSTRING *replay_buf = 0;
544
SMTPD_PROXY *proxy = state->proxy;
546
int expect = SMTPD_PROX_WANT_BAD;
551
if (smtpd_proxy_replay_stream == 0)
552
msg_panic("%s: no before-queue filter speed-adjust log", myname);
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));
565
* Delayed connection to the before-queue filter.
567
if (smtpd_proxy_connect(state) < 0)
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.
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));
581
switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf,
589
if (smtpd_proxy_rec_put(proxy->service_stream, rec_type,
590
STR(replay_buf), LEN(replay_buf)) < 0)
595
* Expected server reply type.
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));
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.
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)
615
expect = SMTPD_PROX_WANT_BAD;
619
* Explicit end marker, instead of implicit EOF.
628
return (smtpd_proxy_replay_rdwr_error(state));
630
msg_panic("%s: unexpected record type; %d", myname, rec_type);
635
/* smtpd_proxy_save_cmd - save SMTP command + expected response to replay log */
637
static int smtpd_proxy_save_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
644
if (vstream_ferror(smtpd_proxy_replay_stream)
645
|| vstream_feof(smtpd_proxy_replay_stream))
646
return (smtpd_proxy_replay_rdwr_error(state));
649
* Save the expected reply first, so that the replayer can safely
650
* overwrite the input buffer with the command.
652
rec_fprintf(smtpd_proxy_replay_stream, REC_TYPE_RCPT, "%d", expect);
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.
660
if (fmt == SMTPD_PROXY_CONN_FMT)
664
* Save the command to the replay log, and send it to the before-queue
665
* filter after we have received the entire message.
668
rec_vfprintf(smtpd_proxy_replay_stream, REC_TYPE_FROM, fmt, ap);
672
* If we just saved the "." command, replay the log.
674
return (strcmp(fmt, ".") ? 0 : smtpd_proxy_replay_send(state));
677
/* smtpd_proxy_cmd_warn - report unexpected proxy reply */
679
static void smtpd_proxy_cmd_warn(SMTPD_STATE *state, const char *fmt,
682
SMTPD_PROXY *proxy = state->proxy;
638
943
if (rec_type == REC_TYPE_NORM)
639
944
smtp_vprintf(stream, fmt, ap);
641
msg_panic("smtpd_proxy_rec_fprintf: need REC_TYPE_NORM");
946
msg_panic("%s: need REC_TYPE_NORM", myname);
643
948
return (rec_type);
646
/* smtpd_proxy_close - close proxy connection */
953
/* smtpd_proxy_replay_setup - prepare the replay logfile */
955
static int smtpd_proxy_replay_setup(SMTPD_STATE *state)
957
const char *myname = "smtpd_proxy_replay_setup";
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.
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);
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 */
982
* Create a new replay logfile.
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));
993
msg_info("%s: new speed-adjust stream fd=%d", myname,
994
vstream_fileno(smtpd_proxy_replay_stream));
998
* Needed by our DATA-phase record emulation routines.
1000
vstream_control(smtpd_proxy_replay_stream, VSTREAM_CTL_CONTEXT,
1001
(char *) state, VSTREAM_CTL_END);
1007
/* smtpd_proxy_create - set up smtpd proxy handle */
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)
1016
* When an operation has many arguments it is safer to use named
1017
* parameters, and have the compiler enforce the argument count.
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))
1027
if (state->proxy != 0)
1028
msg_panic("smtpd_proxy_create: handle still exists");
1031
* Connect to the before-queue filter immediately.
1033
if ((flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) == 0) {
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. */
1046
proxy->stream = proxy->service_stream;
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
1058
msg_panic("smtpd_proxy_create: speed-adjust support is not available");
1060
if (smtpd_proxy_replay_setup(state) < 0)
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);
1076
/* smtpd_proxy_close - close proxy connection without destroying handle */
648
1078
void smtpd_proxy_close(SMTPD_STATE *state)
650
(void) vstream_fclose(state->proxy);
1080
SMTPD_PROXY *proxy = state->proxy;
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
1088
if (proxy->service_stream != 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,
1095
(void) vstream_fclose(proxy->service_stream);
1096
if (proxy->stream == proxy->service_stream)
1098
proxy->service_stream = 0;
1102
/* smtpd_proxy_free - destroy smtpd proxy handle */
1104
void smtpd_proxy_free(SMTPD_STATE *state)
1106
SMTPD_PROXY *proxy = state->proxy;
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;
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.
1125
if (smtpd_proxy_replay_stream == 0)
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;
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;
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;
1148
/* smtpd_proxy_parse_opts - parse main.cf options */
1150
int smtpd_proxy_parse_opts(const char *param_name, const char *param_val)
1152
static const NAME_MASK proxy_opts_table[] = {
1153
SMTPD_PROXY_NAME_SPEED_ADJUST, SMTPD_PROXY_FLAG_SPEED_ADJUST,
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.
1164
flags = name_mask(param_name, proxy_opts_table, param_val);
1165
if (flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) {
1167
msg_warn("smtpd_proxy %s support is not available",
1168
SMTPD_PROXY_NAME_SPEED_ADJUST);
1169
flags &= ~SMTPD_PROXY_FLAG_SPEED_ADJUST;