~ubuntu-branches/ubuntu/hoary/postfix/hoary-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: 2004-10-06 11:50:33 UTC
  • Revision ID: james.westby@ubuntu.com-20041006115033-ooo6yfg6kmoteu04
Tags: upstream-2.1.3
ImportĀ upstreamĀ versionĀ 2.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      smtpd_proxy 3
 
4
/* SUMMARY
 
5
/*      SMTP server pass-through proxy client
 
6
/* SYNOPSIS
 
7
/*      #include <smtpd.h>
 
8
/*      #include <smtpd_proxy.h>
 
9
/*
 
10
/*      typedef struct {
 
11
/* .in +4
 
12
/*              /* other fields... */
 
13
/*              VSTREAM *proxy;         /* connection to SMTP proxy */
 
14
/*              VSTRING *proxy_buffer;  /* last SMTP proxy response */
 
15
/*              /* other fields... */
 
16
/* .in -4
 
17
/*      } SMTPD_STATE;
 
18
/*
 
19
/*      int     smtpd_proxy_open(state, service, timeout, ehlo_name, mail_from)
 
20
/*      SMTPD_STATE *state;
 
21
/*      const char *service;
 
22
/*      int     timeout;
 
23
/*      const char *ehlo_name;
 
24
/*      const char *mail_from;
 
25
/*
 
26
/*      int     smtpd_proxy_cmd(state, expect, format, ...)
 
27
/*      SMTPD_STATE *state;
 
28
/*      int     expect;
 
29
/*      cont char *format;
 
30
/*
 
31
/*      void    smtpd_proxy_close(state)
 
32
/*      SMTPD_STATE *state;
 
33
/* RECORD-LEVEL ROUTINES
 
34
/*      int     smtpd_proxy_rec_put(stream, rec_type, data, len)
 
35
/*      VSTREAM *stream;
 
36
/*      int     rec_type;
 
37
/*      const char *data;
 
38
/*      int     len;
 
39
/*
 
40
/*      int     smtpd_proxy_rec_fprintf(stream, rec_type, format, ...)
 
41
/*      VSTREAM *stream;
 
42
/*      int     rec_type;
 
43
/*      cont char *format;
 
44
/* DESCRIPTION
 
45
/*      The functions in this module implement a pass-through proxy
 
46
/*      client.
 
47
/*
 
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.
 
86
/*
 
87
/* Arguments:
 
88
/* .IP server
 
89
/*      The SMTP proxy server host:port. The host or host: part is optional.
 
90
/* .IP timeout
 
91
/*      Time limit for connecting to the proxy server and for
 
92
/*      sending and receiving proxy server commands and replies.
 
93
/* .IP ehlo_name
 
94
/*      The EHLO Hostname that will be sent to the proxy server.
 
95
/* .IP mail_from
 
96
/*      The MAIL FROM command.
 
97
/* .IP state
 
98
/*      SMTP server state.
 
99
/* .IP expect
 
100
/*      Expected proxy server reply status code range. A warning is logged
 
101
/*      when an unexpected reply is received. Specify one of the following:
 
102
/* .RS
 
103
/* .IP SMTPD_PROX_WANT_OK
 
104
/*      The caller expects a reply in the 200 range.
 
105
/* .IP SMTPD_PROX_WANT_MORE
 
106
/*      The caller expects a reply in the 300 range.
 
107
/* .IP SMTPD_PROX_WANT_ANY
 
108
/*      The caller has no expectation. Do not warn for unexpected replies.
 
109
/* .IP SMTPD_PROX_WANT_NONE
 
110
/*      Do not bother waiting for a reply.
 
111
/* .RE
 
112
/* .IP format
 
113
/*      A format string.
 
114
/* .IP stream
 
115
/*      Connection to proxy server.
 
116
/* .IP data
 
117
/*      Pointer to the content of one message content record.
 
118
/* .IP len
 
119
/*      The length of a message content record.
 
120
/* SEE ALSO
 
121
/*      smtpd(8) Postfix smtp server
 
122
/* DIAGNOSTICS
 
123
/*      Fatal errors: memory allocation problem.
 
124
/*
 
125
/*      Warnings: unexpected response from proxy server, unable
 
126
/*      to connect to proxy server, proxy server read/write error.
 
127
/* LICENSE
 
128
/* .ad
 
129
/* .fi
 
130
/*      The Secure Mailer license must be distributed with this software.
 
131
/* AUTHOR(S)
 
132
/*      Wietse Venema
 
133
/*      IBM T.J. Watson Research
 
134
/*      P.O. Box 704
 
135
/*      Yorktown Heights, NY 10598, USA
 
136
/*--*/
 
137
 
 
138
/* System library. */
 
139
 
 
140
#include <sys_defs.h>
 
141
#include <ctype.h>
 
142
 
 
143
/* Utility library. */
 
144
 
 
145
#include <msg.h>
 
146
#include <vstream.h>
 
147
#include <vstring.h>
 
148
#include <stringops.h>
 
149
#include <connect.h>
 
150
#include <name_code.h>
 
151
 
 
152
/* Global library. */
 
153
 
 
154
#include <mail_error.h>
 
155
#include <smtp_stream.h>
 
156
#include <cleanup_user.h>
 
157
#include <mail_params.h>
 
158
#include <rec_type.h>
 
159
#include <mail_proto.h>
 
160
 
 
161
/* Application-specific. */
 
162
 
 
163
#include <smtpd.h>
 
164
#include <smtpd_proxy.h>
 
165
 
 
166
 /*
 
167
  * XFORWARD server features, recognized by the pass-through proxy client.
 
168
  */
 
169
#define SMTPD_PROXY_XFORWARD_NAME  (1<<0)       /* client name */
 
170
#define SMTPD_PROXY_XFORWARD_ADDR  (1<<1)       /* client address */
 
171
#define SMTPD_PROXY_XFORWARD_PROTO (1<<2)       /* protocol */
 
172
#define SMTPD_PROXY_XFORWARD_HELO  (1<<3)       /* client helo */
 
173
#define SMTPD_PROXY_XFORWARD_IDENT (1<<4)       /* message identifier */
 
174
 
 
175
 /*
 
176
  * SLMs.
 
177
  */
 
178
#define STR(x)  vstring_str(x)
 
179
#define LEN(x)  VSTRING_LEN(x)
 
180
#define SMTPD_PROXY_CONNECT ((char *) 0)
 
181
 
 
182
/* smtpd_xforward_flush - flush forwarding information */
 
183
 
 
184
static int smtpd_xforward_flush(SMTPD_STATE *state, VSTRING *buf)
 
185
{
 
186
    int     ret;
 
187
 
 
188
    if (VSTRING_LEN(buf) > 0) {
 
189
        ret = smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK,
 
190
                              XFORWARD_CMD "%s", STR(buf));
 
191
        VSTRING_RESET(buf);
 
192
        return (ret);
 
193
    }
 
194
    return (0);
 
195
}
 
196
 
 
197
/* smtpd_xforward - send forwarding information */
 
198
 
 
199
static int smtpd_xforward(SMTPD_STATE *state, VSTRING *buf, const char *name,
 
200
                                  int value_available, const char *value)
 
201
{
 
202
    size_t  new_len;
 
203
    int     ret;
 
204
 
 
205
#define CONSTR_LEN(s)   (sizeof(s) - 1)
 
206
#define PAYLOAD_LIMIT   (512 - CONSTR_LEN("250 " XFORWARD_CMD "\r\n"))
 
207
 
 
208
    /*
 
209
     * How much space does this attribute need?
 
210
     */
 
211
    if (!value_available)
 
212
        value = XFORWARD_UNAVAILABLE;
 
213
    new_len = strlen(name) + strlen(value) + 2; /* SPACE name = value */
 
214
    if (new_len > PAYLOAD_LIMIT)
 
215
        msg_warn("%s command payload %s=%.10s... exceeds SMTP protocol limit",
 
216
                 XFORWARD_CMD, name, value);
 
217
 
 
218
    /*
 
219
     * Flush the buffer if we need to, and store the attribute.
 
220
     */
 
221
    if (VSTRING_LEN(buf) > 0 && VSTRING_LEN(buf) + new_len > PAYLOAD_LIMIT)
 
222
        if ((ret = smtpd_xforward_flush(state, buf)) < 0)
 
223
            return (ret);
 
224
    vstring_sprintf_append(buf, " %s=%s", name, value);
 
225
 
 
226
    return (0);
 
227
}
 
228
 
 
229
/* smtpd_proxy_open - open proxy connection */
 
230
 
 
231
int     smtpd_proxy_open(SMTPD_STATE *state, const char *service,
 
232
                                 int timeout, const char *ehlo_name,
 
233
                                 const char *mail_from)
 
234
{
 
235
    int     fd;
 
236
    char   *lines;
 
237
    char   *words;
 
238
    VSTRING *buf;
 
239
    int     bad;
 
240
    char   *word;
 
241
    static NAME_CODE xforward_features[] = {
 
242
        XFORWARD_NAME, SMTPD_PROXY_XFORWARD_NAME,
 
243
        XFORWARD_ADDR, SMTPD_PROXY_XFORWARD_ADDR,
 
244
        XFORWARD_PROTO, SMTPD_PROXY_XFORWARD_PROTO,
 
245
        XFORWARD_HELO, SMTPD_PROXY_XFORWARD_HELO,
 
246
        0, 0,
 
247
    };
 
248
 
 
249
    /*
 
250
     * This buffer persists beyond the end of a proxy session so we can
 
251
     * inspect the last command's reply.
 
252
     */
 
253
    if (state->proxy_buffer == 0)
 
254
        state->proxy_buffer = vstring_alloc(10);
 
255
 
 
256
    /*
 
257
     * Connect to proxy.
 
258
     */
 
259
    if ((fd = inet_connect(service, BLOCKING, timeout)) < 0) {
 
260
        state->error_mask |= MAIL_ERROR_SOFTWARE;
 
261
        state->err |= CLEANUP_STAT_PROXY;
 
262
        msg_warn("connect to proxy service %s: %m", service);
 
263
        vstring_sprintf(state->proxy_buffer,
 
264
                        "451 Error: queue file write error");
 
265
        return (-1);
 
266
    }
 
267
    state->proxy = vstream_fdopen(fd, O_RDWR);
 
268
    vstream_control(state->proxy, VSTREAM_CTL_PATH, service, VSTREAM_CTL_END);
 
269
    smtp_timeout_setup(state->proxy, timeout);
 
270
 
 
271
    /*
 
272
     * Get server greeting banner.
 
273
     * 
 
274
     * If this fails then we have a problem because the proxy should always
 
275
     * accept our connection. Make up our own response instead of passing
 
276
     * back the greeting banner: the proxy open might be delayed to the point
 
277
     * that the client expects a MAIL FROM or RCPT TO reply.
 
278
     */
 
279
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, SMTPD_PROXY_CONNECT) != 0) {
 
280
        vstring_sprintf(state->proxy_buffer,
 
281
                        "451 Error: queue file write error");
 
282
        smtpd_proxy_close(state);
 
283
        return (-1);
 
284
    }
 
285
 
 
286
    /*
 
287
     * Send our own EHLO command. If this fails then we have a problem
 
288
     * because the proxy should always accept our EHLO command. Make up our
 
289
     * own response instead of passing back the EHLO reply: the proxy open
 
290
     * might be delayed to the point that the client expects a MAIL FROM or
 
291
     * RCPT TO reply.
 
292
     */
 
293
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "EHLO %s", ehlo_name) != 0) {
 
294
        vstring_sprintf(state->proxy_buffer,
 
295
                        "451 Error: queue file write error");
 
296
        smtpd_proxy_close(state);
 
297
        return (-1);
 
298
    }
 
299
 
 
300
    /*
 
301
     * Parse the EHLO reply and see if we can forward logging information.
 
302
     */
 
303
    state->proxy_xforward_features = 0;
 
304
    lines = STR(state->proxy_buffer);
 
305
    while ((words = mystrtok(&lines, "\n")) != 0) {
 
306
        if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) {
 
307
            if (strcasecmp(word, XFORWARD_CMD) == 0)
 
308
                while ((word = mystrtok(&words, " \t")) != 0)
 
309
                    state->proxy_xforward_features |=
 
310
                        name_code(xforward_features, NAME_CODE_FLAG_NONE, word);
 
311
        }
 
312
    }
 
313
 
 
314
    /*
 
315
     * Send XFORWARD attributes. For robustness, explicitly specify what SMTP
 
316
     * session attributes are known and unknown.
 
317
     */
 
318
    if (state->proxy_xforward_features) {
 
319
        buf = vstring_alloc(100);
 
320
        bad = 0;
 
321
        if ((state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_NAME)
 
322
            && !(bad = smtpd_xforward(state, buf, XFORWARD_NAME,
 
323
                                  IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)),
 
324
                                      FORWARD_NAME(state)))
 
325
            && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_ADDR)
 
326
            && !(bad = smtpd_xforward(state, buf, XFORWARD_ADDR,
 
327
                                  IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)),
 
328
                                      FORWARD_ADDR(state)))
 
329
            && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_HELO)
 
330
            && !(bad = smtpd_xforward(state, buf, XFORWARD_HELO,
 
331
                                  IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)),
 
332
                                      FORWARD_HELO(state)))
 
333
            && (state->proxy_xforward_features & SMTPD_PROXY_XFORWARD_PROTO)
 
334
            && !(bad = smtpd_xforward(state, buf, XFORWARD_PROTO,
 
335
                                IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)),
 
336
                                      FORWARD_PROTO(state))))
 
337
            bad = smtpd_xforward_flush(state, buf);
 
338
        vstring_free(buf);
 
339
        if (bad) {
 
340
            vstring_sprintf(state->proxy_buffer,
 
341
                            "451 Error: queue file write error");
 
342
            smtpd_proxy_close(state);
 
343
            return (-1);
 
344
        }
 
345
    }
 
346
 
 
347
    /*
 
348
     * Pass-through the client's MAIL FROM command. If this fails, then we
 
349
     * have a problem because the proxy should always accept any MAIL FROM
 
350
     * command that was accepted by us.
 
351
     */
 
352
    if (smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK, "%s", mail_from) != 0) {
 
353
        smtpd_proxy_close(state);
 
354
        return (-1);
 
355
    }
 
356
    return (0);
 
357
}
 
358
 
 
359
/* smtpd_proxy_rdwr_error - report proxy communication error */
 
360
 
 
361
static int smtpd_proxy_rdwr_error(VSTREAM *stream, int err)
 
362
{
 
363
    switch (err) {
 
364
        case SMTP_ERR_EOF:
 
365
        msg_warn("lost connection with proxy %s", VSTREAM_PATH(stream));
 
366
        return (err);
 
367
    case SMTP_ERR_TIME:
 
368
        msg_warn("timeout talking to proxy %s", VSTREAM_PATH(stream));
 
369
        return (err);
 
370
    default:
 
371
        msg_panic("smtpd_proxy_rdwr_error: unknown proxy %s stream error %d",
 
372
                  VSTREAM_PATH(stream), err);
 
373
    }
 
374
}
 
375
 
 
376
/* smtpd_proxy_cmd_error - report unexpected proxy reply */
 
377
 
 
378
static void smtpd_proxy_cmd_error(SMTPD_STATE *state, const char *fmt,
 
379
                                          va_list ap)
 
380
{
 
381
    VSTRING *buf;
 
382
 
 
383
    /*
 
384
     * The command can be omitted at the start of an SMTP session. A null
 
385
     * format string is not documented as part of the official interface
 
386
     * because it is used only internally to this module.
 
387
     */
 
388
    buf = vstring_alloc(100);
 
389
    vstring_vsprintf(buf, fmt == SMTPD_PROXY_CONNECT ?
 
390
                     "connection request" : fmt, ap);
 
391
    msg_warn("proxy %s rejected \"%s\": \"%s\"", VSTREAM_PATH(state->proxy),
 
392
             STR(buf), STR(state->proxy_buffer));
 
393
    vstring_free(buf);
 
394
}
 
395
 
 
396
/* smtpd_proxy_cmd - send command to proxy, receive reply */
 
397
 
 
398
int     smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
 
399
{
 
400
    va_list ap;
 
401
    char   *cp;
 
402
    int     last_char;
 
403
    int     err = 0;
 
404
    static VSTRING *buffer = 0;
 
405
 
 
406
    /*
 
407
     * Errors first. Be prepared for delayed errors from the DATA phase.
 
408
     */
 
409
    if (vstream_ftimeout(state->proxy)
 
410
        || vstream_ferror(state->proxy)
 
411
        || vstream_feof(state->proxy)
 
412
        || ((err = vstream_setjmp(state->proxy) != 0)
 
413
            && smtpd_proxy_rdwr_error(state->proxy, err))) {
 
414
        state->error_mask |= MAIL_ERROR_SOFTWARE;
 
415
        state->err |= CLEANUP_STAT_PROXY;
 
416
        vstring_sprintf(state->proxy_buffer,
 
417
                        "451 Error: queue file write error");
 
418
        return (-1);
 
419
    }
 
420
 
 
421
    /*
 
422
     * The command can be omitted at the start of an SMTP session. This is
 
423
     * not documented as part of the official interface because it is used
 
424
     * only internally to this module.
 
425
     */
 
426
    if (fmt != SMTPD_PROXY_CONNECT) {
 
427
 
 
428
        /*
 
429
         * Format the command.
 
430
         */
 
431
        va_start(ap, fmt);
 
432
        vstring_vsprintf(state->proxy_buffer, fmt, ap);
 
433
        va_end(ap);
 
434
 
 
435
        /*
 
436
         * Optionally log the command first, so that we can see in the log
 
437
         * what the program is trying to do.
 
438
         */
 
439
        if (msg_verbose)
 
440
            msg_info("> %s: %s", VSTREAM_PATH(state->proxy),
 
441
                     STR(state->proxy_buffer));
 
442
 
 
443
        /*
 
444
         * Send the command to the proxy server. Since we're going to read a
 
445
         * reply immediately, there is no need to flush buffers.
 
446
         */
 
447
        smtp_fputs(STR(state->proxy_buffer), LEN(state->proxy_buffer),
 
448
                   state->proxy);
 
449
    }
 
450
 
 
451
    /*
 
452
     * Early return if we don't want to wait for a server reply (such as
 
453
     * after sending QUIT.
 
454
     */
 
455
    if (expect == SMTPD_PROX_WANT_NONE)
 
456
        return (0);
 
457
 
 
458
    /*
 
459
     * Censor out non-printable characters in server responses and save
 
460
     * complete multi-line responses if possible.
 
461
     */
 
462
    VSTRING_RESET(state->proxy_buffer);
 
463
    if (buffer == 0)
 
464
        buffer = vstring_alloc(10);
 
465
    for (;;) {
 
466
        last_char = smtp_get(buffer, state->proxy, var_line_limit);
 
467
        printable(STR(buffer), '?');
 
468
        if (last_char != '\n')
 
469
            msg_warn("%s: response longer than %d: %.30s...",
 
470
                     VSTREAM_PATH(state->proxy), var_line_limit,
 
471
                     STR(buffer));
 
472
        if (msg_verbose)
 
473
            msg_info("< %s: %.100s", VSTREAM_PATH(state->proxy),
 
474
                     STR(buffer));
 
475
 
 
476
        /*
 
477
         * Defend against a denial of service attack by limiting the amount
 
478
         * of multi-line text that we are willing to store.
 
479
         */
 
480
        if (LEN(state->proxy_buffer) < var_line_limit) {
 
481
            if (VSTRING_LEN(state->proxy_buffer))
 
482
                VSTRING_ADDCH(state->proxy_buffer, '\n');
 
483
            vstring_strcat(state->proxy_buffer, STR(buffer));
 
484
        }
 
485
 
 
486
        /*
 
487
         * Parse the response into code and text. Ignore unrecognized
 
488
         * garbage. This means that any character except space (or end of
 
489
         * line) will have the same effect as the '-' line continuation
 
490
         * character.
 
491
         */
 
492
        for (cp = STR(buffer); *cp && ISDIGIT(*cp); cp++)
 
493
             /* void */ ;
 
494
        if (cp - STR(buffer) == 3) {
 
495
            if (*cp == '-')
 
496
                continue;
 
497
            if (*cp == ' ' || *cp == 0)
 
498
                break;
 
499
        }
 
500
        msg_warn("received garbage from proxy %s: %.100s",
 
501
                 VSTREAM_PATH(state->proxy), STR(buffer));
 
502
    }
 
503
 
 
504
    /*
 
505
     * Log a warning in case the proxy does not send the expected response.
 
506
     * Silently accept any response when the client expressed no expectation.
 
507
     */
 
508
    if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(state->proxy_buffer)) {
 
509
        va_start(ap, fmt);
 
510
        smtpd_proxy_cmd_error(state, fmt, ap);
 
511
        va_end(ap);
 
512
        return (-1);
 
513
    } else {
 
514
        return (0);
 
515
    }
 
516
}
 
517
 
 
518
/* smtpd_proxy_rec_put - send message content, rec_put() clone */
 
519
 
 
520
int     smtpd_proxy_rec_put(VSTREAM *stream, int rec_type,
 
521
                                    const char *data, int len)
 
522
{
 
523
    int     err;
 
524
 
 
525
    /*
 
526
     * Errors first.
 
527
     */
 
528
    if (vstream_ftimeout(stream) || vstream_ferror(stream)
 
529
        || vstream_feof(stream))
 
530
        return (REC_TYPE_ERROR);
 
531
    if ((err = vstream_setjmp(stream)) != 0)
 
532
        return (smtpd_proxy_rdwr_error(stream, err), REC_TYPE_ERROR);
 
533
 
 
534
    /*
 
535
     * Send one content record. Errors and results must be as with rec_put().
 
536
     */
 
537
    if (rec_type == REC_TYPE_NORM)
 
538
        smtp_fputs(data, len, stream);
 
539
    else if (rec_type == REC_TYPE_CONT)
 
540
        smtp_fwrite(data, len, stream);
 
541
    else
 
542
        msg_panic("smtpd_proxy_rec_put: need REC_TYPE_NORM or REC_TYPE_CONT");
 
543
    return (rec_type);
 
544
}
 
545
 
 
546
/* smtpd_proxy_rec_fprintf - send message content, rec_fprintf() clone */
 
547
 
 
548
int     smtpd_proxy_rec_fprintf(VSTREAM *stream, int rec_type,
 
549
                                        const char *fmt,...)
 
550
{
 
551
    va_list ap;
 
552
    int     err;
 
553
 
 
554
    /*
 
555
     * Errors first.
 
556
     */
 
557
    if (vstream_ftimeout(stream) || vstream_ferror(stream)
 
558
        || vstream_feof(stream))
 
559
        return (REC_TYPE_ERROR);
 
560
    if ((err = vstream_setjmp(stream)) != 0)
 
561
        return (smtpd_proxy_rdwr_error(stream, err), REC_TYPE_ERROR);
 
562
 
 
563
    /*
 
564
     * Send one content record. Errors and results must be as with
 
565
     * rec_fprintf().
 
566
     */
 
567
    va_start(ap, fmt);
 
568
    if (rec_type == REC_TYPE_NORM)
 
569
        smtp_vprintf(stream, fmt, ap);
 
570
    else
 
571
        msg_panic("smtpd_proxy_rec_fprintf: need REC_TYPE_NORM");
 
572
    va_end(ap);
 
573
    return (rec_type);
 
574
}
 
575
 
 
576
/* smtpd_proxy_close - close proxy connection */
 
577
 
 
578
void    smtpd_proxy_close(SMTPD_STATE *state)
 
579
{
 
580
    (void) vstream_fclose(state->proxy);
 
581
    state->proxy = 0;
 
582
}