From 2e7745be77795d897920134877251b51cae6ddbb Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Thu, 4 May 2000 00:00:00 +0000 Subject: [PATCH] snapshot-20000504 --- postfix/.indent.pro | 4 + postfix/base64/.indent.pro | 4 + postfix/bounce/.indent.pro | 4 + postfix/cleanup/.indent.pro | 4 + postfix/dns/.indent.pro | 4 + postfix/error/.indent.pro | 4 + postfix/fsstone/.indent.pro | 4 + postfix/global/.indent.pro | 4 + postfix/global/mail_version.h | 2 +- postfix/global/smtp_stream.c | 86 +--- postfix/lmtp/.indent.pro | 120 +++++ postfix/lmtp/.printfck | 25 + postfix/lmtp/CHANGES | 146 ++++++ postfix/lmtp/Makefile.in | 61 +++ postfix/lmtp/README | 34 ++ postfix/lmtp/README.inet | 80 +++ postfix/lmtp/README.local | 50 ++ postfix/lmtp/fixnames | 5 + postfix/lmtp/global-patch | 65 +++ postfix/lmtp/lmtp.c | 608 ++++++++++++++++++++++ postfix/lmtp/lmtp.h | 155 ++++++ postfix/lmtp/lmtp_addr.c | 202 ++++++++ postfix/lmtp/lmtp_addr.h | 41 ++ postfix/lmtp/lmtp_chat.c | 285 +++++++++++ postfix/lmtp/lmtp_connect.c | 395 +++++++++++++++ postfix/lmtp/lmtp_proto.c | 686 +++++++++++++++++++++++++ postfix/lmtp/lmtp_session.c | 105 ++++ postfix/lmtp/lmtp_state.c | 92 ++++ postfix/lmtp/lmtp_trouble.c | 318 ++++++++++++ postfix/lmtp/mail | 753 ++++++++++++++++++++++++++++ postfix/lmtp/makefile-patch | 19 + postfix/lmtp/man-patch | 218 ++++++++ postfix/lmtp/quote_821_local.c | 170 +++++++ postfix/lmtp/quote_821_local.h | 41 ++ postfix/local/.indent.pro | 4 + postfix/master/.indent.pro | 4 + postfix/pickup/.indent.pro | 4 + postfix/pipe/.indent.pro | 4 + postfix/postalias/.indent.pro | 4 + postfix/postcat/.indent.pro | 4 + postfix/postconf/.indent.pro | 4 + postfix/postdrop/.indent.pro | 4 + postfix/postfix/.indent.pro | 4 + postfix/postkick/.indent.pro | 4 + postfix/postlock/.indent.pro | 4 + postfix/postlog/.indent.pro | 4 + postfix/postmap/.indent.pro | 4 + postfix/postsuper/.indent.pro | 4 + postfix/qmgr/.indent.pro | 4 + postfix/sendmail/.indent.pro | 4 + postfix/showq/.indent.pro | 4 + postfix/smtp/.indent.pro | 4 + postfix/smtp/smtp.c | 3 +- postfix/smtp/smtp.h | 6 - postfix/smtp/smtp_proto.c | 8 +- postfix/smtpd/.indent.pro | 4 + postfix/smtpd/smtpd.c | 4 +- postfix/smtpd/smtpd.h | 6 - postfix/smtpstone/.indent.pro | 4 + postfix/smtpstone/smtp-sink.c | 5 +- postfix/smtpstone/smtp-source.c | 27 +- postfix/spawn/.indent.pro | 4 + postfix/trivial-rewrite/.indent.pro | 4 + postfix/util/.indent.pro | 4 + postfix/util/Makefile.in | 14 +- postfix/util/iostuff.h | 4 +- postfix/util/timed_read.c | 9 +- postfix/util/timed_write.c | 9 +- postfix/util/vstream.c | 48 +- postfix/util/vstream.h | 21 +- postfix/util/vstream_attr.c | 92 ---- 71 files changed, 4913 insertions(+), 229 deletions(-) create mode 100644 postfix/lmtp/.indent.pro create mode 100644 postfix/lmtp/.printfck create mode 100644 postfix/lmtp/CHANGES create mode 100644 postfix/lmtp/Makefile.in create mode 100644 postfix/lmtp/README create mode 100644 postfix/lmtp/README.inet create mode 100644 postfix/lmtp/README.local create mode 100644 postfix/lmtp/fixnames create mode 100644 postfix/lmtp/global-patch create mode 100644 postfix/lmtp/lmtp.c create mode 100644 postfix/lmtp/lmtp.h create mode 100644 postfix/lmtp/lmtp_addr.c create mode 100644 postfix/lmtp/lmtp_addr.h create mode 100644 postfix/lmtp/lmtp_chat.c create mode 100644 postfix/lmtp/lmtp_connect.c create mode 100644 postfix/lmtp/lmtp_proto.c create mode 100644 postfix/lmtp/lmtp_session.c create mode 100644 postfix/lmtp/lmtp_state.c create mode 100644 postfix/lmtp/lmtp_trouble.c create mode 100644 postfix/lmtp/mail create mode 100644 postfix/lmtp/makefile-patch create mode 100644 postfix/lmtp/man-patch create mode 100644 postfix/lmtp/quote_821_local.c create mode 100644 postfix/lmtp/quote_821_local.h delete mode 100644 postfix/util/vstream_attr.c diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/base64/.indent.pro b/postfix/base64/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/base64/.indent.pro +++ b/postfix/base64/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/bounce/.indent.pro b/postfix/bounce/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/bounce/.indent.pro +++ b/postfix/bounce/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/cleanup/.indent.pro b/postfix/cleanup/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/cleanup/.indent.pro +++ b/postfix/cleanup/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/dns/.indent.pro b/postfix/dns/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/dns/.indent.pro +++ b/postfix/dns/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/error/.indent.pro b/postfix/error/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/error/.indent.pro +++ b/postfix/error/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/fsstone/.indent.pro b/postfix/fsstone/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/fsstone/.indent.pro +++ b/postfix/fsstone/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/global/.indent.pro b/postfix/global/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/global/.indent.pro +++ b/postfix/global/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index 087b0c4f1..24fb39cc1 100644 --- a/postfix/global/mail_version.h +++ b/postfix/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20000503" +#define DEF_MAIL_VERSION "Snapshot-20000504" extern char *var_mail_version; /* LICENSE diff --git a/postfix/global/smtp_stream.c b/postfix/global/smtp_stream.c index 0f60200c1..12a411b2b 100644 --- a/postfix/global/smtp_stream.c +++ b/postfix/global/smtp_stream.c @@ -6,10 +6,6 @@ /* SYNOPSIS /* #include /* -/* void smtp_jump_setup(stream, jbuf) -/* VSTREAM *stream; -/* jmp_buf *jbuf; -/* /* void smtp_timeout_setup(stream, timeout) /* VSTREAM *stream; /* int timeout; @@ -46,22 +42,15 @@ /* with error detection: timeouts or unexpected end-of-file. /* A trailing CR LF is added upon writing and removed upon reading. /* -/* smtp_jump_setup() registers a caller context that will be -/* jumped to (with longjmp()) when any routine in this module -/* experiences an error condition (timeout, I/O error, or -/* unexpected EOF). -/* /* smtp_timeout_setup() arranges for a time limit on the smtp read /* and write operations described below. /* This routine alters the behavior of streams as follows: /* .IP \(bu -/* The read routine is replaced by one than calls timed_read(). -/* .IP \(bu -/* The write routine is replaced by one that calls timed_write(). +/* The read/write timeout is set to the specified value. /* .IP \f(bu /* The stream is configured to use double buffering. /* .IP \f(bu -/* A timeout error is reported to the vstream module as an I/O error. +/* The stream is configured to enable exception handling. /* .PP /* smtp_printf() formats its arguments and writes the result to /* the named stream, followed by a CR LF pair. The stream is flushed. @@ -89,9 +78,9 @@ /* DIAGNOSTICS /* .fi /* .ad -/* In case of error, a longjmp() is performed to the context -/* specified with the smtp_jump_setup() call. -/* Error codes passed along with longjmp() are: +/* In case of error, a vstream_longjmp() call is performed to the +/* context specified with vstream_setjmp(). +/* Error codes passed along with vstream_longjmp() are: /* .IP SMTP_ERR_EOF /* An I/O error happened, or the peer has disconnected unexpectedly. /* .IP SMTP_ERR_TIME @@ -100,8 +89,8 @@ /* The timeout deadline affects all I/O on the named stream, not /* just the I/O done on behalf of this module. /* -/* The timeout deadline and exception handling context overwrite -/* any previously set up state on the named stream. +/* The timeout deadline overwrites any previously set up state on +/* the named stream. /* LICENSE /* .ad /* .fi @@ -137,35 +126,9 @@ #include "smtp_stream.h" - /* - * Our private VSTREAM attribute name for keeping track of the - * caller-supplied context for exception handling. - */ -#define SMTP_ATTR_JBUF "smtp_timeout_buf" +/* smtp_timeout_reset - reset per-stream timeout flag */ -/* smtp_timeout_jump - release timeout trap */ - -static void smtp_timeout_jump(VSTREAM *stream, int what) -{ - char *myname = "smtp_timeout_jump"; - jmp_buf *jbuf; - - if ((jbuf = (jmp_buf *) vstream_attr_get(stream, SMTP_ATTR_JBUF)) == 0) - msg_panic("%s: no jump buffer", myname); - longjmp(jbuf[0], what); -} - -/* smtp_jump_setup - configure exception handling context */ - -void smtp_jump_setup(VSTREAM *stream, jmp_buf * jbuf) -{ - vstream_attr_set(stream, SMTP_ATTR_JBUF, - (char *) jbuf, (VSTREAM_ATTR_FREE_FN) 0); -} - -/* smtp_timeout_protect - reset per-stream timeout flag */ - -static void smtp_timeout_protect(VSTREAM *stream) +static void smtp_timeout_reset(VSTREAM *stream) { vstream_clearerr(stream); } @@ -175,7 +138,7 @@ static void smtp_timeout_protect(VSTREAM *stream) static void smtp_timeout_detect(VSTREAM *stream) { if (vstream_ftimeout(stream)) - smtp_timeout_jump(stream, SMTP_ERR_TIME); + vstream_longjmp(stream, SMTP_ERR_TIME); } /* smtp_timeout_setup - configure timeout trap */ @@ -185,13 +148,14 @@ void smtp_timeout_setup(VSTREAM *stream, int maxtime) /* * Stick your TLS/whatever read-write routines here. Notice that the - * read/write interface now includes a timeout parameter, and that a - * read/write routine is supposed to set errno to ETIMEDOUT when the - * alarm clock goes off. + * read/write interface now includes a timeout parameter and application + * context, and that a read/write routine is supposed to set errno to + * ETIMEDOUT when the alarm clock goes off. */ vstream_control(stream, - VSTREAM_CTL_TIMEOUT, maxtime, VSTREAM_CTL_DOUBLE, + VSTREAM_CTL_TIMEOUT, maxtime, + VSTREAM_CTL_EXCEPT, VSTREAM_CTL_END); } @@ -204,7 +168,7 @@ void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap) /* * Do the I/O, protected against timeout. */ - smtp_timeout_protect(stream); + smtp_timeout_reset(stream); vstream_vfprintf(stream, fmt, ap); vstream_fputs("\r\n", stream); err = vstream_fflush(stream); @@ -216,7 +180,7 @@ void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap) if (err != 0) { if (msg_verbose) msg_info("smtp_vprintf: EOF"); - smtp_timeout_jump(stream, SMTP_ERR_EOF); + vstream_longjmp(stream, SMTP_ERR_EOF); } } @@ -244,7 +208,7 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, int bound) * allow for lines ending in bare LF. The idea is to be liberal in what * we accept, strict in what we send. */ - smtp_timeout_protect(stream); + smtp_timeout_reset(stream); last_char = (bound == 0 ? vstring_get(vp, stream) : vstring_get_bound(vp, stream, bound)); @@ -291,7 +255,7 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, int bound) if (vstream_feof(stream) || vstream_ferror(stream)) { if (msg_verbose) msg_info("smtp_get: EOF"); - smtp_timeout_jump(stream, SMTP_ERR_EOF); + vstream_longjmp(stream, SMTP_ERR_EOF); } return (last_char); } @@ -308,7 +272,7 @@ void smtp_fputs(const char *cp, int todo, VSTREAM *stream) /* * Do the I/O, protected against timeout. */ - smtp_timeout_protect(stream); + smtp_timeout_reset(stream); err = (vstream_fwrite(stream, cp, todo) != todo || vstream_fputs("\r\n", stream) == VSTREAM_EOF); smtp_timeout_detect(stream); @@ -319,7 +283,7 @@ void smtp_fputs(const char *cp, int todo, VSTREAM *stream) if (err != 0) { if (msg_verbose) msg_info("smtp_fputs: EOF"); - smtp_timeout_jump(stream, SMTP_ERR_EOF); + vstream_longjmp(stream, SMTP_ERR_EOF); } } @@ -335,7 +299,7 @@ void smtp_fwrite(const char *cp, int todo, VSTREAM *stream) /* * Do the I/O, protected against timeout. */ - smtp_timeout_protect(stream); + smtp_timeout_reset(stream); err = (vstream_fwrite(stream, cp, todo) != todo); smtp_timeout_detect(stream); @@ -345,7 +309,7 @@ void smtp_fwrite(const char *cp, int todo, VSTREAM *stream) if (err != 0) { if (msg_verbose) msg_info("smtp_fwrite: EOF"); - smtp_timeout_jump(stream, SMTP_ERR_EOF); + vstream_longjmp(stream, SMTP_ERR_EOF); } } @@ -358,7 +322,7 @@ void smtp_fputc(int ch, VSTREAM *stream) /* * Do the I/O, protected against timeout. */ - smtp_timeout_protect(stream); + smtp_timeout_reset(stream); stat = VSTREAM_PUTC(ch, stream); smtp_timeout_detect(stream); @@ -368,6 +332,6 @@ void smtp_fputc(int ch, VSTREAM *stream) if (stat == VSTREAM_EOF) { if (msg_verbose) msg_info("smtp_fputc: EOF"); - smtp_timeout_jump(stream, SMTP_ERR_EOF); + vstream_longjmp(stream, SMTP_ERR_EOF); } } diff --git a/postfix/lmtp/.indent.pro b/postfix/lmtp/.indent.pro new file mode 100644 index 000000000..5fbb816df --- /dev/null +++ b/postfix/lmtp/.indent.pro @@ -0,0 +1,120 @@ +-TALIAS_TOKEN +-TARGV +-TBH_TABLE +-TBINATTR +-TBINATTR_INFO +-TBINHASH +-TBINHASH_INFO +-TBOUNCE_STAT +-TCLEANUP_STATE +-TCLIENT_LIST +-TCLNT_STREAM +-TCONFIG_BOOL_FN_TABLE +-TCONFIG_BOOL_TABLE +-TCONFIG_INT_FN_TABLE +-TCONFIG_INT_TABLE +-TCONFIG_STR_FN_TABLE +-TCONFIG_STR_TABLE +-TDELIVER_ATTR +-TDELIVER_REQUEST +-TDICT +-TDICT_DB +-TDICT_DBM +-TDICT_ENV +-TDICT_HT +-TDICT_LDAP +-TDICT_MYSQL +-TDICT_NI +-TDICT_NIS +-TDICT_NISPLUS +-TDICT_NODE +-TDICT_OPEN_INFO +-TDICT_PCRE +-TDICT_REGEXP +-TDICT_REGEXP_RULE +-TDICT_UNIX +-TDNS_FIXED +-TDNS_REPLY +-TDNS_RR +-TDOMAIN_LIST +-TEXPAND_ATTR +-TFILE +-TFORWARD_INFO +-THEADER_OPTS +-THOST +-THTABLE +-THTABLE_INFO +-TINET_ADDR_LIST +-TINT_TABLE +-TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE +-TLOCAL_EXP +-TLOCAL_STATE +-TMAC_EXP +-TMAC_HEAD +-TMAC_PARSE +-TMAIL_PRINT +-TMAIL_SCAN +-TMAPS +-TMASTER_PROC +-TMASTER_SERV +-TMASTER_STATUS +-TMBLOCK +-TMKMAP +-TMKMAP_OPEN_INFO +-TMULTI_SERVER +-TMVECT +-TMYSQL_NAME +-TNAMADR_LIST +-TNAME_MASK +-TPEER_NAME +-TPICKUP_INFO +-TPIPE_ATTR +-TPIPE_PARAMS +-TPLMYSQL +-TQMGR_ENTRY +-TQMGR_MESSAGE +-TQMGR_QUEUE +-TQMGR_RCPT_LIST +-TQMGR_RECIPIENT +-TQMGR_SCAN +-TQMGR_TRANSPORT +-TRECIPIENT +-TRECIPIENT_LIST +-TREC_TYPE_NAME +-TRESOLVE_REPLY +-TRESPONSE +-TSCAN_DIR +-TSCAN_INFO +-TSCAN_OBJ +-TSESSION +-TSINGLE_SERVER +-TSINK_COMMAND +-TSINK_STATE +-TSMTPD_CMD +-TSMTPD_STATE +-TSMTPD_TOKEN +-TSMTP_ADDR +-TSMTP_CMD +-TSMTP_RESP +-TSMTP_SESSION +-TSMTP_STATE +-TSOCKADDR_SIZE +-TSPAWN_ATTR +-TSTRING_TABLE +-TSYS_EXITS_TABLE +-TTOK822 +-TTRIGGER_SERVER +-TUSER_ATTR +-TVBUF +-TVSTREAM +-TVSTREAM_POPEN_ARGS +-TVSTRING +-TWAIT_STATUS_T +-TWATCHDOG +-TWATCH_FD +-Tsasl_conn_t +-Tsasl_secret_t diff --git a/postfix/lmtp/.printfck b/postfix/lmtp/.printfck new file mode 100644 index 000000000..65eb6bfa6 --- /dev/null +++ b/postfix/lmtp/.printfck @@ -0,0 +1,25 @@ +been_here_xt 2 0 +bounce_append 5 0 +cleanup_out_format 1 0 +defer_append 5 0 +mail_command 1 0 +mail_print 1 0 +msg_error 0 0 +msg_fatal 0 0 +msg_info 0 0 +msg_panic 0 0 +msg_warn 0 0 +opened 3 0 +post_mail_fprintf 1 0 +qmgr_message_bounce 2 0 +rec_fprintf 2 0 +sent 4 0 +smtp_cmd 1 0 +smtp_mesg_fail 2 0 +smtp_printf 1 0 +smtp_rcpt_fail 3 0 +smtp_site_fail 2 0 +udp_syslog 1 0 +vstream_fprintf 1 0 +vstream_printf 0 0 +vstring_sprintf 1 0 diff --git a/postfix/lmtp/CHANGES b/postfix/lmtp/CHANGES new file mode 100644 index 000000000..ddabbdfbb --- /dev/null +++ b/postfix/lmtp/CHANGES @@ -0,0 +1,146 @@ +2000 Feb 23 + +* lmtp.c, lmtp_connect.c, global-patch: added the main.cf + configuration parameter "lmtp_tcp_port". If no port is explicitly + specificed for the connection to the inet LMTP server, first + lookup "lmtp" with getservbyname. If that fails, use the value of + this "lmtp_tcp_port" parameter, which as a default value of 24. + + +2000 Feb 21 + +* Updated lmtp.c to yield a lmtp.8 man page that more accurately + reflects the current code. + +* Created man-patch so that lmtp.8 man page is created. Updated + README to include applying this patch. + + +2000 Feb 17 + +* Correctly handle lmtp master.cf arguments of type "inet". + Possible uses are: + + _USAGE_ _MEANING_ + serv=inet: connect over tcp to $nexthop + serv=inet:hostname connect to named host port 24 + (Actually, what "lmtp" is defined to be + in /etc/services.) + serv=inet:hostname:port connect to named host named port + serv=inet:[ip.address] connect to named host port 24 + serv=inet:[ip.address]:port connect to named address named port + + +2000 Feb 15 + +* Put in comment about local_destination_recipient_limit + in README.local. + +* In lmtp_chat.c, changed error reporting so that it goes + to var_error_rcpt, like the other Postfix services. + + +2000 Jan 31 + +* lmtp_proto.c:lmtp_lhlo: Don't worry about LMTP_FEATURE_PIPELINING + for sessions of type LMTP_SERV_TYPE_UNIX. + + +2000 Jan 30 + +* BIG changes. Removed all the pipe stuff from lmtp.c and + lmtp_connect.c Now, lmtp will either do a remote connection much + like the smtp client, or it will connect to a UNIX domain socket + to an auxiliary service (spawn). This makes the lmtp patch + simpler because it doesn't have to worry at all about exec-ing an + external command, and all the resulting security implications. + See README.local for details. + + NOTE: postfix-19991231-pl04 is REQUIRED for this to work! + (Need the "spawn" service.) + +* Updated the makefile-patch for postfix-19991231-pl03. + +* lmtp.h: changed the LMTP_ATTR structure to contain "type", "class", and + "name" fields, reflecting the change from the pipe mechanism to + the local sockets connection. Changed LMTP_SESSION to contain the + "type" field. The connection type is recorded in this field in + case it is needed elsewhere, like in lmtp_proto. + +* lmtp.c:get_service_attr altered for new command syntax. Examples: + + serv=unix:private/lmtpd + serv=inet:public/lmtpd + + After `serv=' is the "type", followed by `:', then the "class", + followed by '/', and then finally the "name". + +* Added SAME_DESTINATION macro to lmtp.c:deliver_message for + readability, and simpler logic. + +* lmtp_connect.c: changed lmtp_connect to contain logic to determine + just what kind of connection to make, removing it from lmtp.c; + removed lmtp_connect_pipe; and added lmtp_connect_local, which + uses mail_connect_wait to connect to the service running the LMTP + server (spawn). In lmtp_connect, the connection type stored in + attr.type is saved into session.type, so it can be used later if + necessary. + +* lmtp_proto.c:lmtp_lhlo: towards the end of this function we check + to see if service->type is LMTP_SERV_TYPE_UNIX so that we don't + try to do a getsockopt on a descriptor that doesn't support it. + Is this the best way to handle this? Or maybe we should just try + it and if it bombs, check the error code before simply going + fatal? + +* Snagged the latest quota_821_local.c from the smtp client. + +* Updated the README files accordingly. + + +2000 Jan 28 + +* Well, wondering about using REWRITE_ADDRESS in lmtp_proto.c, + putting the lowercase thing in there. Though, that would also + apply to sender address. Is that okay? + +* We shouldn't be doing DNS lookups at this stage, so can get rid + of lmtp_unalias.c. Also cut out the unalias portion in + REWRITE_ADDRESS in lmtp_proto.c + + +2000 Jan 11 + +* At the suggestion of Rupa Schomaker, added the following to + lmtp_proto.c: + + lowercase(rcpt->address); /* [AAG] rupa */ + + +1999 Dec 1 (or thereabouts) + +* Added lmtp_session_reset to make it easier to remember to reset + certain things to 0. Did this to lmtp_chat_reset too. + +* Finally, did some work with the connection caching so that an RSET + is sent to the LMTP server. This is done for two reasons: first, + to tell the LMTP server to flush out any status information + because we're about to feed it another message, and second, to + test the link to see if it is still alive. If the link has died + for some reason, it is reestablished. Changes to lmtp.c and + lmtp_proto.c. + +* There was also some tidying in lmtp.c so that things were a little + clearer as to what was going on. I added a few more comments as + well. + +* I tried to make the master.cf argument parsing a little more + consistent with the other Postfix services. Changes to lmtp.c. + +* On the postfix-users mailing list, Valery Brasseur pointed out that + errors encountered during LMTP delivery were not getting bounced + as appropriate. This lead me to investigate the matter, wanting + to put this LMTP capability into service myself. I then realized + that some of the error checking was a tad over zealous, and so + simplified things a bit in lmtp_proto.c. + diff --git a/postfix/lmtp/Makefile.in b/postfix/lmtp/Makefile.in new file mode 100644 index 000000000..60c904e2d --- /dev/null +++ b/postfix/lmtp/Makefile.in @@ -0,0 +1,61 @@ +SHELL = /bin/sh +SRCS = lmtp.c quote_821_local.c lmtp_connect.c lmtp_proto.c lmtp_chat.c \ + lmtp_session.c lmtp_addr.c lmtp_trouble.c lmtp_state.c +OBJS = lmtp.o quote_821_local.o lmtp_connect.o lmtp_proto.o lmtp_chat.o \ + lmtp_session.o lmtp_addr.o lmtp_trouble.o lmtp_state.o +HDRS = lmtp.h +TESTSRC = +WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ + -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ + -Wunused +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +TESTPROG= quote_821_local +PROG = lmtp +INC_DIR = ../include +LIBS = ../lib/libmaster.a ../lib/libglobal.a ../lib/libdns.a ../lib/libutil.a + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +Makefile: Makefile.in + (set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@ + +test: $(TESTPROG) + +update: ../libexec/$(PROG) + +../libexec/$(PROG): $(PROG) + cp $(PROG) ../libexec + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h printfck + sed '1,/^# do not edit/!d' Makefile >printfck/Makefile + set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done + cd printfck; make "INC_DIR=../../include" `cd ..; ls *.o` + +lint: + lint $(DEFS) $(SRCS) $(LINTFIX) + +clean: + rm -f *.o *core $(PROG) $(TESTPROG) junk + rm -rf printfck + +tidy: clean + +quote_821_local: quote_821_local.c $(LIBS) + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS) + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \ + done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @make -f Makefile.in Makefile + +# do not edit below this line - it is generated by 'make depend' diff --git a/postfix/lmtp/README b/postfix/lmtp/README new file mode 100644 index 000000000..1d3966773 --- /dev/null +++ b/postfix/lmtp/README @@ -0,0 +1,34 @@ +README + +The starting point for this work was the original LMTP patch, +lmtp-19990427.tar.gz, found in the experimental directory on the +Postfix mirror ftp sites. Without this original work by Philip A. +Prindeville, I probably wouldn't have taken the time to dive into +this issue. Fortunately, this individual's work gave me a good +starting point to learn about LMTP, and Postfix in general. + +First, in the Postfix source directory, apply the following patches: + + patch -p0 < lmtp/makefile-patch + patch -p0 < lmtp/global-patch + patch -p0 < lmtp/man-patch + +Then compile and install Postfix as usual. There really isn't +anything OS specific so it should compile without problems. + +NOTE: Previously this patch would do a pipe to an external command + to perform local delivery. That is no longer the case! This + version now requires the "spawn" service be defined. See + README.local for details. + +The other way to use this lmtp service is to talk to a remote LMTP +server. An example of this would be if an MX host is to insert +incoming mail directly into a message store that supports the LMTP +protocol. This is described in README.inet. + +The file CHANGES lists some of the changes that have been made +since lmtp-19990427.tar.gz, and TODO discusses some thoughts +that are currently floating around. + +Amos + diff --git a/postfix/lmtp/README.inet b/postfix/lmtp/README.inet new file mode 100644 index 000000000..d52190fe5 --- /dev/null +++ b/postfix/lmtp/README.inet @@ -0,0 +1,80 @@ +README.inet + +This is an example of how to set up a remote LMTP server. This can +be useful if you have a central/firewall mail router that feeds +incoming mail into the appropriate inbox server(s), the central box +can stuff the incoming mail directly into the message store on the +inbox machine using LMTP. This also means the inbox machine doesn't +have to have a SMTP server to handle this intermediate step. Tidy +all the way around. + + +** Inbox Server: + +On the inbox server, in this case a CMU Cyrus imapd/popd server, add +the following to /etc/services: + +pop3 110/tcp # Cyrus POP3 +imap 143/tcp # Cyrus IMAP4 +lmtp 24/tcp + + +Next, put the following in /etc/inetd.conf: + +lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/local/cyrus/bin/deliver -e -l + + +(/usr/sbin/tcpd is from the tcp_wrappers package. You want this to +make sure only your central box can do the stuffing.) + + +** Central Server: + +Similar changes to /etc/services: + +lmtp 24/tcp + + +Now we add this to /etc/postfix/master.cf: + +lmtp unix - - n - - lmtp + + +NOTES: No arguments are specified to lmtp! + Root privs are not necessary! + +We put this in /etc/postfix/transport: + +inbox.domain.org lmtp:inbox.domain.org + + +Naturally, this means we also have to have in +/etc/postfix/main.cf: + +transport_maps = hash:/etc/postfix/transport + + +Use the map type of your choice. + +That's it. + + +Oh, it may be necessary to apply the following patch to deliver so +that the final "bye" is not lost. This should not be necessary in +releases subsequent to 1.6.20 of cyrus-imapd. + +*** deliver.c._orig Tue Dec 21 11:12:47 1999 +--- deliver.c Wed Dec 22 20:12:54 1999 +*************** +*** 1753,1758 **** +--- 1753,1759 ---- + case 'Q': + if (!strcasecmp(buf, "quit")) { + prot_printf(deliver_out,"221 2.0.0 bye\r\n"); ++ prot_flush(deliver_out); + exit(0); + } + goto syntaxerr; + + + diff --git a/postfix/lmtp/README.local b/postfix/lmtp/README.local new file mode 100644 index 000000000..a6286ec00 --- /dev/null +++ b/postfix/lmtp/README.local @@ -0,0 +1,50 @@ +README.local + +This file describes how to use the lmtp service for local delivery. +You'll need postfix-19991231-pl04 or later for this to work because +it relies on the "spawn" service. + +Configure your Postfix as follows: + +/etc/postfix/master.cf: + +#local unix - n n - - local +local unix - - n - - lmtp + serv=unix:private/lmtpd +lmtpd unix - n n - - spawn + user=cyrus:cyrus argv=/usr/local/cyrus/bin/deliver -e -l + + +First, we comment out the original "local" service and define +a new one based on the "lmtp" client. The "serv=" argument says +what LMTP server we're to talk to, in this case the "lmtpd" +service. + +The `-l' option to deliver tells it to go into LMTP mode, and the +`-e' tells it to use the duplicate delivery database, which is +required in order to use the vacation features of Sieve, the +filtering language in Cyrus 1.6.X. + +A note about spawn, this is a new experimental service. The +makefile-patch included with this bundle will add spawn to the +compile targets. However, you may need to check that it actually +gets installed. + +A note about local delivery and the number of recipients. Starting +with postfix-19991231-pl04, it is now possible to specify the +maximum number of recipients per message for local delivery. By +default, this is set to 1 as follows: + + local_destination_recipient_limit = 1 + +You can set it to zero (means no limit) or, safer, set it to some +reasonable number so that your machine doesn't risk running out of +resources on a message with an inordinate number of recipients. + +Why is this of interest? Well, if a message contains multiple +recipients, and all these recipients happen to be on the same Cyrus +partition, then recent (1.6.X) releases of deliver will hard link +the message to each recipient instead of each recipient getting a +copy. So you'll probably want to set the above mail.cf value to +something reasonable to take advantage of this feature in Cyrus. + diff --git a/postfix/lmtp/fixnames b/postfix/lmtp/fixnames new file mode 100644 index 000000000..e6545ff75 --- /dev/null +++ b/postfix/lmtp/fixnames @@ -0,0 +1,5 @@ +sed ' + s/LMTP/SMTP/g + s/lmtp/smtp/g + s/host/namaddr/g +' $* diff --git a/postfix/lmtp/global-patch b/postfix/lmtp/global-patch new file mode 100644 index 000000000..cf604b93b --- /dev/null +++ b/postfix/lmtp/global-patch @@ -0,0 +1,65 @@ +*** ../../orig/global/mail_params.h Thu Jan 27 20:05:29 2000 +--- global/mail_params.h Wed Feb 23 01:26:01 2000 +*************** +*** 624,629 **** +--- 624,683 ---- + extern int var_smtpd_err_sleep; + + /* ++ * LMTP client. Timeouts inspired by RFC 1123. The LMTP recipient limit ++ * determines how many recipient addresses the LMTP client sends along with ++ * each message. Unfortunately, some mailers misbehave and disconnect (smap) ++ * when given more recipients than they are willing to handle. ++ */ ++ #define VAR_LMTP_TCP_PORT "lmtp_tcp_port" ++ #define DEF_LMTP_TCP_PORT 24 ++ extern int var_lmtp_tcp_port; ++ ++ #define VAR_LMTP_CACHE_CONN "lmtp_cache_connection" ++ #define DEF_LMTP_CACHE_CONN 1 ++ extern bool var_lmtp_cache_conn; ++ ++ #define VAR_LMTP_SKIP_QUIT_RESP "lmtp_skip_quit_response" ++ #define DEF_LMTP_SKIP_QUIT_RESP 0 ++ extern bool var_lmtp_skip_quit_resp; ++ ++ #define VAR_LMTP_CONN_TMOUT "lmtp_connect_timeout" ++ #define DEF_LMTP_CONN_TMOUT 0 ++ extern int var_lmtp_conn_tmout; ++ ++ #define VAR_LMTP_RSET_TMOUT "lmtp_rset_timeout" ++ #define DEF_LMTP_RSET_TMOUT 300 ++ extern int var_lmtp_rset_tmout; ++ ++ #define VAR_LMTP_LHLO_TMOUT "lmtp_lhlo_timeout" ++ #define DEF_LMTP_LHLO_TMOUT 300 ++ extern int var_lmtp_lhlo_tmout; ++ ++ #define VAR_LMTP_MAIL_TMOUT "lmtp_mail_timeout" ++ #define DEF_LMTP_MAIL_TMOUT 300 ++ extern int var_lmtp_mail_tmout; ++ ++ #define VAR_LMTP_RCPT_TMOUT "lmtp_rcpt_timeout" ++ #define DEF_LMTP_RCPT_TMOUT 300 ++ extern int var_lmtp_rcpt_tmout; ++ ++ #define VAR_LMTP_DATA0_TMOUT "lmtp_data_init_timeout" ++ #define DEF_LMTP_DATA0_TMOUT 120 ++ extern int var_lmtp_data0_tmout; ++ ++ #define VAR_LMTP_DATA1_TMOUT "lmtp_data_xfer_timeout" ++ #define DEF_LMTP_DATA1_TMOUT 180 ++ extern int var_lmtp_data1_tmout; ++ ++ #define VAR_LMTP_DATA2_TMOUT "lmtp_data_done_timeout" ++ #define DEF_LMTP_DATA2_TMOUT 600 ++ extern int var_lmtp_data2_tmout; ++ ++ #define VAR_LMTP_QUIT_TMOUT "lmtp_quit_timeout" ++ #define DEF_LMTP_QUIT_TMOUT 300 ++ extern int var_lmtp_quit_tmout; ++ ++ /* + * Cleanup service. Header info that exceeds $header_size_limit bytes forces + * the start of the message body. + */ diff --git a/postfix/lmtp/lmtp.c b/postfix/lmtp/lmtp.c new file mode 100644 index 000000000..3ed78e830 --- /dev/null +++ b/postfix/lmtp/lmtp.c @@ -0,0 +1,608 @@ +/*++ +/* NAME +/* lmtp 8 +/* SUMMARY +/* Postfix local delivery via LMTP +/* SYNOPSIS +/* \fBlmtp\fR [generic Postfix daemon options] [server attributes...] +/* DESCRIPTION +/* The LMTP client processes message delivery requests from +/* the queue manager. Each request specifies a queue file, a sender +/* address, a domain or host to deliver to, and recipient information. +/* This program expects to be run from the \fBmaster\fR(8) process +/* manager. +/* +/* The LMTP client updates the queue file and marks recipients +/* as finished, or it informs the queue manager that delivery should +/* be tried again at a later time. Delivery problem reports are sent +/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. +/* +/* There are two basic modes of operation for the LMTP client: +/* .IP \(bu +/* Communication with a local LMTP server via UNIX domain sockets. +/* .IP \(bu +/* Communication with a (possibly remote) LMTP server via +/* Internet sockets. +/* .PP +/* If no server attributes are specified, the LMTP client will contact +/* the destination host derived from the message delivery request using +/* the TCP port defined as \fBlmtp\fR in \fBservices\fR(4). If no such +/* service is found, the \fBlmtp_tcp_port\fR configuration parameter +/* (default value of 24) will be used. +/* +/* In order to use a local LMTP server, this LMTP server will need to +/* be specified via the server attributes described in the following +/* section. Typically, the LMTP client would also be configured as the +/* \fBlocal\fR delivery agent in the \fBmaster.cf\fR file. +/* SERVER ATTRIBUTE SYNTAX +/* .ad +/* .fi +/* The server attributes are given in the \fBmaster.cf\fR file at +/* the end of a service definition. The syntax is as follows: +/* .IP "\fBserv\fR=\fItype\fR:\fIserver\fR" +/* The LMTP server to connect to for final delivery. The \fItype\fR +/* portion can be either \fBunix\fR or \fBinet\fR. The \fIserver\fR +/* portion is the path or address of the LMTP server, depending on the +/* value of \fItype\fR, as shown below: +/* .RS +/* .IP "\fBserv=unix:\fR\fIclass\fR\fB/\fR\fIservname\fR" +/* This specifies that the local LMTP server \fIservname\fR should be +/* contacted for final delivery. Both \fIclass\fR (either \fBpublic\fR +/* or \fBprivate\fR) and \fIservname\fR correspond to the LMTP server +/* entry in the \fBmaster.cf\fR file. This LMTP server will likely +/* be defined as a \fBspawn\fR(8) service. +/* .IP "\fBserv=inet:" +/* If nothing follows the \fBinet:\fR type specifier, a connection will +/* be attempted to the destination host indicated in the delivery request. +/* This simplest case is identical to defining the LMTP client without +/* any server attributes at all. +/* .IP "\fBserv=inet:\fR\fIaddress\fR" +/* In this case, an Internet socket will be made to the server +/* specified by \fIaddress\fR. The connection will use a destination +/* port as described in the previous section. +/* .IP "\fBserv=inet:\fR\fIaddress\fR\fB:\fR\fIport\fR" +/* Connect to the LMTP server at \fIaddress\fR, but this time use port +/* \fIport\fR instead of the default \fBlmtp\fR port. +/* .IP "\fBserv=inet:[\fR\fIipaddr\fR\fB]\fR" +/* The LMTP server to contact is specified using an Internet address +/* in the "dot notation". That is, the numeric IP address rather +/* than the DNS name for the server. The default \fBlmtp\fR port +/* is used. +/* .IP "\fBserv=inet:[\fR\fIipaddr\fR\fB]:\fR\fIport\fR" +/* The LMTP server to contact is specified using the numeric IP address, +/* at the port specified. +/* .RE +/* .PP +/* SECURITY +/* .ad +/* .fi +/* The LMTP client is moderately security-sensitive. It talks to LMTP +/* servers and to DNS servers on the network. The LMTP client can be +/* run chrooted at fixed low privilege. +/* STANDARDS +/* RFC 2033 (LMTP protocol) +/* RFC 821 (SMTP protocol) +/* RFC 1651 (SMTP service extensions) +/* RFC 1870 (Message Size Declaration) +/* RFC 2197 (Pipelining) +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8). +/* Corrupted message files are marked so that the queue manager can +/* move them to the \fBcorrupt\fR queue for further inspection. +/* +/* Depending on the setting of the \fBnotify_classes\fR parameter, +/* the postmaster is notified of bounces, protocol problems, and of +/* other trouble. +/* BUGS +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially relevant to +/* this program. See the Postfix \fBmain.cf\fR file for syntax details +/* and for default values. Use the \fBpostfix reload\fR command after +/* a configuration change. +/* .SH Miscellaneous +/* .ad +/* .fi +/* .IP \fBdebug_peer_level\fR +/* Verbose logging level increment for hosts that match a +/* pattern in the \fBdebug_peer_list\fR parameter. +/* .IP \fBdebug_peer_list\fR +/* List of domain or network patterns. When a remote host matches +/* a pattern, increase the verbose logging level by the amount +/* specified in the \fBdebug_peer_level\fR parameter. +/* .IP \fBerror_notice_recipient\fR +/* Recipient of protocol/policy/resource/software error notices. +/* .IP \fBnotify_classes\fR +/* When this parameter includes the \fBprotocol\fR class, send mail to the +/* postmaster with transcripts of LMTP sessions with protocol errors. +/* .IP \fBlmtp_skip_quit_response\fR +/* Do not wait for the server response after sending QUIT. +/* .IP \fBlmtp_tcp_port\fR +/* The TCP port to be used when connecting to a LMTP server. Used as +/* backup if the \fBlmtp\fR service is not found in \fBservices\fR(4). +/* .SH "Resource controls" +/* .ad +/* .fi +/* .IP \fBlmtp_cache_connection\fR +/* Should we cache the connection to the LMTP server? The effectiveness +/* of cached connections will be determined by the number of LMTP servers +/* in use, and the concurrency limit specified for the LMTP client. +/* Cached connections are closed under any of the following conditions: +/* .RS +/* .IP \(bu +/* The idle timeout for the LMTP client is reached. This limit is +/* enforced by \fBmaster\fR(8). +/* .IP \(bu +/* A message request to a different destination than the one currently +/* cached. +/* .IP \(bu +/* The maximum number of requests per session is reached. This limit is +/* enforced by \fBmaster\fR(8). +/* .IP \(bu +/* Upon the onset of another delivery request, the LMTP server associated +/* with the current session does not respond to the \fBRSET\fR command. +/* .RE +/* .IP \fBlmtp_destination_concurrency_limit\fR +/* Limit the number of parallel deliveries to the same destination. +/* The default limit is taken from the +/* \fBdefault_destination_concurrency_limit\fR parameter. +/* .IP \fBlmtp_destination_recipient_limit\fR +/* Limit the number of recipients per message delivery. +/* The default limit is taken from the +/* \fBdefault_destination_recipient_limit\fR parameter. +/* .IP \fBlocal_destination_recipient_limit\fR +/* Limit the number of recipients per message delivery. +/* The default limit is taken from the +/* \fBdefault_destination_recipient_limit\fR parameter. +/* +/* This parameter becomes significant if the LMTP client is used +/* for local delivery. Some LMTP servers can optimize final delivery +/* if multiple recipients are allowed. Therefore, it may be advantageous +/* to set this to some number greater than one, depending on the capabilities +/* of the machine. +/* +/* Setting this parameter to 0 will lead to an unlimited number of +/* recipients per delivery. However, this could be risky since it may +/* make the machine vulnerable to running out of resources if messages +/* are encountered with an inordinate number of recipients. Exercise +/* care when setting this parameter. +/* .SH "Timeout controls" +/* .ad +/* .fi +/* .IP \fBlmtp_connect_timeout\fR +/* Timeout in seconds for opening a connection to the LMTP server. +/* If no connection can be made within the deadline, the message +/* is deferred. +/* .IP \fBlmtp_lhlo_timeout\fR +/* Timeout in seconds for sending the \fBLHLO\fR command, and for +/* receiving the server response. +/* .IP \fBlmtp_mail_timeout\fR +/* Timeout in seconds for sending the \fBMAIL FROM\fR command, and for +/* receiving the server response. +/* .IP \fBlmtp_rcpt_timeout\fR +/* Timeout in seconds for sending the \fBRCPT TO\fR command, and for +/* receiving the server response. +/* .IP \fBlmtp_data_init_timeout\fR +/* Timeout in seconds for sending the \fBDATA\fR command, and for +/* receiving the server response. +/* .IP \fBlmtp_data_xfer_timeout\fR +/* Timeout in seconds for sending the message content. +/* .IP \fBlmtp_data_done_timeout\fR +/* Timeout in seconds for sending the "\fB.\fR" command, and for +/* receiving the server response. When no response is received, a +/* warning is logged that the mail may be delivered multiple times. +/* .IP \fBlmtp_rset_timeout\fR +/* Timeout in seconds for sending the \fBRSET\fR command, and for +/* receiving the server response. +/* .IP \fBlmtp_quit_timeout\fR +/* Timeout in seconds for sending the \fBQUIT\fR command, and for +/* receiving the server response. +/* SEE ALSO +/* bounce(8) non-delivery status reports +/* local(8) local mail delivery +/* master(8) process manager +/* qmgr(8) queue manager +/* services(4) Internet services and aliases +/* spawn(8) auxiliary command spawner +/* syslogd(8) system logging +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Single server skeleton. */ + +#include + +/* Application-specific. */ + +#include "lmtp.h" + + /* + * Tunable parameters. These have compiled-in defaults that can be overruled + * by settings in the global Postfix configuration file. + */ +int var_lmtp_tcp_port; +int var_lmtp_conn_tmout; +int var_lmtp_rset_tmout; +int var_lmtp_lhlo_tmout; +int var_lmtp_mail_tmout; +int var_lmtp_rcpt_tmout; +int var_lmtp_data0_tmout; +int var_lmtp_data1_tmout; +int var_lmtp_data2_tmout; +int var_lmtp_quit_tmout; +char *var_debug_peer_list; +int var_debug_peer_level; +int var_lmtp_cache_conn; +int var_lmtp_skip_quit_resp; +char *var_notify_classes; +char *var_error_rcpt; + + /* + * Global variables. + * + * lmtp_errno is set by the address lookup routines and by the connection + * management routines. + * + * state is global for the connection caching to work. + */ +int lmtp_errno; +static LMTP_STATE *state = 0; + + +/* get_service_attr - get command-line attributes */ + +static LMTP_ATTR *get_service_attr(char **argv) +{ + char *myname = "get_service_attr"; + LMTP_ATTR *attr = (LMTP_ATTR *) mymalloc(sizeof(*attr)); + char *type; + char *dest; + char *name; + + /* + * Initialize. + */ + attr->type = 0; + attr->class = ""; + attr->name = ""; + + /* + * Iterate over the command-line attribute list. + */ + if (msg_verbose) + msg_info("%s: checking argv for lmtp server", myname); + + for ( /* void */ ; *argv != 0; argv++) { + + /* + * Are we configured to speak to a particular LMTP server? + */ + if (strncasecmp("serv=", *argv, sizeof("serv=") - 1) == 0) { + type = *argv + sizeof("serv=") - 1; + if ((dest = split_at(type, ':')) == 0) /* XXX clobbers argv */ + msg_fatal("%s: invalid serv= arguments: %s", myname, *argv); + + /* + * What kind of socket connection are we to make? + */ + if (strcasecmp("unix", type) == 0) { + attr->type = LMTP_SERV_TYPE_UNIX; + attr->class = dest; + if ((name = split_at(dest, '/')) == 0) /* XXX clobbers argv */ + msg_fatal("%s: invalid serv= arguments: %s", myname, *argv); + attr->name = name; + } else if (strcasecmp("inet", type) == 0) { + attr->type = LMTP_SERV_TYPE_INET; + attr->name = dest; + } else + msg_fatal("%s: invalid serv= arguments: %s", myname, *argv); + break; + } + + /* + * Bad. + */ + else + msg_fatal("%s: unknown attribute name: %s", myname, *argv); + } + + /* + * Give the poor tester a clue of what is going on. + */ + if (msg_verbose) + msg_info("%s: type %d, class \"%s\", name \"%s\".", myname, + attr->type, attr->class, attr->name); + return (attr); +} + +/* deliver_message - deliver message with extreme prejudice */ + +static int deliver_message(DELIVER_REQUEST *request, char **argv) +{ + char *myname = "deliver_message"; + static LMTP_ATTR *attr = 0; + VSTRING *why; + int result; + + /* + * Macro for readability. We're going to the same destination if the + * destination was specified on the command line (attr->name is not + * null), or if the destination of the current session is the same as + * request->nexthop. + */ +#define SAME_DESTINATION() \ + (*(attr)->name \ + || strcasecmp(state->session->destination, request->nexthop) == 0) + + if (msg_verbose) + msg_info("%s: from %s", myname, request->sender); + + /* + * Sanity checks. + */ + if (attr == 0) + attr = get_service_attr(argv); + if (request->rcpt_list.len <= 0) + msg_fatal("%s: recipient count: %d", myname, request->rcpt_list.len); + + /* + * Initialize. Bundle all information about the delivery request, so that + * we can produce understandable diagnostics when something goes wrong + * many levels below. The alternative would be to make everything global. + * + * Note: `state' is global (to this file) so that we can close a cached + * connection via the MAIL_SERVER_EXIT function (cleanup). The alloc for + * `state' is performed in the MAIL_SERVER_PRE_INIT function (pre_init). + * + */ + why = vstring_alloc(100); + state->request = request; + state->src = request->fp; + + /* + * See if we can reuse an existing connection. + */ + if (state->session != 0) { + + /* + * Session already exists from a previous delivery. If we're not + * going to the same destination as before, disconnect and establish + * a connection to the specified destination. + */ + if (!SAME_DESTINATION()) { + lmtp_quit(state); + lmtp_chat_reset(state); + lmtp_session_reset(state); + debug_peer_restore(); + } + + /* + * Probe the session by sending RSET. If the connection is broken, + * clean up our side of the connection. + */ + else if (lmtp_rset(state) != 0) { + lmtp_chat_reset(state); + lmtp_session_reset(state); + debug_peer_restore(); + } + + /* + * Ready to go with another load. + */ + else { + ++state->reuse; + if (msg_verbose) + msg_info("%s: reusing (count %d) session with: %s", + myname, state->reuse, state->session->host); + } + } + + /* + * If no LMTP session exists, establish one. + */ + if (state->session == 0) { + + /* + * Bounce or defer the recipients if no connection can be made. + */ + state->session = lmtp_connect(attr, request, why); + if (state->session == 0) { + lmtp_site_fail(state, lmtp_errno == LMTP_RETRY ? 450 : 550, + "%s", vstring_str(why)); + } + + /* + * Further check connection by sending the LHLO greeting. If we + * cannot talk LMTP to this destination give up, at least for now. + */ + else { + debug_peer_check(state->session->host, state->session->addr); + if (lmtp_lhlo(state) != 0) { + lmtp_session_reset(state); + debug_peer_restore(); + } + } + + } + + /* + * If a session exists, deliver this message to all requested recipients. + * + */ + if (state->session != 0) + lmtp_xfer(state); + + /* + * At the end, notify the postmaster of any protocol errors. + */ + if (state->history != 0 + && (state->error_mask + & name_mask(mail_error_masks, var_notify_classes))) + lmtp_chat_notify(state); + + /* + * Disconnect if we're not cacheing connections. + */ + if (!var_lmtp_cache_conn && state->session != 0) { + lmtp_quit(state); + lmtp_session_reset(state); + debug_peer_restore(); + } + + /* + * Clean up. + */ + vstring_free(why); + result = state->status; + lmtp_chat_reset(state); + + return (result); +} + +/* lmtp_service - perform service for client */ + +static void lmtp_service(VSTREAM *client_stream, char *unused_service, char **argv) +{ + DELIVER_REQUEST *request; + int status; + + /* + * This routine runs whenever a client connects to the UNIX-domain socket + * dedicated to remote LMTP delivery service. What we see below is a + * little protocol to (1) tell the queue manager that we are ready, (2) + * read a request from the queue manager, and (3) report the completion + * status of that request. All connection-management stuff is handled by + * the common code in single_server.c. + */ + if ((request = deliver_request_read(client_stream)) != 0) { + status = deliver_message(request, argv); + deliver_request_done(client_stream, request, status); + } +} + +/* pre_init - pre-jail initialization */ + +static void pre_init(char *unused_name, char **unused_argv) +{ + debug_peer_init(); + state = lmtp_state_alloc(); +} + +/* cleanup - close any open connections, etc. */ + +static void cleanup() +{ + if (state == 0) + return; + + if (state->session != 0) { + lmtp_quit(state); + lmtp_chat_reset(state); + lmtp_session_free(state->session); + debug_peer_restore(); + if (msg_verbose) + msg_info("cleanup: just closed down session"); + } + lmtp_state_free(state); +} + +/* pre_accept - see if tables have changed + +static void pre_accept(char *unused_name, char **unused_argv) +{ + if (dict_changed()) { + msg_info("table has changed -- exiting"); + cleanup(); + exit(0); + } +} + + +/* + main - pass control to the single-threaded skeleton +*/ + +int main(int argc, char **argv) +{ + static CONFIG_STR_TABLE str_table[] = { + VAR_DEBUG_PEER_LIST, DEF_DEBUG_PEER_LIST, &var_debug_peer_list, 0, 0, + VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0, + VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0, + 0, + }; + static CONFIG_INT_TABLE int_table[] = { + VAR_LMTP_TCP_PORT, DEF_LMTP_TCP_PORT, &var_lmtp_tcp_port, 0, 0, + VAR_LMTP_CONN_TMOUT, DEF_LMTP_CONN_TMOUT, &var_lmtp_conn_tmout, 0, 0, + VAR_LMTP_RSET_TMOUT, DEF_LMTP_RSET_TMOUT, &var_lmtp_rset_tmout, 1, 0, + VAR_LMTP_LHLO_TMOUT, DEF_LMTP_LHLO_TMOUT, &var_lmtp_lhlo_tmout, 1, 0, + VAR_LMTP_MAIL_TMOUT, DEF_LMTP_MAIL_TMOUT, &var_lmtp_mail_tmout, 1, 0, + VAR_LMTP_RCPT_TMOUT, DEF_LMTP_RCPT_TMOUT, &var_lmtp_rcpt_tmout, 1, 0, + VAR_LMTP_DATA0_TMOUT, DEF_LMTP_DATA0_TMOUT, &var_lmtp_data0_tmout, 1, 0, + VAR_LMTP_DATA1_TMOUT, DEF_LMTP_DATA1_TMOUT, &var_lmtp_data1_tmout, 1, 0, + VAR_LMTP_DATA2_TMOUT, DEF_LMTP_DATA2_TMOUT, &var_lmtp_data2_tmout, 1, 0, + VAR_LMTP_QUIT_TMOUT, DEF_LMTP_QUIT_TMOUT, &var_lmtp_quit_tmout, 1, 0, + VAR_DEBUG_PEER_LEVEL, DEF_DEBUG_PEER_LEVEL, &var_debug_peer_level, 1, 0, + 0, + }; + static CONFIG_BOOL_TABLE bool_table[] = { + VAR_LMTP_CACHE_CONN, DEF_LMTP_CACHE_CONN, &var_lmtp_cache_conn, + VAR_LMTP_SKIP_QUIT_RESP, DEF_LMTP_SKIP_QUIT_RESP, &var_lmtp_skip_quit_resp, + 0, + }; + + single_server_main(argc, argv, lmtp_service, + MAIL_SERVER_INT_TABLE, int_table, + MAIL_SERVER_STR_TABLE, str_table, + MAIL_SERVER_BOOL_TABLE, bool_table, + MAIL_SERVER_PRE_INIT, pre_init, + MAIL_SERVER_PRE_ACCEPT, pre_accept, + MAIL_SERVER_EXIT, cleanup, + 0); +} diff --git a/postfix/lmtp/lmtp.h b/postfix/lmtp/lmtp.h new file mode 100644 index 000000000..b93d53e9f --- /dev/null +++ b/postfix/lmtp/lmtp.h @@ -0,0 +1,155 @@ +/*++ +/* NAME +/* lmtp 3h +/* SUMMARY +/* lmtp client program +/* SYNOPSIS +/* #include "lmtp.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include +#include + + /* + * Global library. + */ +#include + + /* + * State information associated with each LMTP delivery. We're bundling the + * state so that we can give meaningful diagnostics in case of problems. + */ +typedef struct LMTP_STATE { + VSTREAM *src; /* queue file stream */ + DELIVER_REQUEST *request; /* envelope info, offsets */ + struct LMTP_SESSION *session; /* network connection */ + VSTRING *buffer; /* I/O buffer */ + VSTRING *scratch; /* scratch buffer */ + VSTRING *scratch2; /* scratch buffer */ + int status; /* delivery status */ + int features; /* server features */ + ARGV *history; /* transaction log */ + int error_mask; /* error classes */ + int sndbufsize; /* total window size */ + int sndbuffree; /* remaining window */ + int reuse; /* connection being reused */ +} LMTP_STATE; + +#define LMTP_FEATURE_ESMTP (1<<0) +#define LMTP_FEATURE_8BITMIME (1<<1) +#define LMTP_FEATURE_PIPELINING (1<<2) +#define LMTP_FEATURE_SIZE (1<<3) + + /* + * lmtp.c + */ +extern int lmtp_errno; /* XXX can we get rid of this? */ + + /* + * Structure for connection to LMTP server. + */ +typedef struct LMTP_ATTR { + int type; /* UNIX-domain, INET, etc. */ + char *class; /* class ("public" or "private") */ + char *name; /* service endpoint name */ +} LMTP_ATTR; + + /* + * Service types. + */ +#define LMTP_SERV_TYPE_UNIX 1 /* AF_UNIX domain socket */ +#define LMTP_SERV_TYPE_INET 2 /* AF_INET domain socket */ + + /* + * lmtp_session.c + */ +typedef struct LMTP_SESSION { + VSTREAM *stream; /* network connection */ + char *host; /* mail exchanger */ + char *addr; /* mail exchanger */ + char *destination; /* domain originally sent to */ + int type; /* type of connection */ +} LMTP_SESSION; + +extern LMTP_SESSION *lmtp_session_alloc(VSTREAM *, char *, char *); +extern void lmtp_session_free(LMTP_SESSION *); +extern void lmtp_session_reset(LMTP_STATE *); + + /* + * lmtp_connect.c + */ +extern LMTP_SESSION *lmtp_connect(LMTP_ATTR *, DELIVER_REQUEST *request, VSTRING *); +extern LMTP_SESSION *lmtp_connect_host(char *, unsigned, VSTRING *); +extern LMTP_SESSION *lmtp_connect_local(const char *, const char *, VSTRING *); + + /* + * lmtp_proto.c + */ +extern int lmtp_lhlo(LMTP_STATE *); +extern int lmtp_xfer(LMTP_STATE *); +extern int lmtp_quit(LMTP_STATE *); +extern int lmtp_rset(LMTP_STATE *); + + /* + * lmtp_chat.c + */ +typedef struct LMTP_RESP { /* server response */ + int code; /* status */ + char *str; /* text */ + VSTRING *buf; /* origin of text */ +} LMTP_RESP; + +extern void lmtp_chat_cmd(LMTP_STATE *, char *,...); +extern LMTP_RESP *lmtp_chat_resp(LMTP_STATE *); +extern void lmtp_chat_reset(LMTP_STATE *); +extern void lmtp_chat_notify(LMTP_STATE *); + + /* + * lmtp_trouble.c + */ +extern int lmtp_conn_fail(LMTP_STATE *, int, char *,...); +extern int lmtp_site_fail(LMTP_STATE *, int, char *,...); +extern int lmtp_mesg_fail(LMTP_STATE *, int, char *,...); +extern void lmtp_rcpt_fail(LMTP_STATE *, int, RECIPIENT *, char *,...); +extern int lmtp_stream_except(LMTP_STATE *, int, char *); + + /* + * lmtp_state.c + */ +extern LMTP_STATE *lmtp_state_alloc(void); +extern void lmtp_state_free(LMTP_STATE *); + + /* + * Status codes. Errors must have negative codes so that they do not + * interfere with useful counts of work done. + */ +#define LMTP_OK 0 /* so far, so good */ +#define LMTP_RETRY (-1) /* transient error */ +#define LMTP_FAIL (-2) /* hard error */ + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ diff --git a/postfix/lmtp/lmtp_addr.c b/postfix/lmtp/lmtp_addr.c new file mode 100644 index 000000000..69c49a510 --- /dev/null +++ b/postfix/lmtp/lmtp_addr.c @@ -0,0 +1,202 @@ +/*++ +/* NAME +/* lmtp_addr 3 +/* SUMMARY +/* LMTP server address lookup +/* SYNOPSIS +/* #include "lmtp_addr.h" +/* +/* DNS_RR *lmtp_host_addr(name, why) +/* char *name; +/* VSTRING *why; +/* DESCRIPTION +/* This module implements Internet address lookups. By default, +/* lookups are done via the Internet domain name service (DNS). +/* A reasonable number of CNAME indirections is permitted. +/* +/* lmtp_host_addr() looks up all addresses listed for the named +/* host. The host can be specified as a numerical Internet network +/* address, or as a symbolic host name. +/* +/* Fortunately, we don't have to worry about MX records because +/* those are for SMTP servers, not LMTP servers. +/* +/* Results from lmtp_host_addr() are destroyed by dns_rr_free(), +/* including null lists. +/* DIAGNOSTICS +/* This routine either returns a DNS_RR pointer, or return a null +/* pointer and sets the \fIlmtp_errno\fR global variable accordingly: +/* .IP LMTP_RETRY +/* The request failed due to a soft error, and should be retried later. +/* .IP LMTP_FAIL +/* The request attempt failed due to a hard error. +/* .PP +/* In addition, a textual description of the problem is made available +/* via the \fIwhy\fR argument. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* DNS library. */ + +#include + +/* Application-specific. */ + +#include "lmtp.h" +#include "lmtp_addr.h" + +/* lmtp_print_addr - print address list */ + +static void lmtp_print_addr(char *what, DNS_RR *addr_list) +{ + DNS_RR *addr; + struct in_addr in_addr; + + msg_info("begin %s address list", what); + for (addr = addr_list; addr; addr = addr->next) { + if (addr->data_len > sizeof(addr)) { + msg_warn("skipping address length %d", addr->data_len); + } else { + memcpy((char *) &in_addr, addr->data, sizeof(in_addr)); + msg_info("pref %4d host %s/%s", + addr->pref, addr->name, + inet_ntoa(in_addr)); + } + } + msg_info("end %s address list", what); +} + +/* lmtp_addr_one - address lookup for one host name */ + +static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref, VSTRING *why) +{ + char *myname = "lmtp_addr_one"; + struct in_addr inaddr; + DNS_FIXED fixed; + DNS_RR *addr = 0; + DNS_RR *rr; + struct hostent *hp; + + if (msg_verbose) + msg_info("%s: host %s", myname, host); + + /* + * Interpret a numerical name as an address. + */ + if (ISDIGIT(host[0]) && (inaddr.s_addr = inet_addr(host)) != INADDR_NONE) { + memset((char *) &fixed, 0, sizeof(fixed)); + return (dns_rr_append(addr_list, + dns_rr_create(host, &fixed, pref, + (char *) &inaddr, sizeof(inaddr)))); + } + + /* + * Use gethostbyname() when DNS is disabled. + */ + if (var_disable_dns) { + memset((char *) &fixed, 0, sizeof(fixed)); + if ((hp = gethostbyname(host)) == 0) { + vstring_sprintf(why, "%s: host not found", host); + lmtp_errno = LMTP_FAIL; + } else if (hp->h_addrtype != AF_INET) { + vstring_sprintf(why, "%s: host not found", host); + msg_warn("%s: unknown address family %d for %s", + myname, hp->h_addrtype, host); + lmtp_errno = LMTP_FAIL; + } else { + while (hp->h_addr_list[0]) { + addr_list = dns_rr_append(addr_list, + dns_rr_create(host, &fixed, pref, + hp->h_addr_list[0], + sizeof(inaddr))); + hp->h_addr_list++; + } + } + return (addr_list); + } + + /* + * Append the addresses for this host to the address list. + */ + switch (dns_lookup(host, T_A, 0, &addr, (VSTRING *) 0, why)) { + case DNS_OK: + for (rr = addr; rr; rr = rr->next) + rr->pref = pref; + addr_list = dns_rr_append(addr_list, addr); + break; + default: + lmtp_errno = LMTP_RETRY; + break; + case DNS_NOTFOUND: + case DNS_FAIL: + lmtp_errno = LMTP_FAIL; + break; + } + return (addr_list); +} + +/* lmtp_host_addr - direct host lookup */ + +DNS_RR *lmtp_host_addr(char *host, VSTRING *why) +{ + DNS_RR *addr_list; + + /* + * If the host is specified by numerical address, just convert the + * address to internal form. Otherwise, the host is specified by name. + */ +#define PREF0 0 + addr_list = lmtp_addr_one((DNS_RR *) 0, host, PREF0, why); + if (msg_verbose) + lmtp_print_addr(host, addr_list); + return (addr_list); +} + diff --git a/postfix/lmtp/lmtp_addr.h b/postfix/lmtp/lmtp_addr.h new file mode 100644 index 000000000..579b7f034 --- /dev/null +++ b/postfix/lmtp/lmtp_addr.h @@ -0,0 +1,41 @@ +/*++ +/* NAME +/* lmtp_addr 3h +/* SUMMARY +/* LMTP server address lookup +/* SYNOPSIS +/* #include "lmtp_addr.h" +/* DESCRIPTION +/* .nf + + /* + * DNS library. + */ +#include + + /* + * Internal interfaces. + */ +extern DNS_RR *lmtp_host_addr(char *, VSTRING *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ diff --git a/postfix/lmtp/lmtp_chat.c b/postfix/lmtp/lmtp_chat.c new file mode 100644 index 000000000..097169b76 --- /dev/null +++ b/postfix/lmtp/lmtp_chat.c @@ -0,0 +1,285 @@ +/*++ +/* NAME +/* lmtp_chat 3 +/* SUMMARY +/* LMTP client request/response support +/* SYNOPSIS +/* #include "lmtp.h" +/* +/* typedef struct { +/* .in +4 +/* int code; +/* char *str; +/* VSTRING *buf; +/* .in -4 +/* } LMTP_RESP; +/* +/* void lmtp_chat_cmd(state, format, ...) +/* LMTP_STATE *state; +/* char *format; +/* +/* LMTP_RESP *lmtp_chat_resp(state) +/* LMTP_STATE *state; +/* +/* void lmtp_chat_notify(state) +/* LMTP_STATE *state; +/* +/* void lmtp_chat_reset(state) +/* LMTP_STATE *state; +/* DESCRIPTION +/* This module implements LMTP client support for request/reply +/* conversations, and maintains a limited LMTP transaction log. +/* +/* lmtp_chat_cmd() formats a command and sends it to an LMTP server. +/* Optionally, the command is logged. +/* +/* lmtp_chat_resp() read one LMTP server response. It separates the +/* numerical status code from the text, and concatenates multi-line +/* responses to one string, using a newline as separator. +/* Optionally, the server response is logged. +/* +/* lmtp_chat_notify() sends a copy of the LMTP transaction log +/* to the postmaster for review. The postmaster notice is sent only +/* when delivery is possible immediately. It is an error to call +/* lmtp_chat_notify() when no LMTP transaction log exists. +/* +/* lmtp_chat_reset() resets the transaction log. This is +/* typically done at the beginning or end of an LMTP session, +/* or within a session to discard non-error information. +/* DIAGNOSTICS +/* Fatal errors: memory allocation problem, server response exceeds +/* configurable limit. +/* All other exceptions are handled by long jumps (see smtp_stream(3)). +/* SEE ALSO +/* smtp_stream(3) LMTP session I/O support +/* msg(3) generic logging interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include +#include /* 44BSD stdarg.h uses abort() */ +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "lmtp.h" + +#define STR(x) ((char *) vstring_str(x)) +#define LEN VSTRING_LEN + +/* lmtp_chat_reset - reset LMTP transaction log */ + +void lmtp_chat_reset(LMTP_STATE *state) +{ + if (state->history) { + argv_free(state->history); + state->history = 0; + } + + /* What's status without history? */ + state->status = 0; + state->error_mask = 0; +} + +/* lmtp_chat_append - append record to LMTP transaction log */ + +static void lmtp_chat_append(LMTP_STATE *state, char *direction, char *data) +{ + char *line; + + if (state->history == 0) + state->history = argv_alloc(10); + line = concatenate(direction, data, (char *) 0); + argv_add(state->history, line, (char *) 0); + myfree(line); +} + +/* lmtp_chat_cmd - send an LMTP command */ + +void lmtp_chat_cmd(LMTP_STATE *state, char *fmt,...) +{ + LMTP_SESSION *session = state->session; + va_list ap; + + /* + * Format the command, and update the transaction log. + */ + va_start(ap, fmt); + vstring_vsprintf(state->buffer, fmt, ap); + va_end(ap); + lmtp_chat_append(state, "Out: ", STR(state->buffer)); + + /* + * Optionally log the command first, so we can see in the log what the + * program is trying to do. + */ + if (msg_verbose) + msg_info("> %s: %s", session->host, STR(state->buffer)); + + /* + * Send the command to the LMTP server. + */ + smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream); +} + +/* lmtp_chat_resp - read and process LMTP server response */ + +LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state) +{ + LMTP_SESSION *session = state->session; + static LMTP_RESP rdata; + int more; + char *cp; + int last_char; + + /* + * Initialize the response data buffer. + */ + if (rdata.buf == 0) + rdata.buf = vstring_alloc(100); + + /* + * Censor out non-printable characters in server responses. Concatenate + * multi-line server responses. Separate the status code from the text. + * Leave further parsing up to the application. + */ + VSTRING_RESET(rdata.buf); + for (;;) { + last_char = smtp_get(state->buffer, session->stream, var_line_limit); + cp = printable(STR(state->buffer), '?'); + if (last_char != '\n') + msg_warn("%s: response longer than %d: %.30s...", + session->host, var_line_limit, cp); + if (msg_verbose) + msg_info("< %s: %s", session->host, cp); + while (ISDIGIT(*cp)) + cp++; + rdata.code = (cp - STR(state->buffer) == 3 ? + atoi(STR(state->buffer)) : 0); + more = (*cp == '-'); + + /* + * Defend against a denial of service attack by limiting the amount + * of multi-line text that we are willing to store. + */ + if (LEN(rdata.buf) < var_line_limit) { + if (VSTRING_LEN(rdata.buf)) + VSTRING_ADDCH(rdata.buf, '\n'); + vstring_strcat(rdata.buf, STR(state->buffer)); + lmtp_chat_append(state, "In: ", STR(state->buffer)); + } + if (VSTRING_LEN(state->buffer) == 0) /* XXX remote brain damage */ + continue; + if (!ISDIGIT(STR(state->buffer)[0])) /* XXX remote brain damage */ + continue; + if (more == 0) + break; + } + VSTRING_TERMINATE(rdata.buf); + rdata.str = STR(rdata.buf); + return (&rdata); +} + +/* print_line - line_wrap callback */ + +static void print_line(const char *str, int len, int indent, char *context) +{ + VSTREAM *notice = (VSTREAM *) context; + + post_mail_fprintf(notice, " %*s%.*s", indent, "", len, str); +} + +/* lmtp_chat_notify - notify postmaster */ + +void lmtp_chat_notify(LMTP_STATE *state) +{ + char *myname = "lmtp_chat_notify"; + LMTP_SESSION *session = state->session; + VSTREAM *notice; + char **cpp; + + /* + * Sanity checks. + */ + if (state->history == 0) + msg_panic("%s: no conversation history", myname); + if (msg_verbose) + msg_info("%s: notify postmaster", myname); + + /* + * Construct a message for the postmaster, explaining what this is all + * about. This is junk mail: don't send it when the mail posting service + * is unavailable, and use the double bounce sender address, to prevent + * mail bounce wars. Always prepend one space to message content that we + * generate from untrusted data. + */ +#define NULL_CLEANUP_FLAGS 0 +#define LENGTH 78 +#define INDENT 4 + + notice = post_mail_fopen_nowait(mail_addr_double_bounce(), + var_error_rcpt, + NULL_CLEANUP_FLAGS, "NOTICE"); + if (notice == 0) { + msg_warn("postmaster notify: %m"); + return; + } + post_mail_fprintf(notice, "From: %s (Mail Delivery System)", + mail_addr_mail_daemon()); + post_mail_fprintf(notice, "To: %s (Postmaster)", var_error_rcpt); + post_mail_fprintf(notice, "Subject: %s LMTP client: errors from %s", + var_mail_name, session->host); + post_mail_fputs(notice, ""); + post_mail_fprintf(notice, "Unexpected response from %s.", session->host); + post_mail_fputs(notice, ""); + post_mail_fputs(notice, "Transcript of session follows."); + post_mail_fputs(notice, ""); + argv_terminate(state->history); + for (cpp = state->history->argv; *cpp; cpp++) + line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line, + (char *) notice); + (void) post_mail_fclose(notice); +} diff --git a/postfix/lmtp/lmtp_connect.c b/postfix/lmtp/lmtp_connect.c new file mode 100644 index 000000000..dcf5733ec --- /dev/null +++ b/postfix/lmtp/lmtp_connect.c @@ -0,0 +1,395 @@ +/*++ +/* NAME +/* lmtp_connect 3 +/* SUMMARY +/* connect to LMTP server +/* SYNOPSIS +/* #include "lmtp.h" +/* +/* LMTP_SESSION *lmtp_connect(destination, why) +/* char *destination; +/* VSTRING *why; +/* DESCRIPTION +/* This module implements LMTP connection management. +/* +/* lmtp_connect() attempts to establish an LMTP session with a host. +/* +/* The destination is either a host name or a numeric address. +/* Symbolic or numeric service port information may be appended, +/* separated by a colon (":"). +/* +/* Numerical address information should always be quoted with `[]'. +/* +/* DIAGNOSTICS +/* This routine either returns an LMTP_SESSION pointer, or +/* returns a null pointer and set the \fIlmtp_errno\fR +/* global variable accordingly: +/* .IP LMTP_RETRY +/* The connection attempt failed, but should be retried later. +/* .IP LMTP_FAIL +/* The connection attempt failed. +/* .PP +/* In addition, a textual description of the error is made available +/* via the \fIwhy\fR argument. +/* SEE ALSO +/* lmtp_proto(3) LMTP client protocol +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* DNS library. */ + +#include + +/* Application-specific. */ + +#include "lmtp.h" +#include "lmtp_addr.h" + +/* lmtp_connect_addr - connect to explicit address */ + +static LMTP_SESSION *lmtp_connect_addr(DNS_RR *addr, unsigned port, + VSTRING *why) +{ + char *myname = "lmtp_connect_addr"; + struct sockaddr_in sin; + int sock; + INET_ADDR_LIST *addr_list; + int conn_stat; + int saved_errno; + VSTREAM *stream; + int ch; + unsigned long inaddr; + + /* + * Sanity checks. + */ + if (addr->data_len > sizeof(sin.sin_addr)) { + msg_warn("%s: skip address with length %d", myname, addr->data_len); + lmtp_errno = LMTP_RETRY; + return (0); + } + + /* + * Initialize. + */ + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + + if ((sock = socket(sin.sin_family, SOCK_STREAM, 0)) < 0) + msg_fatal("%s: socket: %m", myname); + + /* do we still need this if? */ + addr_list = own_inet_addr_list(); + if (addr_list->used == 1) { + sin.sin_port = 0; + memcpy((char *) &sin.sin_addr, addr_list->addrs, sizeof(sin.sin_addr)); + inaddr = ntohl(sin.sin_addr.s_addr); + if (!IN_CLASSA(inaddr) + || !(((inaddr & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)) { + if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) + msg_warn("%s: bind %s: %m", myname, inet_ntoa(sin.sin_addr)); + if (msg_verbose) + msg_info("%s: bind %s", myname, inet_ntoa(sin.sin_addr)); + } + } + + /* + * Connect to the LMTP server. + */ + sin.sin_port = port; + memcpy((char *) &sin.sin_addr, addr->data, sizeof(sin.sin_addr)); + + if (msg_verbose) + msg_info("%s: trying: %s[%s] port %d...", + myname, addr->name, inet_ntoa(sin.sin_addr), ntohs(port)); + if (var_lmtp_conn_tmout > 0) { + non_blocking(sock, NON_BLOCKING); + conn_stat = timed_connect(sock, (struct sockaddr *) & sin, + sizeof(sin), var_lmtp_conn_tmout); + saved_errno = errno; + non_blocking(sock, BLOCKING); + errno = saved_errno; + } else { + conn_stat = connect(sock, (struct sockaddr *) & sin, sizeof(sin)); + } + if (conn_stat < 0) { + vstring_sprintf(why, "connect to %s[%s]: %m", + addr->name, inet_ntoa(sin.sin_addr)); + lmtp_errno = LMTP_RETRY; + close(sock); + return (0); + } + + /* + * Skip this host if it takes no action within some time limit. + */ + if (read_wait(sock, var_lmtp_lhlo_tmout) < 0) { + vstring_sprintf(why, "connect to %s[%s]: read timeout", + addr->name, inet_ntoa(sin.sin_addr)); + lmtp_errno = LMTP_RETRY; + close(sock); + return (0); + } + + /* + * Skip this host if it disconnects without talking to us. + */ + stream = vstream_fdopen(sock, O_RDWR); + if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) { + vstring_sprintf(why, "connect to %s[%s]: server dropped connection", + addr->name, inet_ntoa(sin.sin_addr)); + lmtp_errno = LMTP_RETRY; + vstream_fclose(stream); + return (0); + } + + /* + * Skip this host if it sends a 4xx greeting. + */ + if (ch == '4') { + vstring_sprintf(why, "connect to %s[%s]: server refused mail service", + addr->name, inet_ntoa(sin.sin_addr)); + lmtp_errno = LMTP_RETRY; + vstream_fclose(stream); + return (0); + } + vstream_ungetc(stream, ch); + return (lmtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr))); +} + +/* lmtp_connect_host - direct connection to host */ + +LMTP_SESSION *lmtp_connect_host(char *host, unsigned port, VSTRING *why) +{ + LMTP_SESSION *session = 0; + DNS_RR *addr_list; + DNS_RR *addr; + + /* + * Try each address in the specified order until we find one that works. + * The addresses belong to the same A record, so we have no information + * on what address is "best". + */ + addr_list = lmtp_host_addr(host, why); + for (addr = addr_list; addr; addr = addr->next) { + if ((session = lmtp_connect_addr(addr, port, why)) != 0) { + break; + } + } + dns_rr_free(addr_list); + return (session); +} + +/* lmtp_parse_destination - parse destination */ + +static char *lmtp_parse_destination(char *destination, char *def_service, + char **hostp, unsigned *portp) +{ + char *myname = "lmtp_parse_destination"; + char *buf = mystrdup(destination); + char *host = buf; + char *service; + struct servent *sp; + char *protocol = "tcp"; /* XXX configurable? */ + unsigned port; + + if (msg_verbose) + msg_info("%s: %s %s", myname, destination, def_service); + + /* + * Strip quoting. We're working with a copy of the destination argument + * so the stripping can be destructive. + */ + if (*host == '[') { + host++; + host[strcspn(host, "]")] = 0; + } + + /* + * Separate host and service information, or use the default service + * specified by the caller. XXX the ":" character is used in the IPV6 + * address notation, so using split_at_right() is not sufficient. We'd + * have to count the number of ":" instances. + */ + if ((service = split_at_right(host, ':')) == 0) + service = def_service; + if (*service == 0) + msg_fatal("%s: empty service name: %s", myname, destination); + *hostp = host; + + /* + * Convert service to port number, network byte order. + */ + if ((port = atoi(service)) != 0) { + *portp = htons(port); + } else { + /* + * Since most folks aren't going to have lmtp defined as a service, + * use a default value instead of just blowing up. + */ + if ((sp = getservbyname(service, protocol)) == 0) + *portp = htons(var_lmtp_tcp_port); + else + *portp = sp->s_port; + } + return (buf); +} + +/* lmtp_connect_local - local connect to unix domain socket */ + +LMTP_SESSION *lmtp_connect_local(const char *class, const char *name, VSTRING *why) +{ + char *myname = "lmtp_connect_local"; + VSTREAM *stream; + int ch; + + /* + * Connect to the LMTP server. + */ + if (msg_verbose) + msg_info("%s: trying: %s/%s...", myname, class, name); + if ((stream = mail_connect_wait(class, name)) == 0) { + vstring_sprintf(why, "connect to %s: connection failed.", name); + lmtp_errno = LMTP_RETRY; + return (0); + } + + /* + * Skip this process if it takes no action within some time limit. + */ + if (read_wait(vstream_fileno(stream), var_lmtp_lhlo_tmout) < 0) { + vstring_sprintf(why, "connect to %s: read timeout", name); + lmtp_errno = LMTP_RETRY; + vstream_fclose(stream); + return (0); + } + + /* + * Skip this process if it disconnects without talking to us. + */ + if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) { + vstring_sprintf(why, "connect to %s: server dropped connection", name); + lmtp_errno = LMTP_RETRY; + vstream_fclose(stream); + return (0); + } + + /* + * Skip this host if it sends a 4xx greeting. + */ + if (ch == '4') { + vstring_sprintf(why, "connect to %s: server refused mail service", name); + lmtp_errno = LMTP_RETRY; + vstream_fclose(stream); + return (0); + } + vstream_ungetc(stream, ch); + return (lmtp_session_alloc(stream, name, "")); +} + +/* lmtp_connect - establish LMTP connection */ + +LMTP_SESSION *lmtp_connect(LMTP_ATTR *attr, DELIVER_REQUEST *request, VSTRING *why) +{ + char *myname = "lmtp_connect"; + LMTP_SESSION *session; + char *dest_buf; + char *host; + unsigned port; + char *def_service = "lmtp"; /* XXX configurable? */ + + /* + * Are we connecting to a local or inet socket? + */ + if (attr->type == LMTP_SERV_TYPE_UNIX) { + /* + * Connect to local LMTP server. + */ + if (msg_verbose) + msg_info("%s: connecting to %s", myname, attr->name); + session = lmtp_connect_local(attr->class, attr->name, why); + if (session != 0) { + session->destination = mystrdup(attr->name); + session->type = attr->type; + } + } else { + /* + * Connect to LMTP server via inet socket, but where? + */ + if (!*(attr)->name) { + if (msg_verbose) + msg_info("%s: attr->name not set; using request->nexthop", myname); + attr->name = request->nexthop; + } + dest_buf = lmtp_parse_destination(attr->name, def_service, + &host, &port); + + /* + * Now that the inet LMTP server has been determined, connect to it. + */ + if (msg_verbose) + msg_info("%s: connecting to %s port %d", myname, host, ntohs(port)); + session = lmtp_connect_host(host, port, why); + if (session != 0) { + session->destination = mystrdup(attr->name); + session->type = attr->type; + } + myfree(dest_buf); + } + return (session); +} + diff --git a/postfix/lmtp/lmtp_proto.c b/postfix/lmtp/lmtp_proto.c new file mode 100644 index 000000000..76ae3805a --- /dev/null +++ b/postfix/lmtp/lmtp_proto.c @@ -0,0 +1,686 @@ +/*++ +/* NAME +/* lmtp_proto 3 +/* SUMMARY +/* client LMTP protocol +/* SYNOPSIS +/* #include "lmtp.h" +/* +/* int lmtp_lhlo(state) +/* LMTP_STATE *state; +/* +/* int lmtp_xfer(state) +/* LMTP_STATE *state; +/* +/* int lmtp_rset(state) +/* LMTP_STATE *state; +/* +/* int lmtp_quit(state) +/* LMTP_STATE *state; +/* DESCRIPTION +/* This module implements the client side of the LMTP protocol. +/* +/* lmtp_lhlo() performs the initial handshake with the LMTP server. +/* +/* lmtp_xfer() sends message envelope information followed by the +/* message data, but does not finish the conversation. These operations +/* are combined in one function, in order to implement LMTP pipelining. +/* Recipients are marked as "done" in the mail queue file when +/* bounced or delivered. The message delivery status is updated +/* accordingly. +/* +/* lmtp_rset() sends an RSET command and waits for the response. +/* +/* lmtp_quit() sends a QUIT command and waits for the response. +/* DIAGNOSTICS +/* lmtp_lhlo(), lmtp_xfer(), lmtp_rset() and lmtp_quit() return 0 in +/* case of success, -1 in case of failure. For lmtp_xfer(), lmtp_rset() +/* and lmtp_quit(), success means the ability to perform an LMTP +/* conversation, not necessarily OK replies from the server. +/* +/* Warnings: corrupt message file. A corrupt message is marked +/* as "corrupt" by changing its queue file permissions. +/* SEE ALSO +/* lmtp(3h) internal data structures +/* lmtp_chat(3) query/reply LMTP support +/* lmtp_trouble(3) error handlers +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Pipelining code in cooperation with: +/* Jon Ribbens +/* Oaktree Internet Solutions Ltd., +/* Internet House, +/* Canal Basin, +/* Coventry, +/* CV1 4LY, United Kingdom. +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include +#include +#include /* shutdown(2) */ +#include +#include +#include +#include /* 44BSD stdarg.h uses abort() */ +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "lmtp.h" +#include "quote_821_local.h" + + /* + * Sender and receiver state. A session does not necessarily go through a + * linear progression, but states are guaranteed to not jump backwards. + * Normal sessions go from MAIL->RCPT->DATA->DOT->LAST. The states MAIL, + * RCPT, and DATA may also be followed by ABORT->LAST. + * + * In order to support connection cacheing, no QUIT is send at the end of mail + * delivery. Instead, at the start of the next mail delivery, the client + * sends RSET to find out if the server is still there, and sends QUIT only + * when closing a connection. The RSET and QUIT commands are sent all by + * themselves in non-pipelining mode. The respective state transitions are + * RSET->LAST and QUIT->LAST. + * + * For the sake of code reuse, the non-pipelined RSET and QUIT commands are + * sent by the same code that implements command pipelining, so that we can + * borrow from the existing code for exception handling and error reporting. + * + */ +#define LMTP_STATE_MAIL 0 +#define LMTP_STATE_RCPT 1 +#define LMTP_STATE_DATA 2 +#define LMTP_STATE_DOT 3 +#define LMTP_STATE_ABORT 4 +#define LMTP_STATE_RSET 5 +#define LMTP_STATE_QUIT 6 +#define LMTP_STATE_LAST 7 + +int *xfer_timeouts[LMTP_STATE_LAST] = { + &var_lmtp_mail_tmout, + &var_lmtp_rcpt_tmout, + &var_lmtp_data0_tmout, + &var_lmtp_data2_tmout, + &var_lmtp_rset_tmout, + &var_lmtp_rset_tmout, + &var_lmtp_quit_tmout, +}; + +char *xfer_states[LMTP_STATE_LAST] = { + "sending MAIL FROM", + "sending RCPT TO", + "sending DATA command", + "sending end of data -- message may be sent more than once", + "sending RSET", + "sending RSET", + "sending QUIT", +}; + +/* lmtp_lhlo - perform initial handshake with LMTP server */ + +int lmtp_lhlo(LMTP_STATE *state) +{ + char *myname = "lmtp_lhlo"; + LMTP_SESSION *session = state->session; + DELIVER_REQUEST *request = state->request; + LMTP_RESP *resp; + int except; + char *lines; + char *words; + char *word; + int n; + SOCKOPT_SIZE optlen = sizeof(state->sndbufsize); + + /* + * Prepare for disaster. + */ + smtp_timeout_setup(state->session->stream, var_lmtp_lhlo_tmout); + if ((except = vstream_setjmp(state->session->stream)) != 0) + return (lmtp_stream_except(state, except, "sending LHLO")); + + /* + * Read and parse the server's LMTP greeting banner. + */ + if (((resp = lmtp_chat_resp(state))->code / 100) != 2) + return (lmtp_site_fail(state, resp->code, + "%s refused to talk to me: %s", + session->host, translit(resp->str, "\n", " "))); + + /* + * See if we are talking to ourself. This should not be possible with the + * way we implement DNS lookups. However, people are known to sometimes + * screw up the naming service. And, mailer loops are still possible when + * our own mailer routing tables are mis-configured. + */ + words = resp->str; + + /* + * Return the compliment. + */ + lmtp_chat_cmd(state, "LHLO %s", var_myhostname); + if ((resp = lmtp_chat_resp(state))->code / 100 != 2) + return (lmtp_site_fail(state, resp->code, + "%s refused to talk to me: %s", + session->host, + translit(resp->str, "\n", " "))); + + /* + * Pick up some useful features offered by the LMTP server. XXX Until we + * have a portable routine to convert from string to off_t with proper + * overflow detection, ignore the message size limit advertised by the + * LMTP server. Otherwise, we might do the wrong thing when the server + * advertises a really huge message size limit. + */ + lines = resp->str; + (void) mystrtok(&lines, "\n"); + while ((words = mystrtok(&lines, "\n")) != 0) { + if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t")) != 0) { + if (strcasecmp(word, "8BITMIME") == 0) + state->features |= LMTP_FEATURE_8BITMIME; + else if (strcasecmp(word, "PIPELINING") == 0) + state->features |= LMTP_FEATURE_PIPELINING; + else if (strcasecmp(word, "SIZE") == 0) + state->features |= LMTP_FEATURE_SIZE; + } + } + if (msg_verbose) + msg_info("server features: 0x%x", state->features); + + /* + * We use LMTP command pipelining if the server said it supported it. + * Since we use blocking I/O, RFC 2197 says that we should inspect the + * TCP window size and not send more than this amount of information. + * Unfortunately this information is not available using the sockets + * interface. However, we *can* get the TCP send buffer size on the local + * TCP/IP stack. We should be able to fill this buffer without being + * blocked, and then the kernel will effectively do non-blocking I/O for + * us by automatically writing out the contents of its send buffer while + * we are reading in the responses. In addition to TCP buffering we have + * to be aware of application-level buffering by the vstream module, + * which is limited to a couple kbytes. + * + * Don't worry about command pipelining for local connections. + */ + if (state->features & LMTP_FEATURE_PIPELINING + && state->session->type != LMTP_SERV_TYPE_UNIX) { + if (getsockopt(vstream_fileno(state->session->stream), SOL_SOCKET, + SO_SNDBUF, (char *) &state->sndbufsize, &optlen) < 0) + msg_fatal("%s: getsockopt: %m", myname); + if (msg_verbose) + msg_info("Using LMTP PIPELINING, TCP send buffer size is %d", + state->sndbufsize); + } else + state->sndbufsize = 0; + state->sndbuffree = state->sndbufsize; + + return (0); +} + +/* lmtp_loop - the LMTP state machine */ + +static int lmtp_loop(LMTP_STATE *state, int init_state) +{ + char *myname = "lmtp_loop"; + DELIVER_REQUEST *request = state->request; + LMTP_SESSION *session = state->session; + LMTP_RESP *resp; + RECIPIENT *rcpt; + VSTRING *next_command = vstring_alloc(100); + int *survivors = 0; + int next_state; + int next_rcpt; + int send_state; + int recv_state; + int send_rcpt; + int recv_rcpt; + int nrcpt; + int except; + int rec_type; + int prev_type = 0; + int mail_from_rejected; + int recv_dot; + + /* + * Macros for readability. XXX Isn't LMTP supposed to be case + * insensitive? + */ +#define REWRITE_ADDRESS(addr) do { \ + if (*(addr)) { \ + quote_821_local(state->scratch, addr); \ + myfree(addr); \ + addr = mystrdup(vstring_str(state->scratch)); \ + lowercase(addr); \ + } \ + } while (0) + +#define RETURN(x) do { \ + vstring_free(next_command); \ + if (survivors) \ + myfree((char *) survivors); \ + return (x); \ + } while (0) + +#define SENDER_IS_AHEAD \ + (recv_state < send_state || recv_rcpt != send_rcpt) + +#define SENDER_IN_WAIT_STATE \ + (send_state == LMTP_STATE_DOT || send_state == LMTP_STATE_LAST) + + /* + * Pipelining support requires two loops: one loop for sending and one + * for receiving. Each loop has its own independent state. Most of the + * time the sender can run ahead of the receiver by as much as the TCP + * send buffer permits. There are only two places where the sender must + * wait for status information from the receiver: once after sending DATA + * and once after sending QUIT. + * + * The sender state advances until the TCP send buffer would overflow, or + * until the sender needs status information from the receiver. At that + * point the receiver starts processing responses. Once the receiver has + * caught up with the sender, the sender resumes sending commands. If the + * receiver detects a serious problem (MAIL FROM rejected, all RCPT TO + * commands rejected, DATA rejected) it forces the sender to abort the + * LMTP dialog with RSET. + */ + nrcpt = 0; + recv_state = send_state = init_state; + next_rcpt = send_rcpt = recv_rcpt = recv_dot = 0; + mail_from_rejected = 0; + + while (recv_state != LMTP_STATE_LAST) { + + /* + * Build the next command. + */ + switch (send_state) { + + /* + * Sanity check. + */ + default: + msg_panic("%s: bad sender state %d", myname, send_state); + + /* + * Build the MAIL FROM command. + */ + case LMTP_STATE_MAIL: + if (*request->sender) + REWRITE_ADDRESS(request->sender); + vstring_sprintf(next_command, "MAIL FROM:<%s>", request->sender); + if (state->features & LMTP_FEATURE_SIZE) + vstring_sprintf_append(next_command, " SIZE=%lu", + request->data_size); + next_state = LMTP_STATE_RCPT; + break; + + /* + * Build one RCPT TO command before we have seen the MAIL FROM + * response. + */ + case LMTP_STATE_RCPT: + rcpt = request->rcpt_list.info + send_rcpt; + REWRITE_ADDRESS(rcpt->address); + vstring_sprintf(next_command, "RCPT TO:<%s>", rcpt->address); + if ((next_rcpt = send_rcpt + 1) == request->rcpt_list.len) + next_state = LMTP_STATE_DATA; + break; + + /* + * Build the DATA command before we have seen all the RCPT TO + * responses. + */ + case LMTP_STATE_DATA: + vstring_strcpy(next_command, "DATA"); + next_state = LMTP_STATE_DOT; + break; + + /* + * Build the "." command before we have seen the DATA response. + */ + case LMTP_STATE_DOT: + vstring_strcpy(next_command, "."); + next_state = LMTP_STATE_LAST; + break; + + /* + * Can't happen. The LMTP_STATE_ABORT sender state is entered by + * the receiver and is left before the bottom of the main loop. + */ + case LMTP_STATE_ABORT: + msg_panic("%s: sender abort state", myname); + + /* + * Build the RSET command. XXX This command does not belong here + * because it will be sent in non-pipelining mode. But having it + * here means that we can reuse existing code for error handling. + */ + case LMTP_STATE_RSET: + vstring_strcpy(next_command, "RSET"); + next_state = LMTP_STATE_LAST; + break; + + /* + * Build the QUIT command. XXX This command does not belong here + * because it will be sent in non-pipelining mode. But having it + * here means that we can reuse existing code for error handling. + */ + case LMTP_STATE_QUIT: + vstring_strcpy(next_command, "QUIT"); + next_state = LMTP_STATE_LAST; + break; + + /* + * The final sender state has no action associated with it. + */ + case LMTP_STATE_LAST: + VSTRING_RESET(next_command); + break; + } + VSTRING_TERMINATE(next_command); + + /* + * Process responses until the receiver has caught up. Vstreams + * automatically flush buffered output when reading new data. + */ + if (SENDER_IN_WAIT_STATE + || (SENDER_IS_AHEAD + && VSTRING_LEN(next_command) + 2 > state->sndbuffree)) { + while (SENDER_IS_AHEAD) { + + /* + * Sanity check. + */ + if (recv_state < LMTP_STATE_MAIL + || recv_state > LMTP_STATE_QUIT) + msg_panic("%s: bad receiver state %d (sender state %d)", + myname, recv_state, send_state); + + /* + * Receive the next server response. Use the proper timeout, + * and log the proper client state in case of trouble. + */ + smtp_timeout_setup(state->session->stream, + *xfer_timeouts[recv_state]); + if ((except = vstream_setjmp(state->session->stream)) != 0) + RETURN(lmtp_stream_except(state, except, + xfer_states[recv_state])); + resp = lmtp_chat_resp(state); + + /* + * Process the response. + */ + switch (recv_state) { + + /* + * Process the MAIL FROM response. When the server + * rejects the sender, set the mail_from_rejected flag so + * that the receiver may apply a course correction. + */ + case LMTP_STATE_MAIL: + if (resp->code / 100 != 2) { + lmtp_mesg_fail(state, resp->code, + "%s said: %s", session->host, + translit(resp->str, "\n", " ")); + mail_from_rejected = 1; + } + recv_state = LMTP_STATE_RCPT; + break; + + /* + * Process one RCPT TO response. If MAIL FROM was + * rejected, ignore RCPT TO responses: all recipients are + * dead already. When all recipients are rejected the + * receiver may apply a course correction. + */ + case LMTP_STATE_RCPT: + if (!mail_from_rejected) { + rcpt = request->rcpt_list.info + recv_rcpt; + if (resp->code / 100 == 2) { + if (survivors == 0) + survivors = (int *) + mymalloc(request->rcpt_list.len + * sizeof(int)); + survivors[nrcpt++] = recv_rcpt; + } else { + lmtp_rcpt_fail(state, resp->code, rcpt, + "%s said: %s", session->host, + translit(resp->str, "\n", " ")); + rcpt->offset = 0; /* in case deferred */ + } + } + if (++recv_rcpt == request->rcpt_list.len) + recv_state = LMTP_STATE_DATA; + break; + + /* + * Process the DATA response. When the server rejects + * DATA, set nrcpt to a negative value so that the + * receiver can apply a course correction. + */ + case LMTP_STATE_DATA: + if (resp->code / 100 != 3) { + if (nrcpt > 0) + lmtp_mesg_fail(state, resp->code, + "%s said: %s", session->host, + translit(resp->str, "\n", " ")); + nrcpt = -1; + } + recv_state = LMTP_STATE_DOT; + break; + + /* + * Process the end of message response. Ignore the + * response when no recipient was accepted: all + * recipients are dead already, and the next receiver + * state is LMTP_STATE_LAST regardless. Otherwise, if the + * message transfer fails, bounce all remaining + * recipients, else cross off the recipients that were + * delivered. + */ + case LMTP_STATE_DOT: + if (nrcpt > 0) { + rcpt = request->rcpt_list.info + survivors[recv_dot]; + if (resp->code / 100 == 2) { + if (rcpt->offset) { + sent(request->queue_id, rcpt->address, + session->host, request->arrival_time, + "%s", resp->str); + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + } else { + lmtp_rcpt_fail(state, resp->code, rcpt, + "%s said: %s", session->host, + translit(resp->str, "\n", " ")); + rcpt->offset = 0; /* in case deferred */ + } + } + + /* + * We get one response per valid RCPT TO: + */ + if (msg_verbose) + msg_info("%s: recv_dot = %d", myname, recv_dot); + if (++recv_dot >= nrcpt) { + if (msg_verbose) + msg_info("%s: finished . command", myname); + recv_state = LMTP_STATE_LAST; + } + break; + + /* + * Ignore the RSET response. + */ + case LMTP_STATE_ABORT: + recv_state = LMTP_STATE_LAST; + break; + + /* + * Ignore the RSET response. + */ + case LMTP_STATE_RSET: + recv_state = LMTP_STATE_LAST; + break; + + /* + * Ignore the QUIT response. + */ + case LMTP_STATE_QUIT: + recv_state = LMTP_STATE_LAST; + break; + } + } + + /* + * At this point, the sender and receiver are fully synchronized, + * so that the entire TCP send buffer becomes available again. + */ + state->sndbuffree = state->sndbufsize; + + /* + * We know the server response to every command that was sent. + * Apply a course correction if necessary: the sender wants to + * send RCPT TO but MAIL FROM was rejected; the sender wants to + * send DATA but all recipients were rejected; the sender wants + * to deliver the message but DATA was rejected. + */ + if ((send_state == LMTP_STATE_RCPT && mail_from_rejected) + || (send_state == LMTP_STATE_DATA && nrcpt == 0) + || (send_state == LMTP_STATE_DOT && nrcpt < 0)) { + send_state = recv_state = LMTP_STATE_ABORT; + send_rcpt = recv_rcpt = 0; + vstring_strcpy(next_command, "RSET"); + next_state = LMTP_STATE_LAST; + next_rcpt = 0; + } + } + + /* + * Make the next sender state the current sender state. + */ + if (send_state == LMTP_STATE_LAST) + continue; + + /* + * Special case if the server accepted the DATA command. If the + * server accepted at least one recipient send the entire message. + * Otherwise, just send "." as per RFC 2197. + */ + if (send_state == LMTP_STATE_DOT && nrcpt > 0) { + smtp_timeout_setup(state->session->stream, + var_lmtp_data1_tmout); + if ((except = vstream_setjmp(state->session->stream)) != 0) + RETURN(lmtp_stream_except(state, except, + "sending message body")); + + if (vstream_fseek(state->src, request->data_offset, SEEK_SET) < 0) + msg_fatal("seek queue file: %m"); + + while ((rec_type = rec_get(state->src, state->scratch, 0)) > 0) { + if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) + break; + if (prev_type != REC_TYPE_CONT) + if (vstring_str(state->scratch)[0] == '.') + smtp_fputc('.', session->stream); + if (rec_type == REC_TYPE_CONT) + smtp_fwrite(vstring_str(state->scratch), + VSTRING_LEN(state->scratch), + session->stream); + else + smtp_fputs(vstring_str(state->scratch), + VSTRING_LEN(state->scratch), + session->stream); + prev_type = rec_type; + } + + if (prev_type == REC_TYPE_CONT) /* missing newline at end */ + smtp_fputs("", 0, session->stream); + if (vstream_ferror(state->src)) + msg_fatal("queue file read error"); + if (rec_type != REC_TYPE_XTRA) + RETURN(mark_corrupt(state->src)); + } + + /* + * Copy the next command to the buffer and update the sender state. + */ + if (state->sndbuffree > 0) + state->sndbuffree -= VSTRING_LEN(next_command) + 2; + lmtp_chat_cmd(state, "%s", vstring_str(next_command)); + send_state = next_state; + send_rcpt = next_rcpt; + } + + RETURN(0); +} + +/* lmtp_xfer - send a batch of envelope information and the message data */ + +int lmtp_xfer(LMTP_STATE *state) +{ + return (lmtp_loop(state, LMTP_STATE_MAIL)); +} + +/* lmtp_rset - reset dialog with peer */ + +int lmtp_rset(LMTP_STATE *state) +{ + return (lmtp_loop(state, LMTP_STATE_RSET)); +} + +/* lmtp_quit - say goodbye to peer */ + +int lmtp_quit(LMTP_STATE *state) +{ + return (lmtp_loop(state, LMTP_STATE_QUIT)); +} diff --git a/postfix/lmtp/lmtp_session.c b/postfix/lmtp/lmtp_session.c new file mode 100644 index 000000000..80471dff0 --- /dev/null +++ b/postfix/lmtp/lmtp_session.c @@ -0,0 +1,105 @@ +/*++ +/* NAME +/* lmtp_session 3 +/* SUMMARY +/* LMTP_SESSION structure management +/* SYNOPSIS +/* #include "lmtp.h" +/* +/* LMTP_SESSION *lmtp_session_alloc(stream, host, addr) +/* VSTREAM *stream; +/* char *host; +/* char *addr; +/* +/* void lmtp_session_free(session) +/* LMTP_SESSION *session; +/* +/* void lmtp_session_reset(state) +/* LMTP_STATE *state; +/* DESCRIPTION +/* lmtp_session_alloc() allocates memory for an LMTP_SESSION structure +/* and initializes it with the given stream and host name and address +/* information. The host name and address strings are copied. The code +/* assumes that the stream is connected to the "best" alternative. +/* +/* lmtp_session_free() destroys an LMTP_SESSION structure and its +/* members, making memory available for reuse. +/* +/* lmtp_session_reset() is just a little helper to make sure everything +/* is set to zero after the session has been freed. This means I don't +/* have to keep repeating the same chunks of code for cached connections. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include + +/* Application-specific. */ + +#include "lmtp.h" + +/* lmtp_session_alloc - allocate and initialize LMTP_SESSION structure */ + +LMTP_SESSION *lmtp_session_alloc(VSTREAM *stream, char *host, char *addr) +{ + LMTP_SESSION *session; + + session = (LMTP_SESSION *) mymalloc(sizeof(*session)); + session->stream = stream; + session->host = mystrdup(host); + session->addr = mystrdup(addr); + session->destination = 0; + return (session); +} + +/* lmtp_session_free - destroy LMTP_SESSION structure and contents */ + +void lmtp_session_free(LMTP_SESSION *session) +{ + if (vstream_ispipe(session->stream)) + vstream_pclose(session->stream); + else + vstream_fclose(session->stream); + myfree(session->host); + myfree(session->addr); + if (session->destination) + myfree(session->destination); + myfree((char *) session); +} + +/* lmtp_session_reset - clean things up so a new session can be created */ + +void lmtp_session_reset(LMTP_STATE *state) +{ + if (state->session) { + lmtp_session_free(state->session); + state->session = 0; + } + state->reuse = 0; +} + diff --git a/postfix/lmtp/lmtp_state.c b/postfix/lmtp/lmtp_state.c new file mode 100644 index 000000000..c52bfb94f --- /dev/null +++ b/postfix/lmtp/lmtp_state.c @@ -0,0 +1,92 @@ +/*++ +/* NAME +/* lmtp_state 8 +/* SUMMARY +/* initialize/cleanup shared state +/* SYNOPSIS +/* #include "lmtp.h" +/* +/* LMTP_STATE *lmtp_state_alloc() +/* +/* void lmtp_state_free(state) +/* LMTP_STATE *state; +/* DESCRIPTION +/* lmtp_state_init() initializes the shared state, and allocates +/* memory for buffers etc. +/* +/* lmtp_cleanup() destroys memory allocated by lmtp_state_init(). +/* STANDARDS +/* DIAGNOSTICS +/* BUGS +/* SEE ALSO +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#include "lmtp.h" + +/* lmtp_state_alloc - initialize */ + +LMTP_STATE *lmtp_state_alloc(void) +{ + LMTP_STATE *state = (LMTP_STATE *) mymalloc(sizeof(*state)); + + state->src = 0; + state->request = 0; + state->session = 0; + state->buffer = vstring_alloc(100); + state->scratch = vstring_alloc(100); + state->scratch2 = vstring_alloc(100); + state->status = 0; + state->features = 0; + state->history = 0; + state->error_mask = 0; + state->sndbufsize = 0; + state->sndbuffree = 0; + state->reuse = 0; + return (state); +} + +/* lmtp_state_free - destroy state */ + +void lmtp_state_free(LMTP_STATE *state) +{ + vstring_free(state->buffer); + vstring_free(state->scratch); + vstring_free(state->scratch2); + myfree((char *) state); +} diff --git a/postfix/lmtp/lmtp_trouble.c b/postfix/lmtp/lmtp_trouble.c new file mode 100644 index 000000000..9a13b69d1 --- /dev/null +++ b/postfix/lmtp/lmtp_trouble.c @@ -0,0 +1,318 @@ +/*++ +/* NAME +/* lmtp_trouble 3 +/* SUMMARY +/* error handler policies +/* SYNOPSIS +/* #include "lmtp.h" +/* +/* int lmtp_site_fail(state, code, format, ...) +/* LMTP_STATE *state; +/* int code; +/* char *format; +/* +/* int lmtp_mesg_fail(state, code, format, ...) +/* LMTP_STATE *state; +/* int code; +/* char *format; +/* +/* void lmtp_rcpt_fail(state, code, recipient, format, ...) +/* LMTP_STATE *state; +/* int code; +/* RECIPIENT *recipient; +/* char *format; +/* +/* int lmtp_stream_except(state, exception, description) +/* LMTP_STATE *state; +/* int exception; +/* char *description; +/* DESCRIPTION +/* This module handles all non-fatal errors that can happen while +/* attempting to deliver mail via LMTP, and implements the policy +/* of how to deal with the error. Depending on the nature of +/* the problem, delivery of a single message is deferred, delivery +/* of all messages to the same domain is deferred, or one or more +/* recipients are given up as non-deliverable and a bounce log is +/* updated. +/* +/* In addition, when an unexpected response code is seen such +/* as 3xx where only 4xx or 5xx are expected, or any error code +/* that suggests a syntax error or something similar, the +/* protocol error flag is set so that the postmaster receives +/* a transcript of the session. No notification is generated for +/* what appear to be configuration errors - very likely, they +/* would suffer the same problem and just cause more trouble. +/* +/* lmtp_site_fail() handles the case where the program fails to +/* complete the initial LMTP handshake: the server is not reachable, +/* is not running, does not want talk to us, or we talk to ourselves. +/* The \fIcode\fR gives an error status code; the \fIformat\fR +/* argument gives a textual description. The policy is: soft +/* error: defer delivery of all messages to this domain; hard +/* error: bounce all recipients of this message. +/* The result is non-zero. +/* +/* lmtp_mesg_fail() handles the case where the lmtp server +/* does not accept the sender address or the message data. +/* The policy is: soft errors: defer delivery of this message; +/* hard error: bounce all recipients of this message. +/* The result is non-zero. +/* +/* lmtp_rcpt_fail() handles the case where a recipient is not +/* accepted by the server for reasons other than that the server +/* recipient limit is reached. The policy is: soft error: defer +/* delivery to this recipient; hard error: bounce this recipient. +/* +/* lmtp_stream_except() handles the exceptions generated by +/* the smtp_stream(3) module (i.e. timeouts and I/O errors). +/* The \fIexception\fR argument specifies the type of problem. +/* The \fIdescription\fR argument describes at what stage of +/* the LMTP dialog the problem happened. The policy is to defer +/* delivery of all messages to the same domain. The result is non-zero. +/* DIAGNOSTICS +/* Panic: unknown exception code. +/* SEE ALSO +/* lmtp_proto(3) lmtp high-level protocol +/* smtp_stream(3) lmtp low-level protocol +/* defer(3) basic message defer interface +/* bounce(3) basic message bounce interface +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include +#include /* 44BSD stdarg.h uses abort() */ +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "lmtp.h" + +#define LMTP_SOFT(code) (((code) / 100) == 4) +#define LMTP_HARD(code) (((code) / 100) == 5) +#define KEEP BOUNCE_FLAG_KEEP + +/* lmtp_check_code - check response code */ + +static void lmtp_check_code(LMTP_STATE *state, int code) +{ + + /* + * The intention of this stuff is to alert the postmaster when the local + * Postfix LMTP client screws up, protocol wise. RFC 821 says that x0z + * replies "refer to syntax errors, syntactically correct commands that + * don't fit any functional category, and unimplemented or superfluous + * commands". Unfortunately, this also triggers postmaster notices when + * remote servers screw up, protocol wise. This is becoming a common + * problem now that response codes are configured manually as part of + * anti-UCE systems, by people who aren't aware of RFC details. + */ + if ((!LMTP_SOFT(code) && !LMTP_HARD(code)) + || code == 555 /* RFC 1869, section 6.1. */ + || (code >= 500 && code < 510)) + state->error_mask |= MAIL_ERROR_PROTOCOL; +} + +/* lmtp_site_fail - defer site or bounce recipients */ + +int lmtp_site_fail(LMTP_STATE *state, int code, char *format,...) +{ + DELIVER_REQUEST *request = state->request; + LMTP_SESSION *session = state->session; + RECIPIENT *rcpt; + int status; + int nrcpt; + int soft_error = LMTP_SOFT(code); + va_list ap; + VSTRING *why = vstring_alloc(100); + + /* + * Initialize. + */ + va_start(ap, format); + vstring_vsprintf(why, format, ap); + va_end(ap); + + /* + * If this is a soft error, postpone further deliveries to this domain. + * Otherwise, generate a bounce record for each recipient. + */ + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->offset == 0) + continue; + status = (soft_error ? defer_append : bounce_append) + (KEEP, request->queue_id, rcpt->address, + session ? session->host : "none", + request->arrival_time, "%s", vstring_str(why)); + if (status == 0) { + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + state->status |= status; + } + if (soft_error && request->hop_status == 0) + request->hop_status = mystrdup(vstring_str(why)); + + /* + * Cleanup. + */ + vstring_free(why); + return (-1); +} + +/* lmtp_mesg_fail - defer message or bounce all recipients */ + +int lmtp_mesg_fail(LMTP_STATE *state, int code, char *format,...) +{ + DELIVER_REQUEST *request = state->request; + LMTP_SESSION *session = state->session; + RECIPIENT *rcpt; + int status; + int nrcpt; + va_list ap; + VSTRING *why = vstring_alloc(100); + + /* + * Initialize. + */ + va_start(ap, format); + vstring_vsprintf(why, format, ap); + va_end(ap); + + /* + * If this is a soft error, postpone delivery of this message. Otherwise, + * generate a bounce record for each recipient. + */ + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->offset == 0) + continue; + status = (LMTP_SOFT(code) ? defer_append : bounce_append) + (KEEP, request->queue_id, rcpt->address, + session->host, request->arrival_time, + "%s", vstring_str(why)); + if (status == 0) { + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + state->status |= status; + } + lmtp_check_code(state, code); + + /* + * Cleanup. + */ + vstring_free(why); + return (-1); +} + +/* lmtp_rcpt_fail - defer or bounce recipient */ + +void lmtp_rcpt_fail(LMTP_STATE *state, int code, RECIPIENT *rcpt, + char *format,...) +{ + DELIVER_REQUEST *request = state->request; + LMTP_SESSION *session = state->session; + int status; + va_list ap; + + /* + * If this is a soft error, postpone delivery to this recipient. + * Otherwise, generate a bounce record for this recipient. + */ + va_start(ap, format); + status = (LMTP_SOFT(code) ? vdefer_append : vbounce_append) + (KEEP, request->queue_id, rcpt->address, session->host, + request->arrival_time, format, ap); + va_end(ap); + if (status == 0) { + deliver_completed(state->src, rcpt->offset); + rcpt->offset = 0; + } + lmtp_check_code(state, code); + state->status |= status; +} + +/* lmtp_stream_except - defer domain after I/O problem */ + +int lmtp_stream_except(LMTP_STATE *state, int code, char *description) +{ + DELIVER_REQUEST *request = state->request; + LMTP_SESSION *session = state->session; + RECIPIENT *rcpt; + int nrcpt; + VSTRING *why = vstring_alloc(100); + + /* + * Initialize. + */ + switch (code) { + default: + msg_panic("lmtp_stream_except: unknown exception %d", code); + case SMTP_ERR_EOF: + vstring_sprintf(why, "lost connection with %s while %s", + session->host, description); + break; + case SMTP_ERR_TIME: + vstring_sprintf(why, "conversation with %s timed out while %s", + session->host, description); + break; + } + + /* + * At this point, the status of individual recipients remains unresolved. + * All we know is that we should stay away from this host for a while. + */ + for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { + rcpt = request->rcpt_list.info + nrcpt; + if (rcpt->offset == 0) + continue; + state->status |= defer_append(KEEP, request->queue_id, + rcpt->address, session->host, + request->arrival_time, + "%s", vstring_str(why)); + } + + /* + * Cleanup. + */ + vstring_free(why); + return (-1); +} diff --git a/postfix/lmtp/mail b/postfix/lmtp/mail new file mode 100644 index 000000000..35b9743cd --- /dev/null +++ b/postfix/lmtp/mail @@ -0,0 +1,753 @@ +From wietse@porcupine.org Sat Apr 15 10:47:09 2000 +Return-Path: +Delivered-To: wietse@hades.porcupine.org +Received: from spike.porcupine.org (spike.porcupine.org [168.100.189.2]) + by hades.porcupine.org (Postfix) with ESMTP id 567DC18A54 + for ; Sat, 15 Apr 2000 10:47:09 -0400 (EDT) +Received: by spike.porcupine.org (Postfix, from userid 100) + id BE9C14563D; Sat, 15 Apr 2000 10:47:08 -0400 (EDT) +Delivered-To: wietse@porcupine.org +Received: from ns0.utdallas.edu (ns0.utdallas.edu [129.110.10.1]) + by spike.porcupine.org (Postfix) with ESMTP id E91E045630 + for ; Sat, 15 Apr 2000 09:37:17 -0400 (EDT) +Received: from spartacus.utdallas.edu (spartacus.utdallas.edu [129.110.3.11]) + by ns0.utdallas.edu (Postfix) with SMTP id 7D0601A00B9 + for ; Sat, 15 Apr 2000 08:36:35 -0500 (CDT) +To: Wietse Venema +Subject: [Markku Järvinen ] postfix lmtp +From: Amos Gouaux +Date: 15 Apr 2000 08:37:54 -0500 +Message-ID: +Lines: 73 +User-Agent: Gnus/5.0804 (Gnus v5.8.4) XEmacs/21.1 (Bryce Canyon) +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="=-=-=" +Sender: wietse@porcupine.org +Status: ROr + +--=-=-= + +I suspect you've already dealt with this one during the merging, but +forwarding in case not. + +I do recall one reason why I just inserted RSET into the state +machine as I did--I wanted to check the response. Though, this is +easily remedied by having a mini state machine in the lmtp_rset +function. + +I guess I thought that having RSET in the state machine would be +okay because of this: + + while ((rec_type = rec_get(state->src, state->scratch, 0)) > 0) { + if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) + break; + if (prev_type != REC_TYPE_CONT) + if (vstring_str(state->scratch)[0] == '.') + smtp_fputc('.', session->stream); + if (rec_type == REC_TYPE_CONT) + smtp_fwrite(vstring_str(state->scratch), + VSTRING_LEN(state->scratch), + session->stream); + else + smtp_fputs(vstring_str(state->scratch), + VSTRING_LEN(state->scratch), + session->stream); + prev_type = rec_type; + } + +Wouldn't this just suck in the entire message text, then put a '.' +into the dialog? How would a RSET in the message text jumble up the +state machine? + +Amos + + + +--=-=-= +Content-Type: message/rfc822; charset="" +Content-Disposition: inline +Content-Transfer-Encoding: quoted-printable + +Return-Path: +X-Sieve: cmu-sieve 1.3 +Received: from antivirus.tpo.fi (ns3.tpo.fi [212.63.10.250]) + by ns0.utdallas.edu (Postfix) with ESMTP id 0700019FFF2 + for ; Fri, 14 Apr 2000 04:14:18 -0500 (CDT) +Received: from ky.tpo.fi (localhost [127.0.0.1]) + by antivirus.tpo.fi (8.9.3/8.9.3) with ESMTP id MAA09192 + for ; Fri, 14 Apr 2000 12:14:51 +0300 (EET DST) +Rec= +eived: from mtaj (home-f.ttk.tpo.fi [212.63.14.2]) + by ky.tpo.fi (Postfix) with SMTP id 801AFF568 + for ; Fri, 14 Apr 2000 12:14:50 +0300 (EET DST) +Mes= +sage-ID: <05f601bfa5f1$bb2097c0$69fd1fac@ttk.tpo.fi> +From: Markku J=E4rvinen +To: +Subject: postfix lmtp +Date: Fri, 14 Apr 2000 12:13:42 +0300 +MIME-Version: 1.0 +Content-Type: text/plain; + charset=3D"iso-8859-1" +Content-Transfer-Encoding: 7bit +X-Priority: 3 +X-MSMail-Priority: Normal +X-Mailer: Microsoft Outlook Express 5.00.2919.6600 +X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2919.6600 + +Hi! + +In line 349 of lmtp_proto.c you just print the sender into LMTP-transac= +tion. +vstring_sprintf(next_command, "MAIL FROM:<%s>", request->sender); +This fails when the sender address has spaces in it, you should first r= +un it +through quota_821_local to get it into the right format for LMTP (same = +as +SMTP). + + - Markku + + + +--=-=-=-- + + + + +From wietse@porcupine.org Sat Feb 26 09:17:05 2000 +Return-Path: +Delivered-To: wietse@hades.porcupine.org +Received: from spike.porcupine.org (spike.porcupine.org [168.100.189.2]) + by hades.porcupine.org (Postfix) with ESMTP id E0D7F1886D + for ; Sat, 26 Feb 2000 09:17:04 -0500 (EST) +Received: by spike.porcupine.org (Postfix, from userid 100) + id A520145659; Sat, 26 Feb 2000 09:17:04 -0500 (EST) +Delivered-To: wietse@porcupine.org +Received: from ns0.utdallas.edu (ns0.utdallas.edu [129.110.10.1]) + by spike.porcupine.org (Postfix) with ESMTP id 5773F45657 + for ; Fri, 25 Feb 2000 19:41:51 -0500 (EST) +Received: from spartacus.utdallas.edu (spartacus.utdallas.edu [129.110.3.11]) + by ns0.utdallas.edu (Postfix) with SMTP id 782F91A00D7 + for ; Fri, 25 Feb 2000 18:04:16 -0600 (CST) +To: wietse@porcupine.org (Wietse Venema) +Subject: Re: lmtp update +References: <20000221181534.11F7C45659@spike.porcupine.org> +From: Amos Gouaux +Date: 25 Feb 2000 18:04:54 -0600 +In-Reply-To: wietse@porcupine.org's message of "Mon, 21 Feb 2000 13:15:34 -0500 (EST)" +Message-ID: +Lines: 6 +User-Agent: Gnus/5.0804 (Gnus v5.8.4) XEmacs/21.1 (Bryce Canyon) +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="=-=-=" +Sender: wietse@porcupine.org +Status: RO + +--=-=-= + +How's this? + +Amos + + +--=-=-= +Content-Disposition: attachment; filename=lmtp-man +Content-Description: lmtp-man + +.TH LMTP 8 +.ad +.fi +.SH NAME +lmtp +\- +Postfix local delivery via LMTP +.SH SYNOPSIS +.na +.nf +\fBlmtp\fR [generic Postfix daemon options] [server attributes...] +.SH DESCRIPTION +.ad +.fi +The LMTP client processes message delivery requests from +the queue manager. Each request specifies a queue file, a sender +address, a domain or host to deliver to, and recipient information. +This program expects to be run from the \fBmaster\fR(8) process +manager. + +The LMTP client updates the queue file and marks recipients +as finished, or it informs the queue manager that delivery should +be tried again at a later time. Delivery problem reports are sent +to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate. + +There are two basic modes of operation for the LMTP client: +.IP \(bu +Communication with a local LMTP server via UNIX domain sockets. +.IP \(bu +Communication with a (possibly remote) LMTP server via +Internet sockets. +.PP +If no server attributes are specified, the LMTP client will contact +the destination host derived from the message delivery request using +the TCP port defined as \fBlmtp\fR in \fBservices\fR(4). If no such +service is found, the \fBlmtp_tcp_port\fR configuration parameter +(default value of 24) will be used. + +In order to use a local LMTP server, this LMTP server will need to +be specified via the server attributes described in the following +section. Typically, the LMTP client would also be configured as the +\fBlocal\fR delivery agent in the \fBmaster.cf\fR file. +.SH SERVER ATTRIBUTE SYNTAX +.na +.nf +.ad +.fi +The server attributes are given in the \fBmaster.cf\fR file at +the end of a service definition. The syntax is as follows: +.IP "\fBserv\fR=\fItype\fR:\fIserver\fR" +The LMTP server to connect to for final delivery. The \fItype\fR +portion can be either \fBunix\fR or \fBinet\fR. The \fIserver\fR +portion is the path or address of the LMTP server, depending on the +value of \fItype\fR, as shown below: +.RS +.IP "\fBserv=unix:\fR\fIclass\fR\fB/\fR\fIservname\fR" +This specifies that the local LMTP server \fIservname\fR should be +contacted for final delivery. Both \fIclass\fR (either \fBpublic\fR +or \fBprivate\fR) and \fIservname\fR correspond to the LMTP server +entry in the \fBmaster.cf\fR file. This LMTP server will likely +be defined as a \fBspawn\fR(8) service. +.IP "\fBserv=inet:" +If nothing follows the \fBinet:\fR type specifier, a connection will +be attempted to the destination host indicated in the delivery request. +This simplest case is identical to defining the LMTP client without +any server attributes at all. +.IP "\fBserv=inet:\fR\fIaddress\fR" +In this case, an Internet socket will be made to the server +specified by \fIaddress\fR. The connection will use a destination +port as described in the previous section. +.IP "\fBserv=inet:\fR\fIaddress\fR\fB:\fR\fIport\fR" +Connect to the LMTP server at \fIaddress\fR, but this time use port +\fIport\fR instead of the default \fBlmtp\fR port. +.IP "\fBserv=inet:[\fR\fIipaddr\fR\fB]\fR" +The LMTP server to contact is specified using an Internet address +in the "dot notation". That is, the numeric IP address rather +than the DNS name for the server. The default \fBlmtp\fR port +is used. +.IP "\fBserv=inet:[\fR\fIipaddr\fR\fB]:\fR\fIport\fR" +The LMTP server to contact is specified using the numeric IP address, +at the port specified. +.RE +.PP +.SH SECURITY +.na +.nf +.ad +.fi +The LMTP client is moderately security-sensitive. It talks to LMTP +servers and to DNS servers on the network. The LMTP client can be +run chrooted at fixed low privilege. +.SH STANDARDS +.na +.nf +RFC 2033 (LMTP protocol) +RFC 821 (SMTP protocol) +RFC 1651 (SMTP service extensions) +RFC 1870 (Message Size Declaration) +RFC 2197 (Pipelining) +.SH DIAGNOSTICS +.ad +.fi +Problems and transactions are logged to \fBsyslogd\fR(8). +Corrupted message files are marked so that the queue manager can +move them to the \fBcorrupt\fR queue for further inspection. + +Depending on the setting of the \fBnotify_classes\fR parameter, +the postmaster is notified of bounces, protocol problems, and of +other trouble. +.SH BUGS +.ad +.fi +.SH CONFIGURATION PARAMETERS +.na +.nf +.ad +.fi +The following \fBmain.cf\fR parameters are especially relevant to +this program. See the Postfix \fBmain.cf\fR file for syntax details +and for default values. Use the \fBpostfix reload\fR command after +a configuration change. +.SH Miscellaneous +.ad +.fi +.IP \fBdebug_peer_level\fR +Verbose logging level increment for hosts that match a +pattern in the \fBdebug_peer_list\fR parameter. +.IP \fBdebug_peer_list\fR +List of domain or network patterns. When a remote host matches +a pattern, increase the verbose logging level by the amount +specified in the \fBdebug_peer_level\fR parameter. +.IP \fBerror_notice_recipient\fR +Recipient of protocol/policy/resource/software error notices. +.IP \fBnotify_classes\fR +When this parameter includes the \fBprotocol\fR class, send mail to the +postmaster with transcripts of LMTP sessions with protocol errors. +.IP \fBlmtp_skip_quit_response\fR +Do not wait for the server response after sending QUIT. +.IP \fBlmtp_tcp_port\fR +The TCP port to be used when connecting to a LMTP server. Used as +backup if the \fBlmtp\fR service is not found in \fBservices\fR(4). +.SH "Resource controls" +.ad +.fi +.IP \fBlmtp_cache_connection\fR +Should we cache the connection to the LMTP server? The effectiveness +of cached connections will be determined by the number of LMTP servers +in use, and the concurrency limit specified for the LMTP client. +Cached connections are closed under any of the following conditions: +.RS +.IP \(bu +The idle timeout for the LMTP client is reached. This limit is +enforced by \fBmaster\fR(8). +.IP \(bu +A message request to a different destination than the one currently +cached. +.IP \(bu +The maximum number of requests per session is reached. This limit is +enforced by \fBmaster\fR(8). +.IP \(bu +Upon the onset of another delivery request, the LMTP server associated +with the current session does not respond to the \fBRSET\fR command. +.RE +.IP \fBlmtp_destination_concurrency_limit\fR +Limit the number of parallel deliveries to the same destination. +The default limit is taken from the +\fBdefault_destination_concurrency_limit\fR parameter. +.IP \fBlmtp_destination_recipient_limit\fR +Limit the number of recipients per message delivery. +The default limit is taken from the +\fBdefault_destination_recipient_limit\fR parameter. +.IP \fBlocal_destination_recipient_limit\fR +Limit the number of recipients per message delivery. +The default limit is taken from the +\fBdefault_destination_recipient_limit\fR parameter. + +This parameter becomes significant if the LMTP client is used +for local delivery. Some LMTP servers can optimize final delivery +if multiple recipients are allowed. Therefore, it may be advantageous +to set this to some number greater than one, depending on the capabilities +of the machine. + +Setting this parameter to 0 will lead to an unlimited number of +recipients per delivery. However, this could be risky since it may +make the machine vulnerable to running out of resources if messages +are encountered with an inordinate number of recipients. Exercise +care when setting this parameter. +.SH "Timeout controls" +.ad +.fi +.IP \fBlmtp_connect_timeout\fR +Timeout in seconds for opening a connection to the LMTP server. +If no connection can be made within the deadline, the message +is deferred. +.IP \fBlmtp_lhlo_timeout\fR +Timeout in seconds for sending the \fBLHLO\fR command, and for +receiving the server response. +.IP \fBlmtp_mail_timeout\fR +Timeout in seconds for sending the \fBMAIL FROM\fR command, and for +receiving the server response. +.IP \fBlmtp_rcpt_timeout\fR +Timeout in seconds for sending the \fBRCPT TO\fR command, and for +receiving the server response. +.IP \fBlmtp_data_init_timeout\fR +Timeout in seconds for sending the \fBDATA\fR command, and for +receiving the server response. +.IP \fBlmtp_data_xfer_timeout\fR +Timeout in seconds for sending the message content. +.IP \fBlmtp_data_done_timeout\fR +Timeout in seconds for sending the "\fB.\fR" command, and for +receiving the server response. When no response is received, a +warning is logged that the mail may be delivered multiple times. +.IP \fBlmtp_rset_timeout\fR +Timeout in seconds for sending the \fBRSET\fR command, and for +receiving the server response. +.IP \fBlmtp_quit_timeout\fR +Timeout in seconds for sending the \fBQUIT\fR command, and for +receiving the server response. +.SH SEE ALSO +.na +.nf +bounce(8) non-delivery status reports +local(8) local mail delivery +master(8) process manager +qmgr(8) queue manager +services(4) Internet services and aliases +spawn(8) auxiliary command spawner +syslogd(8) system logging +.SH LICENSE +.na +.nf +.ad +.fi +The Secure Mailer license must be distributed with this software. +.SH AUTHOR(S) +.na +.nf +Wietse Venema +IBM T.J. Watson Research +P.O. Box 704 +Yorktown Heights, NY 10598, USA + +Alterations for LMTP by: +Philip A. Prindeville +Mirapoint, Inc. +USA. + +Additional work on LMTP by: +Amos Gouaux +University of Texas at Dallas +P.O. Box 830688, MC34 +Richardson, TX 75083, USA + +--=-=-=-- + + + + +From wietse@porcupine.org Sat Feb 5 09:32:03 2000 +Return-Path: +Delivered-To: wietse@hades.porcupine.org +Received: from spike.porcupine.org (spike.porcupine.org [168.100.189.2]) + by hades.porcupine.org (Postfix) with ESMTP id A7661188A7 + for ; Sat, 5 Feb 2000 09:32:03 -0500 (EST) +Received: by spike.porcupine.org (Postfix, from userid 100) + id 700394563E; Sat, 5 Feb 2000 09:32:03 -0500 (EST) +Delivered-To: wietse@porcupine.org +Received: from ns0.utdallas.edu (ns0.utdallas.edu [129.110.10.1]) + by spike.porcupine.org (Postfix) with ESMTP id 605FE4563C + for ; Mon, 31 Jan 2000 18:35:02 -0500 (EST) +Received: from spartacus.utdallas.edu (spartacus.utdallas.edu [129.110.3.11]) + by ns0.utdallas.edu (Postfix) with SMTP id 02E4C1A005D + for ; Mon, 31 Jan 2000 17:34:59 -0600 (CST) +To: wietse@porcupine.org (Wietse Venema) +Subject: Re: lmtp-20000130.tar.gz +References: <20000131225228.008114563F@spike.porcupine.org> +From: Amos Gouaux +Date: 31 Jan 2000 17:35:34 -0600 +In-Reply-To: wietse@porcupine.org's message of "Mon, 31 Jan 2000 17:52:28 -0500 (EST)" +Message-ID: +Lines: 119 +User-Agent: Gnus/5.0803 (Gnus v5.8.3) XEmacs/21.1 (Bryce Canyon) +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Sender: wietse@porcupine.org +Status: O + +>>>>> On Mon, 31 Jan 2000 17:52:28 -0500 (EST), +>>>>> Wietse Venema (wv) writes: + +wv> For local transports, command pipelining does not have the benefit +wv> that it has for TCP over non-local connections. + +So in other words I was making things too complicated. +Figures. I have a knack for doing that. + +I've updated alpha-lmtp.tar.gz again. + +Here's the CHANGES file: + + +2000 Jan 31 + +* lmtp_proto.c:lmtp_lhlo: Don't worry about LMTP_FEATURE_PIPELINING + for sessions of type LMTP_SERV_TYPE_UNIX. + + +2000 Jan 30 + +* BIG changes. Removed all the pipe stuff from lmtp.c and + lmtp_connect.c Now, lmtp will either do a remote connection much + like the smtp client, or it will connect to a UNIX domain socket + to an auxiliary service (spawn). This makes the lmtp patch + simpler because it doesn't have to worry at all about exec-ing an + external command, and all the resulting security implications. + See README.local for details. + + NOTE: postfix-19991231-pl04 is REQUIRED for this to work! + (Need the "spawn" service.) + +* Updated the makefile-patch for postfix-19991231-pl03. + +* lmtp.h: changed the LMTP_ATTR structure to contain "type", "class", and + "name" fields, reflecting the change from the pipe mechanism to + the local sockets connection. Changed LMTP_SESSION to contain the + "type" field. The connection type is recorded in this field in + case it is needed elsewhere, like in lmtp_proto. + +* lmtp.c:get_service_attr altered for new command syntax. Examples: + + serv=unix:private/lmtpd + serv=inet:public/lmtpd + + After `serv=' is the "type", followed by `:', then the "class", + followed by '/', and then finally the "name". + +* Added SAME_DESTINATION macro to lmtp.c:deliver_message for + readability, and simpler logic. + +* lmtp_connect.c: changed lmtp_connect to contain logic to determine + just what kind of connection to make, removing it from lmtp.c; + removed lmtp_connect_pipe; and added lmtp_connect_local, which + uses mail_connect_wait to connect to the service running the LMTP + server (spawn). In lmtp_connect, the connection type stored in + attr.type is saved into session.type, so it can be used later if + necessary. + +* lmtp_proto.c:lmtp_lhlo: towards the end of this function we check + to see if service->type is LMTP_SERV_TYPE_UNIX so that we don't + try to do a getsockopt on a descriptor that doesn't support it. + Is this the best way to handle this? Or maybe we should just try + it and if it bombs, check the error code before simply going + fatal? + +* Snagged the latest quota_821_local.c from the smtp client. + +* Updated the README files accordingly. + + +2000 Jan 28 + +* Well, wondering about using REWRITE_ADDRESS in lmtp_proto.c, + putting the lowercase thing in there. Though, that would also + apply to sender address. Is that okay? + +* We shouldn't be doing DNS lookups at this stage, so can get rid + of lmtp_unalias.c. Also cut out the unalias portion in + REWRITE_ADDRESS in lmtp_proto.c + + +2000 Jan 11 + +* At the suggestion of Rupa Schomaker, added the following to + lmtp_proto.c: + + lowercase(rcpt->address); /* [AAG] rupa */ + + +1999 Dec 1 (or thereabouts) + +* Added lmtp_session_reset to make it easier to remember to reset + certain things to 0. Did this to lmtp_chat_reset too. + +* Finally, did some work with the connection caching so that an RSET + is sent to the LMTP server. This is done for two reasons: first, + to tell the LMTP server to flush out any status information + because we're about to feed it another message, and second, to + test the link to see if it is still alive. If the link has died + for some reason, it is reestablished. Changes to lmtp.c and + lmtp_proto.c. + +* There was also some tidying in lmtp.c so that things were a little + clearer as to what was going on. I added a few more comments as + well. + +* I tried to make the master.cf argument parsing a little more + consistent with the other Postfix services. Changes to lmtp.c. + +* On the postfix-users mailing list, Valery Brasseur pointed out that + errors encountered during LMTP delivery were not getting bounced + as appropriate. This lead me to investigate the matter, wanting + to put this LMTP capability into service myself. I then realized + that some of the error checking was a tad over zealous, and so + simplified things a bit in lmtp_proto.c. + + + + + + +From wietse@porcupine.org Wed Dec 8 19:50:01 1999 +Return-Path: +Delivered-To: wietse@hades.porcupine.org +Received: from spike.porcupine.org (spike.porcupine.org [168.100.189.2]) + by hades.porcupine.org (Postfix) with ESMTP id D83EE18868 + for ; Wed, 8 Dec 1999 19:50:00 -0500 (EST) +Received: by spike.porcupine.org (Postfix, from userid 100) + id 827F645AFB; Wed, 8 Dec 1999 10:51:18 -0500 (EST) +Delivered-To: wietse@porcupine.org +Received: from ns0.utdallas.edu (ns0.utdallas.edu [129.110.10.1]) + by spike.porcupine.org (Postfix) with ESMTP id 90E1A457F8 + for ; Tue, 7 Dec 1999 11:37:16 -0500 (EST) +Received: from spartacus.utdallas.edu (spartacus.utdallas.edu [129.110.3.11]) + by ns0.utdallas.edu (Postfix) with SMTP id 139E719FFFE + for ; Tue, 7 Dec 1999 10:37:02 -0600 (CST) +To: wietse@porcupine.org (Wietse Venema) +Subject: Re: LMTP stuff +References: <19991206162939.1C9BB458EB@spike.porcupine.org> +From: Amos Gouaux +Date: 07 Dec 1999 10:37:25 -0600 +In-Reply-To: wietse@porcupine.org's message of "Mon, 6 Dec 1999 11:29:38 -0500 (EST)" +Message-ID: +Lines: 46 +User-Agent: Gnus/5.070099 (Pterodactyl Gnus v0.99) XEmacs/21.1 (Bryce Canyon) +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Sender: wietse@porcupine.org +Status: RO + +Okay, I put out the following tar file: + +ftp://ftp.utdallas.edu/pub/staff/amos/postfix/lmtp-19990427-02.tar.gz + +It's not quite as tidy as I would like it, and hope to eventually +make it. I've been making changes gradually, in part because it +might be fun to play with the connection caching later, and in part +because I'm new to the internals of Postfix. Speaking of which, I +must say it has been a real pleasure poking around this code. It's +fascinating to see how you've created this infrastructure by which +all the various components communicate with one another. Pretty +slick stuff. Very educational too. + +Most of the changes are in lmtp_proto.c and lmtp.c. I noticed in +the former that he's passing status around a lot, using the +recv_state and send_state members instead of using local vars for +these values. I'm guess that was to keep track of the state +throughout the connection caching. I left that as is. + +In lmtp.c I removed the for loop he had, and parse the argv in a +separate function to make it a bit cleaner. I'm assuming that with +the lmtp service there won't be much of a need to expand the argv +like it is with the pipe service, correct? + +After seeing one of your posts yesterday about the nexthop arg to +pipe, I'm wondering if this lmtp should support that as well. I +noticed in the LMTP RFC that this LMTP can either be a local +program, or communicate to a "Gateway Delivery Agent". If there was +a nexthop arg to lmtp, folks could specify this gateway host there. +Or, they could use the transport map and not define any args to lmtp +at all. + +It's amazing how much time can be consumed just contemplating what +args should be permissible, and what's the most efficient way to +process them. I was even wondering, if no variable expansion should +take place, if `LMTP_ATTR attr' should be global, and the call to +get_service_attr placed in a function pointed to by +MAIL_SERVER_PRE_INIT so it's only invoked once. Still more things +to learn. + +Oh well, I've flung quite a bit mail at this thing and it seems to +be handling it fine. So perhaps it will at least be sufficient to +satisfy folks for the time being. + +Amos + + + + + +From wietse@porcupine.org Tue Nov 23 18:09:44 1999 +Return-Path: +Delivered-To: wietse@hades.porcupine.org +Received: from spike.porcupine.org (spike.porcupine.org [168.100.189.2]) + by hades.porcupine.org (Postfix) with ESMTP id BD43F18864 + for ; Tue, 23 Nov 1999 18:09:44 -0500 (EST) +Received: by spike.porcupine.org (Postfix, from userid 100) + id 4CC0445A9B; Tue, 23 Nov 1999 13:24:06 -0500 (EST) +Delivered-To: wietse@porcupine.org +Received: from russian-caravan.cloud9.net (russian-caravan.cloud9.net [168.100.1.4]) + by spike.porcupine.org (Postfix) with ESMTP id 979F145A9A + for ; Tue, 23 Nov 1999 13:19:06 -0500 (EST) +Received: by russian-caravan.cloud9.net (Postfix) + id AE6E576434; Tue, 23 Nov 1999 13:16:34 -0500 (EST) +Delivered-To: postfix-users-outgoing@cloud9.net +Received: by russian-caravan.cloud9.net (Postfix, from userid 54) + id 340DA76423; Tue, 23 Nov 1999 13:16:34 -0500 (EST) +Delivered-To: postfix-users@cloud9.net +Received: from atn01.axime.com (atn01.axime.com [160.92.1.141]) + by russian-caravan.cloud9.net (Postfix) with ESMTP id 32BE6763C6 + for ; Tue, 23 Nov 1999 13:16:32 -0500 (EST) +Received: from atos-group.com (sys-pc21.segin.com [172.18.2.119]) + by atn01.axime.com (8.8.8/8.8.8[Atos Multimedia]) with ESMTP id TAA25333; + Tue, 23 Nov 1999 19:16:20 +0100 (MET) +Message-ID: <383AD9F2.8915CCA6@atos-group.com> +Date: Tue, 23 Nov 1999 18:16:18 +0000 +From: valery brasseur +Organization: Atos Multimedia +X-Mailer: Mozilla 4.7 [en] (X11; I; Linux 2.2.12 i686) +X-Accept-Language: en, fr-FR +MIME-Version: 1.0 +To: Amos Gouaux +Cc: postfix-users@postfix.org +Subject: Re: LMTP? +References: <3839189C.C4E94EA1@atos-group.com> +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +Precedence: bulk +Sender: wietse@porcupine.org +Status: RO + +Here is the patch a use on the lmtp part of postfix for using LMTP on +Solaris (note that these diffs are not necessary for Linux !) +It's seems that the probleme come from the interpretation of the return +code from cyrus-deliver. + +note : they are against lmtp-19990427.tar.gz + +--- lmtp/lmtp_proto.c Tue Apr 20 09:42:45 1999 ++++ /postfix-19990906/lmtp/lmtp_proto.c Thu Sep 2 15:04:57 1999 +@@ -445,12 +445,12 @@ + if (resp->code / 100 == 2) { + ++nrcpt; + recipient_list_add(&survivors, rcpt->offset, +rcpt->address); +- } else if (resp->code == 550 ++ } else /* if (resp->code == 550 + && strncmp(resp->str, "550 5.1.1", 9) +== 0) { + deliver_completed(state->src, -1); + state->status |= -1; + rcpt->offset = 0; +- } else { ++ } else */ {o + lmtp_rcpt_fail(state, resp->code, rcpt, + "host %s said: %s", +session->host, + translit(resp->str, "\n", " +")); + + +Hope it will help. + +Amos Gouaux wrote: +> +> >>>>> On Mon, 22 Nov 1999 10:19:08 +0000, +> >>>>> valery brasseur (vb) writes: +> +> vb> I use it with cyrus, but I have done made some patch to the LMTP code +> vb> and deliver code because return code where not what the other was +> vb> expected !!! +> +> Do you think you could submit these patches to the list? +> +> I knew something had to be amiss. Using the Postfix sendmail +> command I attempted to send mail to a non-existent user, jdoe. The +> syslog from Postfix indicated successful delivery: +> +> Nov 22 07:03:45 area52 postfix/pipe[3082]: 6316124718: to=, relay=lmtp, delay=0, status=sent (jdoe@area52.utdallas.edu) +> +> However, when I run deliver by hand, the response isn't so positive: +> +> rcpt to: +> 550 5.1.1 User unknown +> +> Thanks, +> Amos + +-- +Valery BRASSEUR | Phone # +33 320 60 7982 +Atos Branche Multimedia | Fax # +33 320 60 7649 + "Unix -- where you can do anything in two keystrokes or less..." + -- Unknown + + + + diff --git a/postfix/lmtp/makefile-patch b/postfix/lmtp/makefile-patch new file mode 100644 index 000000000..0cf80e908 --- /dev/null +++ b/postfix/lmtp/makefile-patch @@ -0,0 +1,19 @@ +*** ../../orig/Makefile.in Fri Dec 31 09:49:41 1999 +--- Makefile.in Fri Feb 25 16:27:24 2000 +*************** +*** 4,10 **** + DIRS = util global dns master postfix smtpstone sendmail error \ + pickup cleanup smtpd local trivial-rewrite qmgr smtp bounce pipe \ + showq postalias postcat postconf postdrop postkick postlock postlog \ +! postmap postsuper # spawn man html + + default: update + +--- 4,10 ---- + DIRS = util global dns master postfix smtpstone sendmail error \ + pickup cleanup smtpd local trivial-rewrite qmgr smtp bounce pipe \ + showq postalias postcat postconf postdrop postkick postlock postlog \ +! postmap postsuper lmtp spawn man # html + + default: update + diff --git a/postfix/lmtp/man-patch b/postfix/lmtp/man-patch new file mode 100644 index 000000000..551002f16 --- /dev/null +++ b/postfix/lmtp/man-patch @@ -0,0 +1,218 @@ +*** ../../orig/man/Makefile.in Thu Jun 24 18:39:22 1999 +--- man/Makefile.in Fri Feb 25 16:35:53 2000 +*************** +*** 2,8 **** + + DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \ + man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 man8/showq.8 \ +! man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 + COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \ + man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \ + man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \ +--- 2,8 ---- + + DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \ + man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 man8/showq.8 \ +! man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 man8/lmtp.8 + COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \ + man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \ + man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \ +*************** +*** 24,99 **** + rm -f $(DAEMONS) $(COMMANDS) $(CONFIG) + + man8/bounce.8: ../bounce/bounce.c +! srctoman $? >$@ + + man8/defer.8: + echo .so man8/bounce.8 >$@ + + man8/cleanup.8: ../cleanup/cleanup.c +! srctoman $? >$@ + + man8/error.8: ../error/error.c +! srctoman $? >$@ + + man8/local.8: ../local/local.c +! srctoman $? >$@ + + man8/master.8: ../master/master.c +! srctoman $? >$@ + + man8/pickup.8: ../pickup/pickup.c +! srctoman $? >$@ + + man8/pipe.8: ../pipe/pipe.c +! srctoman $? >$@ + + man8/qmgr.8: ../qmgr/qmgr.c +! srctoman $? >$@ + + man8/showq.8: ../showq/showq.c +! srctoman $? >$@ + + man8/smtp.8: ../smtp/smtp.c +! srctoman $? >$@ + + man8/smtpd.8: ../smtpd/smtpd.c +! srctoman $? >$@ + + man8/trivial-rewrite.8: ../trivial-rewrite/trivial-rewrite.c +! srctoman $? >$@ + + man1/postalias.1: ../postalias/postalias.c +! srctoman $? >$@ + + man1/postcat.1: ../postcat/postcat.c +! srctoman $? >$@ + + man1/postconf.1: ../postconf/postconf.c +! srctoman $? >$@ + + man1/postdrop.1: ../postdrop/postdrop.c +! srctoman $? >$@ + + man1/postfix.1: ../postfix/postfix.c +! srctoman $? >$@ + + man1/postkick.1: ../postkick/postkick.c +! srctoman $? >$@ + + man1/postlock.1: ../postlock/postlock.c +! srctoman $? >$@ + + man1/postlog.1: ../postlog/postlog.c +! srctoman $? >$@ + + man1/postmap.1: ../postmap/postmap.c +! srctoman $? >$@ + + man1/postsuper.1: ../postsuper/postsuper.c +! srctoman $? >$@ + + man1/sendmail.1: ../sendmail/sendmail.c +! srctoman $? >$@ + + man1/mailq.1: + echo .so man1/sendmail.1 >$@ +--- 24,102 ---- + rm -f $(DAEMONS) $(COMMANDS) $(CONFIG) + + man8/bounce.8: ../bounce/bounce.c +! ../mantools/srctoman $? >$@ + + man8/defer.8: + echo .so man8/bounce.8 >$@ + + man8/cleanup.8: ../cleanup/cleanup.c +! ../mantools/srctoman $? >$@ + + man8/error.8: ../error/error.c +! ../mantools/srctoman $? >$@ + + man8/local.8: ../local/local.c +! ../mantools/srctoman $? >$@ + + man8/master.8: ../master/master.c +! ../mantools/srctoman $? >$@ + + man8/pickup.8: ../pickup/pickup.c +! ../mantools/srctoman $? >$@ + + man8/pipe.8: ../pipe/pipe.c +! ../mantools/srctoman $? >$@ + + man8/qmgr.8: ../qmgr/qmgr.c +! ../mantools/srctoman $? >$@ + + man8/showq.8: ../showq/showq.c +! ../mantools/srctoman $? >$@ + + man8/smtp.8: ../smtp/smtp.c +! ../mantools/srctoman $? >$@ + + man8/smtpd.8: ../smtpd/smtpd.c +! ../mantools/srctoman $? >$@ + + man8/trivial-rewrite.8: ../trivial-rewrite/trivial-rewrite.c +! ../mantools/srctoman $? >$@ + ++ man8/lmtp.8: ../lmtp/lmtp.c ++ ../mantools/srctoman $? >$@ ++ + man1/postalias.1: ../postalias/postalias.c +! ../mantools/srctoman $? >$@ + + man1/postcat.1: ../postcat/postcat.c +! ../mantools/srctoman $? >$@ + + man1/postconf.1: ../postconf/postconf.c +! ../mantools/srctoman $? >$@ + + man1/postdrop.1: ../postdrop/postdrop.c +! ../mantools/srctoman $? >$@ + + man1/postfix.1: ../postfix/postfix.c +! ../mantools/srctoman $? >$@ + + man1/postkick.1: ../postkick/postkick.c +! ../mantools/srctoman $? >$@ + + man1/postlock.1: ../postlock/postlock.c +! ../mantools/srctoman $? >$@ + + man1/postlog.1: ../postlog/postlog.c +! ../mantools/srctoman $? >$@ + + man1/postmap.1: ../postmap/postmap.c +! ../mantools/srctoman $? >$@ + + man1/postsuper.1: ../postsuper/postsuper.c +! ../mantools/srctoman $? >$@ + + man1/sendmail.1: ../sendmail/sendmail.c +! ../mantools/srctoman $? >$@ + + man1/mailq.1: + echo .so man1/sendmail.1 >$@ +*************** +*** 102,120 **** + echo .so man1/sendmail.1 >$@ + + man5/access.5: ../conf/access +! srctoman - $? >$@ + + man5/aliases.5: ../conf/aliases +! srctoman - $? >$@ + + man5/canonical.5: ../conf/canonical +! srctoman - $? >$@ + + man5/relocated.5: ../conf/relocated +! srctoman - $? >$@ + + man5/transport.5: ../conf/transport +! srctoman - $? >$@ + + man5/virtual.5: ../conf/virtual +! srctoman - $? >$@ +--- 105,123 ---- + echo .so man1/sendmail.1 >$@ + + man5/access.5: ../conf/access +! ../mantools/srctoman - $? >$@ + + man5/aliases.5: ../conf/aliases +! ../mantools/srctoman - $? >$@ + + man5/canonical.5: ../conf/canonical +! ../mantools/srctoman - $? >$@ + + man5/relocated.5: ../conf/relocated +! ../mantools/srctoman - $? >$@ + + man5/transport.5: ../conf/transport +! ../mantools/srctoman - $? >$@ + + man5/virtual.5: ../conf/virtual +! ../mantools/srctoman - $? >$@ diff --git a/postfix/lmtp/quote_821_local.c b/postfix/lmtp/quote_821_local.c new file mode 100644 index 000000000..49c277203 --- /dev/null +++ b/postfix/lmtp/quote_821_local.c @@ -0,0 +1,170 @@ +/*++ +/* NAME +/* quote_821_local 3 +/* SUMMARY +/* quote local part of address +/* SYNOPSIS +/* #include "quote_821_local.h" +/* +/* VSTRING *quote_821_local(dst, src) +/* VSTRING *dst; +/* char *src; +/* DESCRIPTION +/* quote_821_local() quotes the local part of a mailbox address and +/* returns a result that can be used in SMTP commands as specified +/* by RFC 821. +/* +/* Arguments: +/* .IP dst +/* The result. +/* .IP src +/* The input address. +/* STANDARDS +/* RFC 821 (SMTP protocol) +/* BUGS +/* The code assumes that the domain is RFC 821 clean. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ + +/* System library. */ + +#include +#include +#include + +/* Utility library. */ + +#include + +/* Global library. */ + +#include "quote_821_local.h" + +/* Application-specific. */ + +#define YES 1 +#define NO 0 + +/* is_821_dot_string - is this local-part an rfc 821 dot-string? */ + +static int is_821_dot_string(char *local_part, char *end) +{ + char *cp; + int ch; + + /* + * Detect any deviations from the definition of dot-string. We could use + * lookup tables to speed up some of the work, but hey, how large can a + * local-part be anyway? + */ + if (local_part[0] == 0 || local_part[0] == '.') + return (NO); + for (cp = local_part; cp < end && (ch = *cp) != 0; cp++) { + if (ch == '.' && cp[1] == '.') + return (NO); + if (ch > 127) + return (NO); + if (ch == ' ') + return (NO); + if (ISCNTRL(ch)) + return (NO); + if (ch == '<' || ch == '>' + || ch == '(' || ch == ')' + || ch == '[' || ch == ']' + || ch == '\\' || ch == ',' + || ch == ';' || ch == ':' + /* || ch == '@' */ || ch == '"') + return (NO); + } + if (cp[-1] == '.') + return (NO); + return (YES); +} + +/* make_821_quoted_string - make quoted-string from local-part */ + +static VSTRING *make_821_quoted_string(VSTRING *dst, char *local_part, char *end) +{ + char *cp; + int ch; + + /* + * Put quotes around the result, and prepend a backslash to characters + * that need quoting when they occur in a quoted-string. + */ + VSTRING_RESET(dst); + VSTRING_ADDCH(dst, '"'); + for (cp = local_part; cp < end && (ch = *cp) != 0; cp++) { + if (ch > 127 || ch == '\r' || ch == '\n' || ch == '"' || ch == '\\') + VSTRING_ADDCH(dst, '\\'); + VSTRING_ADDCH(dst, ch); + } + VSTRING_ADDCH(dst, '"'); + VSTRING_TERMINATE(dst); + return (dst); +} + +/* quote_821_local - quote local part of address according to rfc 821 */ + +VSTRING *quote_821_local(VSTRING *dst, char *addr) +{ + char *at; + + /* + * According to RFC 821, a local-part is a dot-string or a quoted-string. + * We first see if the local-part is a dot-string. If it is not, we turn + * it into a quoted-string. Anything else would be too painful. + */ + if ((at = strrchr(addr, '@')) == 0) /* just in case */ + at = addr + strlen(addr); /* should not happen */ + if (is_821_dot_string(addr, at)) { + return (vstring_strcpy(dst, addr)); + } else { + make_821_quoted_string(dst, addr, at); + return (vstring_strcat(dst, at)); + } +} + +#ifdef TEST + + /* + * Test program for local-part quoting as per rfc 821 + */ +#include +#include +#include +#include "quote_821_local.h" + +main(void) +{ + VSTRING *src = vstring_alloc(100); + VSTRING *dst = vstring_alloc(100); + + while (vstring_fgets_nonl(src, VSTREAM_IN)) { + vstream_fprintf(VSTREAM_OUT, "%s\n", + vstring_str(quote_821_local(dst, vstring_str(src)))); + vstream_fflush(VSTREAM_OUT); + } + exit(0); +} + +#endif diff --git a/postfix/lmtp/quote_821_local.h b/postfix/lmtp/quote_821_local.h new file mode 100644 index 000000000..17543f2e9 --- /dev/null +++ b/postfix/lmtp/quote_821_local.h @@ -0,0 +1,41 @@ +/*++ +/* NAME +/* quote_821_local 3h +/* SUMMARY +/* quote rfc 821 local part +/* SYNOPSIS +/* #include "quote_821_local.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * External interface. + */ +extern VSTRING *quote_821_local(VSTRING *, char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Alterations for LMTP by: +/* Philip A. Prindeville +/* Mirapoint, Inc. +/* USA. +/* +/* Additional work on LMTP by: +/* Amos Gouaux +/* University of Texas at Dallas +/* P.O. Box 830688, MC34 +/* Richardson, TX 75083, USA +/*--*/ diff --git a/postfix/local/.indent.pro b/postfix/local/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/local/.indent.pro +++ b/postfix/local/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/master/.indent.pro b/postfix/master/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/master/.indent.pro +++ b/postfix/master/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/pickup/.indent.pro b/postfix/pickup/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/pickup/.indent.pro +++ b/postfix/pickup/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/pipe/.indent.pro b/postfix/pipe/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/pipe/.indent.pro +++ b/postfix/pipe/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postalias/.indent.pro b/postfix/postalias/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postalias/.indent.pro +++ b/postfix/postalias/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postcat/.indent.pro b/postfix/postcat/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postcat/.indent.pro +++ b/postfix/postcat/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postconf/.indent.pro b/postfix/postconf/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postconf/.indent.pro +++ b/postfix/postconf/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postdrop/.indent.pro b/postfix/postdrop/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postdrop/.indent.pro +++ b/postfix/postdrop/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postfix/.indent.pro b/postfix/postfix/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postfix/.indent.pro +++ b/postfix/postfix/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postkick/.indent.pro b/postfix/postkick/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postkick/.indent.pro +++ b/postfix/postkick/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postlock/.indent.pro b/postfix/postlock/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postlock/.indent.pro +++ b/postfix/postlock/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postlog/.indent.pro b/postfix/postlog/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postlog/.indent.pro +++ b/postfix/postlog/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postmap/.indent.pro b/postfix/postmap/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postmap/.indent.pro +++ b/postfix/postmap/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/postsuper/.indent.pro b/postfix/postsuper/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/postsuper/.indent.pro +++ b/postfix/postsuper/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/qmgr/.indent.pro b/postfix/qmgr/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/qmgr/.indent.pro +++ b/postfix/qmgr/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/sendmail/.indent.pro b/postfix/sendmail/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/sendmail/.indent.pro +++ b/postfix/sendmail/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/showq/.indent.pro b/postfix/showq/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/showq/.indent.pro +++ b/postfix/showq/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/smtp/.indent.pro b/postfix/smtp/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/smtp/.indent.pro +++ b/postfix/smtp/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/smtp/smtp.c b/postfix/smtp/smtp.c index 3100e7155..ac674188a 100644 --- a/postfix/smtp/smtp.c +++ b/postfix/smtp/smtp.c @@ -97,6 +97,7 @@ /* .SH "Authentication controls" /* .IP \fBsmtp_enable_sasl_auth\fR /* Enable per-session authentication as per RFC 2554 (SASL). +/* By default, Postfix is built without SASL support. /* .IP \fBsmtp_sasl_password_maps\fR /* Lookup tables with per-host \fIname\fR:\fIpassword\fR entries. /* No entry for a host means no attempt to authenticate. @@ -198,7 +199,6 @@ #include #include #include -#include /* Single server skeleton. */ @@ -292,7 +292,6 @@ static int deliver_message(DELIVER_REQUEST *request) "%s", vstring_str(why)); } else { debug_peer_check(state->session->host, state->session->addr); - smtp_jump_setup(state->session->stream, state->jbuf); if (smtp_helo(state) == 0) smtp_xfer(state); if (state->history != 0 diff --git a/postfix/smtp/smtp.h b/postfix/smtp/smtp.h index b1ff99142..d73494871 100644 --- a/postfix/smtp/smtp.h +++ b/postfix/smtp/smtp.h @@ -8,11 +8,6 @@ /* DESCRIPTION /* .nf - /* - * System library. - */ -#include - /* * SASL library. */ @@ -57,7 +52,6 @@ typedef struct SMTP_STATE { VSTRING *sasl_decoded; /* decoding buffer */ sasl_callback_t *sasl_callbacks; /* stateful callbacks */ #endif - jmp_buf jbuf[1]; /* exception context */ } SMTP_STATE; #define SMTP_FEATURE_ESMTP (1<<0) diff --git a/postfix/smtp/smtp_proto.c b/postfix/smtp/smtp_proto.c index 7e3f2debe..cc6484d92 100644 --- a/postfix/smtp/smtp_proto.c +++ b/postfix/smtp/smtp_proto.c @@ -64,7 +64,6 @@ #include #include #include /* shutdown(2) */ -#include #include #include #include /* 44BSD stdarg.h uses abort() */ @@ -158,7 +157,7 @@ int smtp_helo(SMTP_STATE *state) * Prepare for disaster. */ smtp_timeout_setup(state->session->stream, var_smtp_helo_tmout); - if ((except = setjmp(state->jbuf[0])) != 0) + if ((except = vstream_setjmp(state->session->stream)) != 0) return (smtp_stream_except(state, except, "sending HELO")); /* @@ -413,6 +412,7 @@ int smtp_xfer(SMTP_STATE *state) * The final sender state has no action associated with it. */ case SMTP_STATE_LAST: + VSTRING_RESET(next_command); break; } VSTRING_TERMINATE(next_command); @@ -439,7 +439,7 @@ int smtp_xfer(SMTP_STATE *state) */ smtp_timeout_setup(state->session->stream, *xfer_timeouts[recv_state]); - if ((except = setjmp(state->jbuf[0])) != 0) + if ((except = vstream_setjmp(state->session->stream)) != 0) RETURN(smtp_stream_except(state, except, xfer_states[recv_state])); resp = smtp_chat_resp(state); @@ -591,7 +591,7 @@ int smtp_xfer(SMTP_STATE *state) if (send_state == SMTP_STATE_DOT && nrcpt > 0) { smtp_timeout_setup(state->session->stream, var_smtp_data1_tmout); - if ((except = setjmp(state->jbuf[0])) != 0) + if ((except = vstream_setjmp(state->session->stream)) != 0) RETURN(smtp_stream_except(state, except, "sending message body")); diff --git a/postfix/smtpd/.indent.pro b/postfix/smtpd/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/smtpd/.indent.pro +++ b/postfix/smtpd/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/smtpd/smtpd.c b/postfix/smtpd/smtpd.c index 5285bcfbd..cea1ab58f 100644 --- a/postfix/smtpd/smtpd.c +++ b/postfix/smtpd/smtpd.c @@ -229,7 +229,6 @@ #include #include #include /* remove() */ -#include #include #include #include @@ -1145,10 +1144,9 @@ static void smtpd_proto(SMTPD_STATE *state) * cleans up, but no attempt is made to inform the client of the nature * of the problem. */ - smtp_jump_setup(state->client, state->jbuf); smtp_timeout_setup(state->client, var_smtpd_tmout); - switch (setjmp(state->jbuf[0])) { + switch (vstream_setjmp(state->client)) { default: msg_panic("smtpd_proto: unknown error reading from %s[%s]", diff --git a/postfix/smtpd/smtpd.h b/postfix/smtpd/smtpd.h index b39bebab7..447ad932e 100644 --- a/postfix/smtpd/smtpd.h +++ b/postfix/smtpd/smtpd.h @@ -8,11 +8,6 @@ /* DESCRIPTION /* .nf - /* - * System library - */ -#include - /* * SASL library. */ @@ -76,7 +71,6 @@ typedef struct SMTPD_STATE { VSTRING *sasl_encoded; VSTRING *sasl_decoded; #endif - jmp_buf jbuf[1]; } SMTPD_STATE; extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *); diff --git a/postfix/smtpstone/.indent.pro b/postfix/smtpstone/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/smtpstone/.indent.pro +++ b/postfix/smtpstone/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/smtpstone/smtp-sink.c b/postfix/smtpstone/smtp-sink.c index 8b49d7590..701fdf05f 100644 --- a/postfix/smtpstone/smtp-sink.c +++ b/postfix/smtpstone/smtp-sink.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -74,7 +73,6 @@ typedef struct SINK_STATE { int data_state; int (*read) (struct SINK_STATE *); int rcpts; - jmp_buf jbuf[1]; } SINK_STATE; #define ST_ANY 0 @@ -285,7 +283,7 @@ static void read_event(int unused_event, char *context) SINK_STATE *state = (SINK_STATE *) context; do { - switch (setjmp(state->jbuf[0])) { + switch (vstream_setjmp(state->stream)) { default: msg_panic("unknown error reading input"); @@ -335,7 +333,6 @@ static void connect_event(int unused_event, char *context) state->stream = vstream_fdopen(fd, O_RDWR); state->read = command_read; state->data_state = 0; - smtp_jump_setup(state->stream, state->jbuf); smtp_timeout_setup(state->stream, var_tmout); smtp_printf(state->stream, "220 %s ESMTP", var_myhostname); event_enable_read(fd, read_event, (char *) state); diff --git a/postfix/smtpstone/smtp-source.c b/postfix/smtpstone/smtp-source.c index 4ca866e6e..0fc1ae8f3 100644 --- a/postfix/smtpstone/smtp-source.c +++ b/postfix/smtpstone/smtp-source.c @@ -65,7 +65,6 @@ #include #include #include -#include #include #include #include @@ -112,7 +111,6 @@ typedef struct SESSION { VSTREAM *stream; /* open connection */ int connect_count; /* # of connect()s to retry */ struct SESSION *next; /* connect() queue linkage */ - jmp_buf jbuf[1]; /* exception handling */ } SESSION; static SESSION *last_session; /* connect() queue tail */ @@ -390,7 +388,6 @@ static void start_connect(SESSION *session) (void) non_blocking(fd, NON_BLOCKING); session->stream = vstream_fdopen(fd, O_RDWR); event_enable_write(fd, connect_done, (char *) session); - smtp_jump_setup(session->stream, session->jbuf); smtp_timeout_setup(session->stream, var_timeout); if (connect(fd, (struct sockaddr *) & sin, sizeof(sin)) < 0 && errno != EINPROGRESS) @@ -429,7 +426,7 @@ static void read_banner(int unused_event, char *context) /* * Prepare for disaster. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while reading server greeting", exception_text(except)); /* @@ -452,12 +449,12 @@ static void read_banner(int unused_event, char *context) static void send_helo(SESSION *session) { int except; - char *protocol = (talk_lmtp ? "LHLO" : "EHLO"); + char *protocol = (talk_lmtp ? "LHLO" : "HELO"); /* * Send the standard greeting with our hostname */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending HELO", exception_text(except)); command(session->stream, "%s %s", protocol, var_myhostname); @@ -480,7 +477,7 @@ static void helo_done(int unused_event, char *context) /* * Get response to HELO command. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending HELO", exception_text(except)); if ((resp = response(session->stream, buffer))->code / 100 != 2) @@ -498,7 +495,7 @@ static void send_mail(SESSION *session) /* * Send the envelope sender address. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending sender", exception_text(except)); command(session->stream, "MAIL FROM:<%s>", sender); @@ -521,7 +518,7 @@ static void mail_done(int unused, char *context) /* * Get response to MAIL command. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending sender", exception_text(except)); if ((resp = response(session->stream, buffer))->code / 100 != 2) @@ -542,7 +539,7 @@ static void send_rcpt(int unused_event, char *context) /* * Send envelope recipient address. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending recipient", exception_text(except)); if (session->rcpt_count > 1) @@ -571,7 +568,7 @@ static void rcpt_done(int unused, char *context) /* * Get response to RCPT command. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending recipient", exception_text(except)); if ((resp = response(session->stream, buffer))->code / 100 != 2) @@ -596,7 +593,7 @@ static void send_data(int unused_event, char *context) /* * Request data transmission. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending DATA command", exception_text(except)); command(session->stream, "DATA"); @@ -620,7 +617,7 @@ static void data_done(int unused_event, char *context) /* * Get response to DATA command. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending DATA command", exception_text(except)); if ((resp = response(session->stream, buffer))->code != 354) msg_fatal("data %d %s", resp->code, resp->str); @@ -644,7 +641,7 @@ static void data_done(int unused_event, char *context) /* * Send some garbage. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending message", exception_text(except)); if (message_length == 0) { smtp_fputs("La de da de da 1.", 17, session->stream); @@ -687,7 +684,7 @@ static void dot_done(int unused_event, char *context) /* * Get response to "." command. */ - if ((except = setjmp(session->jbuf[0])) != 0) + if ((except = vstream_setjmp(session->stream)) != 0) msg_fatal("%s while sending message", exception_text(except)); do { /* XXX this could block */ if ((resp = response(session->stream, buffer))->code / 100 != 2) diff --git a/postfix/spawn/.indent.pro b/postfix/spawn/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/spawn/.indent.pro +++ b/postfix/spawn/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/trivial-rewrite/.indent.pro b/postfix/trivial-rewrite/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/trivial-rewrite/.indent.pro +++ b/postfix/trivial-rewrite/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/util/.indent.pro b/postfix/util/.indent.pro index 2139804a4..5fbb816df 100644 --- a/postfix/util/.indent.pro +++ b/postfix/util/.indent.pro @@ -47,6 +47,10 @@ -TINET_ADDR_LIST -TINT_TABLE -TJMP_BUF_WRAPPER +-TLMTP_ATTR +-TLMTP_RESP +-TLMTP_SESSION +-TLMTP_STATE -TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP diff --git a/postfix/util/Makefile.in b/postfix/util/Makefile.in index dc8e0546c..dda83a113 100644 --- a/postfix/util/Makefile.in +++ b/postfix/util/Makefile.in @@ -21,8 +21,7 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \ write_buf.c write_wait.c dict_unix.c dict_pcre.c stream_listen.c \ stream_connect.c stream_trigger.c dict_regexp.c mac_expand.c \ clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \ - sane_link.c unescape.c timed_read.c timed_write.c binattr.c \ - vstream_attr.c + sane_link.c unescape.c timed_read.c timed_write.c binattr.c OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \ dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \ @@ -45,8 +44,7 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \ write_buf.o write_wait.o dict_unix.o dict_pcre.o stream_listen.o \ stream_connect.o stream_trigger.o dict_regexp.o mac_expand.o \ clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \ - sane_link.o unescape.o timed_read.o timed_write.o binattr.o \ - vstream_attr.o + sane_link.o unescape.o timed_read.o timed_write.o binattr.o HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \ dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \ dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \ @@ -951,14 +949,8 @@ vstream.o: vbuf_print.h vstream.o: vbuf.h vstream.o: iostuff.h vstream.o: vstring.h -vstream.o: binattr.h vstream.o: vstream.h -vstream_attr.o: vstream_attr.c -vstream_attr.o: sys_defs.h -vstream_attr.o: vstream.h -vstream_attr.o: vbuf.h -vstream_attr.o: binattr.h -vstream_attr.o: htable.h +vstream.o: binattr.h vstream_popen.o: vstream_popen.c vstream_popen.o: sys_defs.h vstream_popen.o: msg.h diff --git a/postfix/util/iostuff.h b/postfix/util/iostuff.h index ddc621a32..851c091c8 100644 --- a/postfix/util/iostuff.h +++ b/postfix/util/iostuff.h @@ -24,8 +24,8 @@ extern int peekfd(int); extern int read_wait(int, int); extern int write_wait(int, int); extern int write_buf(int, const char *, int, int); -extern int timed_read(int, void *, unsigned, int); -extern int timed_write(int, void *, unsigned, int); +extern int timed_read(int, void *, unsigned, int, void *); +extern int timed_write(int, void *, unsigned, int, void *); extern void doze(unsigned); extern int duplex_pipe(int *); diff --git a/postfix/util/timed_read.c b/postfix/util/timed_read.c index 045d97c94..1a29a69f9 100644 --- a/postfix/util/timed_read.c +++ b/postfix/util/timed_read.c @@ -6,11 +6,12 @@ /* SYNOPSIS /* #include /* -/* int timed_read(fd, buf, buf_len, timeout) +/* int timed_read(fd, buf, buf_len, timeout, context) /* int fd; /* void *buf; /* unsigned len; /* int timeout; +/* void *context; /* DESCRIPTION /* timed_read() performs a read() operation when the specified /* descriptor becomes readable within a user-specified deadline. @@ -25,6 +26,9 @@ /* .IP timeout /* The deadline in seconds. If this is <= 0, the deadline feature /* is disabled. +/* .IP context +/* Application context. This parameter is unused. It exists only +/* for the sake of VSTREAM compatibility. /* DIAGNOSTICS /* When the operation does not complete within the deadline, the /* result value is -1, and errno is set to ETIMEDOUT. @@ -51,7 +55,8 @@ /* timed_read - read with deadline */ -int timed_read(int fd, void *buf, unsigned len, int timeout) +int timed_read(int fd, void *buf, unsigned len, + int timeout, void *unused_context) { /* diff --git a/postfix/util/timed_write.c b/postfix/util/timed_write.c index 2bc428d1e..723bf036d 100644 --- a/postfix/util/timed_write.c +++ b/postfix/util/timed_write.c @@ -6,11 +6,12 @@ /* SYNOPSIS /* #include /* -/* int timed_write(fd, buf, buf_len, timeout) +/* int timed_write(fd, buf, buf_len, timeout, context) /* int fd; /* const void *buf; /* unsigned len; /* int timeout; +/* void *context; /* DESCRIPTION /* timed_write() performs a write() operation when the specified /* descriptor becomes writable within a user-specified deadline. @@ -25,6 +26,9 @@ /* .IP timeout /* The deadline in seconds. If this is <= 0, the deadline feature /* is disabled. +/* .IP context +/* Application context. This parameter is unused. It exists only +/* for the sake of VSTREAM compatibility. /* DIAGNOSTICS /* When the operation does not complete within the deadline, the /* result value is -1, and errno is set to ETIMEDOUT. @@ -51,7 +55,8 @@ /* timed_write - write with deadline */ -int timed_write(int fd, void *buf, unsigned len, int timeout) +int timed_write(int fd, void *buf, unsigned len, + int timeout, void *unused_context) { /* diff --git a/postfix/util/vstream.c b/postfix/util/vstream.c index 2274510df..9c845a613 100644 --- a/postfix/util/vstream.c +++ b/postfix/util/vstream.c @@ -93,6 +93,14 @@ /* /* int vstream_peek(stream) /* VSTREAM *stream; +/* +/* int vstream_setjmp(stream, buffer) +/* VSTREAM *stream; +/* jmp_buf *buffer; +/* +/* void longjmp(stream, val) +/* VSTREAM *stream; +/* int val; /* DESCRIPTION /* The \fIvstream\fR module implements light-weight buffered I/O /* similar to the standard I/O routines. @@ -201,12 +209,15 @@ /* value) pairs, terminated with VSTREAM_CTL_END. /* The following lists the names and the types of the corresponding /* value arguments. -/* .IP "VSTREAM_CTL_READ_FN (int (*)(int, void *, unsigned))" +/* .IP "VSTREAM_CTL_READ_FN (int (*)(int, void *, unsigned, int, void *))" /* The argument specifies an alternative for the timed_read(3) function, /* for example, a read function that performs encryption. -/* .IP "VSTREAM_CTL_WRITE_FN (int (*)(int, void *, unsigned))" +/* .IP "VSTREAM_CTL_WRITE_FN (int (*)(int, void *, unsigned, int, void *))" /* The argument specifies an alternative for the timed_write(3) function, /* for example, a write function that performs encryption. +/* .IP "VSTREAM_CTL_CONTEXT (char *)" +/* The argument specifies application context that is passed on to +/* the application-specified read/write routines. No copy is made. /* .IP "VSTREAM_CTL_PATH (char *)" /* Updates the stored pathname of the specified stream. The pathname /* is copied. @@ -228,6 +239,10 @@ /* The deadline for a descriptor to become readable in case of a read /* request, or writable in case of a write request. Specify a value /* <= 0 to disable deadlines. +/* .IP "VSTREAM_CTL_EXCEPT (no value)" +/* Enable exception handling with vstream_setjmp() and vstream_longjmp(). +/* This involves allocation of additional memory that normally isn't +/* used. /* .PP /* vstream_fileno() gives access to the file handle associated with /* a buffered stream. With streams that have separate read/write @@ -254,12 +269,22 @@ /* /* vstream_peek() returns the number of characters that can be /* read from the named stream without refilling the read buffer. +/* +/* vstream_setjmp() saves processing context and makes that context +/* available for use with vstream_longjmp(). Normally, vstream_setjmp() +/* returns zero. A non-zero result means that vstream_setjmp() returned +/* through a vstream_longjmp() call; the result is the \fIval\fR argment +/* given to vstream_longjmp(). +/* +/* NB: non-local jumps such as vstream_longjmp() are not safe +/* for jumping out of any vstream routine. /* DIAGNOSTICS /* Panics: interface violations. Fatal errors: out of memory. /* SEE ALSO /* timed_read(3) default read routine /* timed_write(3) default write routine /* vbuf_print(3) formatting engine +/* setjmp(3) non-local jumps /* BUGS /* Should use mmap() on reasonable systems. /* LICENSE @@ -291,7 +316,6 @@ #include "vbuf_print.h" #include "iostuff.h" #include "vstring.h" -#include "binattr.h" #include "vstream.h" /* Application-specific. */ @@ -499,7 +523,7 @@ static int vstream_fflush_some(VSTREAM *stream, int to_flush) * any. */ for (data = (char *) bp->data, len = to_flush; len > 0; len -= n, data += n) { - if ((n = stream->write_fn(stream->fd, data, len, stream->timeout)) <= 0) { + if ((n = stream->write_fn(stream->fd, data, len, stream->timeout, stream->context)) <= 0) { bp->flags |= VSTREAM_FLAG_ERR; if (errno == ETIMEDOUT) bp->flags |= VSTREAM_FLAG_TIMEOUT; @@ -625,7 +649,7 @@ static int vstream_buf_get_ready(VBUF *bp) * data as is available right now, whichever is less. Update the cached * file seek position, if any. */ - switch (n = stream->read_fn(stream->fd, bp->data, bp->len, stream->timeout)) { + switch (n = stream->read_fn(stream->fd, bp->data, bp->len, stream->timeout, stream->context)) { case -1: bp->flags |= VSTREAM_FLAG_ERR; if (errno == ETIMEDOUT) @@ -874,7 +898,8 @@ VSTREAM *vstream_fdopen(int fd, int flags) stream->pid = 0; stream->waitpid_fn = 0; stream->timeout = 0; - stream->attr = 0; + stream->context = 0; + stream->jbuf = 0; return (stream); } @@ -929,8 +954,8 @@ int vstream_fclose(VSTREAM *stream) } if (stream->path) myfree(stream->path); - if (stream->attr) - binattr_free(stream->attr); + if (stream->jbuf) + myfree((char *) stream->jbuf); if (!VSTREAM_STATIC(stream)) myfree((char *) stream); return (err ? VSTREAM_EOF : 0); @@ -988,6 +1013,9 @@ void vstream_control(VSTREAM *stream, int name,...) case VSTREAM_CTL_WRITE_FN: stream->write_fn = va_arg(ap, VSTREAM_FN); break; + case VSTREAM_CTL_CONTEXT: + stream->context = va_arg(ap, char *); + break; case VSTREAM_CTL_PATH: if (stream->path) myfree(stream->path); @@ -1023,6 +1051,10 @@ void vstream_control(VSTREAM *stream, int name,...) case VSTREAM_CTL_TIMEOUT: stream->timeout = va_arg(ap, int); break; + case VSTREAM_CTL_EXCEPT: + if (stream->jbuf == 0) + stream->jbuf = (jmp_buf *) mymalloc(sizeof(jmp_buf)); + break; default: msg_panic("%s: bad name %d", myname, name); } diff --git a/postfix/util/vstream.h b/postfix/util/vstream.h index 76cb3d25a..400eaf9a4 100644 --- a/postfix/util/vstream.h +++ b/postfix/util/vstream.h @@ -16,6 +16,7 @@ */ #include #include +#include /* * Utility library. @@ -27,7 +28,7 @@ * Simple buffered stream. The members of this structure are not part of the * official interface and can change without prior notice. */ -typedef int (*VSTREAM_FN) (int, void *, unsigned, int); +typedef int (*VSTREAM_FN) (int, void *, unsigned, int, void *); typedef int (*VSTREAM_WAITPID_FN) (pid_t, WAIT_STATUS_T *, int); typedef struct VSTREAM { @@ -35,16 +36,17 @@ typedef struct VSTREAM { int fd; /* file handle, no 256 limit */ VSTREAM_FN read_fn; /* buffer fill action */ VSTREAM_FN write_fn; /* buffer fill action */ + void *context; /* application context */ long offset; /* cached seek info */ char *path; /* give it at least try */ int read_fd; /* read channel (double-buffered) */ int write_fd; /* write channel (double-buffered) */ VBUF read_buf; /* read buffer (double-buffered) */ VBUF write_buf; /* write buffer (double-buffered) */ - int timeout; /* read/write timout */ pid_t pid; /* vstream_popen/close() */ VSTREAM_WAITPID_FN waitpid_fn; /* vstream_popen/close() */ - BINATTR *attr; /* optional binary attribute list */ + int timeout; /* read/write timout */ + jmp_buf *jbuf; /* exception handling */ } VSTREAM; extern VSTREAM vstream_fstd[]; /* pre-defined streams */ @@ -104,6 +106,8 @@ extern void vstream_control(VSTREAM *, int,...); #define VSTREAM_CTL_WRITE_FD 6 #define VSTREAM_CTL_WAITPID_FN 7 #define VSTREAM_CTL_TIMEOUT 8 +#define VSTREAM_CTL_EXCEPT 9 +#define VSTREAM_CTL_CONTEXT 10 extern VSTREAM *vstream_printf(const char *,...); extern VSTREAM *vstream_fprintf(VSTREAM *, const char *,...); @@ -128,14 +132,11 @@ extern VSTREAM *vstream_vfprintf(VSTREAM *, const char *, va_list); extern int vstream_peek(VSTREAM *); /* - * Attribute management, a way of tacking on arbitrary information onto a - * VSTREAM without destroying the VSTREAM abstraction itself. + * Exception handling. We use pointer to jmp_buf to avoid a lot of unused + * baggage for streams that don't need this functionality. */ -#define VSTREAM_ATTR_FREE_FN BINATTR_FREE_FN - -extern void vstream_attr_set(VSTREAM *, const char *, char *, VSTREAM_ATTR_FREE_FN); -extern char *vstream_attr_get(VSTREAM *, const char *); -extern void vstream_attr_unset(VSTREAM *, const char *); +#define vstream_setjmp(stream) setjmp((stream)->jbuf[0]) +#define vstream_longjmp(stream, val) longjmp((stream)->jbuf[0], (val)) /* LICENSE /* .ad diff --git a/postfix/util/vstream_attr.c b/postfix/util/vstream_attr.c deleted file mode 100644 index 00484b72b..000000000 --- a/postfix/util/vstream_attr.c +++ /dev/null @@ -1,92 +0,0 @@ -/*++ -/* NAME -/* vstream_attr 3 -/* SUMMARY -/* per-stream attribute list management -/* SYNOPSIS -/* #include -/* -/* void vstream_attr_set(stream, name, value, free_fn) -/* VSTREAM *stream; -/* const char *name; -/* char *value; -/* void (*free_fn)(char *); -/* -/* char *vstream_attr_get(stream, name) -/* VSTREAM *stream; -/* const char *name; -/* -/* void vstream_attr_unset(stream, name) -/* VSTREAM *stream; -/* const char *name; -/* DESCRIPTION -/* This module maintains an optional per-stream open attribute -/* list for arbitrary binary values. It is in fact a convienience -/* interface built on top of the binattr(3) module. -/* -/* vstream_attr_set() adds or replaces the named attribute. -/* -/* vstream_attr_get() looks up the named attribute. The result -/* is the value stored with vstream_attr_set() or a null pointer -/* when the requested information is not found. -/* -/* vstream_attr_unset() removes the named attribute. This operation -/* is undefined for attributes that do not exist. -/* -/* Arguments: -/* .IP stream -/* Open VSTREAM. -/* .IP name -/* Attribute name, in the form of a null-terminated list. -/* The name is copied. -/* .IP value -/* Arbitrary binary value. The value is not copied. -/* .IP free_fn -/* Null pointer, or pointer to function that destroys the value -/* that was stored with vstream_attr_set(). -/* LICENSE -/* .ad -/* .fi -/* The Secure Mailer license must be distributed with this software. -/* AUTHOR(S) -/* Wietse Venema -/* IBM T.J. Watson Research -/* P.O. Box 704 -/* Yorktown Heights, NY 10598, USA -/*--*/ - -/* System library. */ - -#include - -/* Utility library. */ - -#include -#include - -/* vstream_attr_set - add or replace per-stream attribute */ - -void vstream_attr_set(VSTREAM *stream, const char *name, char *value, BINATTR_FREE_FN free_fn) -{ - if (stream->attr == 0) - stream->attr = binattr_create(1); - binattr_set(stream->attr, name, value, free_fn); -} - -/* vstream_attr_get - look up per-stream attribute */ - -char *vstream_attr_get(VSTREAM *stream, const char *name) -{ - if (stream->attr == 0) - return (0); - else - return (binattr_get(stream->attr, name)); -} - -/* vstream_attr_unset - unset per-stream attribute */ - -void vstream_attr_unset(VSTREAM *stream, const char *name) -{ - if (stream->attr) - binattr_unset(stream->attr, name); -}