2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

snapshot-20000504

This commit is contained in:
Wietse Venema 2000-05-04 00:00:00 +00:00
parent 17e2c5a67a
commit 2e7745be77
71 changed files with 4913 additions and 229 deletions

4
postfix/.indent.pro vendored
View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -15,7 +15,7 @@
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "Snapshot-20000503"
#define DEF_MAIL_VERSION "Snapshot-20000504"
extern char *var_mail_version;
/* LICENSE

View File

@ -6,10 +6,6 @@
/* 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;
@ -46,22 +42,15 @@
/* with error detection: timeouts or unexpected end-of-file.
/* A trailing CR LF is added upon writing and removed upon reading.
/*
/* smtp_jump_setup() registers a caller context that will be
/* jumped to (with longjmp()) when any routine in this module
/* experiences an error condition (timeout, I/O error, or
/* unexpected EOF).
/*
/* smtp_timeout_setup() arranges for a time limit on the smtp read
/* and write operations described below.
/* This routine alters the behavior of streams as follows:
/* .IP \(bu
/* The read routine is replaced by one than calls timed_read().
/* .IP \(bu
/* The write routine is replaced by one that calls timed_write().
/* The read/write timeout is set to the specified value.
/* .IP \f(bu
/* The stream is configured to use double buffering.
/* .IP \f(bu
/* A timeout error is reported to the vstream module as an I/O error.
/* The stream is configured to enable exception handling.
/* .PP
/* smtp_printf() formats its arguments and writes the result to
/* the named stream, followed by a CR LF pair. The stream is flushed.
@ -89,9 +78,9 @@
/* DIAGNOSTICS
/* .fi
/* .ad
/* In case of error, a longjmp() is performed to the context
/* specified with the smtp_jump_setup() call.
/* Error codes passed along with longjmp() are:
/* In case of error, a vstream_longjmp() call is performed to the
/* context specified with vstream_setjmp().
/* Error codes passed along with vstream_longjmp() are:
/* .IP SMTP_ERR_EOF
/* An I/O error happened, or the peer has disconnected unexpectedly.
/* .IP SMTP_ERR_TIME
@ -100,8 +89,8 @@
/* The timeout deadline affects all I/O on the named stream, not
/* just the I/O done on behalf of this module.
/*
/* The timeout deadline and exception handling context overwrite
/* any previously set up state on the named stream.
/* The timeout deadline overwrites any previously set up state on
/* the named stream.
/* LICENSE
/* .ad
/* .fi
@ -137,35 +126,9 @@
#include "smtp_stream.h"
/*
* Our private VSTREAM attribute name for keeping track of the
* caller-supplied context for exception handling.
*/
#define SMTP_ATTR_JBUF "smtp_timeout_buf"
/* smtp_timeout_reset - reset per-stream timeout flag */
/* smtp_timeout_jump - release timeout trap */
static void smtp_timeout_jump(VSTREAM *stream, int what)
{
char *myname = "smtp_timeout_jump";
jmp_buf *jbuf;
if ((jbuf = (jmp_buf *) vstream_attr_get(stream, SMTP_ATTR_JBUF)) == 0)
msg_panic("%s: no jump buffer", myname);
longjmp(jbuf[0], what);
}
/* smtp_jump_setup - configure exception handling context */
void smtp_jump_setup(VSTREAM *stream, jmp_buf * jbuf)
{
vstream_attr_set(stream, SMTP_ATTR_JBUF,
(char *) jbuf, (VSTREAM_ATTR_FREE_FN) 0);
}
/* smtp_timeout_protect - reset per-stream timeout flag */
static void smtp_timeout_protect(VSTREAM *stream)
static void smtp_timeout_reset(VSTREAM *stream)
{
vstream_clearerr(stream);
}
@ -175,7 +138,7 @@ static void smtp_timeout_protect(VSTREAM *stream)
static void smtp_timeout_detect(VSTREAM *stream)
{
if (vstream_ftimeout(stream))
smtp_timeout_jump(stream, SMTP_ERR_TIME);
vstream_longjmp(stream, SMTP_ERR_TIME);
}
/* smtp_timeout_setup - configure timeout trap */
@ -185,13 +148,14 @@ void smtp_timeout_setup(VSTREAM *stream, int maxtime)
/*
* Stick your TLS/whatever read-write routines here. Notice that the
* read/write interface now includes a timeout parameter, and that a
* read/write routine is supposed to set errno to ETIMEDOUT when the
* alarm clock goes off.
* read/write interface now includes a timeout parameter and application
* context, and that a read/write routine is supposed to set errno to
* ETIMEDOUT when the alarm clock goes off.
*/
vstream_control(stream,
VSTREAM_CTL_TIMEOUT, maxtime,
VSTREAM_CTL_DOUBLE,
VSTREAM_CTL_TIMEOUT, maxtime,
VSTREAM_CTL_EXCEPT,
VSTREAM_CTL_END);
}
@ -204,7 +168,7 @@ void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
/*
* Do the I/O, protected against timeout.
*/
smtp_timeout_protect(stream);
smtp_timeout_reset(stream);
vstream_vfprintf(stream, fmt, ap);
vstream_fputs("\r\n", stream);
err = vstream_fflush(stream);
@ -216,7 +180,7 @@ void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
if (err != 0) {
if (msg_verbose)
msg_info("smtp_vprintf: EOF");
smtp_timeout_jump(stream, SMTP_ERR_EOF);
vstream_longjmp(stream, SMTP_ERR_EOF);
}
}
@ -244,7 +208,7 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, int bound)
* allow for lines ending in bare LF. The idea is to be liberal in what
* we accept, strict in what we send.
*/
smtp_timeout_protect(stream);
smtp_timeout_reset(stream);
last_char = (bound == 0 ? vstring_get(vp, stream) :
vstring_get_bound(vp, stream, bound));
@ -291,7 +255,7 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, int bound)
if (vstream_feof(stream) || vstream_ferror(stream)) {
if (msg_verbose)
msg_info("smtp_get: EOF");
smtp_timeout_jump(stream, SMTP_ERR_EOF);
vstream_longjmp(stream, SMTP_ERR_EOF);
}
return (last_char);
}
@ -308,7 +272,7 @@ void smtp_fputs(const char *cp, int todo, VSTREAM *stream)
/*
* Do the I/O, protected against timeout.
*/
smtp_timeout_protect(stream);
smtp_timeout_reset(stream);
err = (vstream_fwrite(stream, cp, todo) != todo
|| vstream_fputs("\r\n", stream) == VSTREAM_EOF);
smtp_timeout_detect(stream);
@ -319,7 +283,7 @@ void smtp_fputs(const char *cp, int todo, VSTREAM *stream)
if (err != 0) {
if (msg_verbose)
msg_info("smtp_fputs: EOF");
smtp_timeout_jump(stream, SMTP_ERR_EOF);
vstream_longjmp(stream, SMTP_ERR_EOF);
}
}
@ -335,7 +299,7 @@ void smtp_fwrite(const char *cp, int todo, VSTREAM *stream)
/*
* Do the I/O, protected against timeout.
*/
smtp_timeout_protect(stream);
smtp_timeout_reset(stream);
err = (vstream_fwrite(stream, cp, todo) != todo);
smtp_timeout_detect(stream);
@ -345,7 +309,7 @@ void smtp_fwrite(const char *cp, int todo, VSTREAM *stream)
if (err != 0) {
if (msg_verbose)
msg_info("smtp_fwrite: EOF");
smtp_timeout_jump(stream, SMTP_ERR_EOF);
vstream_longjmp(stream, SMTP_ERR_EOF);
}
}
@ -358,7 +322,7 @@ void smtp_fputc(int ch, VSTREAM *stream)
/*
* Do the I/O, protected against timeout.
*/
smtp_timeout_protect(stream);
smtp_timeout_reset(stream);
stat = VSTREAM_PUTC(ch, stream);
smtp_timeout_detect(stream);
@ -368,6 +332,6 @@ void smtp_fputc(int ch, VSTREAM *stream)
if (stat == VSTREAM_EOF) {
if (msg_verbose)
msg_info("smtp_fputc: EOF");
smtp_timeout_jump(stream, SMTP_ERR_EOF);
vstream_longjmp(stream, SMTP_ERR_EOF);
}
}

120
postfix/lmtp/.indent.pro vendored Normal file
View File

@ -0,0 +1,120 @@
-TALIAS_TOKEN
-TARGV
-TBH_TABLE
-TBINATTR
-TBINATTR_INFO
-TBINHASH
-TBINHASH_INFO
-TBOUNCE_STAT
-TCLEANUP_STATE
-TCLIENT_LIST
-TCLNT_STREAM
-TCONFIG_BOOL_FN_TABLE
-TCONFIG_BOOL_TABLE
-TCONFIG_INT_FN_TABLE
-TCONFIG_INT_TABLE
-TCONFIG_STR_FN_TABLE
-TCONFIG_STR_TABLE
-TDELIVER_ATTR
-TDELIVER_REQUEST
-TDICT
-TDICT_DB
-TDICT_DBM
-TDICT_ENV
-TDICT_HT
-TDICT_LDAP
-TDICT_MYSQL
-TDICT_NI
-TDICT_NIS
-TDICT_NISPLUS
-TDICT_NODE
-TDICT_OPEN_INFO
-TDICT_PCRE
-TDICT_REGEXP
-TDICT_REGEXP_RULE
-TDICT_UNIX
-TDNS_FIXED
-TDNS_REPLY
-TDNS_RR
-TDOMAIN_LIST
-TEXPAND_ATTR
-TFILE
-TFORWARD_INFO
-THEADER_OPTS
-THOST
-THTABLE
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-TMAC_PARSE
-TMAIL_PRINT
-TMAIL_SCAN
-TMAPS
-TMASTER_PROC
-TMASTER_SERV
-TMASTER_STATUS
-TMBLOCK
-TMKMAP
-TMKMAP_OPEN_INFO
-TMULTI_SERVER
-TMVECT
-TMYSQL_NAME
-TNAMADR_LIST
-TNAME_MASK
-TPEER_NAME
-TPICKUP_INFO
-TPIPE_ATTR
-TPIPE_PARAMS
-TPLMYSQL
-TQMGR_ENTRY
-TQMGR_MESSAGE
-TQMGR_QUEUE
-TQMGR_RCPT_LIST
-TQMGR_RECIPIENT
-TQMGR_SCAN
-TQMGR_TRANSPORT
-TRECIPIENT
-TRECIPIENT_LIST
-TREC_TYPE_NAME
-TRESOLVE_REPLY
-TRESPONSE
-TSCAN_DIR
-TSCAN_INFO
-TSCAN_OBJ
-TSESSION
-TSINGLE_SERVER
-TSINK_COMMAND
-TSINK_STATE
-TSMTPD_CMD
-TSMTPD_STATE
-TSMTPD_TOKEN
-TSMTP_ADDR
-TSMTP_CMD
-TSMTP_RESP
-TSMTP_SESSION
-TSMTP_STATE
-TSOCKADDR_SIZE
-TSPAWN_ATTR
-TSTRING_TABLE
-TSYS_EXITS_TABLE
-TTOK822
-TTRIGGER_SERVER
-TUSER_ATTR
-TVBUF
-TVSTREAM
-TVSTREAM_POPEN_ARGS
-TVSTRING
-TWAIT_STATUS_T
-TWATCHDOG
-TWATCH_FD
-Tsasl_conn_t
-Tsasl_secret_t

25
postfix/lmtp/.printfck Normal file
View File

@ -0,0 +1,25 @@
been_here_xt 2 0
bounce_append 5 0
cleanup_out_format 1 0
defer_append 5 0
mail_command 1 0
mail_print 1 0
msg_error 0 0
msg_fatal 0 0
msg_info 0 0
msg_panic 0 0
msg_warn 0 0
opened 3 0
post_mail_fprintf 1 0
qmgr_message_bounce 2 0
rec_fprintf 2 0
sent 4 0
smtp_cmd 1 0
smtp_mesg_fail 2 0
smtp_printf 1 0
smtp_rcpt_fail 3 0
smtp_site_fail 2 0
udp_syslog 1 0
vstream_fprintf 1 0
vstream_printf 0 0
vstring_sprintf 1 0

146
postfix/lmtp/CHANGES Normal file
View File

@ -0,0 +1,146 @@
2000 Feb 23
* lmtp.c, lmtp_connect.c, global-patch: added the main.cf
configuration parameter "lmtp_tcp_port". If no port is explicitly
specificed for the connection to the inet LMTP server, first
lookup "lmtp" with getservbyname. If that fails, use the value of
this "lmtp_tcp_port" parameter, which as a default value of 24.
2000 Feb 21
* Updated lmtp.c to yield a lmtp.8 man page that more accurately
reflects the current code.
* Created man-patch so that lmtp.8 man page is created. Updated
README to include applying this patch.
2000 Feb 17
* Correctly handle lmtp master.cf arguments of type "inet".
Possible uses are:
_USAGE_ _MEANING_
serv=inet: connect over tcp to $nexthop
serv=inet:hostname connect to named host port 24
(Actually, what "lmtp" is defined to be
in /etc/services.)
serv=inet:hostname:port connect to named host named port
serv=inet:[ip.address] connect to named host port 24
serv=inet:[ip.address]:port connect to named address named port
2000 Feb 15
* Put in comment about local_destination_recipient_limit
in README.local.
* In lmtp_chat.c, changed error reporting so that it goes
to var_error_rcpt, like the other Postfix services.
2000 Jan 31
* lmtp_proto.c:lmtp_lhlo: Don't worry about LMTP_FEATURE_PIPELINING
for sessions of type LMTP_SERV_TYPE_UNIX.
2000 Jan 30
* BIG changes. Removed all the pipe stuff from lmtp.c and
lmtp_connect.c Now, lmtp will either do a remote connection much
like the smtp client, or it will connect to a UNIX domain socket
to an auxiliary service (spawn). This makes the lmtp patch
simpler because it doesn't have to worry at all about exec-ing an
external command, and all the resulting security implications.
See README.local for details.
NOTE: postfix-19991231-pl04 is REQUIRED for this to work!
(Need the "spawn" service.)
* Updated the makefile-patch for postfix-19991231-pl03.
* lmtp.h: changed the LMTP_ATTR structure to contain "type", "class", and
"name" fields, reflecting the change from the pipe mechanism to
the local sockets connection. Changed LMTP_SESSION to contain the
"type" field. The connection type is recorded in this field in
case it is needed elsewhere, like in lmtp_proto.
* lmtp.c:get_service_attr altered for new command syntax. Examples:
serv=unix:private/lmtpd
serv=inet:public/lmtpd
After `serv=' is the "type", followed by `:', then the "class",
followed by '/', and then finally the "name".
* Added SAME_DESTINATION macro to lmtp.c:deliver_message for
readability, and simpler logic.
* lmtp_connect.c: changed lmtp_connect to contain logic to determine
just what kind of connection to make, removing it from lmtp.c;
removed lmtp_connect_pipe; and added lmtp_connect_local, which
uses mail_connect_wait to connect to the service running the LMTP
server (spawn). In lmtp_connect, the connection type stored in
attr.type is saved into session.type, so it can be used later if
necessary.
* lmtp_proto.c:lmtp_lhlo: towards the end of this function we check
to see if service->type is LMTP_SERV_TYPE_UNIX so that we don't
try to do a getsockopt on a descriptor that doesn't support it.
Is this the best way to handle this? Or maybe we should just try
it and if it bombs, check the error code before simply going
fatal?
* Snagged the latest quota_821_local.c from the smtp client.
* Updated the README files accordingly.
2000 Jan 28
* Well, wondering about using REWRITE_ADDRESS in lmtp_proto.c,
putting the lowercase thing in there. Though, that would also
apply to sender address. Is that okay?
* We shouldn't be doing DNS lookups at this stage, so can get rid
of lmtp_unalias.c. Also cut out the unalias portion in
REWRITE_ADDRESS in lmtp_proto.c
2000 Jan 11
* At the suggestion of Rupa Schomaker, added the following to
lmtp_proto.c:
lowercase(rcpt->address); /* [AAG] rupa */
1999 Dec 1 (or thereabouts)
* Added lmtp_session_reset to make it easier to remember to reset
certain things to 0. Did this to lmtp_chat_reset too.
* Finally, did some work with the connection caching so that an RSET
is sent to the LMTP server. This is done for two reasons: first,
to tell the LMTP server to flush out any status information
because we're about to feed it another message, and second, to
test the link to see if it is still alive. If the link has died
for some reason, it is reestablished. Changes to lmtp.c and
lmtp_proto.c.
* There was also some tidying in lmtp.c so that things were a little
clearer as to what was going on. I added a few more comments as
well.
* I tried to make the master.cf argument parsing a little more
consistent with the other Postfix services. Changes to lmtp.c.
* On the postfix-users mailing list, Valery Brasseur pointed out that
errors encountered during LMTP delivery were not getting bounced
as appropriate. This lead me to investigate the matter, wanting
to put this LMTP capability into service myself. I then realized
that some of the error checking was a tad over zealous, and so
simplified things a bit in lmtp_proto.c.

61
postfix/lmtp/Makefile.in Normal file
View File

@ -0,0 +1,61 @@
SHELL = /bin/sh
SRCS = lmtp.c quote_821_local.c lmtp_connect.c lmtp_proto.c lmtp_chat.c \
lmtp_session.c lmtp_addr.c lmtp_trouble.c lmtp_state.c
OBJS = lmtp.o quote_821_local.o lmtp_connect.o lmtp_proto.o lmtp_chat.o \
lmtp_session.o lmtp_addr.o lmtp_trouble.o lmtp_state.o
HDRS = lmtp.h
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
-Wunused
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
TESTPROG= quote_821_local
PROG = lmtp
INC_DIR = ../include
LIBS = ../lib/libmaster.a ../lib/libglobal.a ../lib/libdns.a ../lib/libutil.a
.c.o:; $(CC) $(CFLAGS) -c $*.c
$(PROG): $(OBJS) $(LIBS)
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
Makefile: Makefile.in
(set -e; echo "# DO NOT EDIT"; $(OPTS) sh ../makedefs; cat $?) >$@
test: $(TESTPROG)
update: ../libexec/$(PROG)
../libexec/$(PROG): $(PROG)
cp $(PROG) ../libexec
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
cp *.h printfck
sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
cd printfck; make "INC_DIR=../../include" `cd ..; ls *.o`
lint:
lint $(DEFS) $(SRCS) $(LINTFIX)
clean:
rm -f *.o *core $(PROG) $(TESTPROG) junk
rm -rf printfck
tidy: clean
quote_821_local: quote_821_local.c $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIBS) $(SYSLIBS)
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \
$(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
-e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
@make -f Makefile.in Makefile
# do not edit below this line - it is generated by 'make depend'

34
postfix/lmtp/README Normal file
View File

@ -0,0 +1,34 @@
README
The starting point for this work was the original LMTP patch,
lmtp-19990427.tar.gz, found in the experimental directory on the
Postfix mirror ftp sites. Without this original work by Philip A.
Prindeville, I probably wouldn't have taken the time to dive into
this issue. Fortunately, this individual's work gave me a good
starting point to learn about LMTP, and Postfix in general.
First, in the Postfix source directory, apply the following patches:
patch -p0 < lmtp/makefile-patch
patch -p0 < lmtp/global-patch
patch -p0 < lmtp/man-patch
Then compile and install Postfix as usual. There really isn't
anything OS specific so it should compile without problems.
NOTE: Previously this patch would do a pipe to an external command
to perform local delivery. That is no longer the case! This
version now requires the "spawn" service be defined. See
README.local for details.
The other way to use this lmtp service is to talk to a remote LMTP
server. An example of this would be if an MX host is to insert
incoming mail directly into a message store that supports the LMTP
protocol. This is described in README.inet.
The file CHANGES lists some of the changes that have been made
since lmtp-19990427.tar.gz, and TODO discusses some thoughts
that are currently floating around.
Amos

80
postfix/lmtp/README.inet Normal file
View File

@ -0,0 +1,80 @@
README.inet
This is an example of how to set up a remote LMTP server. This can
be useful if you have a central/firewall mail router that feeds
incoming mail into the appropriate inbox server(s), the central box
can stuff the incoming mail directly into the message store on the
inbox machine using LMTP. This also means the inbox machine doesn't
have to have a SMTP server to handle this intermediate step. Tidy
all the way around.
** Inbox Server:
On the inbox server, in this case a CMU Cyrus imapd/popd server, add
the following to /etc/services:
pop3 110/tcp # Cyrus POP3
imap 143/tcp # Cyrus IMAP4
lmtp 24/tcp
Next, put the following in /etc/inetd.conf:
lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/local/cyrus/bin/deliver -e -l
(/usr/sbin/tcpd is from the tcp_wrappers package. You want this to
make sure only your central box can do the stuffing.)
** Central Server:
Similar changes to /etc/services:
lmtp 24/tcp
Now we add this to /etc/postfix/master.cf:
lmtp unix - - n - - lmtp
NOTES: No arguments are specified to lmtp!
Root privs are not necessary!
We put this in /etc/postfix/transport:
inbox.domain.org lmtp:inbox.domain.org
Naturally, this means we also have to have in
/etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/transport
Use the map type of your choice.
That's it.
Oh, it may be necessary to apply the following patch to deliver so
that the final "bye" is not lost. This should not be necessary in
releases subsequent to 1.6.20 of cyrus-imapd.
*** deliver.c._orig Tue Dec 21 11:12:47 1999
--- deliver.c Wed Dec 22 20:12:54 1999
***************
*** 1753,1758 ****
--- 1753,1759 ----
case 'Q':
if (!strcasecmp(buf, "quit")) {
prot_printf(deliver_out,"221 2.0.0 bye\r\n");
+ prot_flush(deliver_out);
exit(0);
}
goto syntaxerr;

50
postfix/lmtp/README.local Normal file
View File

@ -0,0 +1,50 @@
README.local
This file describes how to use the lmtp service for local delivery.
You'll need postfix-19991231-pl04 or later for this to work because
it relies on the "spawn" service.
Configure your Postfix as follows:
/etc/postfix/master.cf:
#local unix - n n - - local
local unix - - n - - lmtp
serv=unix:private/lmtpd
lmtpd unix - n n - - spawn
user=cyrus:cyrus argv=/usr/local/cyrus/bin/deliver -e -l
First, we comment out the original "local" service and define
a new one based on the "lmtp" client. The "serv=" argument says
what LMTP server we're to talk to, in this case the "lmtpd"
service.
The `-l' option to deliver tells it to go into LMTP mode, and the
`-e' tells it to use the duplicate delivery database, which is
required in order to use the vacation features of Sieve, the
filtering language in Cyrus 1.6.X.
A note about spawn, this is a new experimental service. The
makefile-patch included with this bundle will add spawn to the
compile targets. However, you may need to check that it actually
gets installed.
A note about local delivery and the number of recipients. Starting
with postfix-19991231-pl04, it is now possible to specify the
maximum number of recipients per message for local delivery. By
default, this is set to 1 as follows:
local_destination_recipient_limit = 1
You can set it to zero (means no limit) or, safer, set it to some
reasonable number so that your machine doesn't risk running out of
resources on a message with an inordinate number of recipients.
Why is this of interest? Well, if a message contains multiple
recipients, and all these recipients happen to be on the same Cyrus
partition, then recent (1.6.X) releases of deliver will hard link
the message to each recipient instead of each recipient getting a
copy. So you'll probably want to set the above mail.cf value to
something reasonable to take advantage of this feature in Cyrus.

5
postfix/lmtp/fixnames Normal file
View File

@ -0,0 +1,5 @@
sed '
s/LMTP/SMTP/g
s/lmtp/smtp/g
s/host/namaddr/g
' $*

65
postfix/lmtp/global-patch Normal file
View File

@ -0,0 +1,65 @@
*** ../../orig/global/mail_params.h Thu Jan 27 20:05:29 2000
--- global/mail_params.h Wed Feb 23 01:26:01 2000
***************
*** 624,629 ****
--- 624,683 ----
extern int var_smtpd_err_sleep;
/*
+ * LMTP client. Timeouts inspired by RFC 1123. The LMTP recipient limit
+ * determines how many recipient addresses the LMTP client sends along with
+ * each message. Unfortunately, some mailers misbehave and disconnect (smap)
+ * when given more recipients than they are willing to handle.
+ */
+ #define VAR_LMTP_TCP_PORT "lmtp_tcp_port"
+ #define DEF_LMTP_TCP_PORT 24
+ extern int var_lmtp_tcp_port;
+
+ #define VAR_LMTP_CACHE_CONN "lmtp_cache_connection"
+ #define DEF_LMTP_CACHE_CONN 1
+ extern bool var_lmtp_cache_conn;
+
+ #define VAR_LMTP_SKIP_QUIT_RESP "lmtp_skip_quit_response"
+ #define DEF_LMTP_SKIP_QUIT_RESP 0
+ extern bool var_lmtp_skip_quit_resp;
+
+ #define VAR_LMTP_CONN_TMOUT "lmtp_connect_timeout"
+ #define DEF_LMTP_CONN_TMOUT 0
+ extern int var_lmtp_conn_tmout;
+
+ #define VAR_LMTP_RSET_TMOUT "lmtp_rset_timeout"
+ #define DEF_LMTP_RSET_TMOUT 300
+ extern int var_lmtp_rset_tmout;
+
+ #define VAR_LMTP_LHLO_TMOUT "lmtp_lhlo_timeout"
+ #define DEF_LMTP_LHLO_TMOUT 300
+ extern int var_lmtp_lhlo_tmout;
+
+ #define VAR_LMTP_MAIL_TMOUT "lmtp_mail_timeout"
+ #define DEF_LMTP_MAIL_TMOUT 300
+ extern int var_lmtp_mail_tmout;
+
+ #define VAR_LMTP_RCPT_TMOUT "lmtp_rcpt_timeout"
+ #define DEF_LMTP_RCPT_TMOUT 300
+ extern int var_lmtp_rcpt_tmout;
+
+ #define VAR_LMTP_DATA0_TMOUT "lmtp_data_init_timeout"
+ #define DEF_LMTP_DATA0_TMOUT 120
+ extern int var_lmtp_data0_tmout;
+
+ #define VAR_LMTP_DATA1_TMOUT "lmtp_data_xfer_timeout"
+ #define DEF_LMTP_DATA1_TMOUT 180
+ extern int var_lmtp_data1_tmout;
+
+ #define VAR_LMTP_DATA2_TMOUT "lmtp_data_done_timeout"
+ #define DEF_LMTP_DATA2_TMOUT 600
+ extern int var_lmtp_data2_tmout;
+
+ #define VAR_LMTP_QUIT_TMOUT "lmtp_quit_timeout"
+ #define DEF_LMTP_QUIT_TMOUT 300
+ extern int var_lmtp_quit_tmout;
+
+ /*
* Cleanup service. Header info that exceeds $header_size_limit bytes forces
* the start of the message body.
*/

608
postfix/lmtp/lmtp.c Normal file
View File

@ -0,0 +1,608 @@
/*++
/* NAME
/* lmtp 8
/* SUMMARY
/* Postfix local delivery via LMTP
/* SYNOPSIS
/* \fBlmtp\fR [generic Postfix daemon options] [server attributes...]
/* DESCRIPTION
/* The LMTP client processes message delivery requests from
/* the queue manager. Each request specifies a queue file, a sender
/* address, a domain or host to deliver to, and recipient information.
/* This program expects to be run from the \fBmaster\fR(8) process
/* manager.
/*
/* The LMTP client updates the queue file and marks recipients
/* as finished, or it informs the queue manager that delivery should
/* be tried again at a later time. Delivery problem reports are sent
/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate.
/*
/* There are two basic modes of operation for the LMTP client:
/* .IP \(bu
/* Communication with a local LMTP server via UNIX domain sockets.
/* .IP \(bu
/* Communication with a (possibly remote) LMTP server via
/* Internet sockets.
/* .PP
/* If no server attributes are specified, the LMTP client will contact
/* the destination host derived from the message delivery request using
/* the TCP port defined as \fBlmtp\fR in \fBservices\fR(4). If no such
/* service is found, the \fBlmtp_tcp_port\fR configuration parameter
/* (default value of 24) will be used.
/*
/* In order to use a local LMTP server, this LMTP server will need to
/* be specified via the server attributes described in the following
/* section. Typically, the LMTP client would also be configured as the
/* \fBlocal\fR delivery agent in the \fBmaster.cf\fR file.
/* SERVER ATTRIBUTE SYNTAX
/* .ad
/* .fi
/* The server attributes are given in the \fBmaster.cf\fR file at
/* the end of a service definition. The syntax is as follows:
/* .IP "\fBserv\fR=\fItype\fR:\fIserver\fR"
/* The LMTP server to connect to for final delivery. The \fItype\fR
/* portion can be either \fBunix\fR or \fBinet\fR. The \fIserver\fR
/* portion is the path or address of the LMTP server, depending on the
/* value of \fItype\fR, as shown below:
/* .RS
/* .IP "\fBserv=unix:\fR\fIclass\fR\fB/\fR\fIservname\fR"
/* This specifies that the local LMTP server \fIservname\fR should be
/* contacted for final delivery. Both \fIclass\fR (either \fBpublic\fR
/* or \fBprivate\fR) and \fIservname\fR correspond to the LMTP server
/* entry in the \fBmaster.cf\fR file. This LMTP server will likely
/* be defined as a \fBspawn\fR(8) service.
/* .IP "\fBserv=inet:"
/* If nothing follows the \fBinet:\fR type specifier, a connection will
/* be attempted to the destination host indicated in the delivery request.
/* This simplest case is identical to defining the LMTP client without
/* any server attributes at all.
/* .IP "\fBserv=inet:\fR\fIaddress\fR"
/* In this case, an Internet socket will be made to the server
/* specified by \fIaddress\fR. The connection will use a destination
/* port as described in the previous section.
/* .IP "\fBserv=inet:\fR\fIaddress\fR\fB:\fR\fIport\fR"
/* Connect to the LMTP server at \fIaddress\fR, but this time use port
/* \fIport\fR instead of the default \fBlmtp\fR port.
/* .IP "\fBserv=inet:[\fR\fIipaddr\fR\fB]\fR"
/* The LMTP server to contact is specified using an Internet address
/* in the "dot notation". That is, the numeric IP address rather
/* than the DNS name for the server. The default \fBlmtp\fR port
/* is used.
/* .IP "\fBserv=inet:[\fR\fIipaddr\fR\fB]:\fR\fIport\fR"
/* The LMTP server to contact is specified using the numeric IP address,
/* at the port specified.
/* .RE
/* .PP
/* SECURITY
/* .ad
/* .fi
/* The LMTP client is moderately security-sensitive. It talks to LMTP
/* servers and to DNS servers on the network. The LMTP client can be
/* run chrooted at fixed low privilege.
/* STANDARDS
/* RFC 2033 (LMTP protocol)
/* RFC 821 (SMTP protocol)
/* RFC 1651 (SMTP service extensions)
/* RFC 1870 (Message Size Declaration)
/* RFC 2197 (Pipelining)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* Corrupted message files are marked so that the queue manager can
/* move them to the \fBcorrupt\fR queue for further inspection.
/*
/* Depending on the setting of the \fBnotify_classes\fR parameter,
/* the postmaster is notified of bounces, protocol problems, and of
/* other trouble.
/* BUGS
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
/* The following \fBmain.cf\fR parameters are especially relevant to
/* this program. See the Postfix \fBmain.cf\fR file for syntax details
/* and for default values. Use the \fBpostfix reload\fR command after
/* a configuration change.
/* .SH Miscellaneous
/* .ad
/* .fi
/* .IP \fBdebug_peer_level\fR
/* Verbose logging level increment for hosts that match a
/* pattern in the \fBdebug_peer_list\fR parameter.
/* .IP \fBdebug_peer_list\fR
/* List of domain or network patterns. When a remote host matches
/* a pattern, increase the verbose logging level by the amount
/* specified in the \fBdebug_peer_level\fR parameter.
/* .IP \fBerror_notice_recipient\fR
/* Recipient of protocol/policy/resource/software error notices.
/* .IP \fBnotify_classes\fR
/* When this parameter includes the \fBprotocol\fR class, send mail to the
/* postmaster with transcripts of LMTP sessions with protocol errors.
/* .IP \fBlmtp_skip_quit_response\fR
/* Do not wait for the server response after sending QUIT.
/* .IP \fBlmtp_tcp_port\fR
/* The TCP port to be used when connecting to a LMTP server. Used as
/* backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
/* .SH "Resource controls"
/* .ad
/* .fi
/* .IP \fBlmtp_cache_connection\fR
/* Should we cache the connection to the LMTP server? The effectiveness
/* of cached connections will be determined by the number of LMTP servers
/* in use, and the concurrency limit specified for the LMTP client.
/* Cached connections are closed under any of the following conditions:
/* .RS
/* .IP \(bu
/* The idle timeout for the LMTP client is reached. This limit is
/* enforced by \fBmaster\fR(8).
/* .IP \(bu
/* A message request to a different destination than the one currently
/* cached.
/* .IP \(bu
/* The maximum number of requests per session is reached. This limit is
/* enforced by \fBmaster\fR(8).
/* .IP \(bu
/* Upon the onset of another delivery request, the LMTP server associated
/* with the current session does not respond to the \fBRSET\fR command.
/* .RE
/* .IP \fBlmtp_destination_concurrency_limit\fR
/* Limit the number of parallel deliveries to the same destination.
/* The default limit is taken from the
/* \fBdefault_destination_concurrency_limit\fR parameter.
/* .IP \fBlmtp_destination_recipient_limit\fR
/* Limit the number of recipients per message delivery.
/* The default limit is taken from the
/* \fBdefault_destination_recipient_limit\fR parameter.
/* .IP \fBlocal_destination_recipient_limit\fR
/* Limit the number of recipients per message delivery.
/* The default limit is taken from the
/* \fBdefault_destination_recipient_limit\fR parameter.
/*
/* This parameter becomes significant if the LMTP client is used
/* for local delivery. Some LMTP servers can optimize final delivery
/* if multiple recipients are allowed. Therefore, it may be advantageous
/* to set this to some number greater than one, depending on the capabilities
/* of the machine.
/*
/* Setting this parameter to 0 will lead to an unlimited number of
/* recipients per delivery. However, this could be risky since it may
/* make the machine vulnerable to running out of resources if messages
/* are encountered with an inordinate number of recipients. Exercise
/* care when setting this parameter.
/* .SH "Timeout controls"
/* .ad
/* .fi
/* .IP \fBlmtp_connect_timeout\fR
/* Timeout in seconds for opening a connection to the LMTP server.
/* If no connection can be made within the deadline, the message
/* is deferred.
/* .IP \fBlmtp_lhlo_timeout\fR
/* Timeout in seconds for sending the \fBLHLO\fR command, and for
/* receiving the server response.
/* .IP \fBlmtp_mail_timeout\fR
/* Timeout in seconds for sending the \fBMAIL FROM\fR command, and for
/* receiving the server response.
/* .IP \fBlmtp_rcpt_timeout\fR
/* Timeout in seconds for sending the \fBRCPT TO\fR command, and for
/* receiving the server response.
/* .IP \fBlmtp_data_init_timeout\fR
/* Timeout in seconds for sending the \fBDATA\fR command, and for
/* receiving the server response.
/* .IP \fBlmtp_data_xfer_timeout\fR
/* Timeout in seconds for sending the message content.
/* .IP \fBlmtp_data_done_timeout\fR
/* Timeout in seconds for sending the "\fB.\fR" command, and for
/* receiving the server response. When no response is received, a
/* warning is logged that the mail may be delivered multiple times.
/* .IP \fBlmtp_rset_timeout\fR
/* Timeout in seconds for sending the \fBRSET\fR command, and for
/* receiving the server response.
/* .IP \fBlmtp_quit_timeout\fR
/* Timeout in seconds for sending the \fBQUIT\fR command, and for
/* receiving the server response.
/* SEE ALSO
/* bounce(8) non-delivery status reports
/* local(8) local mail delivery
/* master(8) process manager
/* qmgr(8) queue manager
/* services(4) Internet services and aliases
/* spawn(8) auxiliary command spawner
/* syslogd(8) system logging
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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);
}

155
postfix/lmtp/lmtp.h Normal file
View File

@ -0,0 +1,155 @@
/*++
/* 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
/*--*/

202
postfix/lmtp/lmtp_addr.c Normal file
View File

@ -0,0 +1,202 @@
/*++
/* NAME
/* lmtp_addr 3
/* SUMMARY
/* LMTP server address lookup
/* SYNOPSIS
/* #include "lmtp_addr.h"
/*
/* DNS_RR *lmtp_host_addr(name, why)
/* char *name;
/* VSTRING *why;
/* DESCRIPTION
/* This module implements Internet address lookups. By default,
/* lookups are done via the Internet domain name service (DNS).
/* A reasonable number of CNAME indirections is permitted.
/*
/* lmtp_host_addr() looks up all addresses listed for the named
/* host. The host can be specified as a numerical Internet network
/* address, or as a symbolic host name.
/*
/* Fortunately, we don't have to worry about MX records because
/* those are for SMTP servers, not LMTP servers.
/*
/* Results from lmtp_host_addr() are destroyed by dns_rr_free(),
/* including null lists.
/* DIAGNOSTICS
/* This routine either returns a DNS_RR pointer, or return a null
/* pointer and sets the \fIlmtp_errno\fR global variable accordingly:
/* .IP LMTP_RETRY
/* The request failed due to a soft error, and should be retried later.
/* .IP LMTP_FAIL
/* The request attempt failed due to a hard error.
/* .PP
/* In addition, a textual description of the problem is made available
/* via the \fIwhy\fR argument.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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);
}

41
postfix/lmtp/lmtp_addr.h Normal file
View File

@ -0,0 +1,41 @@
/*++
/* 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
/*--*/

285
postfix/lmtp/lmtp_chat.c Normal file
View File

@ -0,0 +1,285 @@
/*++
/* NAME
/* lmtp_chat 3
/* SUMMARY
/* LMTP client request/response support
/* SYNOPSIS
/* #include "lmtp.h"
/*
/* typedef struct {
/* .in +4
/* int code;
/* char *str;
/* VSTRING *buf;
/* .in -4
/* } LMTP_RESP;
/*
/* void lmtp_chat_cmd(state, format, ...)
/* LMTP_STATE *state;
/* char *format;
/*
/* LMTP_RESP *lmtp_chat_resp(state)
/* LMTP_STATE *state;
/*
/* void lmtp_chat_notify(state)
/* LMTP_STATE *state;
/*
/* void lmtp_chat_reset(state)
/* LMTP_STATE *state;
/* DESCRIPTION
/* This module implements LMTP client support for request/reply
/* conversations, and maintains a limited LMTP transaction log.
/*
/* lmtp_chat_cmd() formats a command and sends it to an LMTP server.
/* Optionally, the command is logged.
/*
/* lmtp_chat_resp() read one LMTP server response. It separates the
/* numerical status code from the text, and concatenates multi-line
/* responses to one string, using a newline as separator.
/* Optionally, the server response is logged.
/*
/* lmtp_chat_notify() sends a copy of the LMTP transaction log
/* to the postmaster for review. The postmaster notice is sent only
/* when delivery is possible immediately. It is an error to call
/* lmtp_chat_notify() when no LMTP transaction log exists.
/*
/* lmtp_chat_reset() resets the transaction log. This is
/* typically done at the beginning or end of an LMTP session,
/* or within a session to discard non-error information.
/* DIAGNOSTICS
/* Fatal errors: memory allocation problem, server response exceeds
/* configurable limit.
/* All other exceptions are handled by long jumps (see smtp_stream(3)).
/* SEE ALSO
/* smtp_stream(3) LMTP session I/O support
/* msg(3) generic logging interface
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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);
}

395
postfix/lmtp/lmtp_connect.c Normal file
View File

@ -0,0 +1,395 @@
/*++
/* NAME
/* lmtp_connect 3
/* SUMMARY
/* connect to LMTP server
/* SYNOPSIS
/* #include "lmtp.h"
/*
/* LMTP_SESSION *lmtp_connect(destination, why)
/* char *destination;
/* VSTRING *why;
/* DESCRIPTION
/* This module implements LMTP connection management.
/*
/* lmtp_connect() attempts to establish an LMTP session with a host.
/*
/* The destination is either a host name or a numeric address.
/* Symbolic or numeric service port information may be appended,
/* separated by a colon (":").
/*
/* Numerical address information should always be quoted with `[]'.
/*
/* DIAGNOSTICS
/* This routine either returns an LMTP_SESSION pointer, or
/* returns a null pointer and set the \fIlmtp_errno\fR
/* global variable accordingly:
/* .IP LMTP_RETRY
/* The connection attempt failed, but should be retried later.
/* .IP LMTP_FAIL
/* The connection attempt failed.
/* .PP
/* In addition, a textual description of the error is made available
/* via the \fIwhy\fR argument.
/* SEE ALSO
/* lmtp_proto(3) LMTP client protocol
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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);
}

686
postfix/lmtp/lmtp_proto.c Normal file
View File

@ -0,0 +1,686 @@
/*++
/* NAME
/* lmtp_proto 3
/* SUMMARY
/* client LMTP protocol
/* SYNOPSIS
/* #include "lmtp.h"
/*
/* int lmtp_lhlo(state)
/* LMTP_STATE *state;
/*
/* int lmtp_xfer(state)
/* LMTP_STATE *state;
/*
/* int lmtp_rset(state)
/* LMTP_STATE *state;
/*
/* int lmtp_quit(state)
/* LMTP_STATE *state;
/* DESCRIPTION
/* This module implements the client side of the LMTP protocol.
/*
/* lmtp_lhlo() performs the initial handshake with the LMTP server.
/*
/* lmtp_xfer() sends message envelope information followed by the
/* message data, but does not finish the conversation. These operations
/* are combined in one function, in order to implement LMTP pipelining.
/* Recipients are marked as "done" in the mail queue file when
/* bounced or delivered. The message delivery status is updated
/* accordingly.
/*
/* lmtp_rset() sends an RSET command and waits for the response.
/*
/* lmtp_quit() sends a QUIT command and waits for the response.
/* DIAGNOSTICS
/* lmtp_lhlo(), lmtp_xfer(), lmtp_rset() and lmtp_quit() return 0 in
/* case of success, -1 in case of failure. For lmtp_xfer(), lmtp_rset()
/* and lmtp_quit(), success means the ability to perform an LMTP
/* conversation, not necessarily OK replies from the server.
/*
/* Warnings: corrupt message file. A corrupt message is marked
/* as "corrupt" by changing its queue file permissions.
/* SEE ALSO
/* lmtp(3h) internal data structures
/* lmtp_chat(3) query/reply LMTP support
/* lmtp_trouble(3) error handlers
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Pipelining code in cooperation with:
/* Jon Ribbens
/* Oaktree Internet Solutions Ltd.,
/* Internet House,
/* Canal Basin,
/* Coventry,
/* CV1 4LY, United Kingdom.
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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));
}

105
postfix/lmtp/lmtp_session.c Normal file
View File

@ -0,0 +1,105 @@
/*++
/* NAME
/* lmtp_session 3
/* SUMMARY
/* LMTP_SESSION structure management
/* SYNOPSIS
/* #include "lmtp.h"
/*
/* LMTP_SESSION *lmtp_session_alloc(stream, host, addr)
/* VSTREAM *stream;
/* char *host;
/* char *addr;
/*
/* void lmtp_session_free(session)
/* LMTP_SESSION *session;
/*
/* void lmtp_session_reset(state)
/* LMTP_STATE *state;
/* DESCRIPTION
/* lmtp_session_alloc() allocates memory for an LMTP_SESSION structure
/* and initializes it with the given stream and host name and address
/* information. The host name and address strings are copied. The code
/* assumes that the stream is connected to the "best" alternative.
/*
/* lmtp_session_free() destroys an LMTP_SESSION structure and its
/* members, making memory available for reuse.
/*
/* lmtp_session_reset() is just a little helper to make sure everything
/* is set to zero after the session has been freed. This means I don't
/* have to keep repeating the same chunks of code for cached connections.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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;
}

92
postfix/lmtp/lmtp_state.c Normal file
View File

@ -0,0 +1,92 @@
/*++
/* NAME
/* lmtp_state 8
/* SUMMARY
/* initialize/cleanup shared state
/* SYNOPSIS
/* #include "lmtp.h"
/*
/* LMTP_STATE *lmtp_state_alloc()
/*
/* void lmtp_state_free(state)
/* LMTP_STATE *state;
/* DESCRIPTION
/* lmtp_state_init() initializes the shared state, and allocates
/* memory for buffers etc.
/*
/* lmtp_cleanup() destroys memory allocated by lmtp_state_init().
/* STANDARDS
/* DIAGNOSTICS
/* BUGS
/* SEE ALSO
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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);
}

318
postfix/lmtp/lmtp_trouble.c Normal file
View File

@ -0,0 +1,318 @@
/*++
/* NAME
/* lmtp_trouble 3
/* SUMMARY
/* error handler policies
/* SYNOPSIS
/* #include "lmtp.h"
/*
/* int lmtp_site_fail(state, code, format, ...)
/* LMTP_STATE *state;
/* int code;
/* char *format;
/*
/* int lmtp_mesg_fail(state, code, format, ...)
/* LMTP_STATE *state;
/* int code;
/* char *format;
/*
/* void lmtp_rcpt_fail(state, code, recipient, format, ...)
/* LMTP_STATE *state;
/* int code;
/* RECIPIENT *recipient;
/* char *format;
/*
/* int lmtp_stream_except(state, exception, description)
/* LMTP_STATE *state;
/* int exception;
/* char *description;
/* DESCRIPTION
/* This module handles all non-fatal errors that can happen while
/* attempting to deliver mail via LMTP, and implements the policy
/* of how to deal with the error. Depending on the nature of
/* the problem, delivery of a single message is deferred, delivery
/* of all messages to the same domain is deferred, or one or more
/* recipients are given up as non-deliverable and a bounce log is
/* updated.
/*
/* In addition, when an unexpected response code is seen such
/* as 3xx where only 4xx or 5xx are expected, or any error code
/* that suggests a syntax error or something similar, the
/* protocol error flag is set so that the postmaster receives
/* a transcript of the session. No notification is generated for
/* what appear to be configuration errors - very likely, they
/* would suffer the same problem and just cause more trouble.
/*
/* lmtp_site_fail() handles the case where the program fails to
/* complete the initial LMTP handshake: the server is not reachable,
/* is not running, does not want talk to us, or we talk to ourselves.
/* The \fIcode\fR gives an error status code; the \fIformat\fR
/* argument gives a textual description. The policy is: soft
/* error: defer delivery of all messages to this domain; hard
/* error: bounce all recipients of this message.
/* The result is non-zero.
/*
/* lmtp_mesg_fail() handles the case where the lmtp server
/* does not accept the sender address or the message data.
/* The policy is: soft errors: defer delivery of this message;
/* hard error: bounce all recipients of this message.
/* The result is non-zero.
/*
/* lmtp_rcpt_fail() handles the case where a recipient is not
/* accepted by the server for reasons other than that the server
/* recipient limit is reached. The policy is: soft error: defer
/* delivery to this recipient; hard error: bounce this recipient.
/*
/* lmtp_stream_except() handles the exceptions generated by
/* the smtp_stream(3) module (i.e. timeouts and I/O errors).
/* The \fIexception\fR argument specifies the type of problem.
/* The \fIdescription\fR argument describes at what stage of
/* the LMTP dialog the problem happened. The policy is to defer
/* delivery of all messages to the same domain. The result is non-zero.
/* DIAGNOSTICS
/* Panic: unknown exception code.
/* SEE ALSO
/* lmtp_proto(3) lmtp high-level protocol
/* smtp_stream(3) lmtp low-level protocol
/* defer(3) basic message defer interface
/* bounce(3) basic message bounce interface
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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);
}

753
postfix/lmtp/mail Normal file
View File

@ -0,0 +1,753 @@
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

View File

@ -0,0 +1,19 @@
*** ../../orig/Makefile.in Fri Dec 31 09:49:41 1999
--- Makefile.in Fri Feb 25 16:27:24 2000
***************
*** 4,10 ****
DIRS = util global dns master postfix smtpstone sendmail error \
pickup cleanup smtpd local trivial-rewrite qmgr smtp bounce pipe \
showq postalias postcat postconf postdrop postkick postlock postlog \
! postmap postsuper # spawn man html
default: update
--- 4,10 ----
DIRS = util global dns master postfix smtpstone sendmail error \
pickup cleanup smtpd local trivial-rewrite qmgr smtp bounce pipe \
showq postalias postcat postconf postdrop postkick postlock postlog \
! postmap postsuper lmtp spawn man # html
default: update

218
postfix/lmtp/man-patch Normal file
View File

@ -0,0 +1,218 @@
*** ../../orig/man/Makefile.in Thu Jun 24 18:39:22 1999
--- man/Makefile.in Fri Feb 25 16:35:53 2000
***************
*** 2,8 ****
DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \
man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 man8/showq.8 \
! man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8
COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \
man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \
man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \
--- 2,8 ----
DAEMONS = man8/bounce.8 man8/defer.8 man8/cleanup.8 man8/error.8 man8/local.8 \
man8/master.8 man8/pickup.8 man8/pipe.8 man8/qmgr.8 man8/showq.8 \
! man8/smtp.8 man8/smtpd.8 man8/trivial-rewrite.8 man8/lmtp.8
COMMANDS= man1/postalias.1 man1/postcat.1 man1/postconf.1 man1/postfix.1 \
man1/postkick.1 man1/postlock.1 man1/postlog.1 man1/postdrop.1 \
man1/postmap.1 man1/sendmail.1 man1/mailq.1 man1/newaliases.1 \
***************
*** 24,99 ****
rm -f $(DAEMONS) $(COMMANDS) $(CONFIG)
man8/bounce.8: ../bounce/bounce.c
! srctoman $? >$@
man8/defer.8:
echo .so man8/bounce.8 >$@
man8/cleanup.8: ../cleanup/cleanup.c
! srctoman $? >$@
man8/error.8: ../error/error.c
! srctoman $? >$@
man8/local.8: ../local/local.c
! srctoman $? >$@
man8/master.8: ../master/master.c
! srctoman $? >$@
man8/pickup.8: ../pickup/pickup.c
! srctoman $? >$@
man8/pipe.8: ../pipe/pipe.c
! srctoman $? >$@
man8/qmgr.8: ../qmgr/qmgr.c
! srctoman $? >$@
man8/showq.8: ../showq/showq.c
! srctoman $? >$@
man8/smtp.8: ../smtp/smtp.c
! srctoman $? >$@
man8/smtpd.8: ../smtpd/smtpd.c
! srctoman $? >$@
man8/trivial-rewrite.8: ../trivial-rewrite/trivial-rewrite.c
! srctoman $? >$@
man1/postalias.1: ../postalias/postalias.c
! srctoman $? >$@
man1/postcat.1: ../postcat/postcat.c
! srctoman $? >$@
man1/postconf.1: ../postconf/postconf.c
! srctoman $? >$@
man1/postdrop.1: ../postdrop/postdrop.c
! srctoman $? >$@
man1/postfix.1: ../postfix/postfix.c
! srctoman $? >$@
man1/postkick.1: ../postkick/postkick.c
! srctoman $? >$@
man1/postlock.1: ../postlock/postlock.c
! srctoman $? >$@
man1/postlog.1: ../postlog/postlog.c
! srctoman $? >$@
man1/postmap.1: ../postmap/postmap.c
! srctoman $? >$@
man1/postsuper.1: ../postsuper/postsuper.c
! srctoman $? >$@
man1/sendmail.1: ../sendmail/sendmail.c
! srctoman $? >$@
man1/mailq.1:
echo .so man1/sendmail.1 >$@
--- 24,102 ----
rm -f $(DAEMONS) $(COMMANDS) $(CONFIG)
man8/bounce.8: ../bounce/bounce.c
! ../mantools/srctoman $? >$@
man8/defer.8:
echo .so man8/bounce.8 >$@
man8/cleanup.8: ../cleanup/cleanup.c
! ../mantools/srctoman $? >$@
man8/error.8: ../error/error.c
! ../mantools/srctoman $? >$@
man8/local.8: ../local/local.c
! ../mantools/srctoman $? >$@
man8/master.8: ../master/master.c
! ../mantools/srctoman $? >$@
man8/pickup.8: ../pickup/pickup.c
! ../mantools/srctoman $? >$@
man8/pipe.8: ../pipe/pipe.c
! ../mantools/srctoman $? >$@
man8/qmgr.8: ../qmgr/qmgr.c
! ../mantools/srctoman $? >$@
man8/showq.8: ../showq/showq.c
! ../mantools/srctoman $? >$@
man8/smtp.8: ../smtp/smtp.c
! ../mantools/srctoman $? >$@
man8/smtpd.8: ../smtpd/smtpd.c
! ../mantools/srctoman $? >$@
man8/trivial-rewrite.8: ../trivial-rewrite/trivial-rewrite.c
! ../mantools/srctoman $? >$@
+ man8/lmtp.8: ../lmtp/lmtp.c
+ ../mantools/srctoman $? >$@
+
man1/postalias.1: ../postalias/postalias.c
! ../mantools/srctoman $? >$@
man1/postcat.1: ../postcat/postcat.c
! ../mantools/srctoman $? >$@
man1/postconf.1: ../postconf/postconf.c
! ../mantools/srctoman $? >$@
man1/postdrop.1: ../postdrop/postdrop.c
! ../mantools/srctoman $? >$@
man1/postfix.1: ../postfix/postfix.c
! ../mantools/srctoman $? >$@
man1/postkick.1: ../postkick/postkick.c
! ../mantools/srctoman $? >$@
man1/postlock.1: ../postlock/postlock.c
! ../mantools/srctoman $? >$@
man1/postlog.1: ../postlog/postlog.c
! ../mantools/srctoman $? >$@
man1/postmap.1: ../postmap/postmap.c
! ../mantools/srctoman $? >$@
man1/postsuper.1: ../postsuper/postsuper.c
! ../mantools/srctoman $? >$@
man1/sendmail.1: ../sendmail/sendmail.c
! ../mantools/srctoman $? >$@
man1/mailq.1:
echo .so man1/sendmail.1 >$@
***************
*** 102,120 ****
echo .so man1/sendmail.1 >$@
man5/access.5: ../conf/access
! srctoman - $? >$@
man5/aliases.5: ../conf/aliases
! srctoman - $? >$@
man5/canonical.5: ../conf/canonical
! srctoman - $? >$@
man5/relocated.5: ../conf/relocated
! srctoman - $? >$@
man5/transport.5: ../conf/transport
! srctoman - $? >$@
man5/virtual.5: ../conf/virtual
! srctoman - $? >$@
--- 105,123 ----
echo .so man1/sendmail.1 >$@
man5/access.5: ../conf/access
! ../mantools/srctoman - $? >$@
man5/aliases.5: ../conf/aliases
! ../mantools/srctoman - $? >$@
man5/canonical.5: ../conf/canonical
! ../mantools/srctoman - $? >$@
man5/relocated.5: ../conf/relocated
! ../mantools/srctoman - $? >$@
man5/transport.5: ../conf/transport
! ../mantools/srctoman - $? >$@
man5/virtual.5: ../conf/virtual
! ../mantools/srctoman - $? >$@

View File

@ -0,0 +1,170 @@
/*++
/* NAME
/* quote_821_local 3
/* SUMMARY
/* quote local part of address
/* SYNOPSIS
/* #include "quote_821_local.h"
/*
/* VSTRING *quote_821_local(dst, src)
/* VSTRING *dst;
/* char *src;
/* DESCRIPTION
/* quote_821_local() quotes the local part of a mailbox address and
/* returns a result that can be used in SMTP commands as specified
/* by RFC 821.
/*
/* Arguments:
/* .IP dst
/* The result.
/* .IP src
/* The input address.
/* STANDARDS
/* RFC 821 (SMTP protocol)
/* BUGS
/* The code assumes that the domain is RFC 821 clean.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Alterations for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* P.O. Box 830688, MC34
/* Richardson, TX 75083, USA
/*--*/
/* System library. */
#include <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

View File

@ -0,0 +1,41 @@
/*++
/* NAME
/* quote_821_local 3h
/* SUMMARY
/* quote rfc 821 local part
/* SYNOPSIS
/* #include "quote_821_local.h"
/* DESCRIPTION
/* .nf
/*
* Utility library.
*/
#include <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
/*--*/

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -97,6 +97,7 @@
/* .SH "Authentication controls"
/* .IP \fBsmtp_enable_sasl_auth\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
/* By default, Postfix is built without SASL support.
/* .IP \fBsmtp_sasl_password_maps\fR
/* Lookup tables with per-host \fIname\fR:\fIpassword\fR entries.
/* No entry for a host means no attempt to authenticate.
@ -198,7 +199,6 @@
#include <debug_peer.h>
#include <mail_error.h>
#include <deliver_pass.h>
#include <smtp_stream.h>
/* Single server skeleton. */
@ -292,7 +292,6 @@ static int deliver_message(DELIVER_REQUEST *request)
"%s", vstring_str(why));
} else {
debug_peer_check(state->session->host, state->session->addr);
smtp_jump_setup(state->session->stream, state->jbuf);
if (smtp_helo(state) == 0)
smtp_xfer(state);
if (state->history != 0

View File

@ -8,11 +8,6 @@
/* DESCRIPTION
/* .nf
/*
* System library.
*/
#include <setjmp.h>
/*
* SASL library.
*/
@ -57,7 +52,6 @@ typedef struct SMTP_STATE {
VSTRING *sasl_decoded; /* decoding buffer */
sasl_callback_t *sasl_callbacks; /* stateful callbacks */
#endif
jmp_buf jbuf[1]; /* exception context */
} SMTP_STATE;
#define SMTP_FEATURE_ESMTP (1<<0)

View File

@ -64,7 +64,6 @@
#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() */
@ -158,7 +157,7 @@ int smtp_helo(SMTP_STATE *state)
* Prepare for disaster.
*/
smtp_timeout_setup(state->session->stream, var_smtp_helo_tmout);
if ((except = setjmp(state->jbuf[0])) != 0)
if ((except = vstream_setjmp(state->session->stream)) != 0)
return (smtp_stream_except(state, except, "sending HELO"));
/*
@ -413,6 +412,7 @@ int smtp_xfer(SMTP_STATE *state)
* The final sender state has no action associated with it.
*/
case SMTP_STATE_LAST:
VSTRING_RESET(next_command);
break;
}
VSTRING_TERMINATE(next_command);
@ -439,7 +439,7 @@ int smtp_xfer(SMTP_STATE *state)
*/
smtp_timeout_setup(state->session->stream,
*xfer_timeouts[recv_state]);
if ((except = setjmp(state->jbuf[0])) != 0)
if ((except = vstream_setjmp(state->session->stream)) != 0)
RETURN(smtp_stream_except(state, except,
xfer_states[recv_state]));
resp = smtp_chat_resp(state);
@ -591,7 +591,7 @@ int smtp_xfer(SMTP_STATE *state)
if (send_state == SMTP_STATE_DOT && nrcpt > 0) {
smtp_timeout_setup(state->session->stream,
var_smtp_data1_tmout);
if ((except = setjmp(state->jbuf[0])) != 0)
if ((except = vstream_setjmp(state->session->stream)) != 0)
RETURN(smtp_stream_except(state, except,
"sending message body"));

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -229,7 +229,6 @@
#include <netdb.h>
#include <string.h>
#include <stdio.h> /* remove() */
#include <setjmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
@ -1145,10 +1144,9 @@ static void smtpd_proto(SMTPD_STATE *state)
* cleans up, but no attempt is made to inform the client of the nature
* of the problem.
*/
smtp_jump_setup(state->client, state->jbuf);
smtp_timeout_setup(state->client, var_smtpd_tmout);
switch (setjmp(state->jbuf[0])) {
switch (vstream_setjmp(state->client)) {
default:
msg_panic("smtpd_proto: unknown error reading from %s[%s]",

View File

@ -8,11 +8,6 @@
/* DESCRIPTION
/* .nf
/*
* System library
*/
#include <setjmp.h>
/*
* SASL library.
*/
@ -76,7 +71,6 @@ typedef struct SMTPD_STATE {
VSTRING *sasl_encoded;
VSTRING *sasl_decoded;
#endif
jmp_buf jbuf[1];
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -41,7 +41,6 @@
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <setjmp.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
@ -74,7 +73,6 @@ typedef struct SINK_STATE {
int data_state;
int (*read) (struct SINK_STATE *);
int rcpts;
jmp_buf jbuf[1];
} SINK_STATE;
#define ST_ANY 0
@ -285,7 +283,7 @@ static void read_event(int unused_event, char *context)
SINK_STATE *state = (SINK_STATE *) context;
do {
switch (setjmp(state->jbuf[0])) {
switch (vstream_setjmp(state->stream)) {
default:
msg_panic("unknown error reading input");
@ -335,7 +333,6 @@ static void connect_event(int unused_event, char *context)
state->stream = vstream_fdopen(fd, O_RDWR);
state->read = command_read;
state->data_state = 0;
smtp_jump_setup(state->stream, state->jbuf);
smtp_timeout_setup(state->stream, var_tmout);
smtp_printf(state->stream, "220 %s ESMTP", var_myhostname);
event_enable_read(fd, read_event, (char *) state);

View File

@ -65,7 +65,6 @@
#include <sys/wait.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
@ -112,7 +111,6 @@ typedef struct SESSION {
VSTREAM *stream; /* open connection */
int connect_count; /* # of connect()s to retry */
struct SESSION *next; /* connect() queue linkage */
jmp_buf jbuf[1]; /* exception handling */
} SESSION;
static SESSION *last_session; /* connect() queue tail */
@ -390,7 +388,6 @@ static void start_connect(SESSION *session)
(void) non_blocking(fd, NON_BLOCKING);
session->stream = vstream_fdopen(fd, O_RDWR);
event_enable_write(fd, connect_done, (char *) session);
smtp_jump_setup(session->stream, session->jbuf);
smtp_timeout_setup(session->stream, var_timeout);
if (connect(fd, (struct sockaddr *) & sin, sizeof(sin)) < 0
&& errno != EINPROGRESS)
@ -429,7 +426,7 @@ static void read_banner(int unused_event, char *context)
/*
* Prepare for disaster.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while reading server greeting", exception_text(except));
/*
@ -452,12 +449,12 @@ static void read_banner(int unused_event, char *context)
static void send_helo(SESSION *session)
{
int except;
char *protocol = (talk_lmtp ? "LHLO" : "EHLO");
char *protocol = (talk_lmtp ? "LHLO" : "HELO");
/*
* Send the standard greeting with our hostname
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending HELO", exception_text(except));
command(session->stream, "%s %s", protocol, var_myhostname);
@ -480,7 +477,7 @@ static void helo_done(int unused_event, char *context)
/*
* Get response to HELO command.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending HELO", exception_text(except));
if ((resp = response(session->stream, buffer))->code / 100 != 2)
@ -498,7 +495,7 @@ static void send_mail(SESSION *session)
/*
* Send the envelope sender address.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending sender", exception_text(except));
command(session->stream, "MAIL FROM:<%s>", sender);
@ -521,7 +518,7 @@ static void mail_done(int unused, char *context)
/*
* Get response to MAIL command.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending sender", exception_text(except));
if ((resp = response(session->stream, buffer))->code / 100 != 2)
@ -542,7 +539,7 @@ static void send_rcpt(int unused_event, char *context)
/*
* Send envelope recipient address.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending recipient", exception_text(except));
if (session->rcpt_count > 1)
@ -571,7 +568,7 @@ static void rcpt_done(int unused, char *context)
/*
* Get response to RCPT command.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending recipient", exception_text(except));
if ((resp = response(session->stream, buffer))->code / 100 != 2)
@ -596,7 +593,7 @@ static void send_data(int unused_event, char *context)
/*
* Request data transmission.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending DATA command", exception_text(except));
command(session->stream, "DATA");
@ -620,7 +617,7 @@ static void data_done(int unused_event, char *context)
/*
* Get response to DATA command.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending DATA command", exception_text(except));
if ((resp = response(session->stream, buffer))->code != 354)
msg_fatal("data %d %s", resp->code, resp->str);
@ -644,7 +641,7 @@ static void data_done(int unused_event, char *context)
/*
* Send some garbage.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending message", exception_text(except));
if (message_length == 0) {
smtp_fputs("La de da de da 1.", 17, session->stream);
@ -687,7 +684,7 @@ static void dot_done(int unused_event, char *context)
/*
* Get response to "." command.
*/
if ((except = setjmp(session->jbuf[0])) != 0)
if ((except = vstream_setjmp(session->stream)) != 0)
msg_fatal("%s while sending message", exception_text(except));
do { /* XXX this could block */
if ((resp = response(session->stream, buffer))->code / 100 != 2)

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -47,6 +47,10 @@
-TINET_ADDR_LIST
-TINT_TABLE
-TJMP_BUF_WRAPPER
-TLMTP_ATTR
-TLMTP_RESP
-TLMTP_SESSION
-TLMTP_STATE
-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP

View File

@ -21,8 +21,7 @@ SRCS = argv.c argv_split.c attr.c basename.c binhash.c chroot_uid.c \
write_buf.c write_wait.c dict_unix.c dict_pcre.c stream_listen.c \
stream_connect.c stream_trigger.c dict_regexp.c mac_expand.c \
clean_env.c watchdog.c spawn_command.c duplex_pipe.c sane_rename.c \
sane_link.c unescape.c timed_read.c timed_write.c binattr.c \
vstream_attr.c
sane_link.c unescape.c timed_read.c timed_write.c binattr.c
OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
@ -45,8 +44,7 @@ OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
write_buf.o write_wait.o dict_unix.o dict_pcre.o stream_listen.o \
stream_connect.o stream_trigger.o dict_regexp.o mac_expand.o \
clean_env.o watchdog.o spawn_command.o duplex_pipe.o sane_rename.o \
sane_link.o unescape.o timed_read.o timed_write.o binattr.o \
vstream_attr.o
sane_link.o unescape.o timed_read.o timed_write.o binattr.o
HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
@ -951,14 +949,8 @@ vstream.o: vbuf_print.h
vstream.o: vbuf.h
vstream.o: iostuff.h
vstream.o: vstring.h
vstream.o: binattr.h
vstream.o: vstream.h
vstream_attr.o: vstream_attr.c
vstream_attr.o: sys_defs.h
vstream_attr.o: vstream.h
vstream_attr.o: vbuf.h
vstream_attr.o: binattr.h
vstream_attr.o: htable.h
vstream.o: binattr.h
vstream_popen.o: vstream_popen.c
vstream_popen.o: sys_defs.h
vstream_popen.o: msg.h

View File

@ -24,8 +24,8 @@ extern int peekfd(int);
extern int read_wait(int, int);
extern int write_wait(int, int);
extern int write_buf(int, const char *, int, int);
extern int timed_read(int, void *, unsigned, int);
extern int timed_write(int, void *, unsigned, int);
extern int timed_read(int, void *, unsigned, int, void *);
extern int timed_write(int, void *, unsigned, int, void *);
extern void doze(unsigned);
extern int duplex_pipe(int *);

View File

@ -6,11 +6,12 @@
/* 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.
@ -25,6 +26,9 @@
/* .IP timeout
/* The deadline in seconds. If this is <= 0, the deadline feature
/* is disabled.
/* .IP context
/* Application context. This parameter is unused. It exists only
/* for the sake of VSTREAM compatibility.
/* DIAGNOSTICS
/* When the operation does not complete within the deadline, the
/* result value is -1, and errno is set to ETIMEDOUT.
@ -51,7 +55,8 @@
/* timed_read - read with deadline */
int timed_read(int fd, void *buf, unsigned len, int timeout)
int timed_read(int fd, void *buf, unsigned len,
int timeout, void *unused_context)
{
/*

View File

@ -6,11 +6,12 @@
/* 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.
@ -25,6 +26,9 @@
/* .IP timeout
/* The deadline in seconds. If this is <= 0, the deadline feature
/* is disabled.
/* .IP context
/* Application context. This parameter is unused. It exists only
/* for the sake of VSTREAM compatibility.
/* DIAGNOSTICS
/* When the operation does not complete within the deadline, the
/* result value is -1, and errno is set to ETIMEDOUT.
@ -51,7 +55,8 @@
/* timed_write - write with deadline */
int timed_write(int fd, void *buf, unsigned len, int timeout)
int timed_write(int fd, void *buf, unsigned len,
int timeout, void *unused_context)
{
/*

View File

@ -93,6 +93,14 @@
/*
/* int vstream_peek(stream)
/* VSTREAM *stream;
/*
/* int vstream_setjmp(stream, buffer)
/* VSTREAM *stream;
/* jmp_buf *buffer;
/*
/* void longjmp(stream, val)
/* VSTREAM *stream;
/* int val;
/* DESCRIPTION
/* The \fIvstream\fR module implements light-weight buffered I/O
/* similar to the standard I/O routines.
@ -201,12 +209,15 @@
/* value) pairs, terminated with VSTREAM_CTL_END.
/* The following lists the names and the types of the corresponding
/* value arguments.
/* .IP "VSTREAM_CTL_READ_FN (int (*)(int, void *, unsigned))"
/* .IP "VSTREAM_CTL_READ_FN (int (*)(int, void *, unsigned, int, void *))"
/* The argument specifies an alternative for the timed_read(3) function,
/* for example, a read function that performs encryption.
/* .IP "VSTREAM_CTL_WRITE_FN (int (*)(int, void *, unsigned))"
/* .IP "VSTREAM_CTL_WRITE_FN (int (*)(int, void *, unsigned, int, void *))"
/* The argument specifies an alternative for the timed_write(3) function,
/* for example, a write function that performs encryption.
/* .IP "VSTREAM_CTL_CONTEXT (char *)"
/* The argument specifies application context that is passed on to
/* the application-specified read/write routines. No copy is made.
/* .IP "VSTREAM_CTL_PATH (char *)"
/* Updates the stored pathname of the specified stream. The pathname
/* is copied.
@ -228,6 +239,10 @@
/* The deadline for a descriptor to become readable in case of a read
/* request, or writable in case of a write request. Specify a value
/* <= 0 to disable deadlines.
/* .IP "VSTREAM_CTL_EXCEPT (no value)"
/* Enable exception handling with vstream_setjmp() and vstream_longjmp().
/* This involves allocation of additional memory that normally isn't
/* used.
/* .PP
/* vstream_fileno() gives access to the file handle associated with
/* a buffered stream. With streams that have separate read/write
@ -254,12 +269,22 @@
/*
/* vstream_peek() returns the number of characters that can be
/* read from the named stream without refilling the read buffer.
/*
/* vstream_setjmp() saves processing context and makes that context
/* available for use with vstream_longjmp(). Normally, vstream_setjmp()
/* returns zero. A non-zero result means that vstream_setjmp() returned
/* through a vstream_longjmp() call; the result is the \fIval\fR argment
/* given to vstream_longjmp().
/*
/* NB: non-local jumps such as vstream_longjmp() are not safe
/* for jumping out of any vstream routine.
/* DIAGNOSTICS
/* Panics: interface violations. Fatal errors: out of memory.
/* SEE ALSO
/* timed_read(3) default read routine
/* timed_write(3) default write routine
/* vbuf_print(3) formatting engine
/* setjmp(3) non-local jumps
/* BUGS
/* Should use mmap() on reasonable systems.
/* LICENSE
@ -291,7 +316,6 @@
#include "vbuf_print.h"
#include "iostuff.h"
#include "vstring.h"
#include "binattr.h"
#include "vstream.h"
/* Application-specific. */
@ -499,7 +523,7 @@ static int vstream_fflush_some(VSTREAM *stream, int to_flush)
* any.
*/
for (data = (char *) bp->data, len = to_flush; len > 0; len -= n, data += n) {
if ((n = stream->write_fn(stream->fd, data, len, stream->timeout)) <= 0) {
if ((n = stream->write_fn(stream->fd, data, len, stream->timeout, stream->context)) <= 0) {
bp->flags |= VSTREAM_FLAG_ERR;
if (errno == ETIMEDOUT)
bp->flags |= VSTREAM_FLAG_TIMEOUT;
@ -625,7 +649,7 @@ static int vstream_buf_get_ready(VBUF *bp)
* data as is available right now, whichever is less. Update the cached
* file seek position, if any.
*/
switch (n = stream->read_fn(stream->fd, bp->data, bp->len, stream->timeout)) {
switch (n = stream->read_fn(stream->fd, bp->data, bp->len, stream->timeout, stream->context)) {
case -1:
bp->flags |= VSTREAM_FLAG_ERR;
if (errno == ETIMEDOUT)
@ -874,7 +898,8 @@ VSTREAM *vstream_fdopen(int fd, int flags)
stream->pid = 0;
stream->waitpid_fn = 0;
stream->timeout = 0;
stream->attr = 0;
stream->context = 0;
stream->jbuf = 0;
return (stream);
}
@ -929,8 +954,8 @@ int vstream_fclose(VSTREAM *stream)
}
if (stream->path)
myfree(stream->path);
if (stream->attr)
binattr_free(stream->attr);
if (stream->jbuf)
myfree((char *) stream->jbuf);
if (!VSTREAM_STATIC(stream))
myfree((char *) stream);
return (err ? VSTREAM_EOF : 0);
@ -988,6 +1013,9 @@ void vstream_control(VSTREAM *stream, int name,...)
case VSTREAM_CTL_WRITE_FN:
stream->write_fn = va_arg(ap, VSTREAM_FN);
break;
case VSTREAM_CTL_CONTEXT:
stream->context = va_arg(ap, char *);
break;
case VSTREAM_CTL_PATH:
if (stream->path)
myfree(stream->path);
@ -1023,6 +1051,10 @@ void vstream_control(VSTREAM *stream, int name,...)
case VSTREAM_CTL_TIMEOUT:
stream->timeout = va_arg(ap, int);
break;
case VSTREAM_CTL_EXCEPT:
if (stream->jbuf == 0)
stream->jbuf = (jmp_buf *) mymalloc(sizeof(jmp_buf));
break;
default:
msg_panic("%s: bad name %d", myname, name);
}

View File

@ -16,6 +16,7 @@
*/
#include <fcntl.h>
#include <stdarg.h>
#include <setjmp.h>
/*
* Utility library.
@ -27,7 +28,7 @@
* Simple buffered stream. The members of this structure are not part of the
* official interface and can change without prior notice.
*/
typedef int (*VSTREAM_FN) (int, void *, unsigned, int);
typedef int (*VSTREAM_FN) (int, void *, unsigned, int, void *);
typedef int (*VSTREAM_WAITPID_FN) (pid_t, WAIT_STATUS_T *, int);
typedef struct VSTREAM {
@ -35,16 +36,17 @@ typedef struct VSTREAM {
int fd; /* file handle, no 256 limit */
VSTREAM_FN read_fn; /* buffer fill action */
VSTREAM_FN write_fn; /* buffer fill action */
void *context; /* application context */
long offset; /* cached seek info */
char *path; /* give it at least try */
int read_fd; /* read channel (double-buffered) */
int write_fd; /* write channel (double-buffered) */
VBUF read_buf; /* read buffer (double-buffered) */
VBUF write_buf; /* write buffer (double-buffered) */
int timeout; /* read/write timout */
pid_t pid; /* vstream_popen/close() */
VSTREAM_WAITPID_FN waitpid_fn; /* vstream_popen/close() */
BINATTR *attr; /* optional binary attribute list */
int timeout; /* read/write timout */
jmp_buf *jbuf; /* exception handling */
} VSTREAM;
extern VSTREAM vstream_fstd[]; /* pre-defined streams */
@ -104,6 +106,8 @@ extern void vstream_control(VSTREAM *, int,...);
#define VSTREAM_CTL_WRITE_FD 6
#define VSTREAM_CTL_WAITPID_FN 7
#define VSTREAM_CTL_TIMEOUT 8
#define VSTREAM_CTL_EXCEPT 9
#define VSTREAM_CTL_CONTEXT 10
extern VSTREAM *vstream_printf(const char *,...);
extern VSTREAM *vstream_fprintf(VSTREAM *, const char *,...);
@ -128,14 +132,11 @@ extern VSTREAM *vstream_vfprintf(VSTREAM *, const char *, va_list);
extern int vstream_peek(VSTREAM *);
/*
* Attribute management, a way of tacking on arbitrary information onto a
* VSTREAM without destroying the VSTREAM abstraction itself.
* Exception handling. We use pointer to jmp_buf to avoid a lot of unused
* baggage for streams that don't need this functionality.
*/
#define VSTREAM_ATTR_FREE_FN BINATTR_FREE_FN
extern void vstream_attr_set(VSTREAM *, const char *, char *, VSTREAM_ATTR_FREE_FN);
extern char *vstream_attr_get(VSTREAM *, const char *);
extern void vstream_attr_unset(VSTREAM *, const char *);
#define vstream_setjmp(stream) setjmp((stream)->jbuf[0])
#define vstream_longjmp(stream, val) longjmp((stream)->jbuf[0], (val))
/* LICENSE
/* .ad

View File

@ -1,92 +0,0 @@
/*++
/* 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);
}