1
0
Fork 0

qmail 1.01

This commit is contained in:
D. J. Bernstein 1997-10-28 00:00:00 +01:00 committed by Rolf Eike Beer
commit af9625ddf9
401 changed files with 30689 additions and 0 deletions

43
BLURB Normal file
View file

@ -0,0 +1,43 @@
qmail is a secure, reliable, efficient, simple message transfer agent.
It is meant as a replacement for the entire sendmail-binmail system on
typical Internet-connected UNIX hosts.
Secure: Security isn't just a goal, but an absolute requirement. Mail
delivery is critical for users; it cannot be turned off, so it must be
completely secure. (This is why I started writing qmail: I was sick of
the security holes in sendmail and other MTAs.)
Reliable: qmail's straight-paper-path philosophy guarantees that a
message, once accepted into the system, will never be lost. qmail also
supports maildir, a new, super-reliable user mailbox format. Maildirs,
unlike mbox files and mh folders, won't be corrupted if the system
crashes during delivery. Even better, not only can a user safely read
his mail over NFS, but any number of NFS clients can deliver mail to him
at the same time.
Efficient: On a Pentium under BSD/OS, qmail can easily sustain 200000
local messages per day---that's separate messages injected and delivered
to mailboxes in a real test! Although remote deliveries are inherently
limited by the slowness of DNS and SMTP, qmail overlaps 20 simultaneous
deliveries by default, so it zooms quickly through mailing lists. (This
is why I finished qmail: I had to get a big mailing list set up.)
Simple: qmail is vastly smaller than any other Internet MTA. Some
reasons why: (1) Other MTAs have separate forwarding, aliasing, and
mailing list mechanisms. qmail has one simple forwarding mechanism that
lets users handle their own mailing lists. (2) Other MTAs offer a
spectrum of delivery modes, from fast+unsafe to slow+queued. qmail-send
is instantly triggered by new items in the queue, so the qmail system
has just one delivery mode: fast+queued. (3) Other MTAs include, in
effect, a specialized version of inetd that watches the load average.
qmail's design inherently limits the machine load, so qmail-smtpd can
safely run from your system's inetd.
Replacement for sendmail: qmail supports host and user masquerading,
full host hiding, virtual domains, null clients, list-owner rewriting,
relay control, double-bounce recording, arbitrary RFC 822 address lists,
cross-host mailing list loop detection, per-recipient checkpointing,
downed host backoffs, independent message retry schedules, etc. In
short, it's up to speed on modern MTA features. qmail also includes a
drop-in ``sendmail'' wrapper so that it will be used transparently by
your current UAs.

26
BLURB2 Normal file
View file

@ -0,0 +1,26 @@
Mailing list management is one of qmail's strengths. Notable features:
* qmail lets each user handle his own mailing lists. The delivery
instructions for user-whatever go into ~user/.qmail-whatever.
* qmail makes it really easy to set up mailing list owners. If the user
touches ~user/.qmail-whatever-owner, all bounces will come back to him.
* qmail supports VERPs, which permit completely reliable automated
bounce handling for mailing lists of any size.
* SPEED---qmail blasts through mailing lists an order of magnitude
faster than sendmail. For example, one message was successfully
delivered to 150 hosts around the world in just 70 seconds, with qmail's
out-of-the-box configuration.
* qmail automatically prevents mailing list loops, even across hosts.
* qlist, included in the qmail package, deals with subscription requests
safely and automatically.
* qmail allows inconceivably gigantic mailing lists. No random limits.
* qmail handles aliasing and forwarding with the same simple mechanism.
For example, Postmaster is controlled by ~alias/.qmail-postmaster. This
means that cross-host loop detection also applies to aliases.

90
BLURB3 Normal file
View file

@ -0,0 +1,90 @@
Here are some of qmail's features.
Setup:
* automatic adaptation to your UNIX variant---no configuration needed
* AIX, BSD/OS, FreeBSD, HP/UX, Irix, Linux, OSF/1, SunOS, Solaris, and more
* automatic per-host configuration (qmail-config)
* quick installation---no big list of decisions to make
Security:
* clear separation between addresses, files, and programs
* minimization of setuid code (qmail-queue)
* minimization of root code (qmail-start, qmail-lspawn)
* five-way trust partitioning---security in depth
* optional logging of one-way hashes, entire contents, etc. (QUEUE_EXTRA)
Message construction (qmail-inject):
* RFC 822, RFC 1123
* full support for address groups
* automatic conversion of old-style headers to RFC 822 format
* header line length limited only by memory
* host masquerading (control/defaulthost)
* user masquerading (MAILUSER, MAILHOST)
* sendmail hook for compatibility with current user agents
SMTP service (qmail-smtpd):
* RFC 821, RFC 1123, RFC 1651, RFC 1652, RFC 1854
* 8-bit clean
* 931/1413/ident/TAP callback (tcp-env)
* relay control---stop unauthorized relaying by outsiders (control/rcpthosts)
* no interference between relay control and forwarding
* tcpd hook---reject SMTP connections from known abusers
* automatic recognition of local IP addresses
* per-buffer timeouts
* hop counting
Queue management (qmail-send):
* instant handling of messages added to queue
* parallelism limit (control/concurrencyremote, control/concurrencylocal)
* split queue directory---no slowdown when queue gets big
* quadratic retry schedule---old messages tried less often
* independent message retry schedules
* automatic safe queueing---no loss of mail if system crashes
* automatic per-recipient checkpointing
* automatic queue cleanups (qmail-clean)
* queue viewing (qmail-qread)
* detailed delivery statistics (qmailanalog, available separately)
Bounces (qmail-send):
* QSBMF bounce messages---both machine-readable and human-readable
* HCMSSC support---language-independent RFC 1893 error codes
* double bounces sent to postmaster
Routing by domain (qmail-send):
* any number of names for local host (control/locals)
* any number of virtual domains (control/virtualdomains)
* domain wildcards (control/virtualdomains)
* configurable percent hack support (control/percenthack)
* UUCP hook
SMTP delivery (qmail-remote):
* RFC 821, RFC 974, RFC 1123
* 8-bit clean
* automatic downed host backoffs
* artificial routing---smarthost, localnet, mailertable (control/smtproutes)
* per-buffer timeouts
* passive SMTP queue---perfect for SLIP/PPP (serialmail, available separately)
Forwarding and mailing lists (qmail-local):
* address wildcards (.qmail-default, .qmail-foo-default, etc.)
* sendmail/smail /etc/aliases compatibility (qmsmac, available separately)
* mailing list owners---automatically divert bounces and vacation messages
* VERPs---automatic recipient identification for mailing list bounces
* Delivered-To---automatic loop prevention, even across hosts
* automatic subscription management (qlist)
Local delivery (qmail-local):
* user-controlled address hierarchy---fred controls fred-anything
* mbox delivery
* reliable NFS delivery (maildir)
* user-controlled program delivery: procmail etc. (qmail-command)
* optional new-mail notification (qbiff)
* optional NRUDT return receipts (qreceipt)
* conditional filtering (condredirect)
POP3 service (qmail-popup, qmail-pop3d):
* RFC 1939
* UIDL support
* TOP support
* APOP hook
* modular password checking (checkpassword, available separately)

44
BLURB4 Normal file
View file

@ -0,0 +1,44 @@
qmail's modular, lightweight design and sensible queue management make
it the fastest available message transfer agent. Here's how it stacks up
against the competition in five different speed measurements.
* Scheduling: I sent a message to 8192 ``trash'' recipients on my home
machine. All the deliveries were done in a mere 78 seconds---a rate of
over 9 MILLION deliveries a day! Compare this to the speed advertised
for Zmailer's scheduling: 1.1 million deliveries a day on a
SparcStation-10/50. (My home machine is a 16MB Pentium-100 under BSD/OS,
with the default qmail configuration. qmail's logs were piped through
accustamp and written to disk as usual.)
* Local mailing lists: When qmail is delivering a message to a mailbox,
it physically writes the message to disk before it announces success---
that way, mail doesn't get lost if the power goes out. I tried sending a
message to 1024 local mailboxes on the same disk on my home machine; all
the deliveries were done in 25.5 seconds. That's more than 3.4 MILLION
deliveries a day! Sending 1024 copies to a _single_ mailbox was just as
fast. Compare these figures to Zmailer's advertised rate for throwing
recipients away without even delivering the message---only 0.48 million
per day on the SparcStation.
* Mailing lists with remote recipients: qmail uses the same delivery
strategy that makes LSOFT's LSMTP so fast for outgoing mailing lists---
you choose how many parallel SMTP connections you want to run, and qmail
runs exactly that many. Of course, performance varies depending on how
far away your recipients are. The advantage of qmail over other packages
is its smallness: for example, one Linux user is running 60 simultaneous
connections, without swapping, on a machine with just 16MB of memory!
* Separate local messages: What LSOFT doesn't tell you about LSMTP is
how many _separate_ messages it can handle in a day. Does it get bogged
down as the queue fills up? On my home machine, I disabled qmail's
deliveries and then sent 5000 separate messages to one recipient. The
messages were all safely written to the queue disk in 23 minutes, with
no slowdown as the queue filled up. After I reenabled deliveries, all
the messages were delivered to the recipient's mailbox in under 12
minutes. End-to-end rate: more than 200000 individual messages a day!
* Overall performance: What really matters is how well qmail performs
with your mail load. Red Hat Software found one day that their mail hub,
a 48MB Pentium running sendmail 8.7, was running out of steam at 70000
messages a day. They shifted the load to qmail---on a _smaller_ machine,
a 16MB 486/66---and now they're doing fine.

1204
CHANGES Normal file

File diff suppressed because it is too large Load diff

574
FAQ Normal file
View file

@ -0,0 +1,574 @@
1. Controlling the appearance of outgoing messages
1.1. How do I set up host masquerading?
1.2. How do I set up user masquerading?
2. Routing outgoing messages
2.1. How do I send local messages to another host?
2.2. How do I set up a null client?
2.3. How do I send outgoing mail through UUCP?
2.4. How do I set up a separate queue for a SLIP/PPP link?
2.5. How do I deal with ``CNAME lookup failed temporarily''?
3. Routing incoming messages by host
3.1. How do I receive mail for another host name?
3.2. How do I set up a virtual domain?
3.3. How do I set up several virtual domains for one user?
4. Routing incoming messages by user
4.1. How do I forward unrecognized usernames to another host?
4.2. How do I set up a mailing list?
4.3. How do I use majordomo with qmail?
4.4. How do I use procmail with qmail?
4.5. How do I use elm's filter with qmail?
4.6. How do I create aliases with dots?
5. Setting up servers
5.1. How do I run qmail-smtpd under tcpserver?
5.2. How do I set up qmail-qmtpd?
5.3. How do I set up qmail-pop3d?
5.4. How do I allow selected clients to use this host as a relay?
5.5. How do I fix up messages from broken SMTP clients?
6. Configuring MUAs to work with qmail
6.1. How do I make BSD mail generate a Date with the local time zone?
6.2. How do I stop pine from crashing?
6.3. How do I make MH work with qmail?
6.4. How do I stop Sun's dtcm from hanging?
7. Managing the mail system
7.1. How do I safely stop qmail-send?
7.2. How do I manually run the queue?
7.3. How do I rejuvenate a message?
7.4. How do I organize a big network?
7.5. How do I back up and restore the queue disk?
8. Miscellany
8.1. How do I tell qmail to do more deliveries at once?
8.2. How do I keep a copy of all incoming and outgoing mail messages?
8.3. How do I switch slowly from sendmail to qmail?
1. Controlling the appearance of outgoing messages
1.1. How do I set up host masquerading? All the users on this host,
zippy.af.mil, are users on af.mil. When joe sends a message to fred, the
message should say ``From: joe@af.mil'' and ``To: fred@af.mil'', without
``zippy'' anywhere.
Answer: echo af.mil > /var/qmail/control/defaulthost; chmod 644
/var/qmail/control/defaulthost.
1.2. How do I set up user masquerading? I'd like my own From lines to
show boss@af.mil rather than god@heaven.af.mil.
Answer: Add MAILHOST=af.mil and MAILUSER=boss to your environment. To
override From lines supplied by your MUA, add QMAILINJECT=f to your
environment.
2. Routing outgoing messages
2.1. How do I send local messages to another host? All the mail for
af.mil should be delivered to our disk server, pokey.af.mil. I've set up
an MX from af.mil to pokey.af.mil, but when a user on the af.mil host
sends a message to boss@af.mil, af.mil tries to deliver it locally. How
do I stop that?
Answer: Remove af.mil from /var/qmail/control/locals. If qmail-send is
running, give it a HUP. Make sure the MX is set up properly before you
do this. Also make sure that pokey can receive mail for af.mil---see
question 3.1.
2.2. How do I set up a null client? I'd like zippy.af.mil to
send all mail to bigbang.af.mil.
Answer: echo :bigbang.af.mil > /var/qmail/control/smtproutes;
chmod 644 /var/qmail/control/smtproutes. Disable local delivery as in
question 2.1. Turn off qmail-smtpd in /etc/inetd.conf.
2.3. How do I send outgoing mail through UUCP? I need qmail to send all
outgoing mail via UUCP to my upstream UUCP site, gonzo.
Answer: Put
:alias-uucp
into control/virtualdomains and
|preline -df /usr/bin/uux - -r -gC -a"$SENDER" gonzo!rmail "($EXT2@$HOST)"
into ~alias/.qmail-uucp-default. (For some UUCP software you will need
to use -d instead of -df. Also, you may need to insert a space between
-a and "$SENDER" for bounces to work properly.) If qmail-send is
running, give it a HUP.
2.4. How do I set up a separate queue for a SLIP/PPP link?
Answer: Use serialmail (http://pobox.com/~djb/serialmail.html).
2.5. How do I deal with ``CNAME lookup failed temporarily''? The log
showed that a message was deferred for this reason. Why is qmail doing
CNAME lookups, anyway?
Answer: The SMTP standard does not permit aliased hostnames, so qmail
has to do a CNAME lookup in DNS for every sender and recipient host. If
the relevant DNS server is down, qmail defers the message. It will try
again soon.
3. Routing incoming messages by host
3.1. How do I receive mail for another host name? I'd like our disk
server, pokey.af.mil, to receive mail addressed to af.mil. I've set up
an MX from af.mil to pokey.af.mil, but how do I get pokey to treat
af.mil as a name for the local host?
Answer: Add af.mil to /var/qmail/control/locals and to
/var/qmail/control/rcpthosts. If qmail-send is running, give it a HUP.
3.2. How do I set up a virtual domain? I'd like any mail for
nowhere.mil, including root@nowhere.mil and postmaster@nowhere.mil and
so on, to be delivered to Bob. I've set up the MX already.
Answer: Put
nowhere.mil:bob
into control/virtualdomains. Add nowhere.mil to control/rcpthosts. If
qmail-send is running, give it a HUP.
Now mail for whatever@nowhere.mil will be delivered locally to
bob-whatever. Bob can set up ~bob/.qmail-default to catch all the
possible addresses, ~bob/.qmail-info to catch info@nowhere.mil, etc.
3.3. How do I set up several virtual domains for one user? Bob wants
another virtual domain, everywhere.org, but he wants to handle
nowhere.mil users and everywhere.org users differently. How can we do
that without setting up a second account?
Answer: Put two lines into control/virtualdomains:
nowhere.mil:bob-nowhere
everywhere.org:bob-everywhere
Add nowhere.mil and everywhere.org to control/rcpthosts. If qmail-send
is running, give it a HUP.
Now Bob can set up separate .qmail-nowhere-* and everywhere-* files. He
can even set up .qmail-nowhere-default and .qmail-everywhere-default.
4. Routing incoming messages by user
4.1. How do I forward unrecognized usernames to another host? I'd like
to set up a LUSER_RELAY pointing at bigbang.af.mil.
Answer: Put
| forward "$LOCAL"@bigbang.af.mil
into ~alias/.qmail-default.
4.2. How do I set up a mailing list? I'd like me-sos@my.host.name to be
forwarded to a bunch of people.
Answer: Put a list of addresses into ~me/.qmail-sos, one per line. Then
incoming mail for me-sos will be forwarded to each of those addresses.
You should also touch ~me/.qmail-sos-owner so that bounces come back to
you rather than the original sender. If you want subscriptions to be
handled automatically, put
| qlist2 sos my.host.name
into ~me/.qmail-sos-request. Anyone who wants to subscribe can simply
send a message to me-sos-request@my.host.name.
4.3. How do I use majordomo with qmail?
Answer: You need to patch majordomo so that it creates qmail-style
lists. See ftp://koobera.math.uic.edu/pub/software/majordomo+qmail.gz.
Exception: qmsmac understands sendmail-style :include: files, so you
shouldn't patch majordomo if you're using qmsmac.
4.4. How do I use procmail with qmail?
Answer: Put
| preline procmail
into ~/.qmail. You'll have to use a full path for procmail unless
procmail is in the system's startup PATH. Note that procmail will try to
deliver to /usr/spool/mail/$USER by default; to change this, change
SYSTEM_MBOX in procmail's config.h.
4.5. How do I use elm's filter with qmail?
Answer: Put
| preline filter
into ~/.qmail. You'll have to use a full path for filter unless filter
is in the system's startup PATH.
4.6. How do I create aliases with dots? I tried setting up
~alias/.qmail-P.D.Q.Bach, but it doesn't do anything.
Answer: Use .qmail-p:d:q:bach. Dots are converted to colons, and
uppercase is converted to lowercase.
5. Setting up servers
5.1. How do I run qmail-smtpd under tcpserver? inetd is barfing at high
loads, cutting off service for ten-minute stretches. I'd also like
better connection logging.
Answer: First, install the tcpserver program, part of the ucspi-tcp
package (http://pobox.com/~djb/ucspi-tcp.html). Second, remove the smtp
line from /etc/inetd.conf, and put the line
tcpserver -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd &
into your system startup files. Replace 7770 with your qmaild uid, and
replace 2108 with your nofiles gid. Don't forget the &. The change will
take effect at your next reboot.
By default, tcpserver allows at most 40 simultaneous qmail-smtpd
processes. To raise this limit to 400, use tcpserver -c 400. To keep
track of who's connecting and for how long, run (on two lines)
tcpserver -v -u 7770 -g 2108 0 smtp /var/qmail/bin/qmail-smtpd \
2>&1 | /var/qmail/bin/splogger smtpd 3 &
5.2. How do I set up qmail-qmtpd?
Answer: Two steps. First, put a
qmtp 209/tcp
line into /etc/services. Second, put (all on one line)
qmtp stream tcp nowait qmaild
/var/qmail/bin/tcp-env tcp-env /var/qmail/bin/qmail-qmtpd
into /etc/inetd.conf, and give inetd a HUP.
If you have tcpserver installed, skip the inetd step, and set up
tcpserver -u 7770 -g 2108 0 qmtp /var/qmail/bin/qmail-qmtpd &
replacing 7770 and 2108 with the qmaild uid and nofiles gid. See
question 5.1 for more details.
5.3. How do I set up qmail-pop3d?
Answer: Four steps. First, install the checkpassword program
(http://pobox.com/~djb/checkpwd.html). Second, make sure you have a
pop3 110/tcp
line in /etc/services. Third, put (all on one line)
pop3 stream tcp nowait root /var/qmail/bin/qmail-popup
qmail-popup YOURHOST /bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir
into /etc/inetd.conf, and give inetd a HUP; replace YOURHOST with your
host's fully qualified domain name. Fourth, set up Maildir delivery for
any user who wants to read mail via POP.
If you have tcpserver installed, skip the inetd step, and set up (on two
lines)
tcpserver 0 pop3 /var/qmail/bin/qmail-popup YOURHOST \
/bin/checkpassword /var/qmail/bin/qmail-pop3d Maildir &
replacing YOURHOST with your host's fully qualified domain name. See
question 5.1 for more details.
Security note: pop3d should be used only within a secure network;
otherwise an eavesdropper can steal passwords.
5.4. How do I allow selected clients to use this host as a relay? I see
that qmail-smtpd rejects messages to any host not listed in
control/rcpthosts. I know I could entirely disable this feature by
removing control/rcpthosts, but I want to be more selective.
Answer: Three steps. First, install tcp-wrappers, available separately,
including hosts_options. Second, change your qmail-smtpd line in
inetd.conf to
smtp stream tcp nowait qmaild /usr/local/bin/tcpd
/var/qmail/bin/tcp-env /var/qmail/bin/qmail-smtpd
(all on one line) and give inetd a HUP. Third, in tcpd's hosts.allow,
make a line setting the environment variable RELAYCLIENT to the empty
string for the selected clients:
tcp-env: 1.2.3.4, 1.2.3.5: setenv = RELAYCLIENT
Here 1.2.3.4 and 1.2.3.5 are the clients' IP addresses. qmail-smtpd
ignores control/rcpthosts when RELAYCLIENT is set. (It also appends
RELAYCLIENT to each envelope recipient address. See question 5.5 for an
application.)
Alternative procedure, if you are using tcpserver: Install tcpcontrol
(http://pobox.com/~djb/tcpcontrol.html). Create /etc/tcp.smtp containing
1.2.3.6:allow,RELAYCLIENT=""
127.:allow,RELAYCLIENT=""
to allow clients with IP addresses 1.2.3.6 and 127.*. Run
tcpmakectl /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
Finally, insert
tcpcontrol /etc/tcp.smtp.cdb
before /var/qmail/bin/qmail-smtpd in your tcpserver line.
5.5. How do I fix up messages from broken SMTP clients?
Answer: Three steps. First, put
| [ "@$HOST" = "@fixme" ] || ( echo Permission denied; exit 100 )
| qmail-inject -f "$SENDER" -- "$EXT2"
into ~alias/.qmail-fixup-default. Second, put
fixme:fixup
into /var/qmail/control/virtualdomains, and give qmail-send a HUP.
Third, follow the procedure in question 5.4, but set RELAYCLIENT to the
string ``@fixme'':
tcp-env: 1.2.3.6, 1.2.3.7: setenv = RELAYCLIENT @fixme
Here 1.2.3.6 and 1.2.3.7 are the clients' IP addresses. If you are using
tcpserver and tcpcontrol instead of inetd and tcpd, put
1.2.3.6:allow,RELAYCLIENT="@fixme"
1.2.3.7:allow,RELAYCLIENT="@fixme"
into /etc/tcp.smtp, and run tcpmakectl as in question 5.4.
6. Configuring MUAs to work with qmail
6.1. How do I make BSD mail generate a Date with the local time zone?
When I send mail, I'd rather use the local time zone than GMT, since
some MUAs don't know how to display Date in the receiver's time zone.
Answer: Put
set sendmail=/var/qmail/bin/datemail
into your .mailrc or your system-wide Mail.rc. Beware that BSD mail is
neither secure nor reliable.
6.2. How do I stop pine from crashing? When I ask any version of pine
past 3.91 to send mail, it crashes.
Answer: Put
sendmail-path=/usr/lib/sendmail -oem -oi -t
into /usr/local/lib/pine.conf. (This will work with sendmail too.)
Beware that pine is neither secure nor reliable.
6.3. How do I make MH work with qmail?
Answer: Put
postproc: /usr/mh/lib/spost
into each user's .mh_profile. (This will work with sendmail too.) Beware
that MH is neither secure nor reliable.
6.4. How do I stop Sun's dtcm from hanging?
Answer: There is a novice programming error in dtcm, known as ``failure
to close the output side of the pipe in the child.'' Sun has, at the
time of this writing, not yet provided a patch. Sorry.
7. Managing the mail system
7.1. How do I safely stop qmail-send? Back when we were running
sendmail, it was always tricky to kill sendmail without risking the loss
of current deliveries; what should I do with qmail-send?
Answer: Go ahead and kill the qmail-send process. It will shut down
cleanly. Wait for ``exiting'' to show up in the log. To restart it, run
qmail-start the same way as it's run from your system boot scripts.
7.2. How do I manually run the queue? I'd like qmail to try delivering
all the remote messages right now.
Answer: Give the qmail-send process an ALRM.
7.3. How do I rejuvenate a message? Somebody broke into Eric's computer
again; it's going to be down for at least another two days. I know Eric
has been expecting an important message---in fact, I see it sitting here
in /var/qmail/queue/mess/15/26902. It's been in the queue for six days;
how can I make sure it isn't bounced tomorrow?
Answer: Just touch /var/qmail/queue/info/15/26902. (This is the only
form of queue modification that's safe while qmail is running.)
7.4. How do I organize a big network? I have a lot of machines, and I
don't know where to start.
Answer: First, choose the domain name where your users will receive
mail. This is normally the shortest domain name you control. If you are
in charge of *.movie.edu, you can use addresses like joe@movie.edu.
Second, choose the machine that will know what to do with different
users at movie.edu. Set up a host name in DNS for this machine:
mailhost.movie.edu IN A 1.2.3.4
4.3.2.1.in-addr.arpa IN PTR mailhost.movie.edu
Here 1.2.3.4 is the IP address of that machine.
Third, make a list of machines where mail should end up. For example, if
mail for Bob should end up on Bob's workstation, put Bob's workstation
onto the list. For each of these machines, set up a host name in DNS:
bobshost.movie.edu IN A 1.2.3.7
7.3.2.1.in-addr.arpa IN PTR bobshost.movie.edu
Fourth, install qmail on bobshost.movie.edu. qmail will automatically
configure itself to accept messages for bob@bobshost.movie.edu and
deliver them to ~bob/Mailbox on bobshost. Do the same for the other
machines where mail should end up.
Fifth, install qmail on mailhost.movie.edu. Put
movie.edu:alias-movie
into control/virtualdomains on mailhost. Then forward bob@movie.edu to
bob@bobshost.movie.edu, by putting
bob@bobshost.movie.edu
into ~alias/.qmail-movie-bob. Do the same for other users.
Sixth, put movie.edu into control/rcpthosts on mailhost.movie.edu, so
that mailhost.movie.edu will accept messages for users at movie.edu.
Seventh, set up an MX record in DNS to deliver movie.edu messages to
mailhost:
movie.edu IN MX 10 mailhost.movie.edu
Eighth, on all your machines, put movie.edu into control/defaulthost.
7.5. How do I back up and restore the queue disk?
Answer: You can't.
One difficulty is that you can't get a consistent snapshot of the queue
while qmail-send is running. Another difficulty is that messages in the
queue must have filenames that match their inode numbers.
However, the big problem is that backups---even twice-daily backups---
are far too unreliable for mail. If your disk dies, there will be very
little overlap between the messages saved in the last backup and the
messages that were lost.
There are several ways to add real reliability to a mail server. Battery
backups will keep your server alive, letting you park the disk to avoid
a head crash, when the power goes out. Solid-state disks have their own
battery backups. RAID boxes let you replace dead disks without losing
any data.
8. Miscellany
8.1. How do I tell qmail to do more deliveries at once? It's running
only 20 parallel qmail-remote processes.
Answer: Decide how many deliveries you want to allow at once. Put that
number into control/concurrencyremote. Restart qmail-send as in question
7.1. If your system has resource limits, make sure you set the
descriptors limit to at least double the concurrency plus 5; otherwise
you'll get lots of unnecessary deferrals whenever a big burst of mail
shows up. Note that qmail also imposes a compile-time concurrency limit,
120 by default; this is set in conf-spawn.
8.2. How do I keep a copy of all incoming and outgoing mail messages?
Answer: Set QUEUE_EXTRA to "Tlog\0" and QUEUE_EXTRALEN to 5 in extra.h.
Recompile qmail. Put ./msg-log into ~alias/.qmail-log.
You can also use QUEUE_EXTRA to, e.g., record the Message-ID of every
message: run
| awk '/^$/ { exit } /^[mM][eE][sS][sS][aA][gG][eE]-/ { print }'
from ~alias/.qmail-log.
8.3. How do I switch slowly from sendmail to qmail? I'm thinking of
moving the heaven.af.mil network over to qmail, but first I'd like to
give my users a chance to try out qmail without affecting current
sendmail deliveries. We're using NFS.
Answer: Find a host in your network, say pc.heaven.af.mil, that isn't
running an SMTP server. (If addresses at pc.heaven.af.mil are used, you
should already have an MX pointing pc.heaven.af.mil to your mail hub.)
Set up a new MX record pointing lists.heaven.af.mil to pc.heaven.af.mil.
Install qmail on pc.heaven.af.mil. Replace pc with lists in the control
files. Make the qmail man pages available on all your machines.
Now tell your users about qmail. A user can forward joe@heaven.af.mil to
joe@lists.heaven.af.mil to get ~/Mailbox delivery; he can set up .qmail
files; he can start running his own mailing lists @lists.heaven.af.mil.
When you're ready to turn sendmail off, you can set up pc.heaven.af.mil
as your new mail hub. Add heaven.af.mil to control/locals, and change
the heaven.af.mil MX to point to pc.heaven.af.mil. Make sure you leave
lists.heaven.af.mil in control/locals so that transition addresses will
continue to work.

401
FILES Normal file
View file

@ -0,0 +1,401 @@
BLURB
BLURB2
BLURB3
BLURB4
README
FAQ
INSTALL
INSTALL.alias
INSTALL.boot
INSTALL.ctl
INSTALL.ids
INSTALL.mbox
INSTALL.qsmhook
UPGRADE
THOUGHTS
TODO
THANKS
CHANGES
RFCHCSC
RFCLOOPS
RFCMXPS
RFCNETSTR
RFCNRUDT
RFCQMTP
RFCQSBMF
RFCVERP
SECURITY
INTERNALS
FILES
VERSION
SYSDEPS
TARGETS
Makefile
conf-break
auto_break.h
conf-spawn
auto_spawn.h
chkspawn.c
conf-split
auto_split.h
conf-patrn
auto_patrn.h
conf-users
conf-groups
auto_uids.h
auto_usera.h
extra.h
addresses.5
condredirect.1
dot-qmail.9
envelopes.5
forgeries.7
forward.1
maildir2mbox.1
maildirmake.1
maildirwatch.1
mailsubj.1
mbox.5
preline.1
qbiff.1
qlist.1
qmail-clean.8
qmail-command.8
qmail-control.9
qmail-getpw.9
qmail-header.5
qmail-inject.8
qmail-limits.9
qmail-local.8
qmail-log.5
qmail-lspawn.8
qmail-newu.9
qmail-pop3d.8
qmail-popup.8
qmail-pw2u.9
qmail-qmtpd.8
qmail-qread.8
qmail-qstat.8
qmail-queue.8
qmail-remote.8
qmail-rspawn.8
qmail-send.9
qmail-showctl.8
qmail-smtpd.8
qmail-start.9
qmail-tcpto.8
qmail-upgrade.9
qmail-users.9
qmail.7
qreceipt.1
splogger.8
tcp-env.1
qmail-clean.c
qmail-config.sh
qmail-getpw.c
qmail-hier.c
qmail-inject.c
qmail-local.c
qmail-lspawn.c
qmail-newu.c
qmail-pop3d.c
qmail-popup.c
qmail-pw2u.c
qmail-qmtpd.c
qmail-qread.c
qmail-qstat.sh
qmail-queue.c
qmail-remote.c
qmail-rspawn.c
qmail-send.c
qmail-showctl.c
qmail-smtpd.c
qmail-start.c
qmail-tcpto.c
spawn.c
dnscname.c
dnsfq.c
dnsip.c
dnsmxip.c
dnsptr.c
hostname.c
ipmeprint.c
tcp-env.c
sendmail.c
qlist.c
qreceipt.c
qsmhook.c
qbiff.c
forward.c
preline.c
predate.c
condredirect.c
maildirmake.c
maildir2mbox.c
maildirwatch.c
splogger.c
qail.sh
elq.sh
pinq.sh
qlist2.sh
qmail-upq.sh
datemail.sh
mailsubj.sh
qlx.h
constmap.h
constmap.c
dnsdoe.h
dnsdoe.c
fmtqfn.h
fmtqfn.c
gfrom.h
gfrom.c
myctime.h
myctime.c
newfield.h
newfield.c
qsutil.h
qsutil.c
readsubdir.h
readsubdir.c
received.h
received.c
tcpto.h
tcpto.c
tcpto_clean.c
trigger.h
trigger.c
triggerpull.h
triggerpull.c
trynpbg1.c
trysyslog.c
conf-cc
conf-ld
find-systype.sh
make-compile.sh
make-load.sh
make-makelib.sh
trycpp.c
warn-auto.sh
auto-str.c
auto-int.c
auto-int8.c
auto-gid.c
auto-uid.c
install.c
instcheck.c
alloc.3
alloc.h
alloc.c
alloc_re.c
case.3
case.h
case_diffb.c
case_diffs.c
case_lowerb.c
case_lowers.c
case_starts.c
cdb.3
cdb.h
cdb_hash.c
cdb_seek.c
cdb_unpack.c
cdbmake.h
cdbmake_add.c
cdbmake_hash.c
cdbmake_pack.c
cdbmss.h
cdbmss.c
coe.3
coe.h
coe.c
fd.h
fd_copy.3
fd_copy.c
fd_move.3
fd_move.c
fifo_make.3
fifo.h
fifo.c
trymkffo.c
fork.h1
fork.h2
tryvfork.c
now.3
now.h
now.c
open.h
open_append.c
open_excl.c
open_read.c
open_trunc.c
open_write.c
seek.h
seek_cur.c
seek_end.c
seek_set.c
seek_trunc.c
conf-qmail
auto_qmail.h
qmail.h
qmail.c
gen_alloc.h
gen_allocdefs.h
stralloc.3
stralloc.h
stralloc_eady.c
stralloc_pend.c
stralloc_copy.c
stralloc_opyb.c
stralloc_opys.c
stralloc_cat.c
stralloc_catb.c
stralloc_cats.c
stralloc_arts.c
strerr.h
strerr_sys.c
strerr_die.c
substdio.h
substdio.c
substdi.c
substdo.c
substdio_copy.c
subfd.h
subfderr.c
subfdouts.c
subfdout.c
subfdins.c
subfdin.c
readwrite.h
exit.h
timeoutconn.h
timeoutconn.c
timeoutread.h
timeoutread.c
timeoutwrite.h
timeoutwrite.c
remoteinfo.h
remoteinfo.c
uint32.h1
uint32.h2
tryulong32.c
wait.3
wait.h
wait_pid.c
wait_nohang.c
trywaitp.c
sig.h
sig_alarm.c
sig_block.c
sig_catch.c
sig_pause.c
sig_pipe.c
sig_child.c
sig_term.c
sig_hup.c
sig_misc.c
sig_bug.c
trysgact.c
trysgprm.c
env.3
env.h
env.c
envread.c
byte.h
byte_chr.c
byte_copy.c
byte_cr.c
byte_diff.c
byte_rchr.c
byte_zero.c
str.h
str_chr.c
str_cpy.c
str_diff.c
str_diffn.c
str_len.c
str_rchr.c
str_start.c
lock.h
lock_ex.c
lock_exnb.c
lock_un.c
tryflock.c
getln.3
getln.h
getln.c
getln2.3
getln2.c
sgetopt.3
sgetopt.h
sgetopt.c
subgetopt.3
subgetopt.h
subgetopt.c
error.3
error_str.3
error_temp.3
error.h
error.c
error_str.c
error_temp.c
fmt.h
fmt_str.c
fmt_strn.c
fmt_uint.c
fmt_uint0.c
fmt_ulong.c
scan.h
scan_ulong.c
scan_8long.c
scan_nbblong.c
slurpclose.h
slurpclose.c
quote.h
quote.c
hfield.h
hfield.c
headerbody.h
headerbody.c
token822.h
token822.c
control.h
control.c
datetime.3
datetime.h
datetime.c
datetime_un.c
prioq.h
prioq.c
date822fmt.h
date822fmt.c
dns.h
dns.c
trylsock.c
tryrsolv.c
ip.h
ip.c
ipalloc.h
ipalloc.c
select.h1
select.h2
trysysel.c
ndelay.h
ndelay.c
ndelay_off.c
direntry.3
direntry.h1
direntry.h2
trydrent.c
prot.h
prot.c
chkshsgr.c
warn-shsgr
tryshsgr.c
ipme.h
ipme.c
trysalen.c
maildir.5
maildir.h
maildir.c
tcp-environ.5

181
INSTALL Normal file
View file

@ -0,0 +1,181 @@
SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and
information generally), the qmail system comes with NO WARRANTY. It's
much more secure and reliable than sendmail, but that's not saying much.
Things you have to decide before starting:
* The qmail home directory, normally /var/qmail. To change this
directory, edit conf-qmail now.
* The names of the qmail users and the qmail groups. To change these
names, edit conf-users and conf-groups now.
Installation steps that won't interfere with sendmail:
1. Create the qmail home directory:
# mkdir /var/qmail
2. Read INSTALL.ids. You must set up the qmail group and the qmail
users before compiling the programs.
3. Compile the programs:
# make
4. Create the formatted man pages, *.0:
# make man
5. Create the qmail directory tree:
# make setup
6. Run instcheck to make sure it doesn't print any warnings:
# make check
7. Read INSTALL.ctl and FAQ. Minimal survival command:
# ./qmail-config
8. Read INSTALL.alias. Minimal survival command:
# (cd ~alias; touch .qmail-postmaster .qmail-mailer-daemon .qmail-root)
# chmod 644 ~alias/.qmail*
9. Read INSTALL.mbox.
10. Read qmail-upgrade.0. This is what your users will need to know
about the switch from sendmail to qmail.
Pre-upgrade tests:
11. Enable deliveries of messages injected into qmail:
# env - PATH="/var/qmail/bin:$PATH" \
qmail-start ./Mailbox splogger qmail &
Make sure to include the ./ in ./Mailbox.
12. Look for a
qmail: running
line in syslog. qmail-send always prints either ``cannot start'' or
``running''. (The big number is a splogger timestamp.)
13. Do a ps and look for the qmail daemons. There should be four of
them, all idle: qmail-send, running as qmails; qmail-lspawn, running
as root; qmail-rspawn, running as qmailr; and qmail-clean, running
as qmailq. You will also see the splogger process.
14. Local-local test: Send yourself an empty message. (Replace ``me''
with your username. Make sure to include the ``to:'' colon.)
% echo to: me | /var/qmail/bin/qmail-inject
The message will show up immediately in ~/Mailbox, and syslog will
show something like this:
qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666
qmail: starting delivery 1: msg 53 to local me@domain
qmail: delivery 1: success: did_1+0+0/
qmail: end msg 53
(53 is an inode number; 20345 is a process ID; your numbers will
probably be different.)
15. Local-error test: Send a message to a nonexistent local address.
% echo to: nonexistent | /var/qmail/bin/qmail-inject
qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666
qmail: starting delivery 2: msg 53 to local nonexistent@domain
qmail: delivery 2: failure: No_such_address.__#5.1.1_/
qmail: bounce msg 53 qp 20357
qmail: end msg 53
qmail: new msg 54
qmail: info msg 54: bytes 743 from <> qp 20357 uid 666
qmail: starting delivery 3: msg 54 to local me@domain
qmail: delivery 3: success: did_1+0+0/
qmail: end msg 54
You will now have a bounce message in ~/Mailbox.
16. Local-remote test: Send an empty message to your account on another
machine.
% echo to: me@wherever | /var/qmail/bin/qmail-inject
qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666
qmail: starting delivery 4: msg 53 to remote me@wherever
qmail: delivery 4: success: 1.2.3.4_accepted_message./...
qmail: end msg 53
There will be a pause between ``starting delivery'' and ``success'';
SMTP is slow. Check that the message is in your mailbox on the other
machine.
17. Local-postmaster test: Send mail to postmaster, any capitalization.
% echo to: POSTmaster | /var/qmail/bin/qmail-inject
Look for the message in ~alias/Mailbox.
18. Double-bounce test: Send a message with a completely bad envelope.
% /var/qmail/bin/qmail-inject -f nonexistent
To: unknownuser
Subject: testing
This is a test. This is only a test.
%
(Use end-of-file, not dot, to end the message.) Look for the double
bounce in ~alias/Mailbox.
19. Group membership test:
% cat > ~me/.qmail-groups
|groups >> MYGROUPS; exit 0
% /var/qmail/bin/qmail-inject me-groups < /dev/null
% cat ~me/MYGROUPS
MYGROUPS will show your normal gid and nothing else. (Under Solaris,
make sure to use /usr/ucb/groups; /usr/bin/groups is broken.)
Upgrading from sendmail to qmail:
20. Read INSTALL.boot. You must replace the sendmail invocation in your
boot scripts with an appropriate qmail invocation.
21. Kill the sendmail daemon. You should first kill -STOP the daemon; if
any children are running, you should kill -CONT, wait, kill -STOP
again, and repeat ad nauseam. If there aren't any children, kill
-TERM and then kill -CONT.
22. Replace sendmail with a link to qmail's ``sendmail'' wrapper:
# mv /usr/lib/sendmail /usr/lib/sendmail.bak
# ln -s /var/qmail/bin/sendmail /usr/lib/sendmail
23. Set up qmail-smtpd in /etc/inetd.conf (all on one line):
smtp stream tcp nowait qmaild /var/qmail/bin/tcp-env
tcp-env /var/qmail/bin/qmail-smtpd
Also comment out comsat in /etc/inetd.conf.
24. Reboot. (Or kill -HUP your inetd and make sure the qmail daemons
are running.)
25. Try to flush the sendmail queue:
# /usr/lib/sendmail.bak -q
You can safely run sendmail.bak -q (or even sendmail.bak -q15m)
while qmail is running. Do this until the sendmail queue is empty.
This may take several days.
26. Disable all the sendmail and binmail programs in your system. The
safest approach is to chmod 0 everything. Some locations to check:
/usr/sbin/sendmail, /usr/lib/sendmail.bak, /usr/lib/sendmail.mx,
/bin/mail, /usr/libexec/mail.local.
27. Make sure that ``mail'' still invokes a reasonable mailer. Under
SVR4 you may want to link mail to mailx.
Post-upgrade tests (can be done immediately after step 24):
28. SMTP server test: Forge some mail locally via SMTP.
% telnet 127.0.0.1 25
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
220 domain ESMTP
helo dude
250-domain
250-PIPELINING
250 8BITMIME
mail <me@domain>
250 ok
rcpt <me@domain>
250 ok
data
354 go ahead
Subject: testing
This is a test.
.
250 ok 812345679 qp 12345
quit
221 domain
Connection closed by foreign host.
%
Look for the message in your mailbox.
29. Remote-local test: Send yourself some mail from another machine.
30. Remote-error test: I think you can figure this one out.
31. UA test: Try sending mail, first to a local account, then to a
remote account, with your normal user agent.
32. Remote-postmaster test: Send mail from another machine to
PoStMaStEr@domain. Look for the message in ~alias/Mailbox.
That's it! To report success:
% ( echo 'First M. Last'; cat `cat SYSDEPS` ) \
| mail djb-qst@koobera.math.uic.edu
Replace First M. Last with your name. If you have questions about qmail,
contact qmail@pobox.com.

40
INSTALL.alias Normal file
View file

@ -0,0 +1,40 @@
qmail lets each user control all addresses of the form user-anything.
Addresses that don't start with a username are controlled by a special
user, alias. Delivery instructions for foo go into ~alias/.qmail-foo;
delivery instructions for user-foo go into ~user/.qmail-foo. See
qmail-upgrade.0 and dot-qmail.0 for the full story.
qmail doesn't have any built-in support for /etc/aliases. If you have a
big /etc/aliases and you'd like to keep it, install the qmsmac package,
available separately. /etc/aliases should already include the aliases
discussed below---Postmaster, MAILER-DAEMON, and root.
If you don't have a big /etc/aliases, you'll find it easier to use
qmail's native alias mechanism. Here's a checklist of aliases you should
set up right now.
* Postmaster. You're not an Internet citizen if this address doesn't
work. Simply touch (and chmod 644) ~alias/.qmail-postmaster; any mail
for Postmaster will be delivered to ~alias/Mailbox.
* MAILER-DAEMON. Not required, but users sometimes respond to bounce
messages. Touch (and chmod 644) ~alias/.qmail-mailer-daemon.
* root. Under qmail, root never receives mail. Your system may generate
mail messages to root every night; if you don't have an alias for root,
those messages will bounce. (They'll end up double-bouncing to the
postmaster.) Set up an alias for root in ~alias/.qmail-root. .qmail
files are similar to .forward files, but beware that they are strictly
line-oriented---see dot-qmail.0 for details.
* Other non-user accounts. Under qmail, non-user accounts don't get
mail; ``user'' means a non-root account that owns ~account. Set up
aliases for any non-user accounts that normally receive mail.
Note that special accounts such as ftp, www, and uucp should always have
home directories owned by root.
* Default. If you want, you can touch ~alias/.qmail-default to catch
everything else. Beware: this will also catch typos and other addresses
that should probably be bounced instead. It won't catch addresses that
start with a user name---the user can set up his own ~/.qmail-default.

16
INSTALL.boot Normal file
View file

@ -0,0 +1,16 @@
The qmail daemons have to be restarted whenever your system reboots.
Meanwhile, sendmail doesn't have to be started any more. Here's what you
should do.
Find sendmail in your boot scripts. It's usually in either /etc/rc or
/etc/init.d/sendmail. It looks like
sendmail -bd -q15m
-q15m means it should run the queue every 15 minutes; you may see a
different number. Comment out this line, and replace it with
env - PATH="/var/qmail/bin:$PATH" \
csh -cf 'qmail-start ./Mailbox splogger qmail &'
That's it. (Make sure you include the ./ and the &.)

29
INSTALL.ctl Normal file
View file

@ -0,0 +1,29 @@
As you've seen, qmail has essentially no pre-compilation configuration.
You should never have to recompile it unless you want to change the
qmail home directory, usernames, or uids.
qmail does allow quite a bit of easy post-installation configuration. If
you care how your machine greets other machines via SMTP, for example,
you can put an appropriate line into /var/qmail/control/smtpgreeting.
But this is all optional---if control/smtpgreeting doesn't exist, qmail
will do something reasonable by default. You shouldn't worry much about
configuration right now. You can always come back and tune things later.
There's one big exception. You MUST tell qmail your hostname. The easy
way to do this is to run the qmail-config script:
# ./qmail-config
qmail-config finds your fully-qualified hostname in DNS and puts it into
control/me. It also selects good defaults for a few other controls.
(Why doesn't qmail do these lookups on the fly? This was a deliberate
design decision. qmail does all its local functions---header rewriting,
checking if a recipient is local, etc.---without talking to the network.
The point is that qmail can continue accepting and delivering local mail
even if your network connection goes down.)
Next, read through FAQ for information on setting up optional features
like masquerading. If you really want to learn right now what all the
configuration possibilities are, see qmail-control.0.

59
INSTALL.ids Normal file
View file

@ -0,0 +1,59 @@
Here's how to set up the qmail groups and the qmail users.
On some systems there are commands that make this easy. Solaris:
# groupadd nofiles
# useradd -g nofiles -d /var/qmail/alias alias
# useradd -g nofiles -d /var/qmail qmaild
# useradd -g nofiles -d /var/qmail qmaill
# useradd -g nofiles -d /var/qmail qmailp
# groupadd qmail
# useradd -g qmail -d /var/qmail qmailq
# useradd -g qmail -d /var/qmail qmailr
# useradd -g qmail -d /var/qmail qmails
BSDI 2.0:
# addgroup nofiles
# adduser -g nofiles -H/var/qmail/alias -G,,, -s/dev/null -P'*' alias
# adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaild
# adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmaill
# adduser -g nofiles -H/var/qmail -G,,, -s/dev/null -P'*' qmailp
# addgroup qmail
# adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailq
# adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmailr
# adduser -g qmail -H/var/qmail -G,,, -s/dev/null -P'*' qmails
AIX:
# mkgroup -A nofiles
# mkuser pgrp=nofiles home=/var/qmail/alias shell=/bin/true alias
# mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaild
# mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmaill
# mkuser pgrp=nofiles home=/var/qmail shell=/bin/true qmailp
# mkgroup -A qmail
# mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailq
# mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmailr
# mkuser pgrp=qmail home=/var/qmail shell=/bin/true qmails
On other systems, you will have to edit /etc/group and /etc/passwd
manually. First add two new lines to /etc/group, something like
qmail:*:2107:
nofiles:*:2108:
where 2107 and 2108 are different from the other gids in /etc/group.
Next (using vipw) add six new lines to /etc/passwd, something like
alias:*:7790:2108::/var/qmail/alias:/bin/true
qmaild:*:7791:2108::/var/qmail:/bin/true
qmaill:*:7792:2108::/var/qmail:/bin/true
qmailp:*:7793:2108::/var/qmail:/bin/true
qmailq:*:7794:2107::/var/qmail:/bin/true
qmailr:*:7795:2107::/var/qmail:/bin/true
qmails:*:7796:2107::/var/qmail:/bin/true
where 7790 through 7796 are _new_ uids, 2107 is the qmail gid, and 2108
is the nofiles gid. Make sure you use the nofiles gid for qmaild,
qmaill, qmailp, and alias, and the qmail gid for qmailq, qmailr, and
qmails.

112
INSTALL.mbox Normal file
View file

@ -0,0 +1,112 @@
The qmail package includes a local delivery agent, qmail-local, which
provides user-controlled mailing lists, cross-host alias loop detection,
and many other important qmail features.
There's one part of qmail-local that you need to know about right now:
qmail-local doesn't support an insecure central mail spool. It delivers
mail by default into ~user/Mailbox (in mbox format).
This file explains what you should do to deal with this change. It also
points out some reasons that you might want to make an even bigger
change, switching from mbox format to a new format, maildir.
If you desperately don't want to change anything, see INSTALL.qsmhook.
Contents:
1. Throw away /usr/spool/mail!
2. The trouble with mbox
3. Sun's Network F_ail_u_re System
1. Throw away /usr/spool/mail!
/usr/spool/mail, often called /var/spool/mail or /var/mail, is a
security disaster. A user's mailbox belongs in his home directory, not a
shared directory. Even if you don't install qmail, you should destroy
/usr/spool/mail. This takes four steps:
A. Convince your local mailer to deliver to ~user/Mailbox. If you're
using something like procmail, this is easy---just change SYSTEM_MBOX
in config.h. If you're installing qmail, you don't have to do
anything. Otherwise, take a look at hlfsd from
ftp.cs.columbia.edu/pub/amd.
B. Move each /usr/spool/mail/user to ~user/Mailbox. For safety, do
this in single-user mode---you don't want to risk corrupting
mailboxes. (qmail makes it easy to turn off deliveries temporarily:
just kill the qmail-send daemon. But you aren't running qmail yet.)
When you're done, remove /usr/spool/mail.
C. Put ``setenv MAIL $HOME/Mailbox'' in your system-wide .cshrc,
``MAIL=$HOME/Mailbox; export MAIL'' in your system-wide .profile,
``inbox-path=Mailbox'' in your system-wide pine.conf. If you're using
qpopper 2.2, you'll have to recompile with -DHOMEDIRMAIL in CFLAGS
and with /.mail changed to /Mailbox in pop_dropcopy.c. If you're
using elm on a multiuser system, you'll have to recompile elm with
"mailbox" changed to "Mailbox" around line 388 of newmbox.c.
D. Announce the change.
Some vendors, in a misguided attempt to solve the security problems of
/usr/spool/mail, have made all MUAs (e.g., /usr/ucb/Mail) setgid mail.
After you get rid of /usr/spool/mail, you can also disable those
setgid-mail bits.
2. The trouble with mbox
The mbox format---the format of ~user/Mailbox, understood by BSD Mail
and lots of other MUAs---is inherently unreliable.
Think about it: what happens if the system crashes while a program is
appending a new message to ~user/Mailbox? The message will be truncated.
Even worse, if it was truncated in the middle of a line, it will end up
being merged with the next message! Sure, the mailer understands that it
wasn't successful, so it'll try delivering the message again later, but
it can't fix your corrupted mbox.
Other formats, such as mh folders, are just as unreliable.
qmail supports maildir, a crashproof format for incoming mail messages.
maildir is fast and easy for MUAs to use. Even better, maildir works
wonders over NFS---see below.
I don't want to cram maildir down people's throats, so it's not the
default. Nevertheless, I encourage you to start asking for maildir
versions of your favorite MUAs, and to switch over to maildir as soon as
you can.
WARNING: qmail uses flock() to lock ~user/Mailbox. This agrees with the
modern mail.local locking choice. If your MUA doesn't use flock(), your
best bet is to switch to maildir, and to set up synchronous maildir2mbox
execution, as described below.
3. Sun's Network F_ail_u_re System
Anyone who tells you that mail can be safely delivered in mbox format
over NFS is pulling your leg---as explained above, mbox format is
inherently unreliable even on a single machine.
Anyway, NFS is the most unreliable computing environment ever invented,
and qmail doesn't even pretend to support mbox over NFS.
You should switch to maildir, which works fine over NFS without any
locking. You can safely read your mail over NFS if it's in maildir
format. Any number of machines can deliver mail to you at the same time.
(On the other hand, for efficiency, it's better to get NFS out of the
picture---your mail should be delivered on the server that contains your
home directory.)
Here's how to set up qmail to use maildir for your incoming mail:
% maildirmake $HOME/Maildir
% echo ./Maildir/ > ~/.qmail
Make sure you include the trailing slash on Maildir/.
Until your MUA supports maildir, you'll probably want to convert maildir
format to (gaaack) mbox format. I've supplied a maildir2mbox utility
that does the trick, along with some tiny qail and elq and pinq wrappers
that call maildir2mbox before calling Mail or elm or pine.

53
INSTALL.qsmhook Normal file
View file

@ -0,0 +1,53 @@
You can set up qmail to use the same local delivery agent as sendmail,
through a mechanism called qsmhook. This file says how.
Before you do this, may I ask why? If you simply don't want the hassle
of moving user mailboxes from /usr/spool/mail to ~, please reconsider---
/usr/spool/mail has always been a big security problem. See, for
example, CERT advisory 95:02.
If you're trying to preserve /etc/aliases and ~user/.forward, you're
looking the wrong way---those are handled by sendmail internally, not by
the local delivery agent. You can use your old /etc/aliases with qmail
by installing the qmsmac package.
Perhaps you've set up an advanced agent like procmail. But most people
use procmail for nothing more than sorting mail into several mailboxes;
and that's much easier with qmail's local forwarding mechanism, which
gives each user control over user-anything. If you have a few users who
really do need procmail, they can easily run procmail from their own
.qmail files.
Do you still want to set up qsmhook? Send me some e-mail and let me know
why. Perhaps I can provide something for you in a future qmail release.
Here's what to do. First, tack ``:alias'' onto the end of each address
in /var/qmail/control/locals, and put the results into
/var/qmail/control/virtualdomains. For example, if control/locals has
localhost
silverton.berkeley.edu
then control/virtualdomains should now have (without extra spaces)
localhost:alias
silverton.berkeley.edu:alias
Second, cp /dev/null control/locals.
Third, put a line into ~alias/.qmail-default, based on sendmail's Mlocal
line. For example, if sendmail.cf has
Mlocal, P=/bin/mail, F=lsDFMPrmn, S=10, R=20, A=mail -d $u
then ~alias/.qmail-default should have
|qsmhook -x alias- -lsDFMPmn /bin/mail -r %g -d %u
As another example, if sendmail.cf has
Mlocal, P=/usr/lib/mail.local, F=flsSDFMmnP, S=10, R=20, A=mail.local -d $u
then ~alias/.qmail-default should have
|qsmhook -x alias- -lsDFMmnP /usr/lib/mail.local -f %g -d %u

155
INTERNALS Normal file
View file

@ -0,0 +1,155 @@
1. Overview
Here's the data flow in the qmail suite:
qmail-smtpd --- qmail-queue --- qmail-send --- qmail-rspawn --- qmail-remote
/ | \
qmail-inject _/ qmail-clean \_ qmail-lspawn --- qmail-local
Every message is added to a central queue directory by qmail-queue.
qmail-queue is invoked as needed, usually by qmail-inject for locally
generated messages, qmail-smtpd for messages received through SMTP,
qmail-local for forwarded messages, or qmail-send for bounce messages.
Every message is then delivered by qmail-send, in cooperation with
qmail-lspawn and qmail-rspawn, and cleaned up by qmail-clean. These four
programs are long-running daemons.
The queue is designed to be crashproof, provided that the underlying
filesystem is crashproof. All cleanups are handled by qmail-send and
qmail-clean without human intervention. See section 6 for more details.
2. Queue structure
Each message in the queue is identified by a unique number, let's say
457. The queue is organized into several directories, each of which may
contain files related to message 457:
mess/457: the message
todo/457: the envelope: where the message came from, where it's going
intd/457: the envelope, under construction by qmail-queue
info/457: the envelope sender address, after preprocessing
local/457: local envelope recipient addresses, after preprocessing
remote/457: remote envelope recipient addresses, after preprocessing
bounce/457: permanent delivery errors
Here are all possible states for a message. + means a file exists; -
means it does not exist; ? means it may or may not exist.
S1. -mess -intd -todo -info -local -remote -bounce
S2. +mess -intd -todo -info -local -remote -bounce
S3. +mess +intd -todo -info -local -remote -bounce
S4. +mess ?intd +todo ?info ?local ?remote -bounce (queued)
S5. +mess -intd -todo +info ?local ?remote ?bounce (preprocessed)
Guarantee: If mess/457 exists, it has inode number 457.
3. How messages enter the queue
To add a message to the queue, qmail-queue first creates a file in a
separate directory, pid/, with a unique name. The filesystem assigns
that file a unique inode number. qmail-queue looks at that number, say
457. By the guarantee above, message 457 must be in state S1.
qmail-queue renames pid/whatever as mess/457, moving to S2. It writes
the message to mess/457. It then creates intd/457, moving to S3, and
writes the envelope information to intd/457.
Finally qmail-queue creates a new link, todo/457, for intd/457, moving
to S4. At that instant the message has been successfully queued, and
qmail-queue leaves it for further handling by qmail-send.
qmail-queue starts a 24-hour timer before touching any files, and
commits suicide if the timer expires.
4. How queued messages are preprocessed
Once a message has been queued, qmail-send must decide which recipients
are local and which recipients are remote. It may also rewrite some
recipient addresses.
When qmail-send notices todo/457, it knows that message 457 is in S4. It
removes info/457, local/457, and remote/457 if they exist. Then it reads
through todo/457. It creates info/457, possibly local/457, and possibly
remote/457. When it is done, it removes intd/457. The message is still
in S4 at this point. Finally qmail-send removes todo/457, moving to S5.
At that instant the message has been successfully preprocessed.
5. How preprocessed messages are delivered
Messages at S5 are handled as follows. Each address in local/457 and
remote/457 is marked either NOT DONE or DONE.
DONE: The message was successfully delivered, or the last delivery
attempt met with permanent failure. Either way, qmail-send
should not attempt further delivery to this address.
NOT DONE: If there have been any delivery attempts, they have all
met with temporary failure. Either way, qmail-send should
try delivery in the future.
qmail-send may at its leisure try to deliver a message to a NOT DONE
address. If the message is successfully delivered, qmail-send marks the
address as DONE. If the delivery attempt meets with permanent failure,
qmail-send first appends a note to bounce/457, creating bounce/457 if
necessary; then it marks the address as DONE.
qmail-send may handle bounce/457 at any time, as follows: it (1) injects
a new bounce message, created from bounce/457 and mess/457; (2) deletes
bounce/457.
When all addresses in local/457 are DONE, qmail-send deletes local/457.
Same for remote/457.
When local/457 and remote/457 are gone, qmail-send eliminates the
message, as follows. First, if bounce/457 exists, qmail-send handles it
as described above. Once bounce/457 is definitely gone, qmail-send
deletes info/457, moving to S2, and finally mess/457, moving to S1.
6. Cleanups
If the computer crashes while qmail-queue is trying to queue a message,
or while qmail-send is eliminating a message, the message may be left in
state S2 or S3.
When qmail-send sees a message in state S2 or S3---other than one
it is currently eliminating!---where mess/457 is more than 36 hours old,
it deletes intd/457 if that exists, then deletes mess/457. Note that any
qmail-queue handling the message must be dead.
Similarly, when qmail-send sees a file in the pid/ directory that is
more than 36 hours old, it deletes it.
Cleanups are not necessary if the computer crashes while qmail-send is
delivering a message. At worst a message may be delivered twice. (There
is no way for a distributed mail system to eliminate the possibility of
duplication. What if an SMTP connection is broken just before the server
acknowledges successful receipt of the message? The client must assume
the worst and send the message again. Similarly, if the computer crashes
just before qmail-send marks a message as DONE, the new qmail-send must
assume the worst and send the message again. The usual solutions in the
database literature---e.g., keeping log files---amount to saying that
it's the recipient's computer's job to discard duplicate messages.)
7. Further notes
Currently info/457 serves two purposes: first, it records the envelope
sender; second, its modification time is used to decide when a message
has been in the queue too long. In the future info/457 may store more
information. Any non-backwards-compatible changes will be identified by
version numbers.
When qmail-queue has successfully placed a message into the queue, it
pulls a trigger offered by qmail-send. Here is the current triggering
mechanism: lock/trigger is a named pipe. Before scanning todo/,
qmail-send opens lock/trigger O_NDELAY for reading. It then selects for
readability on lock/trigger. qmail-queue pulls the trigger by writing a
byte O_NDELAY to lock/trigger. This makes lock/trigger readable and
wakes up qmail-send. Before scanning todo/ again, qmail-send closes and
reopens lock/trigger.

2181
Makefile Normal file

File diff suppressed because it is too large Load diff

199
README Normal file
View file

@ -0,0 +1,199 @@
qmail 1.01
19970413
Copyright 1997
D. J. Bernstein, qmail@pobox.com
qmail is a secure, reliable, efficient, simple message transfer agent.
It is meant as a replacement for the entire sendmail-binmail system on
typical Internet-connected UNIX hosts. See BLURB, BLURB2, BLURB3, and
BLURB4 for more detailed advertisements.
INSTALL says how to set up and test qmail. If you're upgrading from
1.00, read UPGRADE instead.
See http://pobox.com/~djb/qmail.html for other qmail-related software
and a pointer to the qmail mailing list.
Other documentation here: RFC* explain solutions to several Internet
mail problems; all of these except RFCMXPS are implemented in qmail.
CHANGES and THANKS show how qmail has changed since it was first
released. SECURITY, INTERNALS, THOUGHTS, and TODO record many of the
qmail design decisions.
The rest of this file is a list of systypes where various versions of
qmail have been reported to work. 0.90 was the first gamma version. 0.96
was the final gamma version. 1.00 had exactly the same code as 0.96. To
see your systype, make systype; cat systype.
1.00: a.ux-3.0-svr2-:-:-:mc68030-:- (tnx RF)
0.96: aix-3-2-:-:-:000011216700-:- (tnx JLB)
0.91: aix-3-2-:-:-:000109257500-:-
0.96: aix-4-1-:-:-:000088581000-:- (tnx HJB)
0.95: aix-4-1-:-:-:00061176a600-:- (tnx JS)
1.00: aix-4-1-:-:-:00910033a000-:- (tnx K2J)
0.91: aix-4-2-:-:-:006030934c00-:-
1.00: bsd.os-2.0.1-:i386-:-:i486-:- (tnx KR)
0.93: bsd.os-2.0.1-:i386-:-:pentium-:-
0.96: bsd.os-2.1-:i386-:-:-:- (tnx DAR)
1.00: bsd.os-2.1-:i386-:-:i486-:- (tnx RJC)
0.96: bsd.os-2.1-:i386-:-:pentium-:- (tnx DAR)
0.96: dgux-5.4r2.01-generic-:-:-:aviion-:- (tnx HWM)
0.93: dgux-5.4r3.10-generic-:-:-:aviion-:- (tnx HWM)
0.91: freebsd-2.0.5-release-:i386-:-:-:- (tnx TG)
0.92: freebsd-2.1-stable-:i386-:-:pentium.815\100-:-
0.90: freebsd-2.1.0-release-:i386-:-:cy486dlc-:- (tnx G2A)
1.00: freebsd-2.1.0-release-:i386-:-:i486-dx2-:- (tnx JLB)
1.00: freebsd-2.1.0-release-:i386-:-:i486-dx-:- (tnx chrisj=???)
0.96: freebsd-2.1.0-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MBS)
0.95: freebsd-2.1.5-release-:i386-:-:-:- (tnx NAA)
0.96: freebsd-2.1.5-release-:i386-:-:i486-dx-:- (tnx FN)
0.95: freebsd-2.1.5-release-:i386-:-:pentium.510\60.or.567\66-:- (tnx M2L)
0.95: freebsd-2.1.5-release-:i386-:-:pentium.735\90-:- (tnx AG)
0.92: freebsd-2.1.5-stable-:i386-:-:pentium.735\90.or.815\100-:- (tnx FE)
1.00: freebsd-2.1.6-release-:i386-:-:-:- (tnx TM)
0.96: freebsd-2.1.6-release-:i386-:-:Pentium-Pro.150-:- (tnx CH)
0.96: freebsd-2.1.6-release-:i386-:-:i486-dx-:- (tnx HCJ)
0.95: freebsd-2.1.6-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx M2L)
0.96: freebsd-2.1.6.1-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx MF)
1.00: freebsd-2.1.7-release-:i386-:-:pentium.735\90.or.815\100-:- (tnx JBB)
0.95: freebsd-2.2-beta_a-:i386-:-:i486-dx2-:- (tnx DA)
0.91: freebsd-2.2-current-:i386-:-:i486-dx-:- (tnx DC)
1.00: freebsd-2.2-release-:i386-:-:-:- (tnx MT)
1.00: freebsd-2.2.1-release-:i386-:-:-:- (tnx TM)
1.00: freebsd-2.2.1-release-:i386-:-:i486-dx2-:- (tnx BR)
1.00: freebsd-2.2.1-release-:i386-:-:pentium-:- (tnx IW)
0.92: freebsd-3.0-current-:i386-:-:pentium-:- (tnx J2M)
1.00: hp-ux-b.09.00-a-:-:-:9000.360-:- (tnx VV)
0.91: hp-ux-b.10.01-a-:-:-:9000.712-:- (tnx S2R)
0.94: hp-ux-b.10.01-a-:-:-:9000.715-:- (tnx BG)
0.91: hp-ux-b.10.01-a-:-:-:9000.801-:- (tnx S2T)
0.93: irix-5.3-02091401-:-:-:ip22-:- (tnx PW)
0.91: irix-5.3-11091811-:sgi-:-:ip19-:- (tnx BS)
0.96: irix-5.3-11091812-:-:-:ip22-:- (tnx JL)
1.00: irix-6.2-03131015-:-:-:ip22-:- (tnx SAS)
0.92: linux-1.2.13-:i386-:-:i386-:- (tnx RN)
1.00: linux-1.2.13-:i386-:-:i486-:- (tnx RF)
0.96: linux-1.2.13-:i386-:-:pentium-:- (tnx MEE)
0.91: linux-1.3.90-:alpha-:-:alpha-:- (tnx ES)
0.93: linux-1.99.6-:i386-:-:pentium-:- (tnx TG)
0.94: linux-2.0.0-:i386-:-:i486-:- (tnx PCO)
1.00: linux-2.0.0-:i386-:-:pentium-:- (tnx JJR)
0.91: linux-2.0.1-:alpha-:-:alpha-:- (tnx BET)
0.90: linux-2.0.1-:i386-:-:i486-:- (tnx DF)
0.93: linux-2.0.1-:i386-:-:pentium-:- (tnx FW)
0.95: linux-2.0.6-:i386-:-:pentium-:-
1.00: linux-2.0.6-:i386-:-:ppro-:- (tnx MR)
0.96: linux-2.0.7-:i386-:-:i486-:- (tnx TLM)
0.90: linux-2.0.7-:i386-:-:pentium-:- (tnx K2J)
0.94: linux-2.0.8-:i386-:-:i486-:- (tnx EP)
0.90: linux-2.0.9-:i386-:-:pentium-:- (tnx JM)
0.90: linux-2.0.10-:i386-:-:i486-:- (tnx JL)
0.90: linux-2.0.10-:i386-:-:pentium-:- (tnx S2R)
0.90: linux-2.0.11-:i386-:-:i486-:- (tnx ET)
0.92: linux-2.0.12-:i386-:-:i486-:- (tnx TRR)
0.90: linux-2.0.12-:i386-:-:ppro-:- (tnx SS)
0.96: linux-2.0.13-:i386-:-:pentium-:- (tnx BW)
0.90: linux-2.0.14-:i386-:-:pentium-:- (tnx PS)
0.91: linux-2.0.15-:i386-:-:pentium-:- (tnx CL)
0.90: linux-2.0.16-:i386-:-:i486-:- (tnx AP)
0.91: linux-2.0.17-:i386-:-:i486-:- (tnx JL)
0.91: linux-2.0.17-:i386-:-:pentium-:- (tnx SS)
0.95: linux-2.0.18-:i386-:-:i386-:- (tnx RN)
1.00: linux-2.0.18-:i386-:-:i486-:- (tnx JMS)
1.00: linux-2.0.18-:i386-:-:pentium-:- (tnx D2S)
0.92: linux-2.0.19-:alpha-:-:alpha-:- (tnx BET)
0.92: linux-2.0.20-:i386-:-:pentium-:- (tnx RJH)
0.92: linux-2.0.21-:i386-:-:i486-:- (tnx DCC)
0.91: linux-2.0.21-:i386-:-:pentium-:- (tnx root@contact=???)
0.92: linux-2.0.22-:i386-:-:i486-:- (tnx root@attila=???)
1.00: linux-2.0.22-:i386-:-:pentium-:- (tnx MDI)
1.00: linux-2.0.23-:i386-:-:i486-:- (tnx B2L)
0.93: linux-2.0.23-:i386-:-:pentium-:- (tnx IW)
0.93: linux-2.0.24-:i386-:-:i486-:- (tnx root@cerberus=???)
1.00: linux-2.0.24-:i386-:-:pentium-:- (tnx VV)
0.96: linux-2.0.25-:i386-:-:i486-:- (tnx BDB)
1.00: linux-2.0.25-:i386-:-:pentium-:- (tnx AP)
0.93: linux-2.0.25-:i386-:-:ppro-:- (tnx CS)
0.93: linux-2.0.26-:i386-:-:i486-:- (tnx blynch=???)
0.96: linux-2.0.26-:i386-:-:pentium-:- (tnx ESM)
0.93: linux-2.0.26-:i386-:-:ppro-:- (tnx modus@enews=???)
1.00: linux-2.0.27-:-:-:sparc-:- (tnx SVD)
1.00: linux-2.0.27-:i386-:-:i486-:- (tnx FPL)
1.00: linux-2.0.27-:i386-:-:pentium-:- (tnx root@bullwinkle=???)
1.00: linux-2.0.27-:i386-:-:ppro-:- (tnx DE)
1.00: linux-2.0.28-:i386-:-:i486-:- (tnx H2S)
1.00: linux-2.0.28-:i386-:-:pentium-:- (tnx root@duggy=???)
1.00: linux-2.0.28-:i386-:-:ppro-:- (tnx KJS)
1.00: linux-2.0.29-:i386-:-:i486-:- (tnx PK)
1.00: linux-2.0.29-:i386-:-:pentium-:- (tnx CL)
1.00: linux-2.0.29-:i386-:-:ppro-:- (tnx MMM)
0.92: linux-2.1.1-:i386-:-:i486-:- (tnx nick@sga2=???)
0.92: linux-2.1.7-:i386-:-:ppro-:- (tnx JL)
0.92: linux-2.1.9-:i386-:-:pentium-:- (tnx AC)
0.92: linux-2.1.10-:i386-:-:ppro-:- (tnx S3T)
0.96: linux-2.1.13-:i386-:-:i486-:- (tnx ML)
0.93: linux-2.1.13-:i386-:-:ppro-:- (tnx JL)
0.96: linux-2.1.14-:i386-:-:pentium-:- (tnx SCW)
0.96: linux-2.1.23-:i386-:-:pentium-:- (tnx JF)
0.96: linux-2.1.25-:i386-:-:i486-:- (tnx JBF)
0.96: linux-2.1.25-:i386-:-:pentium-:- (tnx UO)
1.00: linux-2.1.26-:i386-:-:i486-:- (tnx DK)
1.00: linux-2.1.27-:i386-:-:pentium-:- (tnx JF)
1.00: linux-2.1.28-:i386-:-:pentium-:- (tnx RGS)
1.00: linux-2.1.29-:i386-:-:i486-:- (tnx SJW)
0.93: netbsd-1.2-:amiga-:-:amiga.4000.(m68040.cpu/mmu/fpu)-:- (tnx OS)
0.96: netbsd-1.2-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx GH)
0.91: netbsd-1.2-:mac68k-:-:apple.macintosh.se/30..(68030)-:- (tnx hauke=???)
0.92: netbsd-1.2b-:i386-:-:-:- (tnx MG)
0.93: netbsd-1.2b-:i386-:-:pentium.(genuineintel.586-class.cpu)-:- (tnx MG)
0.96: netbsd-1.2c-:pmax-:-:-:- (tnx JLW)
0.92: nextstep-3.3-:hppa-:-:7100lc-:-
0.92: nextstep-3.3-:i386-:-:pentium-:-
0.91: openbsd-1.2-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
0.95: openbsd-2.0-:openbsd.i386-:-:i386-:- (tnx JPH)
0.96: openbsd-2.0-:openbsd.m68k-:-:mac68k-:- (tnx AKB)
1.00: openbsd-2.0-hoth#0-:openbsd.i386-:-:i386-:- (tnx MBS)
1.00: openbsd-2.0-mr_potatoe_head#2-:openbsd.i386-:-:i386-:- (JJMK)
0.96: openbsd-2.0-puma#1-:openbsd.m68k-:-:mac68k-:- (tnx AB)
0.92: osf1-v3.0-347-:-:-:alpha-:- (tnx B2H)
0.92: osf1-v4.0-386-:-:-:alpha-:- (tnx WW)
0.96: sco_sv-3.2-2-:-:-:i386-:- (tnx DEH)
0.96: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4c-:sun4c- (tnx MBS)
0.91: sunos-4.1.3_u1-1-:sparc-:sun4-:sun4m-:sun4m- (tnx IS)
0.92: sunos-4.1.3_u1-2-:sparc-:sun4-:sun4c-:sun4c- (tnx ANR)
0.93: sunos-4.1.3_u1-4-:sparc-:sun4-:sun4m-:sun4m- (tnx J2B)
1.00: sunos-4.1.3_u1-4-:unknown-:sun4-:sun4m-:sun4m- (tnx J2B)
0.91: sunos-4.1.3_u1-9-:sparc-:sun4-:sun4m-:sun4m- (tnx TA)
0.91: sunos-4.1.4-2-:sparc-:sun4-:sun4c-:sun4c- (tnx ST)
0.95: sunos-4.1.4-2-:sparc-:sun4-:sun4m-:sun4m- (tnx TG)
0.95: sunos-4.1c-4.1.3-:sparc-:sun4-:sun4-:s4000- (tnx DBK)
0.91: sunos-5.4-generic-:sparc-:sun4-:sun4m-:sun4m-
0.96: sunos-5.4-generic_101945-10-:sparc-:sun4-:sun4m-:sun4m- (tnx W2K)
0.93: sunos-5.4-generic_101945-23-:-:sun4-:sun4m-:sun4m- (tnx JP)
0.92: sunos-5.4-generic_101945-23-:sparc-:-:sun4c-:- (tnx BH)
1.00: sunos-5.4-generic_101945-34-:sparc-:sun4-:sun4m-:sun4m- (tnx ACB)
0.92: sunos-5.4-generic_101945-36-:-:sun4-:sun4c-:sun4c- (tnx JP)
0.95: sunos-5.4-generic_101945-43-:-:sun4-:sun4m-:sun4m- (tnx JP)
0.90: sunos-5.4-generic_101946-07-:-:i86pc-:i86pc-:i86pc- (tnx CR)
0.96: sunos-5.4-generic_101946-35-:i386-:i86pc-:i86pc-:i86pc- (tnx CK)
0.96: sunos-5.5-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx M2G)
0.93: sunos-5.5-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx CG)
1.00: sunos-5.5-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx SG)
1.00: sunos-5.5-generic_103093-02-:sparc-:sun4-:sun4m-:sun4m- (tnx RF)
0.91: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4d-:sun4d- (tnx M3S)
0.96: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4m-:sun4m- (tnx RDM)
0.91: sunos-5.5-generic_103093-03-:sparc-:sun4-:sun4u-:sun4u- (tnx olav=???)
0.92: sunos-5.5-generic_103093-05-:sparc-:sun4-:sun4m-:sun4m- (tnx KE)
0.93: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4d-:sun4d- (tnx KT)
1.00: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4m-:sun4m- (tnx TEE)
0.94: sunos-5.5-generic_103094-01-:i386-:i86pc-:i86pc-:i86pc- (tnx MD)
0.91: sunos-5.5-generic_103094-03-:i386-:i86pc-:i86pc-:i86pc- (tnx ding=???)
1.00: sunos-5.5.1-generic-:i386-:i86pc-:i86pc-:i86pc- (tnx DML)
1.00: sunos-5.5.1-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx JMT)
1.00: sunos-5.5.1-generic-:sparc-:sun4-:sun4u-:sun4u- (tnx TH)
0.96: sunos-5.5.1-generic_103640-02-:sparc-:sun4-:sun4m-:sun4m- (tnx SGC)
0.95: sunos-5.5.1-generic_103640-03-:sparc-:sun4-:sun4m-:sun4m- (tnx MRG)
1.00: sunos-5.5.1-generic_103640-03-:sparc-:sun4-:sun4u-:sun4u- (tnx EG)
1.00: sunos-5.5.1-generic_103640-05-:sparc-:sun4-:sun4m-:sun4m- (tnx L2L)
0.96: sunos-5.5.1-generic_patch-:i386-:i86pc-:i86pc-:i86pc- (tnx DK)
0.96: ultrix-4.3-1-:pmax-:-:risc-:- (tnx YF)

37
RFCHCSC Normal file
View file

@ -0,0 +1,37 @@
The Hash Convention For Mail System Status Codes (HCMSSC)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
RFC 1893 defines codes for mail delivery failures. For example,
code 5.1.1 means that the specified mailbox does not exist.
The qmail package sprays these codes all over the place, by adding a
code to the text of every error message, preceded by a hash mark and
surrounded by parentheses. It avoids using hash marks elsewhere.
2. Examples
Here is a typical HCMSSC SMTP error message:
421 load average too high, please come back later (#4.3.2)
Here is part of a typical HCMSSC bounce message:
<mail-loop@silverton.berkeley.edu>:
This is looping; it already has my Delivered-To line. (#5.7.1)
But qmail doesn't use HCMSSC when it repeats another MTA's error
message:
<foo@heaven.af.mil>:
127.3.4.5 does not like recipient.
Remote host said: 550 <foo>... User unknown (#5.1.1)
3. Security considerations
Don't take drastic action upon seeing "(#"; it might not be HCMSSC.

338
RFCLOOPS Normal file
View file

@ -0,0 +1,338 @@
Tools in the war on mail loops
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
An automailer means any program that receives a mail message and
automatically sends one or more mail messages. This term is meant to
include not only a mail-based server, such as a mailing list exploder
or a vacation program, but also an SMTP server, which receives a
message from the network and relays it to a local or remote user.
In a network full of automailers, any mistake can cause a mail loop.
Since some automailers generate several outputs in response to a
single input, a loop can produce an exponential explosion of mail.
All the automailers in the qmail package follow a general philosophy
designed to prevent mail loops and limit the damage from any loops
that do occur. These automailers have been repeatedly observed to
fail safe: they stop loops in the face of typical failures by other
hosts. This document explains the philosophy and describes the
automailers.
To some extent the philosophy here simply repeats and amplifies
standard practice as codified in RFC 974 and RFC 1123. Unfortunately,
the standards do not adequately control bounce loops, since they do
not recognize that postmasters want to see double bounces; they do
not adequately control relaying loops; and they do not prevent
cross-host forwarding loops.
Terminology: The mail message received by an automailer is called
input. The mail messages sent by an automailer are called outputs.
For simplicity, this document focuses on the case that the input has
just one envelope recipient.
REMINDER: This document describes the automailers in the qmail
package. Other packages include automailers that do not fit the
descriptions given here.
Beware that the war on mail loops can never be won: any method of
preventing mail loops can be subverted by other hosts. I welcome
further development of techniques that work well in practice.
2. Basics
The output from an automailer is always further down the following
list than the input.
0 hops, <sender> is neither <> nor <#@[]> normal messages
1 hop, <sender> is neither <> nor <#@[]>
2 hops, <sender> is neither <> nor <#@[]>
etc.
0 hops, <sender> is <> bounces
1 hop, <sender> is <>
2 hops, <sender> is <>
etc.
0 hops, <sender> is <#@[]> double bounces
1 hop, <sender> is <#@[]>
2 hops, <sender> is <#@[]>
etc.
Here sender means the envelope sender address. Hops means the number
of Received and Delivered-To fields in the header. See sections 3.3
and 3.4 for an explanation of <> and <#@[]>.
Consequently, no automailer ever generates an entirely new normal
message in response to a normal message. If the output is a normal
message, it always has more hops than the input.
When input and output are both normal messages, both bounces, or both
double bounces, the output header is essentially the same as the
input header. However, when an automailer moves from a normal message
to a bounce, or from a bounce to a double bounce, it generates an
entirely new header.
An automailer may refuse to operate if the input has too many hops.
The definition of too many hops depends on the automailer. This
practice is called hop counting. Note that some existing messages
legitimately take as many as 20 hops. One automailer uses a limit of
100 hops; this will be adequate for all messages in the foreseeable
future.
Hop counting is a weapon of last resort. It will, if correctly
implemented, prevent all infinite loops; however, even a finite loop
can do practically infinite damage, as illustrated in section 4.3.
3. Pre-delivery automailers
Conceptually: The input is a message that has not yet reached its
envelope recipient address. It is fed to a relay, which attempts to
deliver the message directly to, or at least closer to, that address;
if the relay fails permanently, the message is fed to a bouncer or a
double-bouncer. Relays, bouncers, and double-bouncers are examples of
pre-delivery automailers.
A pre-delivery automailer produces at most one output.
The basic weapon against pre-delivery mail loops is gravity. A normal
message always moves closer to its envelope recipient, according to a
notion of distance defined in section 3.1. If it bounces before
reaching the recipient, it turns into a bounce message, which always
moves closer to the original envelope sender. If that in turn
bounces, it turns into a double bounce, which always moves closer to
a local postmaster. (Triple bounces do not exist.)
3.1. Distance
The distance from a DNS domain D to a recipient U@R is defined as
follows, when R has an MX list: the minimum preference of D in the
MX list, or 100000 if D does not appear in the list.
When R has no MX records, the distance from R to U@R is defined as 0,
and the distance from any other domain to U@R is defined as 100000.
Exception: If R is an alias, i.e., if R has a CNAME record, the
distance from any domain to U@R is defined as 500000.
The distance from a host H to U@R is defined as the minimum distance
to U@R from any domain that touches H. (``D touches H'' means ``D has
an A record listing one of H's IP addresses.'')
Exception: If H does not accept mail from the network, its distance
to any recipient is defined as 999999.
3.2. Relays
A relay is a pre-delivery automailer that sends the output towards
the envelope recipient. What this means for intra-host relays is not
discussed here. What this means for cross-host relays is the
following: if the relay is at host H, and it sends its output to host
T, then the distance from T to the output envelope recipient is
always smaller than the distance from H to the input envelope
recipient.
The following facts guarantee that certain cross-host relay behavior
is safe. For proofs of these facts, see Appendix A.
Fact 1: If R is an alias for X, X is not an alias, D touches T,
and T accepts mail from the network, then the distance from T to
U@X is smaller than the distance from H to U@R.
Fact 2: If R is not an alias, R has no MX records, H is not
touched by R, T is touched by R, and T accepts mail from the
network, then T is closer to U@R than H is.
Fact 3: If R is not an alias, R has an MX record with domain X and
preference p, H is not touched by any of the domains in the MX
list for R with preference <= p, T is touched by X, and T accepts
mail from the network, then T is closer to U@R than H is.
Also, a host that does not accept mail from the network can relay
messages to a nearby hub.
A relay adds a new Received header field to the top of the output.
Other than this, the output header, body, and envelope are exactly
the same as the input header, body, and envelope. Exception: If the
input envelope recipient is U@R, R is an alias for X, and X is not
an alias, the output envelope recipient is U@X.
3.3. Bouncers
A bouncer is a pre-delivery automailer that lets the envelope sender
know what happened to a message. Most bouncers send failure notices.
Some bouncers, such as vacation servers and echo servers, send
success notices.
In a bouncer's output, the envelope sender is <>, and the envelope
recipient is the input envelope sender. A bouncer refuses to operate
if the input envelope sender is <> or <#@[]>.
Some mailers on the Internet do not understand the <> convention. In
fact, some mailers will rewrite <> as <@host>. So any message with an
envelope recipient of <> or <@host> is discarded upon local delivery.
Unlike a relay, a bouncer produces output with a new header, not
simply a copy of the input header. For example:
(envelope) from <> to <djb@silverton.berkeley.edu>
Date: 2 Jan 1996 03:38:25 GMT
From: DELIVERY NOTICE SYSTEM <MAILER-DAEMON@heaven.af.mil>
To: djb@silverton.berkeley.edu
Subject: failure notice
However, the body of the bounce indicates the relevant input envelope
recipient, as well as the Message-ID of the input, if the input had a
Message-ID. The body of a failure notice includes a copy of the
entire input message.
3.4. Double-bouncers
A double-bouncer is a pre-delivery automailer that informs a local
postmaster of permanent failures to deliver bounce messages. Such
failures are generally caused by poorly configured hosts that produce
normal messages with faulty envelope sender addresses.
A double-bouncer refuses to operate unless the input envelope sender
is <>. The output envelope sender from a double-bouncer is <#@[]>;
note that <#@[]> cannot be used as an SMTP envelope sender under
RFC 821. The output envelope recipient is predetermined.
Note that double bounces are not suggested by RFC 1123. However,
faulty envelope sender addresses are usually configuration errors
that can and should be fixed. Some postmasters, faced with mail
software that throws away double bounces, resort to keeping copies of
all bounces; but single bounces are rarely the postmaster's problem.
4. Post-delivery automailers
Conceptually: The input is a message that has reached its envelope
recipient address. It is fed to a post-delivery automailer at that
address.
The basic weapon against post-delivery loops is a new header field,
Delivered-To, tracing all the forwarders and mailing lists that a
message has been through. This field has the side benefit of making
it much easier for a user (or for a postmaster seeing a bounce) to
figure out the path that the message took. Delivered-To is similar to
RFC 1327's DL-Expansion-History, but (1) it omits the time stamp,
removing any need for parsing, and (2) it has a much better name.
4.1. Exploders and repliers
There are two basic types of post-delivery automailers: exploders,
where the output envelope recipients are predetermined; and repliers,
where there is just one output, with envelope recipient determined
from the input.
Repliers normally determine the output envelope recipient as either
the input Reply-To header field, if it exists; or else the input
From header field, if it exists; or else the envelope sender. A
replier never produces an output to <> or <#@[]>.
Exploders are classified into mailing lists, where the output
envelope senders are predetermined, and forwarders, where every
output has envelope sender equal to the original envelope sender.
Exception: if the input envelope sender is <> or <#@[]>, then the
output envelope senders are equal to the input envelope sender, even
for a mailing list.
Note that, if the envelope sender of a mailing list with M bad
addresses is another exploder with E bad addresses, the local
postmaster will receive EM double bounces for each message to the
mailing list.
4.2. Delivered-To
Every post-delivery automailer adds a new Delivered-To header field
to the top of each output.
The contents of the Delivered-To field are typically the address of
the automailer, i.e., the input envelope recipient, conventionally
without any quoting. The contents of the Delivered-To field are in
any case entirely predetermined. The automailer checks if exactly the
same Delivered-To field already appears in the header; if so, it
refuses to operate.
A post-delivery automailer preserves existing Delivered-To and
Received fields. In fact, a post-delivery automailer generally
preserves all header fields. The exceptions are limited to known
fields that are not used for loop detection and that must be removed
for correct operation. For example, a replier generally changes the
body of a message and thus should not preserve the SVR4
Content-Length field.
4.3. An example
Aliases and mailing lists are highly dangerous, because they can
generate several outputs for each input.
Here is an extreme example. A user has three accounts, and wants any
message to any of the accounts to be delivered to all three. So he
forwards luser@host1 to luser@host2 and luser@host3, forwards
luser@host2 to luser@host1 and luser@host3, and forwards luser@host3
to luser@host1 and luser@host2.
Without Delivered-To, someone who sends a message to luser@host1 will
receive a practically infinite series of bounces. For example, with a
hop count limit of 50, the sender will receive 1125899906842624
bounces.
If all the hosts, or two out of the three, support Delivered-To, the
message will bounce just a few times. If just one of the hosts
supports Delivered-To, it will be the unfortunate victim of a loop
between the other two hosts---although the total number of bounces
will drop from practically infinite down to a few hundred, with
typical hop count limits.
Appendix A. Proofs of correctness for MX handling
Section 3.2 states three facts about the notion of distance defined
in section 3.1. Here are mathematical proofs of those facts.
Symbols: D, E, R, and X are domains; H and T are hosts; p and q are
nonnegative integers. {} is the empty set.
Hypotheses: M(R), the ``MX list for R,'' is a set of pairs (p,D)
where p <= 65535. There is a set A of domains, called ``aliases.''
There is a relation D->H, called ``D touches H.'' There is a set N of
hosts, called ``hosts that accept mail from the network.''
Definitions: m(D,R) = min { p: p = 100000 or (p,D) in M(R) } when
M(R) is nonempty. When M(R) is empty, m(D,R) is 0 if D = R, 100000
otherwise. f(D,R) is defined as 500000 if R is in A, m(D,R)
otherwise; this is the ``distance from D to U@R,'' for any U. g(H,R)
is defined as min { f(D,R): D->H } if H is in N, 999999 otherwise;
this is the ``distance from H to U@R,'' for any U.
Fact 1 (generalized): If R is in A, X is not in A, D->T, and T is in
N, then g(T,X) < g(H,R). Proof: R is in A, so f(E,R) = 500000 for any
E; thus g(H,R) >= 500000. X is not in A, so f(D,X) = m(D,X) <=
100000; hence g(T,X) <= f(D,X) <= 100000 < g(H,R).
Fact 2: If R is not in A, M(R) = {}, R->T, T is in N, and not R->H,
then g(T,R) < g(H,R). Proof: f(R,R) = m(R,R) = 0 since R is not in A
and M(R) = {}. T is in N so g(T,R) <= f(R,R) = 0 so g(T,R) = 0.
Suppose that g(H,R) <= g(T,R). Then g(H,R) = 0, so f(D,R) = 0 for
some D with D->H, so m(D,R) = 0. But then D = R by definition of m,
so R->H. Contradiction. Thus g(T,R) < g(H,R).
Fact 3: If R is not in A, (p,X) is in M(R), X->T, T is in N, and
(q,D) is not in M(R) whenever D->H and q <= p, then g(T,R) < g(H,R).
Proof: First m(X,R) <= p. R is not in A, so f(X,R) = m(X,R). T is in
N, so g(T,R) <= f(X,R). Thus g(T,R) <= p. Suppose that g(H,R) <= p.
Then f(D,R) <= p for some D with D->H, so m(D,R) <= p. But then
(m(D,R),D) is in M(R). Contradiction. Thus g(T,R) <= p < g(H,R).

122
RFCMXPS Normal file
View file

@ -0,0 +1,122 @@
The Mail Exchanger Protocol Switch (MXPS)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
Mail messages today are transferred through the Simple Mail Transfer
Protocol (SMTP). One can imagine other protocols that achieve the
same results as SMTP but that, for example, use the network more
efficiently.
The Mail Exchanger Protocol Switch (MXPS) lets other protocols
compete with SMTP. A receiver can announce its support for another
protocol while operating properly with MXPS-ignorant senders. A
sender can check for support, with no overhead, while operating
properly with MXPS-ignorant receivers.
All receivers must support SMTP, i.e., must be able to receive
messages via SMTP. Similarly, all senders must be able to send
messages via SMTP.
2. The protocol switch
MXPS abuses the preference field of MX records. A protocol is
assigned to each possible preference.
SMTP is assigned to preferences 0 through 10000.
The initial MXPS experiment will involve preferences between 12800
and 13055 inclusive. These preferences are sliced into 16 portions:
12800, 12816, 12832, 12848, 12864, ..., 13040: slice #0 (SMTP)
12801, 12817, 12833, 12849, 12865, ..., 13041: slice #1 (QMTP)
12802, 12818, 12834, 12850, 12866, ..., 13042: slice #2
...
12815, 12831, 12847, 12863, 12879, ..., 13055: slice #15
Preferences in slice #0 are assigned SMTP. Preferences in slice #1
are assigned the Quick Mail Transfer Protocol (QMTP). Preferences in
the remaining slices may be assigned protocols in the future.
A receiver must support the protocol assigned to its preference. More
precisely, if an MX record points to domain D, and the MX preference
is assigned protocol P, then every host listed as an A record for D
must support protocol P.
When a sender, following the procedure outlined in RFC 974 (and
modified by RFC 1123), attempts to deliver a mail message as
specified by that MX record, it may use protocol P instead of SMTP.
If it does not support protocol P, it may treat the attempt as a
temporary failure and go on to the next MX record. However, the
sender must not skip every MX record.
MX records must never use unassigned preferences. A sender may treat
an unassigned preference as referring to SMTP.
Example:
A.EXAMPLE.ORG IN MX 12801 A.EXAMPLE.ORG
B.EXAMPLE.ORG IN MX 12801 A.EXAMPLE.ORG
IN MX 12816 C.EXAMPLE.ORG
A sender with a message for A.EXAMPLE.ORG will try A.EXAMPLE.ORG by
QMTP. If it does not support QMTP, it will try SMTP instead. Note
that A.EXAMPLE.ORG must support both QMTP and SMTP.
A sender with a message for B.EXAMPLE.ORG will try A.EXAMPLE.ORG by
QMTP, then C.EXAMPLE.ORG by SMTP. If it does not support QMTP, it may
try SMTP instead of QMTP, or it may skip A.EXAMPLE.ORG.
Some of the above requirements might be violated if current
MXPS-ignorant domains use any preferences above 10000. Mail could be
unnecessarily rejected if any existing MXPS-ignorant domains have a
best-preference MX above 10000. I do not know any examples of such
domains.
3. Protocol requirements
MXPS operates purely at the link level. It does not change the
fundamental nature of Internet mail.
The function of a mail transfer protocol is to transmit a message, as
described below, together with an envelope sender address and one or
more envelope recipient addresses.
A recipient address is a sequence of characters---i.e., nonnegative
integers---including an ASCII @ (64). It is parsed as box@dom, where
dom does not contain an @. The interpretation of box is up to the
hosts listed as MX records for dom. A sender address may contain an
@, in which case it is also of the form box@dom; or it may be a
special address, such as the empty string.
A mail message is structured as a sequence of lines. A line is a
sequence of characters. Every mail transfer protocol must be able to
transmit all sufficiently short boring mail messages. A boring mail
message is one where (1) no line has more than 80 characters and (2)
each character is either 9 or between 32 and 127 inclusive. Note that
RFC 1341 defines a mechanism for encoding a message with characters
between 0 and 255 inclusive as a boring mail message of similar
length.
The receiver must indicate, for each recipient address, either
acceptance, permanent rejection, or temporary rejection of the
message. Acceptance means that the receiver has taken responsibility,
in the sense of RFC 1123, section 5.3.3, for delivering the message
to that recipient. Rejection means that the receiver will not deliver
the message to that recipient.
Mail transfer protocols may vary in many details, such as line
encodings, the means of expressing acceptance or rejection, the
maximum number of allowable recipients per envelope, the encoding of
envelope addresses, the nature of optional protocol extensions, etc.
4. Security considerations
MXPS does not change the following facts: An attacker who can subvert
the Domain Name System can steal or forge mail. An attacker who can
subvert TCP/IP can also steal or forge mail.

88
RFCNETSTR Normal file
View file

@ -0,0 +1,88 @@
Netstrings
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
A netstring is a self-delimiting encoding of a string. Netstrings are
very easy to generate and to parse. Any string may be encoded as a
netstring; there are no restrictions on length or on allowed bytes.
Another virtue of a netstring is that it declares the string size up
front. Thus an application can check in advance whether it has enough
space to store the entire string.
Netstrings may be used as a basic building block for reliable network
protocols. Most high-level protocols, in effect, transmit a sequence
of strings; those strings may be encoded as netstrings and then
concatenated into a sequence of characters, which in turn may be
transmitted over a reliable stream protocol such as TCP.
Note that netstrings can be used recursively. The result of encoding
a sequence of strings is a single string. A series of those encoded
strings may in turn be encoded into a single string. And so on.
In this document, a string of 8-bit bytes may be written in two
different forms: as a series of hexadecimal numbers between angle
brackets, or as a sequence of ASCII characters between double quotes.
For example, <68 65 6c 6c 6f 20 77 6f 72 6c 64 21> is a string of
length 12; it is the same as the string "hello world!".
Although this document restricts attention to strings of 8-bit bytes,
netstrings could be used with any 6-bit-or-larger character set.
2. Definition
Any string of 8-bit bytes may be encoded as [len]":"[string]",".
Here [string] is the string and [len] is a nonempty sequence of ASCII
digits giving the length of [string] in decimal. The ASCII digits are
<30> for 0, <31> for 1, and so on up through <39> for 9. Extra zeros
at the front of [len] are prohibited: [len] begins with <30> exactly
when [string] is empty.
For example, the string "hello world!" is encoded as <31 32 3a 68
65 6c 6c 6f 20 77 6f 72 6c 64 21 2c>, i.e., "12:hello world!,". The
empty string is encoded as "0:,".
[len]":"[string]"," is called a netstring. [string] is called the
interpretation of the netstring.
3. Sample code
The following C code starts with a buffer buf of length len and
prints it as a netstring.
if (printf("%lu:",len) < 0) barf();
if (fwrite(buf,1,len,stdout) < len) barf();
if (putchar(',') < 0) barf();
The following C code reads a netstring and decodes it into a
dynamically allocated buffer buf of length len.
if (scanf("%9lu",&len) < 1) barf(); /* >999999999 bytes is bad */
if (getchar() != ':') barf();
buf = malloc(len + 1); /* malloc(0) is not portable */
if (!buf) barf();
if (fread(buf,1,len,stdin) < len) barf();
if (getchar() != ',') barf();
Both of these code fragments assume that the local character set is
ASCII, and that the relevant stdio streams are in binary mode.
4. Security considerations
The famous Finger security hole may be blamed on Finger's use of the
CRLF encoding. In that encoding, each string is simply terminated by
CRLF. This encoding has several problems. Most importantly, it does
not declare the string size in advance. This means that a correct
CRLF parser must be prepared to ask for more and more memory as it is
reading the string. In the case of Finger, a lazy implementor found
this to be too much trouble; instead he simply declared a fixed-size
buffer and used C's gets() function. The rest is history.
In contrast, as the above sample code shows, it is very easy to
handle netstrings without risking buffer overflow. Thus widespread
use of netstrings may improve network security.

89
RFCNRUDT Normal file
View file

@ -0,0 +1,89 @@
Notice-Requested-Upon-Delivery-To (NRUDT)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
The UNIX sendmail program has for many years supported a
Return-Receipt-To (RRT) header field that requests a notice of
successful final delivery.
Notice-Requested-Upon-Delivery-To (NRUDT) has the same basic
function. The big difference is that RRT lists the sender's address,
while NRUDT lists the recipient's address.
This change is critical. RRT works poorly for messages to multiple
recipients, because it requests a notice from every recipient. RRT in
a message to a large mailing list produces a giant, usually
unintentional, flood of mail. This problem is so severe that RRT has
been disabled in recent versions of sendmail.
NRUDT is designed to be adopted immediately, with minimal disruption,
as a solution to the problems of RRT. Note that NRUDT is merely a
request for notification; unlike the link-level Delivery Status
Notification SMTP extension, NRUDT does not provide a guarantee of
notification.
NRUDT is supported by the qreceipt program in the qmail package.
2. Syntax
NRUDT is a field in the header of an RFC 822 mail message. It has the
following syntax:
"Notice-Requested-Upon-Delivery-To" ":" 1#address
See RFC 822 for more information about header fields and addresses.
NRUDT requests that, upon final delivery of the message to any of the
specified addresses, the sender be notified. Note that more than one
address can appear in a single NRUDT header field. Multiple NRUDT
header fields should not appear in a single message.
3. Response
Upon successful final delivery of a message to any address listed in
an NRUDT header field, the host performing delivery may, if desired,
generate a success notice.
The success notice is similar to a failure notice as described in RFC
1123. Its envelope sender is <>. Its envelope recipient is the
envelope sender of the original message; however, if the envelope
sender of the original message is <>, a success notice is not sent.
The body of the success notice does not contain a copy of the
original message, but it does indicate the Message-ID of the original
message, as well as the relevant recipient address.
A success notice may indicate delivery to several addresses. For
example, given the following message:
(envelope) from djb@silverton.berkeley.edu
(envelope) to god@heaven.af.mil, angels@heaven.af.mil
Date: 1 Jan 1996 21:43:34 GMT
From: "D. J. Bernstein" <djb@silverton.berkeley.edu>
Message-Id: <19960101214334.8529.qmail@silverton.berkeley.edu>
Notice-Requested-Upon-Delivery-To: God <god@heaven.af.mil>,
angels@heaven.af.mil (You Know Who You Are)
...
a host may respond as follows:
(envelope) from <> to djb@silverton.berkeley.edu
Date: 1 Jan 1996 21:43:37 GMT
From: DELIVERY NOTICE SYSTEM <MAILER-DAEMON@heaven.af.mil>
To: djb@silverton.berkeley.edu
Subject: success notice
I delivered <19960101214334.8529.qmail@silverton.berkeley.edu>
to the following local mailboxes:
god@heaven.af.mil
angels@heaven.af.mil
Thanks for asking.
However, a success notice is never merged with a failure notice.

229
RFCQMTP Normal file
View file

@ -0,0 +1,229 @@
Quick Mail Transfer Protocol (QMTP)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
The Quick Mail Transfer Protocol (QMTP) is a replacement for the
Simple Mail Transfer Protocol (SMTP). QMTP eliminates any need for
end-of-line scanning between hosts with the same end-of-line
convention. It features automatic pipelining and chunking, 8-bit
transmission, prior declaration of the message size, and efficient
batching. It is designed to be very easy to implement.
QMTP is supported by the qmail-qmtpd and maildir2qmtp programs in the
qmail package.
In this document, a string of 8-bit bytes may be written in two
different forms: as a series of hexadecimal numbers between angle
brackets, or as a sequence of ASCII characters between double quotes.
For example, <68 65 6c 6c 6f 20 77 6f 72 6c 64 21> is a string of
length 12; it is the same as the string "hello world!". Note that
these notations are part of this document, not part of the protocol.
2. Protocol
A QMTP client connects to a QMTP server, as discussed in section 7,
over a reliable stream protocol allowing transmission of 8-bit bytes.
Protocol outline: the client sends one or more packages; after each
package, the server sends back some responses.
The client begins by sending a package. A package contains a mail
message, an envelope sender address, and one or more envelope
recipient addresses. See section 4 for the format of a package.
When the server sees the end of the package, it sends back a series
of responses, one response for each envelope recipient address, in
the same order as given by the client. The server is not permitted to
change the order under any circumstances, even if two addresses are
the same. See section 5 for the format of a response.
The server is not permitted to send any portion of its responses to a
package until the client has sent the final byte of the package. The
client is permitted to close the connection before sending the final
byte of the package; in this case, the server must throw away the
package without attempting to deliver the message. However, the
server must not throw away previously accepted messages.
The client does NOT need to wait for a server response before sending
another package. The server must NOT throw away incoming data when it
sends a response. It is the client's responsibility to avoid
deadlock: if it sends a package before receiving all expected server
responses, it must continuously watch for those responses. The server
is permitted to delay its responses if further data has already shown
up from the client; while it is delaying responses, it must not pause
to wait for further data for the client.
The server is permitted to close the connection at any time, although
high-quality servers will try to avoid doing so. Any response not
received by the client indicates a temporary failure.
A QMTP session should take at most 1 hour. Both sides are expected
to close the connection after this time.
3. Messages
In this document, an ``8-bit mail message'' means a sequence of
lines. Each line is a string of zero or more 8-bit bytes.
A message is called ``safe'' if none of its bytes are <0a>.
Implementation note: Here is the intended interpretation of text
files as messages under some current operating systems. Under DOS, a
message is stored on disk as
first line, <0d 0a>, second line, <0d 0a> ... <0d 0a>, last line.
Under UNIX, a message is stored on disk as
first line, <0a>, second line, <0a> ... <0a>, last line.
Notice that both of these encodings are reversible for safe messages.
In practice, it is very common for the last line to be empty. Many
existing utilities refer to the last line as a ``partial line'' and
ignore it whether or not it is empty.
4. Packages
A package is the concatenation of three strings:
first, an encoded 8-bit mail message;
second, an encoded envelope sender address;
third, an encoded series of encoded envelope recipient addresses.
Each envelope address is a string of 8-bit bytes. The interpretation
of addresses depends on the environment in which QMTP is used and is
outside the scope of this document. Each address is encoded as a
netstring, as discussed in section 6. The series of encoded recipient
addresses is in turn encoded as a netstring.
A message is encoded as a string of 8-bit bytes in one of two ways:
Encoding #1 is <0d>, the first line, <0d 0a>, the second line,
<0d 0a>, the third line, ..., <0d 0a>, the last line.
Encoding #2 is <0a>, the first line, <0a>, the second line, <0a>,
the third line, ..., <0a>, the last line.
This string of 8-bit bytes is in turn encoded as a netstring, as
discussed in section 6.
Every server must be prepared to handle encoding #1 and encoding #2.
A server must not reject a message merely because of its encoding.
Implementation note: The intent of encoding #1 and encoding #2 is to
allow very straightforward handling of text files under DOS and UNIX
respectively. The programmer can print <0d> or <0a> and then simply
copy the file.
5. Responses
Each response is a nonempty string of 8-bit bytes, encoded as a
netstring. The first byte of the string is one of the following:
"K" The message has been accepted for delivery to this envelope
recipient. This is morally equivalent to the 250 response to
DATA in SMTP; it is subject to the reliability requirements
of RFC 1123, section 5.3.3.
"Z" Temporary failure. The client should try again later.
"D" Permanent failure.
The remaining bytes are a description of what happened. It is
expected that the description, when interpreted as UTF-2 characters,
(1) will be human-readable, (2) will not repeat the envelope
recipient address, and (3) will not include formatting characters
other than <20>. However, these expectations are not requirements,
and the client should be ready for arbitrary bytes from the server.
Descriptions beginning with <20> are reserved for future extensions.
In descriptions not beginning with <20>, the character "#" must not
appear except in HCMSSC codes.
A server must NOT accept a safe message unless it can store the
message without corruption. More precisely: if the encoded message
sent by the client matches the encoding of some safe message M, then
acceptance means that the server is accepting responsibility to
deliver M to the envelope recipient. (There is at most one
possibility for M, since encodings are reversible on safe messages.)
Deletion of nulls is NOT permissible; a server that deletes nulls
must reject any message containing nulls. Folding of long lines and
high-bit stripping are also NOT permissible.
Servers are permitted to change unsafe messages.
6. Netstrings
Any string of 8-bit bytes may be encoded as [len]":"[string]",".
Here [string] is the string and [len] is a nonempty sequence of ASCII
digits giving the length of [string] in decimal. The ASCII digits are
<30> for 0, <31> for 1, and so on up through <39> for 9. Extra zeros
at the front of [len] are prohibited: [len] begins with <30> exactly
when [string] is empty.
For example, the string "hello world!" is encoded as <31 32 3a 68
65 6c 6c 6f 20 77 6f 72 6c 64 21 2c>, i.e., "12:hello world!,". The
empty string is encoded as "0:,".
[len]":"[string]"," is called a netstring. [string] is called the
interpretation of the netstring.
7. Encapsulation
QMTP may be used on top of TCP. A QMTP-over-TCP server listens for
TCP connections on port 209.
8. Examples
A client opens a connection and sends the concatenation of the
following strings:
"246:" <0a>
"Received: (qmail-queue invoked by uid 0);"
" 29 Jul 1996 09:36:40 -0000" <0a>
"Date: 29 Jul 1996 11:35:35 -0000" <0a>
"Message-ID: <19960729113535.375.qmail@heaven.af.mil>" <0a>
"From: God@heaven.af.mil" <0a>
"To: djb@silverton.berkeley.edu (D. J. Bernstein)" <0a>
<0a>
"This is a test." <0a> ","
"24:" "God-DSN-37@heaven.af.mil" ","
"30:" "26:djb@silverton.berkeley.edu," ","
"356:" <0d>
"From: MAILER-DAEMON@heaven.af.mil" <0d 0a>
"To:" <0d 0a>
" Hate." <22> "The Quoting" <22>
"@SILVERTON.berkeley.edu," <0d 0a>
" " <22> "\\Backslashes!" <22>
"@silverton.BERKELEY.edu" <0d 0a>
<0d 0a>
"The recipient addresses here could"
" have been encoded in SMTP as" <0d 0a>
"" <0d 0a>
" RCPT TO:<Hate.The\ Quoting@silverton.berkeley.EDU>" <0d 0a>
" RCPT TO:<\\Backslashes!@silverton.berkeley.edu>" <0d 0a>
<0d 0a>
"This ends with a partial last line, right here" ","
"0:" ","
"83:" "39:Hate.The Quoting@silverton.berkeley.edu,"
"36:\Backslashes!@silverton.berkeley.EDU," ","
The server sends the following response, indicating acceptance:
"21:Kok 838640135 qp 1390,"
"21:Kok 838640135 qp 1391,"
"21:Kok 838640135 qp 1391,"
The client closes the connection.

155
RFCQSBMF Normal file
View file

@ -0,0 +1,155 @@
The qmail-send Bounce Message Format (QSBMF)
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
When a message transport agent (MTA) finds itself permanently unable
to deliver a mail message, it generates a new message, generally
known as a bounce message, back to the envelope sender.
Bounce messages produced by the qmail-send program display the list
of failed recipient addresses, an explanation for each address, and a
copy of the original message, in a format that is easy for both
humans and programs to read. For example:
Date: 17 Mar 1996 03:54:40 -0000
From: MAILER-DAEMON@silverton.berkeley.edu
To: djb@silverton.berkeley.edu
Subject: failure notice
Hi. This is the qmail-send program at silverton.berkeley.edu.
I'm afraid I wasn't able to deliver your message to the
following addresses. This is a permanent error; I've given up.
Sorry it didn't work out.
<god@heaven.af.mil>:
Sorry, I couldn't find any host by that name.
--- Below this line is a copy of the message.
Return-Path: <djb@silverton.berkeley.edu>
Received: (qmail 317 invoked by uid 7); 17 Mar 1996 03:54:38 -0000
Date: 17 Mar 1996 03:54:38 -0000
Message-ID: <19960317035438.316.qmail@silverton.berkeley.edu>
From: djb@silverton.berkeley.edu (D. J. Bernstein)
To: god@heaven.af.mil
Subject: are you there?
Just checking.
This document defines qmail-send's format for bounce messages.
In this document, a string of 8-bit bytes may be written in two
different forms: as a series of hexadecimal numbers between angle
brackets, or as a sequence of ASCII characters between double quotes.
For example, <68 65 6c 6c 6f 20 77 6f 72 6c 64 21> is a string of
length 12; it is the same as the string "hello world!".
2. Format
A bounce message may be recognized as QSBMF as follows: its body
begins with the characters "Hi. This is the" exactly as shown.
The body of the message has four pieces: an introductory paragraph,
zero or more recipient paragraphs, a break paragraph, and the
original message.
Each paragraph is a series of non-blank lines followed by a single
blank line. The break paragraph begins with the character "-". All
other paragraphs begin with characters other than "-". The break
paragraph is human-readable but provides no interesting information.
The introductory paragraph is human-readable. It gives the name and
human-comprehensible location of the MTA, but parsers should not
attempt to use this information.
The only type of recipient paragraph described here is a failure
paragraph, which begins with the character "<". Paragraphs beginning
with other characters are reserved for future extensions.
The first line of a failure paragraph ends with the characters ">:".
Everything between the leading "<" and the trailing ">:" is an
(unquoted) Internet mail address.
A failure paragraph asserts that the MTA was permanently unable to
deliver the message to the mail address shown on the first line; the
MTA will not attempt further deliveries to that address. The
remaining lines of the paragraph give a human-readable description of
the reason for failure. Descriptions beginning with <20>, and
descriptions containing "#", are reserved for future extensions.
The envelope sender might not have sent his message to the address
shown. There are two reasons for this. First, the MTA may freely
replace unprintable characters with "_". Second, the original
recipient address may have been an alias for the address shown.
The original message is an exact copy of the message received by the
MTA, including both header and body, preceded by a Return-Path field
showing the envelope sender.
3. Comparison with 1892/1894
RFC 1892 and RFC 1894 together describe a format for delivery status
notifications. I have decided not to use that format, because I
believe that its complexity will prevent wide implementation and
increase the burden on people who manage mailing lists.
QSBMF is dedicated to failure reports, whereas RFC 1894 allows
success reports and deferral reports. Although it would be possible
to add deferral paragraphs and success paragraphs to QSBMF, it would
be even easier to design separate formats for such notices. I have
trouble reading mixed failure/deferral reports.
QSBMF always returns the entire original message. RFC 1892 allows
the MTA to return nothing or to return just the headers; it states
``Return of content may be wasteful of network bandwidth.'' However,
failure notices are very rare, so the overall loss of bandwidth in
this case is insignificant. A much more important issue is storage
space: someone who manages a big mailing list does not want to have
to store several copies of each message in the form of bounces. The
best solution is to have each bounce automatically fed through a
program that stores only the critical information. I expect such
programs to spring up quickly for QSBMF.
RFC 1894 provides language-independent error messages, as described
by RFC 1893. One can achieve the same results more easily by adding
structure to the human-readable failure descriptions, for example
with HCMSSC.
RFC 1894 is able to communicate an ``envelope ID'' and the original
envelope recipient address specified by the sender. Unfortunately,
this information will almost never be available, since it requires
support by every intermediate MTA. All of the applications of this
information can be handled reliably, right now, with VERPs; this
requires support from the sender's MTA but not from other hosts.
RFC 1894 includes several pieces of information that might be of
human interest but can be seen just as easily from Received lines:
the name of the MTA where delivery failed, the name of the previous
MTA, timestamps, etc.
All of these RFC 1894 features have a cost: complexity. A program
cannot parse an 1894 report without parsing RFC 822 header fields
and understanding quite a bit of MIME. This will limit the
availability of parsing software. In the meantime, such reports are
annoying to mailing list maintainers, since they are full of
uninteresting information and are difficult to parse visually.
4. Security considerations
Bounce messages may be forged. Never remove someone from a mailing
list without sending him a message stating that you are doing so,
even if the reason for removal is a series of apparent bounce
messages from his address.
If you send a message along a secret path, you should change the
envelope sender address of the message to yourself, so that a bounce
will not reveal anything to the original sender. In other words: for
secret forwarding, use a mailing list, not a forwarder.
See RFC 1894 for further discussion of these points.

88
RFCVERP Normal file
View file

@ -0,0 +1,88 @@
Variable Envelope Return Paths
D. J. Bernstein, djb@pobox.com
19970201
1. Introduction
The fundamental problem in managing a large mailing list is matching
bounce messages to subscription addresses.
Often a bounce message refers to a failing address that does not
appear on the mailing list. One of the mailing list subscribers is
forwarding messages to that address. Which subscriber? As the list
grows, this question becomes more and more difficult to answer.
Sometimes a bounce message doesn't identify the address that failed.
On occasion it doesn't even include a copy of the original message.
See RFC 1211 for an extensive collection of horror stories.
In theory, one could solve this problem with the DSN option and DSN
format described in RFC 1891, RFC 1892, and RFC 1894. Unfortunately,
the DSN option is useless unless it is supported by every
intermediate MTA. The complexity of RFC 1891 means that it will be
many years, perhaps infinitely many, before DSNs are universally
supported. Furthermore, the complexity of RFC 1894 means that parsing
the subscriber address is difficult even on the occasions that the
address is available.
Variable envelope return paths (VERPs) completely eliminate this
problem _right now_. They automatically and reliably identify the
subscription address relevant to each bounce message. They provide
the address in a form that is trivial for automated bounce handlers
to parse. They require support from the local mailer, but they do not
require support from any other hosts.
2. Variable envelope return paths
Here is how VERPs work: each recipient of the message sees a
different envelope sender address. When a message to the
djb-sos@silverton.berkeley.edu mailing list is sent to
God@heaven.af.mil, for example, it has the following envelope sender:
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu
If the message bounces, the bounce message will be sent back to
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu.
If God is forwarding His mail, the bounce message will still go to
djb-sos-owner-God=heaven.af.mil@silverton.berkeley.edu. No matter how
uninformative the bounce message is, it will display God's
subscription address in its envelope.
Another benefit of VERPs is that God Himself can see what address He
used to subscribe.
Making VERPs work requires two pieces of local software support.
First: it must be easy to modify the outgoing sender address
separately for each envelope recipient. For example, with one mailer,
qmail, a user can simply touch ~/.qmail-list-owner and
~/.qmail-list-owner-default to apply VERPs to user-list.
Second, and more important: it must be easy to identify a collection
of addresses, such as djb-sos-owner-*, and send all mail for those
addresses to one place, while preserving the * information. Under
qmail, all user-list-owner-* mail will be sent to the user once he
touches ~/.qmail-list-owner-default. Sending the mail through an
automated bounce-handling program is just as easy.
With older mailers, applying VERPs would require setting up a new
user-list-owner-recipient alias for each new recipient. This
inconvenience has prevented VERPs from being widely exploited, even
though the idea is not new.
3. Per-message VERPs
VERPs are not restricted to distinguishing mailing list subscribers;
they can also be used to distinguish messages.
For example, a user can send one message with an envelope sender
address of user-dsn-1, the next message with user-dsn-2, and so on.
As long as the local mailer gives all user-dsn-* back to that user,
he can reliably match up incoming bounces with outgoing messages.
Per-message VERPs can be combined with per-recipient VERPs. Every
application of RFC 1891's ORCPT and ENVID can be handled with
VERPs---easily, reliably, and right now.

131
SECURITY Normal file
View file

@ -0,0 +1,131 @@
Background: Every few months CERT announces Yet Another Security Hole In
Sendmail---something that lets local or even remote users take complete
control of the machine. I'm sure there are many more holes waiting to be
discovered; sendmail's design means that any minor bug in 46000 lines of
code is a major security risk. Other popular mailers, such as Smail, and
even mailing-list managers, such as Majordomo, seem nearly as bad.
I started working on qmail because I was sick of this cycle of doom.
Here are some of the things I did to make sure that qmail will never let
an intruder into your machine.
1. Programs and files are not addresses. Don't treat them as addresses.
sendmail treats programs and files as addresses. Obviously random people
can't be allowed to execute arbitrary programs or write to arbitrary
files, so sendmail goes through horrendous contortions trying to keep
track of whether a local user was ``responsible'' for an address. This
has proven to be an unmitigated disaster.
In qmail, programs and files are not addresses. The local delivery
agent, qmail-local, can run programs or write to files as directed by
~user/.qmail, but it's always running as that user. (The notion of
``user'' is configurable, but root is never a user. To prevent silly
mistakes, qmail-local makes sure that neither ~user nor ~user/.qmail is
group-writable or world-writable.)
Security impact: .qmail, like .cshrc and .exrc and various other files,
means that anyone who can write arbitrary files as a user can execute
arbitrary programs as that user. That's it.
2. Do as little as possible in setuid programs.
A setuid program must operate in a very dangerous environment: a user is
under complete control of its fds, args, environ, cwd, tty, rlimits,
timers, signals, and more. Even worse, the list of controlled items
varies from one vendor's UNIX to the next, so it is very difficult to
write portable code that cleans up everything.
Of the twelve most recent sendmail security holes, six worked only
because the entire sendmail system is setuid.
Only one qmail program is setuid: qmail-queue. Its only purpose is to
add a new mail message to the outgoing queue.
3. Do as little as possible as root.
The entire sendmail system runs as root, so there's no way that its
mistakes can be caught by the operating system's built-in protections.
In contrast, only two qmail programs, qmail-start and qmail-lspawn,
run as root.
4. Move separate functions into mutually untrusting programs.
Five of the qmail programs---qmail-smtpd, qmail-send, qmail-rspawn,
qmail-remote, and tcp-env---are not security-critical. Even if all of
these programs are completely compromised, so that an intruder has
control over the qmaild, qmails, and qmailr accounts and the mail queue,
he still can't take over your system. None of the other programs trust
the results from these five.
In fact, these programs don't even trust each other. They are in three
groups: tcp-env and qmail-smtpd, which run as qmaild; qmail-rspawn and
qmail-remote, which run as qmailr; and qmail-send, the queue manager,
which runs as qmails. Each group is immune from attacks by the others.
(From root's point of view, as long as root doesn't send any mail, only
qmail-start and qmail-lspawn are security-critical. They don't write any
files or start any other programs as root.)
5. Don't parse.
I have discovered that there are two types of command interfaces in the
world of computing: good interfaces and user interfaces.
The essence of user interfaces is _parsing_---converting an unstructured
sequence of commands, in a format usually determined more by psychology
than by solid engineering, into structured data.
When another programmer wants to talk to a user interface, he has to
_quote_: convert his structured data into an unstructured sequence of
commands that the parser will, he hopes, convert back into the original
structured data.
This situation is a recipe for disaster. The parser often has bugs: it
fails to handle some inputs according to the documented interface. The
quoter often has bugs: it produces outputs that do not have the right
meaning. Only on rare joyous occasions does it happen that the parser
and the quoter both misinterpret the interface in the same way.
When the original data is controlled by a malicious user, many of these
bugs translate into security holes. Some examples: the Linux login
-froot security hole; the classic find | xargs rm security hole; the
recent Majordomo security hole. Even a simple parser like getopt is
complicated enough for people to screw up the quoting.
In qmail, all the internal file structures are incredibly simple: text0
lines beginning with single-character commands. (text0 format means that
lines are separated by a 0 byte instead of line feed.) The program-level
interfaces don't take options.
All the complexity of parsing RFC 822 address lists and rewriting
headers is in the qmail-inject program, which runs without privileges
and is essentially part of the UA.
The only nasty case is .qmail, qmail's answer to .forward. I tried to
make this as simple as possible, but unfortunately it still has to be
edited by users. As a result, the qlist mailing-list-management program
has to be careful to exclude subscriber addresses that contain newlines.
6. Keep it simple, stupid.
See BLURB for some of the reasons that qmail is so much smaller than
sendmail. There's nothing inherently complicated about writing a mailer.
(Except RFC 822 support; but that's only in qmail-inject.) Security
holes can't show up in features that don't exist.
7. Write bug-free code.
I've mostly given up on the standard C library. Many of its facilities,
particularly stdio, seem designed to encourage bugs. A big chunk of
qmail is stolen from a basic C library that I've been developing for
several years for a variety of applications. The stralloc concept and
getline2() make it very easy to avoid buffer overruns, memory leaks,
and artificial line length limits.

17
SYSDEPS Normal file
View file

@ -0,0 +1,17 @@
VERSION
systype
hasshsgr.h
hasnpbg1.h
select.h
hasflock.h
hassalen.h
fork.h
hassgact.h
direntry.h
hassgprm.h
haswaitp.h
hasmkffo.h
uint32.h
dns.lib
socket.lib
syslog.lib

353
TARGETS Normal file
View file

@ -0,0 +1,353 @@
auto-ccld.sh
make-load
find-systype
systype
load
make-compile
compile
fork.h
qmail-local.o
qmail.o
auto-str.o
make-makelib
makelib
substdio.o
substdi.o
substdo.o
subfderr.o
subfdout.o
subfdouts.o
subfdin.o
subfdins.o
substdio_copy.o
substdio.a
error.o
error_str.o
error_temp.o
error.a
str_len.o
str_diff.o
str_diffn.o
str_cpy.o
str_chr.o
str_rchr.o
str_start.o
byte_chr.o
byte_rchr.o
byte_diff.o
byte_copy.o
byte_cr.o
byte_zero.o
str.a
auto-str
auto_qmail.c
auto_qmail.o
auto-int8.o
fmt_str.o
fmt_strn.o
fmt_uint.o
fmt_uint0.o
fmt_ulong.o
scan_ulong.o
scan_8long.o
scan_nbblong.o
fs.a
auto-int8
auto_patrn.c
auto_patrn.o
quote.o
now.o
gfrom.o
myctime.o
slurpclose.o
case_diffb.o
case_diffs.o
case_lowerb.o
case_lowers.o
case_starts.o
case.a
getln.o
getln2.o
getln.a
subgetopt.o
sgetopt.o
getopt.a
sig_alarm.o
hassgprm.h
sig_block.o
hassgact.h
sig_catch.o
sig_pause.o
sig_pipe.o
sig_child.o
sig_hup.o
sig_term.o
sig_bug.o
sig_misc.o
sig.a
open_append.o
open_excl.o
open_read.o
open_trunc.o
open_write.o
open.a
seek_cur.o
seek_end.o
seek_set.o
seek_trunc.o
seek.a
hasflock.h
lock_ex.o
lock_exnb.o
lock_un.o
lock.a
fd_copy.o
fd_move.o
fd.a
wait_pid.o
haswaitp.h
wait_nohang.o
wait.a
env.o
envread.o
env.a
stralloc_eady.o
stralloc_pend.o
stralloc_copy.o
stralloc_opys.o
stralloc_opyb.o
stralloc_cat.o
stralloc_cats.o
stralloc_catb.o
stralloc_arts.o
stralloc.a
alloc.o
alloc_re.o
alloc.a
datetime.o
datetime_un.o
datetime.a
qmail-local
uint32.h
qmail-lspawn.o
auto-uid.o
auto-uid
auto-gid.o
auto-gid
auto_uids.c
auto_uids.o
auto-int.o
auto-int
auto_spawn.c
auto_spawn.o
select.h
chkspawn.o
chkspawn
spawn.o
chkshsgr.o
chkshsgr
hasshsgr.h
prot.o
coe.o
cdb_hash.o
cdb_unpack.o
cdb_seek.o
cdb.a
qmail-lspawn
qmail-getpw.o
auto_break.c
auto_break.o
auto_usera.c
auto_usera.o
qmail-getpw
qmail-remote.o
control.o
constmap.o
timeoutread.o
timeoutwrite.o
timeoutconn.o
tcpto.o
dns.o
ip.o
ipalloc.o
hassalen.h
ipme.o
ndelay.o
ndelay_off.o
ndelay.a
socket.lib
dns.lib
qmail-remote
qmail-rspawn.o
tcpto_clean.o
qmail-rspawn
direntry.h
qmail-clean.o
auto_split.c
auto_split.o
fmtqfn.o
qmail-clean
qmail-send.o
qsutil.o
newfield.o
prioq.o
hasmkffo.h
fifo.o
hasnpbg1.h
trigger.o
readsubdir.o
date822fmt.o
qmail-send
qmail-start.o
qmail-start
splogger.o
syslog.lib
splogger
qmail-queue.o
triggerpull.o
qmail-queue
qmail-inject.o
headerbody.o
hfield.o
token822.o
qmail-inject
predate.o
predate
datemail
mailsubj
qmail-upq
qmail-config
qmail-showctl.o
qmail-showctl
qmail-newu.o
cdbmss.o
cdbmake_pack.o
cdbmake_hash.o
cdbmake_add.o
cdbmake.a
qmail-newu
qmail-pw2u.o
qmail-pw2u
qmail-qread.o
qmail-qread
qmail-qstat
qmail-tcpto.o
qmail-tcpto
qmail-pop3d.o
qmail-pop3d
qmail-popup.o
qmail-popup
qmail-qmtpd.o
received.o
qmail-qmtpd
qmail-smtpd.o
qmail-smtpd
sendmail.o
sendmail
tcp-env.o
remoteinfo.o
tcp-env
dnscname.o
dnsdoe.o
dnscname
dnsptr.o
dnsptr
dnsip.o
dnsip
dnsmxip.o
dnsmxip
dnsfq.o
dnsfq
hostname.o
hostname
ipmeprint.o
ipmeprint
qlist.o
qlist
qlist2
qreceipt.o
qreceipt
qsmhook.o
qsmhook
qbiff.o
qbiff
forward.o
forward
preline.o
preline
condredirect.o
condredirect
maildirmake.o
maildirmake
maildir2mbox.o
maildir.o
strerr_sys.o
strerr_die.o
strerr.a
maildir2mbox
maildirwatch.o
maildirwatch
qail
elq
pinq
qmail-hier.o
qmail-hier
install.o
install
instcheck.o
instcheck
it
qmail-local.0
qmail-lspawn.0
qmail-getpw.8
qmail-getpw.0
qmail-remote.0
qmail-rspawn.0
qmail-clean.0
qmail-send.8
qmail-send.0
qmail-start.0
splogger.0
qmail-queue.0
qmail-inject.0
mailsubj.0
qmail-showctl.0
qmail-newu.0
qmail-pw2u.8
qmail-pw2u.0
qmail-qread.0
qmail-qstat.0
qmail-tcpto.0
qmail-pop3d.0
qmail-popup.0
qmail-qmtpd.0
qmail-smtpd.0
tcp-env.0
qlist.0
qreceipt.0
qbiff.0
forward.0
preline.0
condredirect.0
maildirmake.0
maildir2mbox.0
maildirwatch.0
qmail.0
qmail-upgrade.7
qmail-upgrade.0
qmail-limits.7
qmail-limits.0
qmail-log.0
qmail-control.0
qmail-header.0
qmail-users.0
dot-qmail.5
dot-qmail.0
qmail-command.0
tcp-environ.0
maildir.0
mbox.0
addresses.0
envelopes.0
forgeries.0
man

217
THANKS Normal file
View file

@ -0,0 +1,217 @@
Thanks to lots of people for success and failure reports, code, ideas,
and documentation. See CHANGES for details of specific contributions.
Sorry if I left anyone out.
SA = Satoshi Adachi
G1A = Graham Adams
NA = Norm Aleks
NAA = Nicholas A. Amato
G2A = Greg Andrews
TA = Tetsuo Aoki
DA = Dave Arcuri
JJB = J. J. Bailey
J2B = Jos Backus
SSB = Stik Bakken
J1B = John Banghart
GB = Glenn Barry
JLB = Julie L. Baumler
SLB = Steven L. Baur
ALB = Allan L. Bazinet
JDHB = Johannes D. H. Beekhuizen
BDB = Boris D. Beletsky
HJB = Herbert J. Bernstein
REB = Ronald E. Bickers
JPB = Joe Block
BB = Bruce Bodger
SB = Stephane Bortzmeyer
PB = Peter Bowyer
ACB = Andy C. Brandt
AB = Alan Briggs
AKB = Allen K. Briggs
JBB = Jason B. Brown
JAB = Jeremy A. Bussard
RJC = Robert J. Carter
EC = Evan Champion
JC = Jim Clausing
HCJ = Helio Coelho Jr.
BC = Bob Collie
SGC = Stephen G. Comings
MC = Michael Cooley
DCC = Daniel C. Cotey
AC = Arne Coucheron
M2C = Mark Crimmins
DC = Dan Cross
MD = Mark Delany
SVD = Stef Van Dessel
RD = Rahul Dhesi
MJD = Mark-Jason Dominus
JD = Joe Doupnik
FE = Frank Ederveen
DE = Daniel Egnor
MWE = Mark W. Eichin
MEE = Mads E. Eilertsen
KE = Kenny Elliott
TEE = Thomas E. Erskine
ME = Marc Ewing
JF = Janos Farkas
DF = Dale Farnsworth
YF = Yaroslav Faybishenko
CF = C. Ferree
JBF = John B. Fleming
C2F = Chuck Foster
RF = Rainer Fraedrich
MF = Massimo Fusaro
CG = Chris Garrigues
BG = Bert Gijsbers
M2G = Michael R. Gile
EG = Eivind Gjelseth
HG = Howard Goldstein
TG = Tim Goodwin
HDG = Hans de Graaff
MG = Michael Graff
PJG = Paul Graham
MRG = Matthew R. Green
SG = Steven Grimm
AG = Armin Gruner
CSH = Clayton S. Haapala
JLH = Jason L. Haar
MLH = May Liss Haarstad
CH = Chael Hall
JPH = Justin P. Hannah
RJH = Randy Harmon
PH = Paul Harrington
DEH = Daniel E. Harris
RFH = Robert F. Harrison
D1H = Dieter Heidner
GH = Gene Hightower
MH = Markus Hofmann
D2H = Dan Hollis
NH = Nick Holloway
TH = Ton Hospel
BH = Brad Howes
TJH = Timothy J. Hunt
B2H = Buck Huppmann
MDI = Miguel de Icaza
BJ = Brian Jackson
AJ = Alan Jaffray
CEJ = Colin Eric Johnson
K2J = Kevin Johnson
K1J = Kyle Jones
SJ = Sudish Joseph
W2K = Wolfram Kahl
JJMK = Jonathan J. M. Katz
CK = Christoph Kaesling
PK = Petri Kaukasoina
D2K = Dax Kelson
TK = Terry Kennedy
DBK = Douglas B. Kerry
WK = Werner Koch
DK = Dave Kopper
J1K = Jost Krieger
J2K = Johannes Kroeger
EK = Eric Krohn
B2L = Brent Laminack
AL = Andreas Lamprecht
L2L = Louis Larry
M3L = Michael Lazarou
CL = Carsten Leonhardt
JRL = John R. Levine
DML = David M. Lew
LL = lilo
FPL = Frederik P. Lindberg
JL = Jim Littlefield
BL = Brian Litzinger
RL = Robert Luce
ML = Martin Lucina
M2L = M. Lyons
TM = Toshinori Maeno
ESM = Edward S. Marshall
J2M = Joel Maslak
TLM = Timothy L. Mayo
DM = David Mazieres
RM = Rich McClellan
SM = Shawn McHorse
JM = Jim Meehan
HWM = Henry W. Miller
RDM = Raul Miller
MMM = Momchil M. Momchev
JGM = John G. Myers
FN = Faried Nawaz
RN = Russell Nelson
TN = Thomas Neumann
UO = Uwe Ohse
PCO = Peter C. Olsen
HHO = Harald Hanche-Olsen
BEO = Bruce E. O'Neel
JP = John Palkovic
AP = Andrew Pam
SP = Stephen Parker
TVP = Tom van Peer
BP = Bruce Perens
CMP = Chase M. Phillips
DP = Dave Platt
EP = Emanuele Pucciarelli
JPR = Jean-Pierre Radley
S1R = Satish Ramachandran
MR = Mosfeq Rashid
TRR = Tracy R. Reed
BR = Brian Reichert
S2R = Sean Reifschneider
DAR = Daniel A. Reish
CR = Christian Riede
KR = Kenji Rikitake
OR = Ollivier Robert
ANR = Adriano Nagelschmidt Rodrigues
JJR = Jaron J. Rubenstein
RS = Robert Sanders
KJS = Kevin J. Sawyer
MBS = Mike Scher
SAS = Steven A. Schrader
CLS = Christopher L. Seawood
OS = Oliver Seiler
D2S = Dan Senie
SS = Simon Shapiro
RGS = Richard G. Sharman
DS = Dave Sill
H2S = Harley Silver
M3S = Morten Skjelland
JS = Jesper Skriver
ES = Eric Smith
IS = Icarus Sparry
CS = Cloyce Spradling
BS = Bjoern Stabell
HS = Harlan Stenn
JMS = Jason M. Stokes
DWS = David Wayne Summers
M2S = Mikael Suokas
PS = Paul Svensson
ET = Eivind Tagseth
PT = Paul Taylor
S2T = Steve Taylor
BT = Brad Templeton
DST = Daniel S. Thibadeau
MT = Mark Thompson
S3T = Steffen Thorsen
KT = Karsten Thygesen
BET = Bennett E. Todd
JMT = John M. Twilley
ST = Steve Tylock
TU = Tetsu Ushijima
VV = Vince Vielhaber
TV = Tommi Virtanen
FW = Frank Wagner
BW = Bill Weinman
IW = Ian Westcott
SJW = Stephen J. White
JW = John Whittaker
LW = Lionel Widdifield
MW = Mate Wierdl
BTW = Brian T. Wightman
PW = Peter Wilkinson
HW = Hal Wine
GAW = Greg A. Woods
SCW = Steven C. Work
JLW = Jason L. Wright
WW = Wei Wu
IKW = Ian Keith Wynne
BZ = Blaz Zupan

437
THOUGHTS Normal file
View file

@ -0,0 +1,437 @@
Please note that this file is not called ``Internet Mail For Dummies.''
It _records_ my thoughts on various issues. It does not _explain_ them.
Paragraphs are not organized except by section. The required background
varies wildly from one paragraph to the next.
In this file, ``sendmail'' means Allman's creation; ``sendmail-clone''
means the program in this package.
1. Security
There are lots of interesting remote denial-of-service attacks on any
mail system. A long-term solution is to insist on prepayment for
unauthorized resource use. The tricky technical problem is to make the
prepayment enforcement mechanism cheaper than the expected cost of the
attacks. (For local denial-of-service attacks it's enough to be able to
figure out which user is responsible.)
qmail-send's log was originally designed for profiling. It subsequently
sprouted some tracing features. However, there's no way to verify
securely that a particular message came from a particular local user;
how do you know the recipient is telling you the truth about the
contents of the message? With QUEUE_EXTRA it'd be possible to record a
one-way hash of each outgoing message, but a user who wants to send
``bad'' mail can avoid qmail entirely.
I originally decided on security grounds not to put qmail advertisements
into SMTP responses: advertisements often act as version identifiers.
But this problem went away when I found a stable qmail URL.
As qmail grows in popularity, the mere knowledge that rcpthosts is so
easily available will deter people from setting up unauthorized MXs.
(I've never seen an unauthorized MX, but I can imagine that it would be
rather annoying.) Note that, unlike the bat book checkcompat() kludge,
rcpthosts doesn't interfere with mailing lists.
qmail-start doesn't bother with tty dissociation. On some old machines
this means that random people can send tty signals to the qmail daemons.
That's a security flaw in the job control subsystem, not in qmail.
The resolver library isn't too bloated (before 4.9.4, at least), but it
uses stdio, which _is_ bloated. Reading /etc/resolv.conf costs lots of
memory in each qmail-remote process. So it's tempting to incorporate a
smaller resolver library into qmail. (Bonus: I'd avoid system-specific
problems with old resolvers.) The problem is that I'd then be writing a
fundamentally insecure library. I'd no longer be able to blame the BIND
authors and vendors for the fact that attackers can easily use DNS to
steal mail. Possible solution: replace dns.c with something that passes
requests (reliably!) to a local daemon; call the original resolver
library from that daemon.
NFS is the primary enemy of security partitioning under UNIX. Here's the
story. Sun knew from the start that NFS was completely insecure. It
tried to hide that fact by disallowing root access over NFS. Intruders
nevertheless broke into system after system, first obtaining bin access
and then obtaining root access. Various people thus decided to compound
Sun's error and build a wall between root and all other users: if all
system files are owned by root, and if there are no security holes other
than NFS, someone who breaks in via NFS won't be able to wipe out the
operating system---he'll merely be able to wipe out all user files. This
clueless policy means that, for example, all the qmail users have to be
replaced by root. See what I mean by ``enemy''? ... Basic NFS comments:
Aside from the cryptographic problem of having hosts communicate
securely, it's obvious that there's an administrative problem of mapping
client uids to server uids. If a host is secure and under your control,
you shouldn't have to map anything. If a host is under someone else's
control, you'll want to map his uids to one local account; it's his
client's job to decide which of his users get to talk NFS in the first
place. Sun's original map---root to nobody, everyone else left alone---
is, as far as I can tell, always wrong.
2. Injecting mail locally (qmail-inject, sendmail-clone)
RFC 822 section 3.4.9 prohibits certain visual effects in headers.
qmail-inject doesn't waste the time to enforce this absurd restriction.
If you will suffer from someone sending you ``flash mail,'' go find a
better mail reader.
qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops
sendmail from adding Apparently-To. Unfortunately, old versions of
sendmail will append a host name. This wasn't fixed until sendmail 8.7.
How many years has it been since RFC 822 came out?
sendmail discards duplicate addresses. This has probably resulted in
more lost and stolen mail over the years than the entire Chicago branch
of the United States Postal Service. The qmail system delivers messages
exactly as it's told to do. Along the same lines: qmail-inject is both
unable and unwilling to support anything like sendmail's (default)
nometoo option. Of course, a list manager could support nometoo.
There should be a mechanism in qmail-inject that does for envelope
recipients what Return-Path does for the envelope sender. Then
qmail-inject -n could print the recipients.
Should qmail-inject bounce messages with no recipients? Should there be
an option for this? If it stays as is (accept the message), qmail-inject
could at least avoid invoking qmail-queue.
It is possible to extract non-unique Message-IDs out of qmail-inject.
Here's how: stop qmail-inject before it gets to the third line of
main(), then wait until the pids wrap around, then restart qmail-inject
and blast the message through, then start another qmail-inject with the
same pid in the same second. I'm not sure how to fix this. (Of course,
the user could just type in his own non-unique Message-IDs.)
The bat book says: ``Rules that hide hosts in a domain should be applied
only to sender addresses.'' Recipient masquerading works fine with
qmail. None of sendmail's pitfalls apply, basically because qmail has a
straight paper path.
I expect to receive some pressure to make up for the failings of MUA
writers who don't understand the concept of reliability. (``Like, duh,
you mean I was supposed to check the sendmail exit code?'')
3. Receiving mail from the network (tcp-env, qmail-smtpd)
RFC 1123 requires VRFY support, but says that it's okay if an
implementation can be configured to not allow VRFY. qmail-smtpd doesn't
allow VRFY. If you desperately want your SMTP server (i.e., inetd) to
provide useful information for VRFY, just compile and install sendmail.
Were the RFC 1123 writers aware of the as-if principle of interface
specification? ... They say that VRFY and EXPN are important for
tracking down cross-host mailing list loops. Catch up to the 1990s,
guys: with Delivered-To, mailing list loops do absolutely no damage,
_and_ one of the list administrators gets a bounce that shows exactly
how the loop occurred. Solve the problem, not the symptom. ... There's a
vastly superior alternative to EXPN. Hint: finger postmaster@ai.mit.edu.
Should dns.c make special allowances for 127.0.0.1/localhost?
badmailfrom (like 8BITMIME) is a waste of code space.
4. Adding messages to the queue (qmail-queue)
Should qmail-queue try to make sure enough disk space is free in
advance? When qmail-queue is invoked by qmail-local or (with ESMTP)
qmail-smtpd or qmail-qmtpd, it could be told a size in advance. I wish
UNIX had an atomic allocate-disk-space routine...
The qmail.h interface (reflecting the qmail-queue interface, which in
turn reflects the current queue file structure) is constitutionally
incapable of handling an address that contains a 0 byte. I can't imagine
that this will be a problem.
Should qmail-queue not bother queueing a message with no recipients?
5. Handling queued mail (qmail-send, qmail-clean)
The queue directory must be local. Mounting it over NFS is extremely
dangerous---not that this stops people from running sendmail that way!
Perhaps it is worth putting together a diskless-host qmail package with
just qmail-inject and an SMTP client in place of qmail-queue. Sending
mail to the server via SMTP is of course vastly better than trying to do
anything over NFS. If the NFS server is up but the mail server is down,
users will just have to wait.
Queue reliability demands that single-byte writes be atomic. This is
true for a fixed-block filesystem such as UFS, and for a logging
filesystem such as LFS.
qmail-send uses 8 bytes of memory per queued message. Double that for
reallocation. (Fix: use a small forest of heaps; i.e., keep several
prioqs.) Double again for buddy malloc()s. (Fix: be clever about the
heap sizes.) 32 bytes is worrisome, but not devastating. Even on my
disk-heavy memory-light machine, I'd run out of inodes long before
running out of memory.
Some mail systems organize the queue by host. This is pointless as a
means of splitting up the queue directory. The real issue is what to do
when you suddenly find out that a host is up. For local SLIP/PPP links
you know in advance which hosts need this treatment, so you can handle
them with virtualdomains and serialmail.
For the old queue structure I implemented recipient list compression:
if mail goes out to a giant mailing list, and most of the recipients are
delivered, make a new, compressed, todo list. But this really isn't
worth the effort: it saves only a tiny bit of CPU time.
qmail-send doesn't have any notions of precedence, priority, fairness,
importance, etc. It handles the queue in first-seen-first-served order.
One could put a lot of work into doing something different, but that
work would be a waste: given the triggering mechanism and qmail's
deferral strategy, it is exceedingly rare for the queue to contain more
than one deliverable message at any given moment.
Exception: Even with all the concurrency tricks, qmail-send can end up
spending a few minutes on a mailing list with thousands of remote
entries. A user might send a new message to a remote address in the
meantime. Perhaps qmail-send should limit its time per message to,
say, thirty recipients. This will require some way to mark recipients
who were already done on this pass. Possible approach: Maintain two todo
lists (for both L and R). Always work on the earlier todo list. Move
deferrals to the other todo list.
qmail-send will never start a pass for a job that it already has. This
means that, if one delivery takes longer than the retry interval, the
next pass will be delayed. I implemented the opposite strategy for the
old queue structure. Some hassles: mark() had to understand how job
input was buffered; every new delivery had to check whether the same
mpos in the same message was already being done.
Some things that qmail-send does synchronously: queueing a bounce
message; doing a cleanup via qmail-clean; classifying and rewriting all
the addresses in a new message. As usual, making these asynchronous
would require some housekeeping, but could speed things up a bit.
(Making bounces asynchronous, without POSIX waitpid(), means that
wait_pid() has to keep a buffer of previous wait()s. Ugh.)
fsync() is a bottleneck. To make this asynchronous would require gobs of
dedicated output processes whose only purpose in life is to watch data
get written to the disk. Inconceivable! (``You keep using that word. I
do not think that word means what you think it means.'')
On the other hand, I could survive without fsync()ing the local and
remote and info files as long as I don't unlink todo. This would require
redefining the queue states. I need to see how much speed can be gained.
Currently qmail-send sends at most one bounce message for each incoming
message. This means that the sender doesn't get flooded with copies of
his own message. On the other hand, a single slow address can hold up
bounces for a bunch of fast addresses. It would be easy to call
injectbounce() more often. What is the best strategy? This feels like
the TCP-buffering issue... don't want to pepper the other guy with
little packets, but do want to get the data across.
qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Given how
simple this is, I'm not inclined to set up some tricky locking solution
where qmail-send records its pid etc. But I just know that, if I provide
this qmail-stop program, someone will screw himself by making another
uid the same as UID_SEND, or making UID_SEND be root, or whatever.
Aargh. Maybe use another named pipe... New solution: Run qmail-start
under an external service controller---it runs in the foreground now.
Bounce messages could include more statistical information in the first
paragraph: when I received the message, how many recipients I was
supposed to handle, how many I successfully dealt with, how many I
already told you about, how many are still in the queue. Have to
emphasize that the number of recipients _here_ is perhaps less than the
number of recipients on the original message.
The readdir() interface hides I/O errors. Lower-level interfaces would
lead me into a thicket of portability problems. I'm really not sure what
to do about this. Of course, a hard I/O error means that mail is toast,
but a soft I/O error shouldn't cause any trouble.
job_open() or pass_dochan() could be paranoid about the same id,channel
already being open; but, since messdone() is so paranoid, the worst
possible effect of a bug along these lines would be double delivery.
Mathematical amusement: The optimal retry schedule is essentially,
though not exactly, independent of the actual distribution of message
delay times. What really matters is how much cost you assign to retries
and to particular increases in latency. qmail's current quadratic retry
schedule says that an hour-long delay in a day-old message is worth the
same as a ten-minute delay in an hour-old message; this doesn't seem so
unreasonable.
Insider information: AOL retries their messages every five minutes for
three days straight. Hmmm.
6. Sending mail through the network (qmail-rspawn, qmail-remote)
Are there any hosts, anywhere, whose mailers are bogged down by huge
messages to multiple recipients at a single host? For typical hosts,
multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a
_slowness_ feature. Separate SMTP transactions have much lower latency.
The multiple-RCPT bandwidth gain _might_ be noticeable for a machine
that sends most messages to a smarthost. It would be easy to have
qmail-rspawn supply qmail-remote with all the addresses at once, as long
as qmail-send says when it's about to block... Putting recipients into
the right order is clearly the UA's job. One multiple-RCPT pitfall is
that a remote host might not be able to deal with (say) 10 recipients,
even though RFC 821 says everyone has to be able to handle 100;
qmail-rspawn would have to notice this and back off. (Not that other
mailers do. Sometimes I'm amazed Internet mail works at all.)
In the opposite direction: It's tempting to remove the @host part of the
qmail-remote recip argument. Or at least avoid double-dns_cname.
There are lots of reasons that qmail-rspawn should take a more active
role in qmail-remote's activities. It should call separate programs to
do (1) MX lookups, (2) SMTP connections, (3) QMTP connections.
I bounce ambiguous MXs. (An ``ambiguous MX'' is a best-preference MX
record sending me mail for a host that I don't recognize as local.)
Automatically treating ambiguous MXs as local is incompatible with my
design decision to keep local delivery working when the network goes
down. It puts more faith in DNS than DNS deserves. Much better: Have
your MX records generated automatically from control/locals.
If I successfully connect to an MX host but it temporarily refuses to
accept the message, I give up and put the message back into the queue.
But several documents seem to suggest that I should try further MX
records. What are they thinking? My approach deals properly with downed
hosts, hosts that are unreachable through a firewall, and load
balancing; what else do people use multiple MX records for?
Currently qmail-remote sends data in 1024-byte buffers. Perhaps it
should try to take account of the MTU.
Perhaps qmail-remote should allocate a fixed amount of DNS/connect()
time across any number of MXs; this idea is due to Mark Delany.
RFC 821 doesn't say what it means by ``text.'' qmail-remote assumes that
the server's reply text doesn't contain bare LFs.
7. Delivering mail locally (qmail-lspawn, qmail-local)
qmail-local doesn't support comsat. comsat is a pointless abomination.
Use qbiff if you want that kind of notification.
The getpwnam() interface hides I/O errors. Solution: qmail-pw2u.
8. sendmail V8's new features
sendmail-8.8.0/doc/op/op.me includes a list of big improvements of
sendmail 8.8.0 over sendmail 5.67. Here's how qmail stacks up against
each of those improvements. (Of course, qmail has its own improvements,
but that's not the point of this list.)
Connection caching, MX piggybacking: Nope. (Profile. Don't speculate.)
Response to RCPT command is fast: Yup.
IP addresses show up in Received lines: Yup.
Self domain literal is properly handled: Yup.
Different timeouts for QUIT, RCPT, etc.: No, just a single timeout.
Proper <> handling, route-address pruning: Yes, but not configurable.
ESMTP support: Yup. (Server-side, including PIPELINING.)
8-bit clean: Yup. (Including server-side 8BITMIME support; same as
sendmail with the 8 option.)
Configurable user database: Yup.
BIND support: Yup.
Keyed files: Yes, in qmsmac.
931/1413/Ident/TAP: Yup.
Correct 822 address list parsing: Yup. (Note that sendmail still has
some major problems with quoting.)
List-owner handling: Yup.
Dynamic header allocation: Yup.
Minimum number of disk blocks: Yes, via tunefs -m.
Checkpointing: Yes, but not configurable---qmail always checkpoints.
Error message configuration: Nope.
GECOS matching: Not directly, but easy to hook in.
Hop limit configuration: No. (qmail's limit is 100 hops. qmail offers
automatic loop protection much more advanced than hop counting.)
MIME error messages: No. (qmail uses QSBMF error messages, which are
much easier to parse.)
Forward file path: Yes, via /etc/passwd.
Incoming SMTP configuration: Yes, via inetd or tcpserver.
Privacy options: Yes, but they're not options.
Best-MX mangling: Nope. See section 6 for further discussion.
7-bit mangling: Nope. qmail always uses 8 bits.
Support for up to 20 MX records: Yes, and more. qmail has no limits
other than memory.
Correct quoting of name-and-address headers: Yup.
VRFY and EXPN now different: Nope. qmail always hides this information.
Multi-word classes, deferred macro expansion, separate envelope/header
$g processing, separate per-mailer envelope and header processing, new
command line flags, new configuration lines, new mailer flags, new
macros: These are sendmail-specific; they wouldn't even make sense for
qmail. For example, _of course_ qmail handles envelopes and headers
separately; they're almost entirely different objects!
9. Miscellany
sendmail-clone and qsmhook are too bletcherous to be documented. (The
official replacement for qsmhook is preline, together with the
qmail-command environment variables.)
I've considered making install atomic, but this is very difficult to do
right, and pointless if it isn't done right.
RN suggests automatically putting together a reasonable set of lines for
/etc/passwd. I perceive this as getting into the adduser business, which
is worrisome: I'll be lynched the first time I screw up somebody's
passwd file. This should be left to OS-specific installation scripts.
The BSD 4.2 inetd didn't allow a username. I think I can safely forget
about this. (DS notes that the username works under Ultrix even though
it's undocumented.)
I should clean up the bput/put choices.
Some of the stralloc_0()s indicate that certain lower-level routines
should grok stralloc.
RN suggests having qlist smash the case of the incoming host name.
K1J suggests that mailing list subscription managers should have
a three-way handshake, to prevent person A from subscribing person B to
a mailing list. qlist doesn't do this, but ezmlm does.
qmail assumes that all times are positive; that pid_t, time_t and ino_t
fit into unsigned long; that gid_t fits into int; that the character set
is ASCII; and that all pointers are interchangeable. Do I care?
The bat book justifies sendmail's insane line-splitting mechanism by
pointing out that it might be useful for ``a 40-character braille
print-driving program.'' C'mon, guys, is that your best excuse?
qmail's mascot is a dolphin.

9
TODO Normal file
View file

@ -0,0 +1,9 @@
do some serious coverage testing, preferably under Purify, without alloc slop
replace INTERNALS and THOUGHTS with a real paper describing qmail
reorganize qmail-inject to do most rw on characters, not tokens
allow concurrency over 255
handle IPv6
turn qmail-upq into a more serious queue-moving utility
expand strerr coverage
rewrite everything from scratch
maybe allow root to clear tcpto on the fly

145
UPGRADE Normal file
View file

@ -0,0 +1,145 @@
SAVE COPIES OF YOUR OUTGOING MAIL! Like any other piece of software (and
information generally), the qmail system comes with NO WARRANTY. It's
much more secure and reliable than sendmail, but that's not saying much.
Here's how to upgrade from qmail 1.00 to qmail 1.01. This procedure will
overwrite the old qmail binaries. Furthermore, it may begin delivering
messages from the queue before you have had a chance to test it.
WARNING: The qmail-start command line has changed.
Before starting, compare conf* to your old conf*, and make any necessary
changes. Do not copy your old conf*; the baseline has changed.
How to install:
1. Compile the programs:
# make
2. Create the formatted man pages, *.0:
# make man
3. Inform your users that mail will not be accepted for a few minutes.
4. Disable deliveries by killing your old qmail-send. Wait for it to
print ``exiting'' in the log.
5. Disable SMTP service by commenting out the smtp line in inetd.conf;
kill -HUP your inetd. (If you are using tcpserver, simply kill -STOP
your tcpserver.) Wait for current qmail-smtpd processes to die. (If
you are running a QMTP server, disable that too.)
6. Install the new binaries and man pages:
# rm /var/qmail/bin/* /var/qmail/man/*/*
# make setup
7. Run instcheck to make sure it doesn't print any warnings:
# make check
8. Reenable deliveries:
# env - PATH="/var/qmail/bin:$PATH" \
qmail-start ./Mailbox splogger qmail &
Make sure to include the ./ in ./Mailbox.
9. Insert ./Mailbox into the qmail-start line in your boot scripts.
10. Reenable SMTP service by restoring the smtp line in inetd.conf; kill
-HUP your inetd. (If you are using tcpserver, simply kill -CONT your
tcpserver. If you are running a QMTP server, reenable that too.)
How to test (steps 11-17 can be done before step 10):
11. Look for a
qmail: running
line in syslog. (The big number is a splogger timestamp.)
12. Local-local test: Send yourself an empty message. (Replace ``me''
with your username. Make sure to include the ``to:'' colon.)
% echo to: me | /var/qmail/bin/qmail-inject
The message will show up immediately in ~/Mailbox, and syslog will
show something like this:
qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20345 uid 666
qmail: starting delivery 1: msg 53 to local me@domain
qmail: delivery 1: success: did_1+0+0/
qmail: end msg 53
(53 is an inode number; 20345 is a process ID; your numbers will
probably be different.)
13. Local-error test: Send a message to a nonexistent local address.
% echo to: nonexistent | /var/qmail/bin/qmail-inject
qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20351 uid 666
qmail: starting delivery 2: msg 53 to local nonexistent@domain
qmail: delivery 2: failure: No_such_address.__#5.1.1_/
qmail: bounce msg 53 qp 20357
qmail: end msg 53
qmail: new msg 54
qmail: info msg 54: bytes 743 from <> qp 20357 uid 666
qmail: starting delivery 3: msg 54 to local me@domain
qmail: delivery 3: success: did_1+0+0/
qmail: end msg 54
You will now have a bounce message in ~/Mailbox.
14. Local-remote test: Send an empty message to your account on another
machine.
% echo to: me@wherever | /var/qmail/bin/qmail-inject
qmail: new msg 53
qmail: info msg 53: bytes 246 from <me@domain> qp 20372 uid 666
qmail: starting delivery 4: msg 53 to remote me@wherever
qmail: delivery 4: success: 1.2.3.4_accepted_message./...
qmail: end msg 53
There will be a pause between ``starting delivery'' and ``success'';
SMTP is slow. Check that the message is in your mailbox on the other
machine.
15. Local-postmaster test: Send mail to postmaster, any capitalization.
% echo to: POSTmaster | /var/qmail/bin/qmail-inject
Look for the message in ~alias/Mailbox.
16. Double-bounce test: Send a message with a completely bad envelope.
% /var/qmail/bin/qmail-inject -f nonexistent
To: unknownuser
Subject: testing
This is a test. This is only a test.
%
(Use end-of-file, not dot, to end the message.) Look for the double
bounce in ~alias/Mailbox.
17. Group membership test:
% cat > ~me/.qmail-groups
|groups >> MYGROUPS; exit 0
% /var/qmail/bin/qmail-inject me-groups < /dev/null
% cat ~me/MYGROUPS
MYGROUPS will show your normal gid and nothing else. (Under Solaris,
make sure to use /usr/ucb/groups; /usr/bin/groups is broken.)
18. SMTP server test: Forge some mail locally via SMTP.
% telnet 127.0.0.1 25
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
220 domain ESMTP
helo dude
250-domain
250-PIPELINING
250 8BITMIME
mail <me@domain>
250 ok
rcpt <me@domain>
250 ok
data
354 go ahead
Subject: testing
This is a test.
.
250 ok 812345679 qp 12345
quit
221 domain
Connection closed by foreign host.
%
Look for the message in your mailbox.
19. Remote-local test: Send yourself some mail from another machine.
20. Remote-error test: I think you can figure this one out.
21. UA test: Try sending mail, first to a local account, then to a
remote account, with your normal user agent.
22. Remote-postmaster test: Send mail from another machine to
PoStMaStEr@domain. Look for the message in ~alias/Mailbox.
That's it! To report success:
% ( echo 'First M. Last'; cat `cat SYSDEPS` ) \
| mail djb-qst@koobera.math.uic.edu
Replace First M. Last with your name. If you have questions about qmail,
contact qmail@pobox.com.

1
VERSION Normal file
View file

@ -0,0 +1 @@
qmail 1.01

260
addresses.5 Normal file
View file

@ -0,0 +1,260 @@
.TH addresses 5
.SH "NAME"
addresses \- formats for Internet mail addresses
.SH "INTRODUCTION"
A
.B mail address
is a string of characters containing @.
Every mail address has a
.B local part
and a
.B domain part\fR.
The domain part is everything after the final @.
The local part is everything before.
For example, the mail addresses
.EX
God@heaven.af.mil
@heaven.af.mil
@at@@heaven.af.mil
.EE
all have domain part
.BR heaven.af.mil .
The local parts are
.BR God ,
empty,
and
.BR @at@ .
Some domains have owners.
It is up to the owner of
.B heaven.af.mil
to say how mail messages will be delivered to addresses with domain part
.BR heaven.af.mil .
The domain part of an address is interpreted without regard to case, so
.EX
God@heaven.af.mil
.br
God@HEAVEN.AF.MIL
.br
God@Heaven.AF.Mil
.EE
all refer to the same domain.
There is one exceptional address that does not contain an @:
namely, the empty string.
The empty string cannot be used as a recipient address.
It can be used as a sender address so that
the real sender doesn't receive bounces.
.SH "QMAIL EXTENSIONS"
The
.B qmail
system allows several further types of addresses in mail envelopes.
First, an envelope recipient address without an @ is interpreted as being at
.IR envnoathost .
For example, if
.I envnoathost
is
.BR heaven.af.mil ,
the address
.B God
will be rewritten as
.BR God@heaven.af.mil .
Second, the address
.B #@[]
is used as an envelope sender address for double bounces.
Third, envelope sender addresses of the form
.I pre\fB@\fIhost\fB-@[]
are used to support variable envelope return paths (VERPs).
.B qmail-send
will rewrite
.I pre\fB@\fIhost\fB-@[]
as
.I prerecip\fB=\fIdomain\fB@\fIhost
for deliveries to
.IR recip\fB@\fIdomain .
Bounces directly from
.B qmail-send
will come back to
.IR pre\fB@\fIhost .
.SH "CHOOSING MAIL ADDRESSES"
Here are some suggestions on choosing mail addresses for the Internet.
Do not use non-ASCII characters.
Under RFC 822 and RFC 821,
these characters cannot be used in mail headers or in SMTP commands.
In practice, they are regularly corrupted.
Do not use ASCII control characters.
NUL is regularly corrupted.
CR and LF cannot be used in some combinations
and are corrupted in all.
None of these characters are usable on business cards.
Avoid spaces and the characters
.EX
\\"<>()[],;:
.EE
These all require quoting in mail headers and in SMTP.
Many existing mail programs do not handle quoting properly.
Do not use @ in a local part.
@ requires quoting in mail headers and in SMTP.
Many programs incorrectly look for the first @,
rather than the last @,
to find the domain part of an address.
In a local part,
do not use two consecutive dots, a dot at the beginning, or a dot at the end.
Any of these would require quoting in mail headers.
Do not use an empty local part; it cannot appear in SMTP commands.
Avoid local parts longer than 64 characters.
Be wary of uppercase letters in local parts.
Some mail programs (and users!) will incorrectly convert
.B God@heaven.af.mil
to
.BR god@heaven.af.mil .
Be wary of the following characters:
.EX
$&!#~`'^*|{}
.EE
Some users will not know
how to feed these characters safely to their mail programs.
In domain names, stick to letters, digits, dash, and dot.
One popular DNS resolver has,
under the banner of security,
recently begun destroying domain names
that contain certain other characters,
including underscore.
Exception: A dotted-decimal IP address in brackets,
such as
.BR [127.0.0.1] ,
identifies a domain owned by whoever owns the host at that IP address,
and can be used safely.
In a domain name,
do not use two consecutive dots,
a dot at the beginning,
or a dot at the end.
This means that,
when a domain name is broken down into components separated by dots,
there are no empty components.
Always use at least one dot in a domain name.
If you own the
.B mil
domain,
don't bother using the address
.BR root@mil ;
most users will be unable to send messages to that address.
Same for the root domain.
Avoid domain names longer than 64 characters.
.SH "ENCODED ADDRESSES IN SMTP COMMANDS"
RFC 821 defines an encoding of mail addresses in SMTP.
For example, the addresses
.EX
God@heaven.af.mil
.br
a"quote@heaven.af.mil
.br
The Almighty.One@heaven.af.mil
.EE
could be encoded in RCPT commands as
.EX
RCPT TO:<God@heaven.af.mil>
.br
RCPT TO:<a\\"quote@heaven.af.mil>
.br
RCPT TO:<The\\ Almighty.One@heaven.af.mil>
.EE
There are several restrictions in RFC 821
on the mail addresses that can be used over SMTP.
Non-ASCII characters are prohibited.
The local part must not be empty.
The domain part must be a sequence of elements separated by dots,
where each element is either a component,
a sequence of digits preceded by #,
or a dotted-decimal IP address surrounded by brackets.
The only allowable characters in components are
letters, digits, and dashes.
Every component must (believe it or not)
have at least three characters;
the first character must be a letter;
the last character must not be a hyphen.
.SH "ENCODED ADDRESSES IN MAIL HEADERS"
RFC 822 defines an encoding of mail addresses
in certain header fields in a mail message.
For example, the addresses
.EX
God@heaven.af.mil
.br
a"quote@heaven.af.mil
.br
The Almighty.One@heaven.af.mil
.EE
could be encoded in a
.B To
field as
.EX
To: God@heaven.af.mil,
.br
<@brl.mil:"a\\"quote"@heaven.af.mil>,
.br
"The Almighty".One@heaven.af.mil
.EE
or perhaps
.EX
To: < "God"@heaven .af.mil>,
.br
"a\\"quote" (Who?) @ heaven . af. mil
.br
, God<"The Almighty.One"@heaven.af.mil>
.EE
There are several restrictions on the mail addresses that can
be used in these header fields.
Non-ASCII characters are prohibited.
The domain part must be a sequence of elements separated by dots,
where each element either (1) begins with [ and ends with ]
or (2) is a nonempty string of printable ASCII characters
not including any of
.EX
\\".<>()[],;:
.EE
and not including space.
.SH "SEE ALSO"
envelopes(5),
qmail-header(5),
qmail-inject(8),
qmail-remote(8),
qmail-smtpd(8)

62
alloc.3 Normal file
View file

@ -0,0 +1,62 @@
.TH alloc 3
.SH NAME
alloc \- allocate memory
.SH SYNTAX
.B #include <alloc.h>
char *\fBalloc\fP(\fInew\fR);
void \fBalloc_free\fP(\fIx\fR);
void \fBalloc_re\fP(&\fIx\fR,\fIold\fR,\fInew\fR);
char *\fIx\fR;
.br
unsigned int \fIold\fR;
.br
unsigned int \fInew\fR;
.SH DESCRIPTION
.B alloc
allocates enough space from the heap for
.I new
bytes of data,
adequately aligned for any data type.
.I new
may be 0.
.B alloc
returns a pointer to the space.
If space is not available,
.B alloc
returns 0,
setting
.B errno
appropriately.
.B alloc_free
returns space to the heap.
.B alloc_re
expands the space allocated to
.I x
from
.I old
bytes to
.I new
bytes.
It allocates new space,
copies
.I old
bytes from the old space to the new space,
returns the old space to the heap,
and changes
.I x
to point to the new space.
It then returns 1.
If space is not available,
.B alloc_re
returns 0,
leaving the old space alone.
.SH "SEE ALSO"
sbrk(2),
malloc(3),
error(3)

32
alloc.c Normal file
View file

@ -0,0 +1,32 @@
#include "alloc.h"
#include "error.h"
extern char *malloc();
extern void free();
#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
#define SPACE 4096 /* must be multiple of ALIGNMENT */
typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
static aligned realspace[SPACE / ALIGNMENT];
#define space ((char *) realspace)
static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
/*@null@*//*@out@*/char *alloc(n)
unsigned int n;
{
char *x;
n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
if (n <= avail) { avail -= n; return space + avail; }
x = malloc(n);
if (!x) errno = error_nomem;
return x;
}
void alloc_free(x)
char *x;
{
if (x >= space)
if (x < space + SPACE)
return; /* XXX: assuming that pointers are flat */
free(x);
}

8
alloc.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef ALLOC_H
#define ALLOC_H
extern /*@null@*//*@out@*/char *alloc();
extern void alloc_free();
extern int alloc_re();
#endif

17
alloc_re.c Normal file
View file

@ -0,0 +1,17 @@
#include "alloc.h"
#include "byte.h"
int alloc_re(x,m,n)
char **x;
unsigned int m;
unsigned int n;
{
char *y;
y = alloc(n);
if (!y) return 0;
byte_copy(y,m,*x);
alloc_free(*x);
*x = y;
return 1;
}

51
auto-gid.c Normal file
View file

@ -0,0 +1,51 @@
#include <sys/types.h>
#include <grp.h>
#include "subfd.h"
#include "substdio.h"
#include "readwrite.h"
#include "exit.h"
#include "scan.h"
#include "fmt.h"
char buf1[256];
substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
void outs(s)
char *s;
{
if (substdio_puts(&ss1,s) == -1) _exit(111);
}
void main(argc,argv)
int argc;
char **argv;
{
char *name;
char *value;
struct group *gr;
char strnum[FMT_ULONG];
name = argv[1];
if (!name) _exit(100);
value = argv[2];
if (!value) _exit(100);
gr = getgrnam(value);
if (!gr) {
substdio_puts(subfderr,"fatal: unable to find group ");
substdio_puts(subfderr,value);
substdio_puts(subfderr,"\n");
substdio_flush(subfderr);
_exit(111);
}
strnum[fmt_ulong(strnum,(unsigned long) gr->gr_gid)] = 0;
outs("int ");
outs(name);
outs(" = ");
outs(strnum);
outs(";\n");
if (substdio_flush(&ss1) == -1) _exit(111);
_exit(0);
}

40
auto-int.c Normal file
View file

@ -0,0 +1,40 @@
#include "substdio.h"
#include "readwrite.h"
#include "exit.h"
#include "scan.h"
#include "fmt.h"
char buf1[256];
substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
void puts(s)
char *s;
{
if (substdio_puts(&ss1,s) == -1) _exit(111);
}
void main(argc,argv)
int argc;
char **argv;
{
char *name;
char *value;
unsigned long num;
char strnum[FMT_ULONG];
name = argv[1];
if (!name) _exit(100);
value = argv[2];
if (!value) _exit(100);
scan_ulong(value,&num);
strnum[fmt_ulong(strnum,num)] = 0;
puts("int ");
puts(name);
puts(" = ");
puts(strnum);
puts(";\n");
if (substdio_flush(&ss1) == -1) _exit(111);
_exit(0);
}

40
auto-int8.c Normal file
View file

@ -0,0 +1,40 @@
#include "substdio.h"
#include "readwrite.h"
#include "exit.h"
#include "scan.h"
#include "fmt.h"
char buf1[256];
substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
void puts(s)
char *s;
{
if (substdio_puts(&ss1,s) == -1) _exit(111);
}
void main(argc,argv)
int argc;
char **argv;
{
char *name;
char *value;
unsigned long num;
char strnum[FMT_ULONG];
name = argv[1];
if (!name) _exit(100);
value = argv[2];
if (!value) _exit(100);
scan_8long(value,&num);
strnum[fmt_ulong(strnum,num)] = 0;
puts("int ");
puts(name);
puts(" = ");
puts(strnum);
puts(";\n");
if (substdio_flush(&ss1) == -1) _exit(111);
_exit(0);
}

44
auto-str.c Normal file
View file

@ -0,0 +1,44 @@
#include "substdio.h"
#include "readwrite.h"
#include "exit.h"
char buf1[256];
substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
void puts(s)
char *s;
{
if (substdio_puts(&ss1,s) == -1) _exit(111);
}
void main(argc,argv)
int argc;
char **argv;
{
char *name;
char *value;
unsigned char ch;
char octal[4];
name = argv[1];
if (!name) _exit(100);
value = argv[2];
if (!value) _exit(100);
puts("char ");
puts(name);
puts("[] = \"\\\n");
while (ch = *value++) {
puts("\\");
octal[3] = 0;
octal[2] = '0' + (ch & 7); ch >>= 3;
octal[1] = '0' + (ch & 7); ch >>= 3;
octal[0] = '0' + (ch & 7);
puts(octal);
}
puts("\\\n\";\n");
if (substdio_flush(&ss1) == -1) _exit(111);
_exit(0);
}

51
auto-uid.c Normal file
View file

@ -0,0 +1,51 @@
#include <sys/types.h>
#include <pwd.h>
#include "subfd.h"
#include "substdio.h"
#include "readwrite.h"
#include "exit.h"
#include "scan.h"
#include "fmt.h"
char buf1[256];
substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
void outs(s) /* was named puts, but Solaris pwd.h includes stdio.h. dorks. */
char *s;
{
if (substdio_puts(&ss1,s) == -1) _exit(111);
}
void main(argc,argv)
int argc;
char **argv;
{
char *name;
char *value;
struct passwd *pw;
char strnum[FMT_ULONG];
name = argv[1];
if (!name) _exit(100);
value = argv[2];
if (!value) _exit(100);
pw = getpwnam(value);
if (!pw) {
substdio_puts(subfderr,"fatal: unable to find user ");
substdio_puts(subfderr,value);
substdio_puts(subfderr,"\n");
substdio_flush(subfderr);
_exit(111);
}
strnum[fmt_ulong(strnum,(unsigned long) pw->pw_uid)] = 0;
outs("int ");
outs(name);
outs(" = ");
outs(strnum);
outs(";\n");
if (substdio_flush(&ss1) == -1) _exit(111);
_exit(0);
}

6
auto_break.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef AUTO_BREAK_H
#define AUTO_BREAK_H
extern char auto_break[];
#endif

6
auto_patrn.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef AUTO_PATRN_H
#define AUTO_PATRN_H
extern int auto_patrn;
#endif

6
auto_qmail.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef AUTO_QMAIL_H
#define AUTO_QMAIL_H
extern char auto_qmail[];
#endif

6
auto_spawn.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef AUTO_SPAWN_H
#define AUTO_SPAWN_H
extern int auto_spawn;
#endif

6
auto_split.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef AUTO_SPLIT_H
#define AUTO_SPLIT_H
extern int auto_split;
#endif

16
auto_uids.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef AUTO_UIDS_H
#define AUTO_UIDS_H
extern int auto_uida;
extern int auto_uidd;
extern int auto_uidl;
extern int auto_uido;
extern int auto_uidp;
extern int auto_uidq;
extern int auto_uidr;
extern int auto_uids;
extern int auto_gidn;
extern int auto_gidq;
#endif

6
auto_usera.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef AUTO_USERA_H
#define AUTO_USERA_H
extern char auto_usera[];
#endif

13
byte.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef BYTE_H
#define BYTE_H
extern unsigned int byte_chr();
extern unsigned int byte_rchr();
extern void byte_copy();
extern void byte_copyr();
extern int byte_diff();
extern void byte_zero();
#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
#endif

20
byte_chr.c Normal file
View file

@ -0,0 +1,20 @@
#include "byte.h"
unsigned int byte_chr(s,n,c)
char *s;
register unsigned int n;
int c;
{
register char ch;
register char *t;
ch = c;
t = s;
for (;;) {
if (!n) break; if (*t == ch) break; ++t; --n;
if (!n) break; if (*t == ch) break; ++t; --n;
if (!n) break; if (*t == ch) break; ++t; --n;
if (!n) break; if (*t == ch) break; ++t; --n;
}
return t - s;
}

14
byte_copy.c Normal file
View file

@ -0,0 +1,14 @@
#include "byte.h"
void byte_copy(to,n,from)
register char *to;
register unsigned int n;
register char *from;
{
for (;;) {
if (!n) return; *to++ = *from++; --n;
if (!n) return; *to++ = *from++; --n;
if (!n) return; *to++ = *from++; --n;
if (!n) return; *to++ = *from++; --n;
}
}

16
byte_cr.c Normal file
View file

@ -0,0 +1,16 @@
#include "byte.h"
void byte_copyr(to,n,from)
register char *to;
register unsigned int n;
register char *from;
{
to += n;
from += n;
for (;;) {
if (!n) return; *--to = *--from; --n;
if (!n) return; *--to = *--from; --n;
if (!n) return; *--to = *--from; --n;
if (!n) return; *--to = *--from; --n;
}
}

16
byte_diff.c Normal file
View file

@ -0,0 +1,16 @@
#include "byte.h"
int byte_diff(s,n,t)
register char *s;
register unsigned int n;
register char *t;
{
for (;;) {
if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
}
return ((int)(unsigned int)(unsigned char) *s)
- ((int)(unsigned int)(unsigned char) *t);
}

23
byte_rchr.c Normal file
View file

@ -0,0 +1,23 @@
#include "byte.h"
unsigned int byte_rchr(s,n,c)
char *s;
register unsigned int n;
int c;
{
register char ch;
register char *t;
register char *u;
ch = c;
t = s;
u = 0;
for (;;) {
if (!n) break; if (*t == ch) u = t; ++t; --n;
if (!n) break; if (*t == ch) u = t; ++t; --n;
if (!n) break; if (*t == ch) u = t; ++t; --n;
if (!n) break; if (*t == ch) u = t; ++t; --n;
}
if (!u) u = t;
return u - s;
}

13
byte_zero.c Normal file
View file

@ -0,0 +1,13 @@
#include "byte.h"
void byte_zero(s,n)
char *s;
register unsigned int n;
{
for (;;) {
if (!n) break; *s++ = 0; --n;
if (!n) break; *s++ = 0; --n;
if (!n) break; *s++ = 0; --n;
if (!n) break; *s++ = 0; --n;
}
}

100
case.3 Normal file
View file

@ -0,0 +1,100 @@
.TH case 3
.SH NAME
case \- convert ASCII uppercase bytes to lowercase
.SH SYNTAX
.B #include <case.h>
void \fBcase_lowers\fP(\fIs\fR);
.br
void \fBcase_lowerb\fP(\fIs\fR,\fIlen\fR);
int \fBcase_diffs\fP(\fIs\fR,\fIt\fR);
.br
int \fBcase_equals\fP(\fIs\fR,\fIt\fR);
.br
int \fBcase_starts\fP(\fIs\fR,\fIt\fR);
int \fBcase_diffb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
.br
int \fBcase_startb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
char *\fIs\fR;
.br
char *\fIt\fR;
.br
unsigned int \fIlen\fR;
.SH DESCRIPTION
.B case_lowers
converts each uppercase byte in the string
.I s
to lowercase.
.I s
must be 0-terminated.
.B case_lowerb
converts each uppercase byte in the buffer
.IR s ,
of length
.IR len ,
to lowercase.
.B case_diffs
lexicographically compares lowercase versions of the strings
.I s
and
.IR t .
It returns something positive, negative, or zero
when the first is larger than, smaller than, or equal to the second.
.I s
and
.I t
must be 0-terminated.
.B case_equals
means
.BR !case_diffs .
.B case_starts
returns 1 if a lowercase version of
.I s
starts with a lowercase version of
.IR t .
.I s
and
.I t
must be 0-terminated.
.B case_diffb
lexicographically compares lowercase versions of the buffers
.I s
and
.IR t ,
each of length
.IR len .
It returns something positive, negative, or zero
when the first is larger than, smaller than, or equal to the second.
.B case_startb
returns 1 if a lowercase version of the buffer
.IR s ,
of length
.IR len ,
starts with a lowercase version of the string
.IR t .
.I t
must be 0-terminated.
The
.B case
routines
are ASCII-specific.
They are suitable for programs that handle
case-independent networking protocols.
All comparisons are performed on unsigned bytes.
.SH "SEE ALSO"
byte_diff(3),
byte_equal(3),
str_diff(3),
str_equal(3),
str_start(3)

13
case.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef CASE_H
#define CASE_H
extern void case_lowers();
extern void case_lowerb();
extern int case_diffs();
extern int case_diffb();
extern int case_starts();
extern int case_startb();
#define case_equals(s,t) (!case_diffs((s),(t)))
#endif

21
case_diffb.c Normal file
View file

@ -0,0 +1,21 @@
#include "case.h"
int case_diffb(s,len,t)
register char *s;
unsigned int len;
register char *t;
{
register unsigned char x;
register unsigned char y;
while (len > 0) {
--len;
x = *s++ - 'A';
if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
y = *t++ - 'A';
if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
if (x != y)
return ((int)(unsigned int) x) - ((int)(unsigned int) y);
}
return 0;
}

19
case_diffs.c Normal file
View file

@ -0,0 +1,19 @@
#include "case.h"
int case_diffs(s,t)
register char *s;
register char *t;
{
register unsigned char x;
register unsigned char y;
for (;;) {
x = *s++ - 'A';
if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
y = *t++ - 'A';
if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
if (x != y) break;
if (!x) break;
}
return ((int)(unsigned int) x) - ((int)(unsigned int) y);
}

14
case_lowerb.c Normal file
View file

@ -0,0 +1,14 @@
#include "case.h"
void case_lowerb(s,len)
char *s;
unsigned int len;
{
unsigned char x;
while (len > 0) {
--len;
x = *s - 'A';
if (x <= 'Z' - 'A') *s = x + 'a';
++s;
}
}

12
case_lowers.c Normal file
View file

@ -0,0 +1,12 @@
#include "case.h"
void case_lowers(s)
char *s;
{
unsigned char x;
while (x = *s) {
x -= 'A';
if (x <= 'Z' - 'A') *s = x + 'a';
++s;
}
}

18
case_starts.c Normal file
View file

@ -0,0 +1,18 @@
#include "case.h"
int case_starts(s,t)
register char *s;
register char *t;
{
register unsigned char x;
register unsigned char y;
for (;;) {
x = *s++ - 'A';
if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
y = *t++ - 'A';
if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
if (!y) return 1;
if (x != y) return 0;
}
}

62
cdb.3 Normal file
View file

@ -0,0 +1,62 @@
.TH cdb 3
.SH NAME
cdb \- read from a constant database
.SH SYNTAX
.B #include <cdb.h>
int \fBcdb_seek(\fP\fIfd,key,len,dlen\fR\fB)\fP;
int \fIfd\fR;
.br
char *\fIkey\fR;
.br
unsigned int \fIlen\fR;
.br
uint32 *\fIdlen\fR;
.SH DESCRIPTION
.B cdb_seek
looks up
.I key
in a constant database.
It returns 1 if
.I key
is present,
0 if
.I key
is not present,
or \-1 if there was a read error.
.I key
is an array of
.I len
characters.
.B cdb_seek
needs an open file descriptor,
.IR fd ,
pointing to the database.
If
.B cdb_seek
returns 1,
it points
.I fd
at the beginning of the data portion of the first record
indexed by
.IR key ,
and it stores the data length in
.IR dlen.
.B cdb_seek
does not provide a way to read subsequent records with the same key.
It's fine to do several
.B cdb_seek
lookups with the same open file descriptor.
Beware, however, that two simultaneous
.B cdb_seek
lookups can fail horribly;
separate processes should not share the same database descriptor.
Furthermore, any updates after the database was opened
will be invisible.
It's rarely a good idea for a long-running program
to hold a database open.
.SH "SEE ALSO"
cdbget(1)

12
cdb.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef CDB_H
#define CDB_H
#include "uint32.h"
extern uint32 cdb_hash();
extern uint32 cdb_unpack();
extern int cdb_bread();
extern int cdb_seek();
#endif

16
cdb_hash.c Normal file
View file

@ -0,0 +1,16 @@
#include "cdb.h"
uint32 cdb_hash(buf,len)
unsigned char *buf;
unsigned int len;
{
uint32 h;
h = 5381;
while (len) {
--len;
h += (h << 5);
h ^= (uint32) *buf++;
}
return h;
}

95
cdb_seek.c Normal file
View file

@ -0,0 +1,95 @@
#include <sys/types.h>
#include <errno.h>
extern int errno;
#include "cdb.h"
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
int cdb_bread(fd,buf,len)
int fd;
char *buf;
int len;
{
int r;
while (len > 0) {
do
r = read(fd,buf,len);
while ((r == -1) && (errno == EINTR));
if (r == -1) return -1;
if (r == 0) { errno = EIO; return -1; }
buf += r;
len -= r;
}
return 0;
}
static int match(fd,key,len)
int fd;
char *key;
unsigned int len;
{
char buf[32];
int n;
int i;
while (len > 0) {
n = sizeof(buf);
if (n > len) n = len;
if (cdb_bread(fd,buf,n) == -1) return -1;
for (i = 0;i < n;++i) if (buf[i] != key[i]) return 0;
key += n;
len -= n;
}
return 1;
}
int cdb_seek(fd,key,len,dlen)
int fd;
char *key;
unsigned int len;
uint32 *dlen;
{
char packbuf[8];
uint32 pos;
uint32 h;
uint32 lenhash;
uint32 h2;
uint32 loop;
uint32 poskd;
h = cdb_hash(key,len);
pos = 8 * (h & 255);
if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1;
if (cdb_bread(fd,packbuf,8) == -1) return -1;
pos = cdb_unpack(packbuf);
lenhash = cdb_unpack(packbuf + 4);
if (!lenhash) return 0;
h2 = (h >> 8) % lenhash;
for (loop = 0;loop < lenhash;++loop) {
if (lseek(fd,(off_t) (pos + 8 * h2),SEEK_SET) == -1) return -1;
if (cdb_bread(fd,packbuf,8) == -1) return -1;
poskd = cdb_unpack(packbuf + 4);
if (!poskd) return 0;
if (cdb_unpack(packbuf) == h) {
if (lseek(fd,(off_t) poskd,SEEK_SET) == -1) return -1;
if (cdb_bread(fd,packbuf,8) == -1) return -1;
if (cdb_unpack(packbuf) == len)
switch(match(fd,key,len)) {
case -1:
return -1;
case 1:
*dlen = cdb_unpack(packbuf + 4);
return 1;
}
}
if (++h2 == lenhash) h2 = 0;
}
return 0;
}

12
cdb_unpack.c Normal file
View file

@ -0,0 +1,12 @@
#include "cdb.h"
uint32 cdb_unpack(buf)
unsigned char *buf;
{
uint32 num;
num = buf[3]; num <<= 8;
num += buf[2]; num <<= 8;
num += buf[1]; num <<= 8;
num += buf[0];
return num;
}

35
cdbmake.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef CDBMAKE_H
#define CDBMAKE_H
#include "uint32.h"
#define CDBMAKE_HPLIST 1000
struct cdbmake_hp { uint32 h; uint32 p; } ;
struct cdbmake_hplist {
struct cdbmake_hp hp[CDBMAKE_HPLIST];
struct cdbmake_hplist *next;
int num;
} ;
struct cdbmake {
char final[2048];
uint32 count[256];
uint32 start[256];
struct cdbmake_hplist *head;
struct cdbmake_hp *split; /* includes space for hash */
struct cdbmake_hp *hash;
uint32 numentries;
} ;
extern void cdbmake_pack();
#define CDBMAKE_HASHSTART ((uint32) 5381)
extern uint32 cdbmake_hashadd();
extern void cdbmake_init();
extern int cdbmake_add();
extern int cdbmake_split();
extern uint32 cdbmake_throw();
#endif

117
cdbmake_add.c Normal file
View file

@ -0,0 +1,117 @@
#include "cdbmake.h"
void cdbmake_init(cdbm)
struct cdbmake *cdbm;
{
cdbm->head = 0;
cdbm->split = 0;
cdbm->hash = 0;
cdbm->numentries = 0;
}
int cdbmake_add(cdbm,h,p,alloc)
struct cdbmake *cdbm;
uint32 h;
uint32 p;
char *(*alloc)();
{
struct cdbmake_hplist *head;
head = cdbm->head;
if (!head || (head->num >= CDBMAKE_HPLIST)) {
head = (struct cdbmake_hplist *) alloc(sizeof(struct cdbmake_hplist));
if (!head) return 0;
head->num = 0;
head->next = cdbm->head;
cdbm->head = head;
}
head->hp[head->num].h = h;
head->hp[head->num].p = p;
++head->num;
++cdbm->numentries;
return 1;
}
int cdbmake_split(cdbm,alloc)
struct cdbmake *cdbm;
char *(*alloc)();
{
int i;
uint32 u;
uint32 memsize;
struct cdbmake_hplist *x;
for (i = 0;i < 256;++i)
cdbm->count[i] = 0;
for (x = cdbm->head;x;x = x->next) {
i = x->num;
while (i--)
++cdbm->count[255 & x->hp[i].h];
}
memsize = 1;
for (i = 0;i < 256;++i) {
u = cdbm->count[i] * 2;
if (u > memsize)
memsize = u;
}
memsize += cdbm->numentries; /* no overflow possible up to now */
u = (uint32) 0 - (uint32) 1;
u /= sizeof(struct cdbmake_hp);
if (memsize > u) return 0;
cdbm->split = (struct cdbmake_hp *) alloc(memsize * sizeof(struct cdbmake_hp));
if (!cdbm->split) return 0;
cdbm->hash = cdbm->split + cdbm->numentries;
u = 0;
for (i = 0;i < 256;++i) {
u += cdbm->count[i]; /* bounded by numentries, so no overflow */
cdbm->start[i] = u;
}
for (x = cdbm->head;x;x = x->next) {
i = x->num;
while (i--)
cdbm->split[--cdbm->start[255 & x->hp[i].h]] = x->hp[i];
}
return 1;
}
uint32 cdbmake_throw(cdbm,pos,b)
struct cdbmake *cdbm;
uint32 pos;
int b;
{
uint32 len;
uint32 j;
uint32 count;
struct cdbmake_hp *hp;
uint32 where;
count = cdbm->count[b];
len = count + count; /* no overflow possible */
cdbmake_pack(cdbm->final + 8 * b,pos);
cdbmake_pack(cdbm->final + 8 * b + 4,len);
if (len) {
for (j = 0;j < len;++j)
cdbm->hash[j].h = cdbm->hash[j].p = 0;
hp = cdbm->split + cdbm->start[b];
for (j = 0;j < count;++j) {
where = (hp->h >> 8) % len;
while (cdbm->hash[where].p)
if (++where == len)
where = 0;
cdbm->hash[where] = *hp++;
}
}
return len;
}

10
cdbmake_hash.c Normal file
View file

@ -0,0 +1,10 @@
#include "cdbmake.h"
uint32 cdbmake_hashadd(h,c)
uint32 h;
unsigned int c;
{
h += (h << 5);
h ^= (uint32) (unsigned char) c;
return h;
}

11
cdbmake_pack.c Normal file
View file

@ -0,0 +1,11 @@
#include "cdbmake.h"
void cdbmake_pack(buf,num)
unsigned char *buf;
uint32 num;
{
*buf++ = num; num >>= 8;
*buf++ = num; num >>= 8;
*buf++ = num; num >>= 8;
*buf = num;
}

65
cdbmss.c Normal file
View file

@ -0,0 +1,65 @@
#include "readwrite.h"
#include "seek.h"
#include "alloc.h"
#include "cdbmss.h"
int cdbmss_start(c,fd)
struct cdbmss *c;
int fd;
{
cdbmake_init(&c->cdbm);
c->fd = fd;
c->pos = sizeof(c->cdbm.final);
substdio_fdbuf(&c->ss,write,fd,c->ssbuf,sizeof(c->ssbuf));
return seek_set(fd,(seek_pos) c->pos);
}
int cdbmss_add(c,key,keylen,data,datalen)
struct cdbmss *c;
unsigned char *key;
unsigned int keylen;
unsigned char *data;
unsigned int datalen;
{
uint32 h;
int i;
cdbmake_pack(c->packbuf,(uint32) keylen);
cdbmake_pack(c->packbuf + 4,(uint32) datalen);
if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
if (substdio_put(&c->ss,key,keylen) == -1) return -1;
if (substdio_put(&c->ss,data,datalen) == -1) return -1;
h = CDBMAKE_HASHSTART;
for (i = 0;i < keylen;++i)
h = cdbmake_hashadd(h,(unsigned int) key[i]);
if (!cdbmake_add(&c->cdbm,h,c->pos,alloc)) return -1;
c->pos += 8 + keylen + datalen; /* XXX: overflow? */
return 0;
}
int cdbmss_finish(c)
struct cdbmss *c;
{
int i;
uint32 len;
uint32 u;
if (!cdbmake_split(&c->cdbm,alloc)) return -1;
for (i = 0;i < 256;++i) {
len = cdbmake_throw(&c->cdbm,c->pos,i);
for (u = 0;u < len;++u) {
cdbmake_pack(c->packbuf,c->cdbm.hash[u].h);
cdbmake_pack(c->packbuf + 4,c->cdbm.hash[u].p);
if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
c->pos += 8; /* XXX: overflow? */
}
}
if (substdio_flush(&c->ss) == -1) return -1;
if (seek_begin(c->fd) == -1) return -1;
return substdio_putflush(&c->ss,c->cdbm.final,sizeof(c->cdbm.final));
}

16
cdbmss.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef CDBMSS_H
#define CDBMSS_H
#include "cdbmake.h"
#include "substdio.h"
struct cdbmss {
char ssbuf[1024];
struct cdbmake cdbm;
substdio ss;
char packbuf[8];
uint32 pos;
int fd;
} ;
#endif

9
chkshsgr.c Normal file
View file

@ -0,0 +1,9 @@
#include "exit.h"
void main()
{
short x[4];
x[0] = x[1] = 0;
if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
_exit(0);
}

48
chkspawn.c Normal file
View file

@ -0,0 +1,48 @@
#include "substdio.h"
#include "subfd.h"
#include "fmt.h"
#include "select.h"
#include "exit.h"
#include "auto_spawn.h"
char num[FMT_ULONG];
fd_set fds;
void main()
{
unsigned long hiddenlimit;
unsigned long maxnumd;
hiddenlimit = sizeof(fds) * 8;
maxnumd = (hiddenlimit - 5) / 2;
if (auto_spawn < 1) {
substdio_puts(subfderr,"Oops. You have set conf-spawn lower than 1.\n");
substdio_flush(subfderr);
_exit(1);
}
if (auto_spawn > 255) {
substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n");
substdio_flush(subfderr);
_exit(1);
}
if (auto_spawn > maxnumd) {
substdio_puts(subfderr,"Oops. Your system's FD_SET() has a hidden limit of ");
substdio_put(subfderr,num,fmt_ulong(num,hiddenlimit));
substdio_puts(subfderr," descriptors.\n\
This means that the qmail daemons could crash if you set the run-time\n\
concurrency higher than ");
substdio_put(subfderr,num,fmt_ulong(num,maxnumd));
substdio_puts(subfderr,". So I'm going to insist that the concurrency\n\
limit in conf-spawn be at most ");
substdio_put(subfderr,num,fmt_ulong(num,maxnumd));
substdio_puts(subfderr,". Right now it's ");
substdio_put(subfderr,num,fmt_ulong(num,(unsigned long) auto_spawn));
substdio_puts(subfderr,".\n");
substdio_flush(subfderr);
_exit(1);
}
_exit(0);
}

25
coe.3 Normal file
View file

@ -0,0 +1,25 @@
.TH coe 3
.SH NAME
coe \- set close-on-exec flag for a descriptor
.SH SYNTAX
.B #include <coe.h>
int \fBcoe\fP(\fIfd\fR);
int \fIfd\fR;
.SH DESCRIPTION
.B coe
sets the close-on-exec flag for
file descriptor
.IR fd ,
returning 0 if it was successful
or -1 on error.
If
.B coe
is successful,
.I fd
will be closed when the process calls
.BR execve .
.SH "SEE ALSO"
execve(2),
fcntl(2)

8
coe.c Normal file
View file

@ -0,0 +1,8 @@
#include <fcntl.h>
#include "coe.h"
int coe(fd)
int fd;
{
return fcntl(fd,F_SETFD,1);
}

6
coe.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef COE_H
#define COE_H
extern int coe();
#endif

54
condredirect.1 Normal file
View file

@ -0,0 +1,54 @@
.TH condredirect 1
.SH NAME
condredirect \- perhaps redirect mail to another address
.SH SYNOPSIS
in
.BR .qmail :
.B |condredirect
.I newaddress
.I program
[
.I arg ...
]
.SH DESCRIPTION
.B condredirect
feeds each new mail message to
.I program
with the given arguments.
If
.I program
exits 0,
.B condredirect
forwards the mail message to
.IR newaddress ,
and then exits 99,
so further commands in
.B .qmail
are ignored.
If
.I program
exits 111,
.B condredirect
exits 111,
so delivery will be retried later.
If
.I program
exits anything else
(or does not exist),
.B condredirect
exits 0,
so the rest of
.B .qmail
will be processed as usual.
Note that
it is not safe for
.I program
to fork a child that
reads the message in the background.
.SH "SEE ALSO"
dot-qmail(5),
qmail-command(8),
qmail-queue(8)

87
condredirect.c Normal file
View file

@ -0,0 +1,87 @@
#include "sig.h"
#include "readwrite.h"
#include "exit.h"
#include "env.h"
#include "error.h"
#include "fork.h"
#include "wait.h"
#include "seek.h"
#include "qmail.h"
#include "stralloc.h"
#include "subfd.h"
#include "substdio.h"
void die_success() { _exit(0); }
void die_99() { _exit(99); }
void die_perm(s) char *s; { substdio_putsflush(subfderr,s); _exit(100); }
void die_temp(s) char *s; { substdio_putsflush(subfderr,s); _exit(111); }
void die_nomem() { die_temp("condredirect: fatal: out of memory\n"); }
struct qmail qqt;
int mywrite(fd,buf,len) int fd; char *buf; int len;
{
qmail_put(&qqt,buf,len);
return len;
}
substdio ssin;
substdio ssout;
char inbuf[SUBSTDIO_INSIZE];
char outbuf[16];
void main(argc,argv)
int argc;
char **argv;
{
char *sender;
char *dtline;
int pid;
int wstat;
if (!argv[1] || !argv[2])
die_perm("condredirect: usage: condredirect newaddress program arg ...\n");
switch(pid = fork())
{
case -1: die_temp("condredirect: fatal: unable to fork\n");
case 0:
execvp(argv[2],argv + 2);
if (error_temp(errno)) _exit(111);
_exit(100);
}
if (wait_pid(&wstat,pid) != pid)
die_perm("condredirect: fatal: internal bug\n");
if (wait_crashed(wstat)) die_temp("condredirect: fatal: child crashed\n");
switch(wait_exitcode(wstat))
{
case 0: break;
case 111: die_temp("condredirect: fatal: temporary child error\n");
default: die_success();
}
if (seek_begin(0) == -1) die_temp("condredirect: fatal: unable to rewind\n");
sig_pipeignore();
sender = env_get("SENDER");
if (!sender) die_perm("condredirect: fatal: SENDER not set\n");
dtline = env_get("DTLINE");
if (!dtline) die_perm("condredirect: fatal: DTLINE not set\n");
if (qmail_open(&qqt) == -1) die_temp("condredirect: fatal: unable to fork\n");
qmail_puts(&qqt,dtline);
substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf));
if (substdio_copy(&ssout,&ssin) != 0)
die_temp("condredirect: fatal: error while reading message\n");
substdio_flush(&ssout);
qmail_from(&qqt,sender);
qmail_to(&qqt,argv[1]);
switch(qmail_close(&qqt))
{
case 0: die_99();
case QMAIL_TOOLONG: die_perm("condredirect: fatal: permanent qmail-queue error\n");
default: die_temp("condredirect: fatal: temporary qmail-queue error\n");
}
}

9
conf-break Normal file
View file

@ -0,0 +1,9 @@
-
This character is the user-ext delimiter. The default delimiter is -,
meaning that user joe controls joe-anything. Some system administrators
prefer + or =.
You can override this choice at run time with the qmail-users mechanism.
Multicharacter delimiters are not permitted.

3
conf-cc Normal file
View file

@ -0,0 +1,3 @@
cc -O2
This will be used to compile .c files.

6
conf-groups Normal file
View file

@ -0,0 +1,6 @@
qmail
nofiles
These are the qmail groups. The second group should not have access to
any files, but it must be usable for processes; this requirement
excludes the ``nogroup'' and ``nobody'' groups on many systems.

3
conf-ld Normal file
View file

@ -0,0 +1,3 @@
cc -s
This will be used to link .o files into an executable.

5
conf-patrn Normal file
View file

@ -0,0 +1,5 @@
022
These stat bits are not allowed in ~ and ~/.qmail.
Note that ~ftp, ~www, ~uucp, etc. should be owned by root.

4
conf-qmail Normal file
View file

@ -0,0 +1,4 @@
/var/qmail
This is the qmail home directory. It must be a local directory, not
shared among machines. This is where qmail queues all mail messages.

5
conf-spawn Normal file
View file

@ -0,0 +1,5 @@
120
This is a silent concurrency limit. You can't set it above 255. On some
systems you can't set it above 125. qmail will refuse to compile if the
limit is too high.

3
conf-split Normal file
View file

@ -0,0 +1,3 @@
23
This is the queue subdirectory split.

15
conf-users Normal file
View file

@ -0,0 +1,15 @@
alias
qmaild
qmaill
root
qmailp
qmailq
qmailr
qmails
The qmail system is heavily partitioned for security; it does almost
nothing as root.
The first eight lines of this file are the alias user, the daemon user,
the log user, the owner of miscellaneous files such as binaries, the
passwd user, the queue user, the remote user, and the send user.

123
constmap.c Normal file
View file

@ -0,0 +1,123 @@
#include "constmap.h"
#include "alloc.h"
#include "case.h"
static constmap_hash hash(s,len)
char *s;
int len;
{
unsigned char ch;
constmap_hash h;
h = 5381;
while (len > 0)
{
ch = *s++ - 'A';
if (ch <= 'Z' - 'A') ch += 'a' - 'A';
h = ((h << 5) + h) ^ ch;
--len;
}
return h;
}
char *constmap(cm,s,len)
struct constmap *cm;
char *s;
int len;
{
constmap_hash h;
int pos;
h = hash(s,len);
pos = cm->first[h & cm->mask];
while (pos != -1)
{
if (h == cm->hash[pos])
if (len == cm->inputlen[pos])
if (!case_diffb(cm->input[pos],len,s))
return cm->input[pos] + cm->inputlen[pos] + 1;
pos = cm->next[pos];
}
return 0;
}
int constmap_init(cm,s,len,flagcolon)
struct constmap *cm;
char *s;
int len;
int flagcolon;
{
int i;
int j;
int k;
int pos;
constmap_hash h;
cm->num = 0;
for (j = 0;j < len;++j) if (!s[j]) ++cm->num;
h = 64;
while (h && (h < cm->num)) h += h;
cm->mask = h - 1;
cm->first = (int *) alloc(sizeof(int) * h);
if (cm->first)
{
cm->input = (char **) alloc(sizeof(char *) * cm->num);
if (cm->input)
{
cm->inputlen = (int *) alloc(sizeof(int) * cm->num);
if (cm->inputlen)
{
cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num);
if (cm->hash)
{
cm->next = (int *) alloc(sizeof(int) * cm->num);
if (cm->next)
{
for (h = 0;h <= cm->mask;++h)
cm->first[h] = -1;
pos = 0;
i = 0;
for (j = 0;j < len;++j)
if (!s[j])
{
k = j - i;
if (flagcolon)
{
for (k = i;k < j;++k)
if (s[k] == ':')
break;
if (k >= j) { i = j + 1; continue; }
k -= i;
}
cm->input[pos] = s + i;
cm->inputlen[pos] = k;
h = hash(s + i,k);
cm->hash[pos] = h;
h &= cm->mask;
cm->next[pos] = cm->first[h];
cm->first[h] = pos;
++pos;
i = j + 1;
}
return 1;
}
alloc_free(cm->hash);
}
alloc_free(cm->inputlen);
}
alloc_free(cm->input);
}
alloc_free(cm->first);
}
return 0;
}
void constmap_free(cm)
struct constmap *cm;
{
alloc_free(cm->next);
alloc_free(cm->hash);
alloc_free(cm->inputlen);
alloc_free(cm->input);
alloc_free(cm->first);
}

22
constmap.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef CONSTMAP_H
#define CONSTMAP_H
typedef unsigned long constmap_hash;
struct constmap
{
int num;
constmap_hash mask;
constmap_hash *hash;
int *first;
int *next;
char **input;
int *inputlen;
}
;
extern int constmap_init();
extern void constmap_free();
extern char *constmap();
#endif

129
control.c Normal file
View file

@ -0,0 +1,129 @@
#include "readwrite.h"
#include "open.h"
#include "getln.h"
#include "stralloc.h"
#include "substdio.h"
#include "error.h"
#include "control.h"
#include "alloc.h"
#include "scan.h"
static char inbuf[64];
static stralloc line = {0};
static stralloc me = {0};
static int meok = 0;
static void striptrailingwhitespace(sa)
stralloc *sa;
{
while (sa->len > 0)
switch(sa->s[sa->len - 1])
{
case '\n': case ' ': case '\t':
--sa->len;
break;
default:
return;
}
}
int control_init()
{
int r;
r = control_readline(&me,"control/me");
if (r == 1) meok = 1;
return r;
}
int control_rldef(sa,fn,flagme,def)
stralloc *sa;
char *fn;
int flagme;
char *def;
{
int r;
r = control_readline(sa,fn);
if (r) return r;
if (flagme) if (meok) return stralloc_copy(sa,&me) ? 1 : -1;
if (def) return stralloc_copys(sa,def) ? 1 : -1;
return r;
}
int control_readline(sa,fn)
stralloc *sa;
char *fn;
{
substdio ss;
int fd;
int match;
fd = open_read(fn);
if (fd == -1) { if (errno == error_noent) return 0; return -1; }
substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
if (getln(&ss,sa,&match,'\n') == -1) { close(fd); return -1; }
striptrailingwhitespace(sa);
close(fd);
return 1;
}
int control_readint(i,fn)
int *i;
char *fn;
{
unsigned long u;
switch(control_readline(&line,fn))
{
case 0: return 0;
case -1: return -1;
}
if (scan_nbblong(line.s,line.len,10,0,&u) == 0) return 0;
*i = u;
return 1;
}
int control_readfile(sa,fn,flagme)
stralloc *sa;
char *fn;
int flagme;
{
substdio ss;
int fd;
int match;
if (!stralloc_copys(sa,"")) return -1;
fd = open_read(fn);
if (fd == -1)
{
if (errno == error_noent)
{
if (flagme && meok)
{
if (!stralloc_copy(sa,&me)) return -1;
if (!stralloc_0(sa)) return -1;
return 1;
}
return 0;
}
return -1;
}
substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
for (;;)
{
if (getln(&ss,&line,&match,'\n') == -1) break;
if (!match && !line.len) { close(fd); return 1; }
striptrailingwhitespace(&line);
if (!stralloc_0(&line)) break;
if (line.s[0])
if (line.s[0] != '#')
if (!stralloc_cat(sa,&line)) break;
if (!match) { close(fd); return 1; }
}
close(fd);
return -1;
}

10
control.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef CONTROL_H
#define CONTROL_H
extern int control_init();
extern int control_readline();
extern int control_rldef();
extern int control_readint();
extern int control_readfile();
#endif

29
date822fmt.c Normal file
View file

@ -0,0 +1,29 @@
#include "datetime.h"
#include "fmt.h"
#include "date822fmt.h"
static char *montab[12] = {
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
};
unsigned int date822fmt(s,dt)
char *s;
struct datetime *dt;
{
unsigned int i;
unsigned int len;
len = 0;
i = fmt_uint(s,dt->mday); len += i; if (s) s += i;
i = fmt_str(s," "); len += i; if (s) s += i;
i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i;
i = fmt_str(s," "); len += i; if (s) s += i;
i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i;
i = fmt_str(s," "); len += i; if (s) s += i;
i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i;
i = fmt_str(s,":"); len += i; if (s) s += i;
i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i;
i = fmt_str(s,":"); len += i; if (s) s += i;
i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i;
i = fmt_str(s," -0000\n"); len += i; if (s) s += i;
return len;
}

7
date822fmt.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef DATE822FMT_H
#define DATE822FMT_H
extern unsigned int date822fmt();
#define DATE822FMT 60
#endif

1
datemail.sh Normal file
View file

@ -0,0 +1 @@
exec QMAIL/bin/predate QMAIL/bin/sendmail ${1+"$@"}

73
datetime.3 Normal file
View file

@ -0,0 +1,73 @@
.TH datetime 3
.SH NAME
datetime \- convert between TAI labels and seconds
.SH SYNTAX
.B #include <datetime.h>
void \fBdatetime_tai\fP(&\fIdt\fR,\fIt\fR);
datetime_sec \fBdatetime_untai\fP(&\fIdt\fR);
struct datetime \fIdt\fR;
.br
datetime_sec \fIt\fR;
.SH DESCRIPTION
International Atomic Time, TAI,
is the fundamental unit for time measurements.
TAI has one label for every second of real time,
without complications such as leap seconds.
A
struct datetime
variable,
such as
.IR dt ,
stores a TAI label.
.I dt\fB.year
is the year number minus 1900;
.I dt\fB.mon
is the month number, from 0 (January) through 11 (December);
.I dt\fB.mday
is the day of the month, from 1 through 31;
.I dt\fB.hour
is the hour, from 0 through 23;
.I dt\fB.min
is the minute, from 0 through 59;
.I dt\fB.sec
is the second, from 0 through 59;
.I dt\fB.wday
is the day of the week, from 0 (Sunday) through 6 (Saturday);
.I dt\fB.yday
is the day of the year, from 0 through 365.
The
.B datetime
library supports more convenient TAI manipulation with
the datetime_sec type.
A datetime_sec value, such as
.IR t ,
is an integer referring to the
.IR t th
second after the beginning of 1970 TAI.
The first second of 1970 TAI was 0;
the next second was 1;
the last second of 1969 TAI was -1.
The difference between two datetime_sec values is a number
of real-time seconds.
.B datetime_tai
converts a datetime_sec to a TAI label.
.B datetime_untai
reads a TAI label
(specifically
.IR dt\fB.year ,
.IR dt\fB.mon ,
.IR dt\fB.mday ,
.IR dt\fB.hour ,
.IR dt\fB.min ,
and
.IR dt\fB.sec )
and returns a datetime_sec.
.SH "SEE ALSO"
now(3)

55
datetime.c Normal file
View file

@ -0,0 +1,55 @@
/* 19950925 */
#include "datetime.h"
void datetime_tai(dt,t)
struct datetime *dt;
datetime_sec t;
{
int day;
int tod;
int year;
int yday;
int wday;
int mon;
tod = t % 86400;
day = t / 86400;
if (tod < 0) { tod += 86400; --day; }
dt->hour = tod / 3600;
tod %= 3600;
dt->min = tod / 60;
dt->sec = tod % 60;
wday = (day + 4) % 7; if (wday < 0) wday += 7;
dt->wday = wday;
day -= 11017;
/* day 0 is march 1, 2000 */
year = 5 + day / 146097;
day = day % 146097; if (day < 0) { day += 146097; --year; }
/* from now on, day is nonnegative */
year *= 4;
if (day == 146096) { year += 3; day = 36524; }
else { year += day / 36524; day %= 36524; }
year *= 25;
year += day / 1461;
day %= 1461;
year *= 4;
yday = (day < 306);
if (day == 1460) { year += 3; day = 365; }
else { year += day / 365; day %= 365; }
yday += day;
day *= 10;
mon = (day + 5) / 306;
day = day + 5 - 306 * mon;
day /= 10;
if (mon >= 10) { yday -= 306; ++year; mon -= 10; }
else { yday += 59; mon += 2; }
dt->yday = yday;
dt->year = year - 1900;
dt->mon = mon;
dt->mday = day + 1;
}

Some files were not shown because too many files have changed in this diff Show more