int smtpd_proxy_create(SMTPD_STATE *state, int flags, const char *service, int timeout, const char *ehlo_name, const char *mail_from) { SMTPD_PROXY *proxy; /* * When an operation has many arguments it is safer to use named * parameters, and have the compiler enforce the argument count. */ #define SMTPD_PROXY_ALLOC(p, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) \ ((p) = (SMTPD_PROXY *) mymalloc(sizeof(*(p))), (p)->a1, (p)->a2, \ (p)->a3, (p)->a4, (p)->a5, (p)->a6, (p)->a7, (p)->a8, (p)->a9, \ (p)->a10, (p)->a11, (p)->a12, (p)) /* * Sanity check. */ if (state->proxy != 0) msg_panic("smtpd_proxy_create: handle still exists"); /* * Connect to the before-queue filter immediately. */ if ((flags & SMTPD_PROXY_FLAG_SPEED_ADJUST) == 0) { state->proxy = SMTPD_PROXY_ALLOC(proxy, stream = 0, request = vstring_alloc(10), reply = vstring_alloc(10), cmd = smtpd_proxy_cmd, rec_fprintf = smtpd_proxy_rec_fprintf, rec_put = smtpd_proxy_rec_put, flags = flags, service_stream = 0, service_name = service, timeout = timeout, ehlo_name = ehlo_name, mail_from = mail_from); if (smtpd_proxy_connect(state) < 0) { /* NOT: smtpd_proxy_free(state); we still need proxy->reply. */ return (-1); } proxy->stream = proxy->service_stream; return (0); } /* * Connect to the before-queue filter after we receive the entire * message. Open the replay logfile early to simplify code. The file is * reused for multiple mail transactions, so there is no need to minimize * its life time. */ else { #ifdef NO_TRUNCATE msg_panic("smtpd_proxy_create: speed-adjust support is not available"); #else if (smtpd_proxy_replay_setup(state) < 0) return (-1); state->proxy = SMTPD_PROXY_ALLOC(proxy, stream = smtpd_proxy_replay_stream, request = vstring_alloc(10), reply = vstring_alloc(10), cmd = smtpd_proxy_save_cmd, rec_fprintf = smtpd_proxy_save_rec_fprintf, rec_put = smtpd_proxy_save_rec_put, flags = flags, service_stream = 0, service_name = service, timeout = timeout, ehlo_name = ehlo_name, mail_from = mail_from); return (0); #endif } }
static int smtpd_proxy_replay_send(SMTPD_STATE *state) { const char *myname = "smtpd_proxy_replay_send"; static VSTRING *replay_buf = 0; SMTPD_PROXY *proxy = state->proxy; int rec_type; int expect = SMTPD_PROX_WANT_BAD; /* * Sanity check. */ if (smtpd_proxy_replay_stream == 0) msg_panic("%s: no before-queue filter speed-adjust log", myname); /* * Errors first. */ if (vstream_ferror(smtpd_proxy_replay_stream) || vstream_feof(smtpd_proxy_replay_stream) || rec_put(smtpd_proxy_replay_stream, REC_TYPE_END, "", 0) != REC_TYPE_END || vstream_fflush(smtpd_proxy_replay_stream)) /* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */ return (smtpd_proxy_replay_rdwr_error(state)); /* * Delayed connection to the before-queue filter. */ if (smtpd_proxy_connect(state) < 0) return (-1); /* * Replay the speed-match log. We do sanity check record content, but we * don't implement a protocol state engine here, since we are reading * from a file that we just wrote ourselves. * * This is different than the MailChannels patented solution that * multiplexes a large number of slowed-down inbound connections over a * small number of fast connections to a local MTA. * * - MailChannels receives mail directly from the Internet. It uses one * connection to the local MTA to reject invalid recipients before * receiving the entire email message at reduced bit rates, and then uses * a different connection to quickly deliver the message to the local * MTA. * * - Postfix receives mail directly from the Internet. The Postfix SMTP * server rejects invalid recipients before receiving the entire message * over the Internet, and then delivers the message quickly to a local * SMTP-based content filter. */ if (replay_buf == 0) replay_buf = vstring_alloc(100); if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0) return (smtpd_proxy_replay_rdwr_error(state)); for (;;) { switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf, REC_FLAG_NONE)) { /* * Message content. */ case REC_TYPE_NORM: case REC_TYPE_CONT: if (smtpd_proxy_rec_put(proxy->service_stream, rec_type, STR(replay_buf), LEN(replay_buf)) < 0) return (-1); break; /* * Expected server reply type. */ case REC_TYPE_RCPT: if (!alldig(STR(replay_buf)) || (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD) msg_panic("%s: malformed server reply type: %s", myname, STR(replay_buf)); break; /* * Client command, or void. Bail out on the first negative proxy * response. This is OK, because the filter must use the same * reply code for all recipients of a multi-recipient message. */ case REC_TYPE_FROM: if (expect == SMTPD_PROX_WANT_BAD) msg_panic("%s: missing server reply type", myname); if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" : SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0) return (-1); expect = SMTPD_PROX_WANT_BAD; break; /* * Explicit end marker, instead of implicit EOF. */ case REC_TYPE_END: return (0); /* * Errors. */ case REC_TYPE_ERROR: return (smtpd_proxy_replay_rdwr_error(state)); default: msg_panic("%s: unexpected record type; %d", myname, rec_type); } } }
static int smtpd_proxy_replay_send(SMTPD_STATE *state) { const char *myname = "smtpd_proxy_replay_send"; static VSTRING *replay_buf = 0; SMTPD_PROXY *proxy = state->proxy; int rec_type; int expect = SMTPD_PROX_WANT_BAD; /* * Sanity check. */ if (smtpd_proxy_replay_stream == 0) msg_panic("%s: no before-queue filter speed-adjust log", myname); /* * Errors first. */ if (vstream_ferror(smtpd_proxy_replay_stream) || vstream_feof(smtpd_proxy_replay_stream) || rec_put(smtpd_proxy_replay_stream, REC_TYPE_END, "", 0) != REC_TYPE_END || vstream_fflush(smtpd_proxy_replay_stream)) /* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */ return (smtpd_proxy_replay_rdwr_error(state)); /* * Delayed connection to the before-queue filter. */ if (smtpd_proxy_connect(state) < 0) return (-1); /* * Replay the speed-match log. We do sanity check record content, but we * don't implement a protocol state engine here, since we are reading * from a file that we just wrote ourselves. */ if (replay_buf == 0) replay_buf = vstring_alloc(100); if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0) return (smtpd_proxy_replay_rdwr_error(state)); for (;;) { switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf, REC_FLAG_NONE)) { /* * Message content. */ case REC_TYPE_NORM: case REC_TYPE_CONT: if (smtpd_proxy_rec_put(proxy->service_stream, rec_type, STR(replay_buf), LEN(replay_buf)) < 0) return (-1); break; /* * Expected server reply type. */ case REC_TYPE_RCPT: if (!alldig(STR(replay_buf)) || (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD) msg_panic("%s: malformed server reply type: %s", myname, STR(replay_buf)); break; /* * Client command, or void. Bail out on the first negative proxy * response. This is OK, because the filter must use the same * reply code for all recipients of a multi-recipient message. */ case REC_TYPE_FROM: if (expect == SMTPD_PROX_WANT_BAD) msg_panic("%s: missing server reply type", myname); if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" : SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0) return (-1); expect = SMTPD_PROX_WANT_BAD; break; /* * Explicit end marker, instead of implicit EOF. */ case REC_TYPE_END: return (0); /* * Errors. */ case REC_TYPE_ERROR: return (smtpd_proxy_replay_rdwr_error(state)); default: msg_panic("%s: unexpected record type; %d", myname, rec_type); } } }