-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
* 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
/* SYNOPSIS
/* #include <smtp_stream.h>
/*
-/* void smtp_jump_setup(stream, jbuf)
-/* VSTREAM *stream;
-/* jmp_buf *jbuf;
-/*
/* void smtp_timeout_setup(stream, timeout)
/* VSTREAM *stream;
/* int timeout;
/* 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.
/* 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
/* 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
#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_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 */
+/* smtp_timeout_reset - reset per-stream timeout flag */
-static void smtp_timeout_protect(VSTREAM *stream)
+static void smtp_timeout_reset(VSTREAM *stream)
{
vstream_clearerr(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 */
/*
* 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);
}
/*
* 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);
if (err != 0) {
if (msg_verbose)
msg_info("smtp_vprintf: EOF");
- smtp_timeout_jump(stream, SMTP_ERR_EOF);
+ vstream_longjmp(stream, SMTP_ERR_EOF);
}
}
* 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));
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);
}
/*
* 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);
if (err != 0) {
if (msg_verbose)
msg_info("smtp_fputs: EOF");
- smtp_timeout_jump(stream, SMTP_ERR_EOF);
+ vstream_longjmp(stream, SMTP_ERR_EOF);
}
}
/*
* 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);
if (err != 0) {
if (msg_verbose)
msg_info("smtp_fwrite: EOF");
- smtp_timeout_jump(stream, SMTP_ERR_EOF);
+ vstream_longjmp(stream, SMTP_ERR_EOF);
}
}
/*
* Do the I/O, protected against timeout.
*/
- smtp_timeout_protect(stream);
+ smtp_timeout_reset(stream);
stat = VSTREAM_PUTC(ch, stream);
smtp_timeout_detect(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);
}
}
--- /dev/null
+-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
--- /dev/null
+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
--- /dev/null
+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.
+
--- /dev/null
+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'
--- /dev/null
+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
+
--- /dev/null
+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;
+
+
+
--- /dev/null
+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.
+
--- /dev/null
+sed '
+ s/LMTP/SMTP/g
+ s/lmtp/smtp/g
+ s/host/namaddr/g
+' $*
--- /dev/null
+*** ../../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.
+ */
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dict.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <argv.h>
+#include <mymalloc.h>
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <deliver_request.h>
+#include <mail_queue.h>
+#include <mail_params.h>
+#include <mail_conf.h>
+#include <debug_peer.h>
+#include <mail_error.h>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* lmtp 3h
+/* SUMMARY
+/* lmtp client program
+/* SYNOPSIS
+/* #include "lmtp.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <vstring.h>
+#include <argv.h>
+
+ /*
+ * Global library.
+ */
+#include <deliver_request.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <mymalloc.h>
+#include <inet_addr_list.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <own_inet_addr.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* 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);
+}
+
--- /dev/null
+/*++
+/* NAME
+/* lmtp_addr 3h
+/* SUMMARY
+/* LMTP server address lookup
+/* SYNOPSIS
+/* #include "lmtp_addr.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * DNS library.
+ */
+#include <dns.h>
+
+ /*
+ * 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
+/*--*/
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <argv.h>
+#include <stringops.h>
+#include <line_wrap.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <smtp_stream.h>
+#include <mail_params.h>
+#include <mail_addr.h>
+#include <post_mail.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <split_at.h>
+#include <mymalloc.h>
+#include <inet_addr_list.h>
+#include <iostuff.h>
+#include <timed_connect.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <own_inet_addr.h>
+
+/* DNS library. */
+
+#include <dns.h>
+
+/* 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);
+}
+
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <sys/socket.h> /* shutdown(2) */
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <smtp_stream.h>
+#include <mail_queue.h>
+#include <recipient_list.h>
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <defer.h>
+#include <bounce.h>
+#include <sent.h>
+#include <record.h>
+#include <rec_type.h>
+#include <off_cvt.h>
+#include <mark_corrupt.h>
+
+/* 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));
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstream.h>
+
+/* 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;
+}
+
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <config.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <smtp_stream.h>
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <bounce.h>
+#include <defer.h>
+#include <mail_error.h>
+
+/* 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);
+}
--- /dev/null
+From wietse@porcupine.org Sat Apr 15 10:47:09 2000
+Return-Path: <wietse@porcupine.org>
+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 <wietse@hades.porcupine.org>; 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 <wietse@porcupine.org>; 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 <wietse@porcupine.org>; Sat, 15 Apr 2000 08:36:35 -0500 (CDT)
+To: Wietse Venema <wietse@porcupine.org>
+Subject: [Markku Järvinen <Markku.Jarvinen@tpo.fi>] postfix lmtp
+From: Amos Gouaux <amos@utdallas.edu>
+Date: 15 Apr 2000 08:37:54 -0500
+Message-ID: <q6m66tj32od.fsf@spartacus.utdallas.edu>
+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: <markku.jarvinen@tpo.fi>
+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 <amos@utdallas.edu>; 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 <amos@utdallas.edu>; 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 <amos@utdallas.edu>; Fri, 14 Apr 2000 12:14:50 +0300 (EET DST)
+Mes=
+sage-ID: <05f601bfa5f1$bb2097c0$69fd1fac@ttk.tpo.fi>
+From: Markku J=E4rvinen <Markku.Jarvinen@tpo.fi>
+To: <amos@utdallas.edu>
+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: <wietse@porcupine.org>
+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 <wietse@hades.porcupine.org>; 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 <wietse@porcupine.org>; 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 <wietse@porcupine.org>; 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 <amos@utdallas.edu>
+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: <q6mitzcvnfd.fsf@spartacus.utdallas.edu>
+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: <wietse@porcupine.org>
+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 <wietse@hades.porcupine.org>; 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 <wietse@porcupine.org>; 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 <wietse@porcupine.org>; 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 <amos@utdallas.edu>
+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: <q6mzotlvmpl.fsf@spartacus.utdallas.edu>
+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 <wietse@porcupine.org> (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: <wietse@porcupine.org>
+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 <wietse@hades.porcupine.org>; 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 <wietse@porcupine.org>; 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 <wietse@porcupine.org>; 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 <amos+lists.postfix@utdallas.edu>
+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: <q6m7liqiu62.fsf@spartacus.utdallas.edu>
+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: <wietse@porcupine.org>
+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 <wietse@hades.porcupine.org>; 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 <wietse@porcupine.org>; 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 <postfix-users@postfix.org>; 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 <vbrasseur@atos-group.com>
+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 <amos+lists.postfix@utdallas.edu>
+Cc: postfix-users@postfix.org
+Subject: Re: LMTP?
+References: <q6memdj3djk.fsf@spartacus.utdallas.edu> <3839189C.C4E94EA1@atos-group.com> <q6mso1yn00k.fsf@spartacus.utdallas.edu>
+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 <vbrasseur@atos-group.com> (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=<jdoe@area52.utdallas.edu>, 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:<jdoe@area52.utdallas.edu>
+> 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
+
+
+
+
--- /dev/null
+*** ../../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
+
--- /dev/null
+*** ../../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 - $? >$@
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* 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 <stdlib.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#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
--- /dev/null
+/*++
+/* NAME
+/* quote_821_local 3h
+/* SUMMARY
+/* quote rfc 821 local part
+/* SYNOPSIS
+/* #include "quote_821_local.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * 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
+/*--*/
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
/* .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.
#include <debug_peer.h>
#include <mail_error.h>
#include <deliver_pass.h>
-#include <smtp_stream.h>
/* Single server skeleton. */
"%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
/* DESCRIPTION
/* .nf
- /*
- * System library.
- */
-#include <setjmp.h>
-
/*
* SASL library.
*/
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)
#include <sys_defs.h>
#include <sys/stat.h>
#include <sys/socket.h> /* shutdown(2) */
-#include <setjmp.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
* 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"));
/*
* The final sender state has no action associated with it.
*/
case SMTP_STATE_LAST:
+ VSTRING_RESET(next_command);
break;
}
VSTRING_TERMINATE(next_command);
*/
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);
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"));
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
#include <netdb.h>
#include <string.h>
#include <stdio.h> /* remove() */
-#include <setjmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
* 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]",
/* DESCRIPTION
/* .nf
- /*
- * System library
- */
-#include <setjmp.h>
-
/*
* SASL library.
*/
VSTRING *sasl_encoded;
VSTRING *sasl_decoded;
#endif
- jmp_buf jbuf[1];
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
-#include <setjmp.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
int data_state;
int (*read) (struct SINK_STATE *);
int rcpts;
- jmp_buf jbuf[1];
} SINK_STATE;
#define ST_ANY 0
SINK_STATE *state = (SINK_STATE *) context;
do {
- switch (setjmp(state->jbuf[0])) {
+ switch (vstream_setjmp(state->stream)) {
default:
msg_panic("unknown error reading input");
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);
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdarg.h>
-#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
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 */
(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)
/*
* 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));
/*
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);
/*
* 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)
/*
* 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);
/*
* 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)
/*
* 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)
/*
* 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)
/*
* 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");
/*
* 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);
/*
* 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);
/*
* 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)
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
+-TLMTP_ATTR
+-TLMTP_RESP
+-TLMTP_SESSION
+-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
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 \
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 \
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
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 *);
/* SYNOPSIS
/* #include <iostuff.h>
/*
-/* 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.
/* .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.
/* 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)
{
/*
/* SYNOPSIS
/* #include <iostuff.h>
/*
-/* 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.
/* .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.
/* 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)
{
/*
/*
/* 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.
/* 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.
/* 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
/*
/* 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
#include "vbuf_print.h"
#include "iostuff.h"
#include "vstring.h"
-#include "binattr.h"
#include "vstream.h"
/* Application-specific. */
* 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;
* 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)
stream->pid = 0;
stream->waitpid_fn = 0;
stream->timeout = 0;
- stream->attr = 0;
+ stream->context = 0;
+ stream->jbuf = 0;
return (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);
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);
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);
}
*/
#include <fcntl.h>
#include <stdarg.h>
+#include <setjmp.h>
/*
* Utility library.
* 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 {
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 */
#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 *,...);
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
+++ /dev/null
-/*++
-/* NAME
-/* vstream_attr 3
-/* SUMMARY
-/* per-stream attribute list management
-/* SYNOPSIS
-/* #include <vstream.h>
-/*
-/* 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 <sys_defs.h>
-
-/* Utility library. */
-
-#include <vstream.h>
-#include <htable.h>
-
-/* 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);
-}