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