qmail 1.01
This commit is contained in:
commit
af9625ddf9
401 changed files with 30689 additions and 0 deletions
43
BLURB
Normal file
43
BLURB
Normal 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
26
BLURB2
Normal 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
90
BLURB3
Normal 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
44
BLURB4
Normal 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.
|
||||||
574
FAQ
Normal file
574
FAQ
Normal 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
401
FILES
Normal 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
181
INSTALL
Normal 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
40
INSTALL.alias
Normal 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
16
INSTALL.boot
Normal 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
29
INSTALL.ctl
Normal 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
59
INSTALL.ids
Normal 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
112
INSTALL.mbox
Normal 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
53
INSTALL.qsmhook
Normal 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
155
INTERNALS
Normal 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.
|
||||||
199
README
Normal file
199
README
Normal 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
37
RFCHCSC
Normal 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
338
RFCLOOPS
Normal 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
122
RFCMXPS
Normal 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
88
RFCNETSTR
Normal 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
89
RFCNRUDT
Normal 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
229
RFCQMTP
Normal 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
155
RFCQSBMF
Normal 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
88
RFCVERP
Normal 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
131
SECURITY
Normal 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
17
SYSDEPS
Normal 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
353
TARGETS
Normal 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
217
THANKS
Normal 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
437
THOUGHTS
Normal 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
9
TODO
Normal 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
145
UPGRADE
Normal 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
1
VERSION
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
qmail 1.01
|
||||||
260
addresses.5
Normal file
260
addresses.5
Normal 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
62
alloc.3
Normal 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
32
alloc.c
Normal 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
8
alloc.h
Normal 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
17
alloc_re.c
Normal 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
51
auto-gid.c
Normal 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
40
auto-int.c
Normal 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
40
auto-int8.c
Normal 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
44
auto-str.c
Normal 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
51
auto-uid.c
Normal 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
6
auto_break.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef AUTO_BREAK_H
|
||||||
|
#define AUTO_BREAK_H
|
||||||
|
|
||||||
|
extern char auto_break[];
|
||||||
|
|
||||||
|
#endif
|
||||||
6
auto_patrn.h
Normal file
6
auto_patrn.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef AUTO_PATRN_H
|
||||||
|
#define AUTO_PATRN_H
|
||||||
|
|
||||||
|
extern int auto_patrn;
|
||||||
|
|
||||||
|
#endif
|
||||||
6
auto_qmail.h
Normal file
6
auto_qmail.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef AUTO_QMAIL_H
|
||||||
|
#define AUTO_QMAIL_H
|
||||||
|
|
||||||
|
extern char auto_qmail[];
|
||||||
|
|
||||||
|
#endif
|
||||||
6
auto_spawn.h
Normal file
6
auto_spawn.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef AUTO_SPAWN_H
|
||||||
|
#define AUTO_SPAWN_H
|
||||||
|
|
||||||
|
extern int auto_spawn;
|
||||||
|
|
||||||
|
#endif
|
||||||
6
auto_split.h
Normal file
6
auto_split.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef AUTO_SPLIT_H
|
||||||
|
#define AUTO_SPLIT_H
|
||||||
|
|
||||||
|
extern int auto_split;
|
||||||
|
|
||||||
|
#endif
|
||||||
16
auto_uids.h
Normal file
16
auto_uids.h
Normal 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
6
auto_usera.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef AUTO_USERA_H
|
||||||
|
#define AUTO_USERA_H
|
||||||
|
|
||||||
|
extern char auto_usera[];
|
||||||
|
|
||||||
|
#endif
|
||||||
13
byte.h
Normal file
13
byte.h
Normal 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
20
byte_chr.c
Normal 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
14
byte_copy.c
Normal 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
16
byte_cr.c
Normal 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
16
byte_diff.c
Normal 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
23
byte_rchr.c
Normal 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
13
byte_zero.c
Normal 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
100
case.3
Normal 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
13
case.h
Normal 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
21
case_diffb.c
Normal 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
19
case_diffs.c
Normal 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
14
case_lowerb.c
Normal 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
12
case_lowers.c
Normal 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
18
case_starts.c
Normal 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
62
cdb.3
Normal 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
12
cdb.h
Normal 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
16
cdb_hash.c
Normal 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
95
cdb_seek.c
Normal 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
12
cdb_unpack.c
Normal 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
35
cdbmake.h
Normal 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
117
cdbmake_add.c
Normal 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
10
cdbmake_hash.c
Normal 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
11
cdbmake_pack.c
Normal 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
65
cdbmss.c
Normal 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
16
cdbmss.h
Normal 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
9
chkshsgr.c
Normal 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
48
chkspawn.c
Normal 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
25
coe.3
Normal 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
8
coe.c
Normal 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
6
coe.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef COE_H
|
||||||
|
#define COE_H
|
||||||
|
|
||||||
|
extern int coe();
|
||||||
|
|
||||||
|
#endif
|
||||||
54
condredirect.1
Normal file
54
condredirect.1
Normal 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
87
condredirect.c
Normal 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
9
conf-break
Normal 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
3
conf-cc
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
cc -O2
|
||||||
|
|
||||||
|
This will be used to compile .c files.
|
||||||
6
conf-groups
Normal file
6
conf-groups
Normal 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
3
conf-ld
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
cc -s
|
||||||
|
|
||||||
|
This will be used to link .o files into an executable.
|
||||||
5
conf-patrn
Normal file
5
conf-patrn
Normal 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
4
conf-qmail
Normal 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
5
conf-spawn
Normal 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
3
conf-split
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
23
|
||||||
|
|
||||||
|
This is the queue subdirectory split.
|
||||||
15
conf-users
Normal file
15
conf-users
Normal 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
123
constmap.c
Normal 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
22
constmap.h
Normal 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
129
control.c
Normal 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
10
control.h
Normal 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
29
date822fmt.c
Normal 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
7
date822fmt.h
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef DATE822FMT_H
|
||||||
|
#define DATE822FMT_H
|
||||||
|
|
||||||
|
extern unsigned int date822fmt();
|
||||||
|
#define DATE822FMT 60
|
||||||
|
|
||||||
|
#endif
|
||||||
1
datemail.sh
Normal file
1
datemail.sh
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
exec QMAIL/bin/predate QMAIL/bin/sendmail ${1+"$@"}
|
||||||
73
datetime.3
Normal file
73
datetime.3
Normal 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
55
datetime.c
Normal 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
Loading…
Reference in a new issue