diff options
author | Erwin Hoffmann <feh@fehcom.de> | 2024-01-14 17:55:54 +0100 |
---|---|---|
committer | Erwin Hoffmann <feh@fehcom.de> | 2024-01-14 17:55:54 +0100 |
commit | a293489ee83c8b05d845a162dc2a4de026f3775d (patch) | |
tree | d50ab15afde698ad2d32bfc251753e5e89204f25 /sqmail-4.3.07 |
s/qmail 4.3.07
Diffstat (limited to 'sqmail-4.3.07')
408 files changed, 46893 insertions, 0 deletions
diff --git a/sqmail-4.3.07/INSTALL b/sqmail-4.3.07/INSTALL new file mode 100644 index 0000000..a51c6d7 --- /dev/null +++ b/sqmail-4.3.07/INSTALL @@ -0,0 +1,253 @@ +Configuration and Installation of s/qmail +----------------------------------------- + +HOW TO INSTALL: +- s/qmail uses D.J.B's slashpackage convention + for installing while trying to conserve the + standard qmail installations: + * untar the sqmail tar file under '/package' + * Move to /package/mail/sqmail/sqmail-V.R.F + and go on with installation +- Set up the s/qmail package with the following + step-by-step options or simple run (as 'root'): + * package/install -- does it all + +A) REQUIREMENTS + +1. Compiler & make utilities. +2. fehQlibs are installed (typically as /usr/local/qlibs) +3. The directory /package is in place. +4. Header files and libs for *SSL. +5. The UCSPI-SSL package to be installed. +6. Header files and libraries for IDN2 support (optional). +7. Header files and libraries for LDAP support (optional). + +Optional but very useful: + +8. The UCSPI-TCP6 package (tcprules, rblsmtpd). +9. DJB's Daemontools installed and working. +10. MRTG to display logging. + + +B) CONFIGURATION + +1. Configuration is done by means of the + `conf-XX` files in this main directory. + +2. Short description: + + conf-break -- the character for VERP addresses [-] + conf-cc -- compiler (no change required) + conf-delivery -- qmail-start default-delivery + conf-djbdns -- DJBNDS libs (not supported yet) + conf-groups*) -- s/qmail groups + conf-home -- home dir of s/qmail [/var/qmail] + conf-idn2 -- include optional path for libidn2 + conf-ids*) -- Unix ids for s/qmail + conf-instances -- QMQ instances to be raised + conf-ld -- loader options to be adjusted (for i386; AMD64 default) + conf-log -- target dir of s/qmail logs [/var/log] + conf-man -- target dir of man pages, usually automatically recognized + conf-patrn -- s/qmail paternalism [002] + conf-qmq -- QMQ environment settings + conf-spawn -- silent concurrency limit [120] + conf-split -- depth of s/qmail dirs [23] + conf-svcdir -- supervise's directory [/service] + conf-ssl -- path to *SSL header files [empty for defaults] + conf-ucspissl -- path to UCSPI-SSL dirs + conf-users*) -- user names + + Configurations labeled with *) need to be treated together. + +3. Depending on your settings, you may need to + adjust the following: + + a) conf-cc: Perhaps remove the -DIDN2 option + if libidn2 is not installed. + Other options are: + -DHIDEVIRTUALUSER + -DDEFERREDBOUNCES + -DSHOWLOG + -DBARELF + b) conf-ld: Adjust architecture of executables. + If you use OpenSSL/LibreSSL from sources outside the + default, you need to include the link path (-L). + c) conf-idn2: Include optional path to 'libidn2'. + +4. s/qmail user settings: + + a) conf-ids: The UIDs and GIDs + b) conf-groups:The s/qmail group names. + c) conf-users: The s/qmail user names. + +5. Directories and system interaction: + + a) conf-home + b) conf-qlibs + c) conf-ssl + d) conf-ucspissl + e) conf-log + f) conf-man + g) conf-svcdir + +6. Run-time issues: + + a) conf-break + b) conf-patrn + c) conf-split + d) conf-delivery + e) conf-instances (still not working yet) + f) conf-qmq (still not uptodate jet) + + +C) INSTALLATION + +1. Upon configuration and verification + to meet requirements, simply do + + package/install + +2. Detail description of installation steps: + + package/dir -- sets up the directories + package/ids -- sets up the s/qmail users + package/ucspissl -- hooks up the required sources and libs with package ucspi-ssl + package/compile -- compiles the sources + package/upgrade -- potentially does the upgrade + package/legacy -- installs the binaries in the qmail directory + package/man -- installes the man pages + + All done be package/install. Additional (initial) settings: + + package/control -- populates the mininmal required control files for running + package/sslenv -- sets up the SSL/TLS environments together with X.509 certs and key files (from ucspi-ssl) + package/service -- sets up the run script for daemontools' /service and additionally the logging + package/scripts setup optional, undocumented and unmaintained scripts + package/run -- touches qmail/alias/ files and sets default-delivery + +3. Installation on OpenBSD + + s/qmail should be placed under + /usr/local/qmail + -- or -- + mount -u -o suid /var + +4. Upgrade from an existing Qmail + + s/qmail will keep your current qmail setup (except for the binaries): + + * Make sure, to have ucspi-ssl installed + * Extract s/qmail under /package + * cd /package/mail/sqmail-V.R.F + * package/ucspissl + * package/compile + * package/legacy + * package/man + * package/upgrade + + In case your qmail installation is out of default, use the conf-* settings (ie. ids). + Make sure, that your qmail 'todo' queue and the 'tcpto' table is empty (qmail-tcpto, qmail-tcpok). + + You need to change the port separator in the control files from ':' to ';' - if applicable. + +5. Deinstallation and re-do installation + + Within s/qmail's installation directory (where this file resides) + simply do: + + rm -r compile + + Alternatively, you can do + + cd compile; make clean + + To re-install man-pages: + + cd man; rm *.gz; make clean + + Now you can continue with re-installation. + +6. Additional compile-time options + + conf-cc allows you to customize compilation for the following needs: + + - Internationalization: Include the option -IDN2. + Be sure, to have IDN2 installed prior of compilation. + + - Virtual user obfuscation: Include the option -DHIDEVRITUALUSER. + Now, the virtual user extension is excluded in the mail header + for the displayed addresses. Vpopmail, however, requires this! + + - Delayed bounces: Use -DDEFERREDBOUNCES. + Now, qmail-remote will retry mail delivery even for not DNS + resolveable host names and IP addresses until queue lifetime + expires. + + - DKIM private key names used for signing are shown + in qmail-remote logs via option -DSHOWLOG. + + - Strict RF 5821 conformance for <CRLF.CRLF> can be + relaxed by -DBARELF (=> 'SMTP smuggling' still not possible). + + - Check conf-cc for more restrictive settings. + + +D) DKIM CONFIGURATION + +1. Key generation: + You need to generate a public/private key pair. + The private key is used to sign outgoing mails. + The public key needs to be in the DNS as DKIM TXT record. + Use the script mkdkimkey (after make in that directory) + to generate RSA/Ed25519 key pairs in the required format. + +2. Signing operation: + Populate the private key in the directory + ssl/domainkeys/<domain> + and symlink it as 'default' (= selector). + Key roll-over is easily supported with different selectors. + Create + control/dkimdomains + with the entry '=:' defaulting to your domain/MTA. + Several domain entries with different attributes can be used. + Upon raising the file 'control/dkimdomains' all outgoing + emails will be automatically DKIM signed in case the + sending domains are listed therein. + +3. Verification operation: + Use qmail-dkverify as paramater in your 'smtpd.tcpd' file: + :allow,QMAILQUEUE="bin/qmail-qmail-dkverify" + Usually, qmail-dkverify works in annotation mode only, thus + simply inlcudes a header for further message processing like this: + X-Authentication-Results: piplus.fehcom.de; dkim=pass; bigchief.fehcom.de + + If you however set 'DKIM=+' as environment variable, mails + failing DKIM verification (wrong signature) will be rejected upon receipt. + This is not recommended, since mails may be subject of re-writing + by mail-scanning MTAs. + +Note: DKIM is inappropriate with QMTP(S) delivery. + +E) MISCELLANEOUS + +1. s/qmail comes with a full set of updated man-pages. + +2. s/qmail supports SPF and SRS natively without additional libs. + +3. qmail-postgrey requires postgrey: [https://postgrey.schweikert.ch/] + +4. Further documentation can be found in ./doc + +5. Convenience files can be found in ./etc + +6. Samples for control files are provided in ./ctl + +7. Additional scripts are located in ./scripts + +8. Start-scripts (for Daemontools) reside in ./service + + +Visit https://www.fehcom.de/sqmail/sqmail.html to +access online man-pages and documentation. + +Date: January, 14th 2024 (feh) diff --git a/sqmail-4.3.07/README.md b/sqmail-4.3.07/README.md new file mode 100644 index 0000000..96a536b --- /dev/null +++ b/sqmail-4.3.07/README.md @@ -0,0 +1,106 @@ +/* \mainpage + +s/qmail -- fast, secure, and reliable email transmission +======================================================== + +WHAT IT IS: +---------- +- s/qmail is a fork of qmail (1.03) including the + features of the Spamcontrol patch together with + IPv6 capabilities and is 64 bit clean +- s/qmail is API- and plug-in-compatible with qmail, + thus add-ons like vpopmail, ezmlm, and vmailmgr + and many others work without changes +- TLS 1.3 enabled using ucspi-ssl +- X.509 cert pinning and allowing strict TLS mode +- Automatic TLSA lookup +- DKIM signing and verification with RSA and Ed25519 +- Wide scale QMTPS support +- Multi-tenancy capabilities +- s/qmail uses the concept of D.J. Bernstein's + coding without compromise +- Linux systemd compatible + + +INCLUDING: +-------- +The following (DJB) packages are included: + +- checkpassword (as qmail-authuser) +- fastforward +- qmailanalog +- qmail-mrtg + + +REQUIREMENTS: +------------ +- fehQlibs (-24) -- can't build w/o it +- ucspi-ssl (> 0.12.x) -- won't run without it +- ucspi-tcp6 (generating the cdb) +- daemontools package (supervising the services), + any other will do as well +- libidn2 for EAI support +- OpenSSL > 1.1.1 or LibreSSL > 3.7.0 to support Ed25519 signatures + + +INSTALLATION: +------------ +- Read the INSTALL document + + +INTERNAL CHANGES FROM QMAIL: +--------------------------- +- Group is now 'sqmail' instead of 'qmail' +- Exttodo + Bigtodo is default +- qmail(-queue) supports additional control tokens and return codes +- Old qmail code for sendmail compatibility removed +- Added QMTPS capabilities (receiving and sending) +- IPv6 supported by default +- AMD64 enabled (64 + 32 bit clean), works on ARM/ARM64 +- SPF DNS lookup for qmail-smtpd +- SRS: srsforward & srsreverse +- SMTPUTF8 + IDN2 support for qmail-remote +- fehQlibs DNS stub resolver +- qmail-postgrey client +- TLSA DNS lookup for qmail-remote +- Additional queue/dkim staging directories +- Less single character buffering for I/O; convenient buffer sizes + + +USER INTERFACE CHANGES: +---------------------- +- Port separator is ';' instead of ':' (due to IPv6) +- smtproutes supports authentication and localip setting +- Added SPF capabilities for qmail-smtpd + spfquery for testing +- Added DNS test routines: dnsmxip, dnsfq, dnscname, dnstxt, dnstlsa +- Added fastforward package +- Added qmailanalog and qmail-mrtg +- Additional TLS control files for X.509 certificates, + key files, and verification handling +- RECIPIENTS extension from Spamcontol +- Added PAMs for Recipient verification + (qmail-vmailuser, qmail-smtpam) +- Added qmail-authuser PAM for SMTP, POP3, and IMAP4 authentication; + supporting Binc IMAP and Dovecot natively +- Added qmail-qmaint for queue maintenance +- Added 'implicit TLS' support for qmail-remote and qmail-smtpam + Port numbers may now prepended with 's' -> implicit TLS +- qmail-users: changed name from 'users/cdb' to 'users/assign.cdb' +- Added qmail-dksign and qmail-dkverify together with qmail-dkim +- DKIM keys are given at SQMAIL/ssl/domainkeys/<domain> +- Authentication support for BincIMAP (separate package) +- Added qmail-ldapam as installable option (tbd) + + +DEDICATION +---------- +- Niklaus Wirth - creator of PASCAL (A Plea for Lean Software) +- Niklaus Wirth: "Eine Optimierung beim Programmieren erfordert Zeit" +[https://www.heise.de/hintergrund/Niklaus-Wirth-Diese-schnell-erstellten-Programme-enthalten-auch-mehr-Fehler-9587266.html] + + +s/qmail (4.3) -- this README covers the main s/qmail merits. + +See doc/CHANGELOG for version information. + +Date: Janary, 8th 2024 (feh) diff --git a/sqmail-4.3.07/conf-break b/sqmail-4.3.07/conf-break new file mode 100644 index 0000000..2cfacf5 --- /dev/null +++ b/sqmail-4.3.07/conf-break @@ -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. diff --git a/sqmail-4.3.07/conf-cc b/sqmail-4.3.07/conf-cc new file mode 100644 index 0000000..307ccfc --- /dev/null +++ b/sqmail-4.3.07/conf-cc @@ -0,0 +1,18 @@ +cc -O2 -Wall -Wno-narrowing -Iinclude -I`head -1 ../conf-qlibs`/include `head -1 ../conf-ssl` -I/usr/local/include + +# This will work for both i386 and AMD64 architecture enabling INET6 support. +# IDN2 support is NOT enabled by default. You do not have 'libidns2' installed and set: -DIDN2 + +# For obfuscation, you can hide the virtual user's local part for VERP addresses; inappropriate for VPOPMAIL: + +cc -O2 -Wall -Wno-narrowing -Iinclude -I`head -1 ../conf-qlibs`/include `head -1 ../conf-ssl` -DHIDEVIRTUALUSER + +# qmail-remote will bounce mails immediately, if no DNS record is found; or mail may stay in the queue until it expires: + +cc -O2 -Wall -Wno-narrowing -Iinclude -I`head -1 ../conf-qlibs`/include `head -1 ../conf-ssl` -DDEFERREDBOUNCES + +# security might be enhanced, using the following compiler flags: + +cc -Wall -pipe -z relro -z now -pie -fPIE -fstack-protector-all -D_FORTIFY_SOURCE=2 -O2 -DIDN2 + +# This is for gcc and with strong security in mind. diff --git a/sqmail-4.3.07/conf-delivery b/sqmail-4.3.07/conf-delivery new file mode 100644 index 0000000..ddee2a0 --- /dev/null +++ b/sqmail-4.3.07/conf-delivery @@ -0,0 +1,29 @@ +./Maildir/ + +** Note: Only the first line will be evaluated! ** + +(1) This is the qmail-start standard delivery to local Maildirs. + +./Mailbox + +(2) This is the qmail-start standard delivery to local mbox'es. + +./Mailbox splogger qmail + +(3) This is the qmail-start standard delivery to local mbox'es + and additional logging to syslog via splogger. + +'|preline procmail' splogger qmail + +(4) Using procmail to deliver messages to /var/spool/mail/$USER by default. + Using splogger to send the log through syslog. + +'|dot-forward .forward |preline procmail' splogger qmail + +(5) Using dot-forward to support sendmail-style ~/.forward files. + Using procmail to deliver messages to /var/spool/mail/$USER by default. + Using splogger to send the log through syslog. + +'|preline -f /usr/local/libexec/dovecot/dovecot-lda' + +(6) Using dovecot's local delivery agent. diff --git a/sqmail-4.3.07/conf-groups b/sqmail-4.3.07/conf-groups new file mode 100644 index 0000000..77353b5 --- /dev/null +++ b/sqmail-4.3.07/conf-groups @@ -0,0 +1,5 @@ +sqmail +nofiles + +# The s/qmail groups: sqmail is used for binary and man files; +# nofiles for auxiliary files. diff --git a/sqmail-4.3.07/conf-home b/sqmail-4.3.07/conf-home new file mode 100644 index 0000000..ae70bfa --- /dev/null +++ b/sqmail-4.3.07/conf-home @@ -0,0 +1,17 @@ +/var/qmail + +# This is the sqmail home directory. It must be a local directory, not +# shared among machines. This is where qmail queues all mail messages. + +/usr/local/qmail + +# This is the alternative of OS with don't allow suid on /var (OpenBSD). + +# The queue (except for bounce message contents) is crashproof, if the +# filesystem guarantees that single-byte writes are atomic and that +# directory operations are synchronous. These guarantees are provided by +# fixed-block filesystems such as UFS and by journaling filesystems. Under +# Linux, make sure that all mail-handling filesystems are mounted with +# synchronous metadata. + +# Note: The sqmail binaries do not need to share the same mount point. diff --git a/sqmail-4.3.07/conf-idn2 b/sqmail-4.3.07/conf-idn2 new file mode 100644 index 0000000..5d45d02 --- /dev/null +++ b/sqmail-4.3.07/conf-idn2 @@ -0,0 +1,8 @@ +-L /usr/local/lib + +# On Linux system, an 'empty' line is fine. + +-L /usr/local/lib + +# In case, the libidn2 is residing elsewhere, +# (eg. FreeBSD) you need to include the path. diff --git a/sqmail-4.3.07/conf-ids b/sqmail-4.3.07/conf-ids new file mode 100644 index 0000000..48a98f8 --- /dev/null +++ b/sqmail-4.3.07/conf-ids @@ -0,0 +1,15 @@ +# sqmail Unix group-ids and user-ids +# Change ids on your own behalf; +# sqmail user names require change of conf-users in addition +# +2108:nofiles:sqmail group for auxiliar files: +2109:sqmail:sqmail group for binary files: +# +7790:alias:sqmail Alias user:nofiles:alias +7791:qmaild:sqmail Daemon user:nofiles +7792:qmaill:sqmail Log user:nofiles +7793:qmailp:sqmail Password user:nofiles +7794:qmailq:sqmail Queue user:sqmail:queue +7795:qmailr:sqmail Remote user:sqmail +7796:qmails:sqmail Send user:sqmail +7797:sqmtls:sqmail TLS user:nofiles:ssl diff --git a/sqmail-4.3.07/conf-instances b/sqmail-4.3.07/conf-instances new file mode 100644 index 0000000..dd96595 --- /dev/null +++ b/sqmail-4.3.07/conf-instances @@ -0,0 +1,14 @@ +## Here, you define the multiple queue instances by name +## Lines with preceeding '#' are ignored as well as any comment after the '#' +## Avoid withspaces here +## IPv4 and IPv6 addresses are possible -- bind & delivery IP address +# +# Instance-ID : Alias Name : IP Address +# ----------- ---------- ---------- +# +#00:Internal_Me:#base-IP # Mails for me will be delivered here +#01:Customer_1:#customer-IP # 1st Customer delivery instance +#02:Customer_2:#customer-IP # 2nd Customer delivery instance +#80:INTERNET:#outgoing-IP # Regular Mails send to the INTERNET are going this way +#90:BOUNCES:#2nd-out-IP # Bounce Mails will make that way; avoid blacklisting by means of a separate IP +#99:BACKUP:127.0.0.1 # Spam and Virus storms will be redirected to this instance -- on demand diff --git a/sqmail-4.3.07/conf-ld b/sqmail-4.3.07/conf-ld new file mode 100644 index 0000000..0ce8ac7 --- /dev/null +++ b/sqmail-4.3.07/conf-ld @@ -0,0 +1,11 @@ +cc -s -m64 + +cc -s -z noexecstack -m64 + +# This is for AMD64 architecture; use else: + +cc -s + +# This will be used to link .o files into an executable. + +# Note: UCSPI-SSL's conf-ld needs to use the same architecture! diff --git a/sqmail-4.3.07/conf-log b/sqmail-4.3.07/conf-log new file mode 100644 index 0000000..6dc338b --- /dev/null +++ b/sqmail-4.3.07/conf-log @@ -0,0 +1,6 @@ +/var/log + +# This is the s/qmail high-level log dir. +# Running package/service will generate automatically the +# required individual log dirs named after the service, +# ie. qmail-send, qmail-smtpd .... etc. diff --git a/sqmail-4.3.07/conf-man b/sqmail-4.3.07/conf-man new file mode 100644 index 0000000..bbb5a1a --- /dev/null +++ b/sqmail-4.3.07/conf-man @@ -0,0 +1,7 @@ + + +# Here, the location of the s/qmail man pages can be specified. +# If this line is empty (or dir invalid), the default is taken +# from manpath. +# Typical directories -- /usr/local/man, /usr/share/man -- are +# considered automatically; except for OpenBSD. diff --git a/sqmail-4.3.07/conf-patrn b/sqmail-4.3.07/conf-patrn new file mode 100644 index 0000000..3c62a89 --- /dev/null +++ b/sqmail-4.3.07/conf-patrn @@ -0,0 +1,6 @@ +002 + +# These stat bits are not allowed in ~ and ~/.qmail. On most systems, the +# default umask is 022 or 077, so 022 will work here. + +# Note that ~ftp, ~www, ~uucp, etc. should be owned by root. diff --git a/sqmail-4.3.07/conf-qlibs b/sqmail-4.3.07/conf-qlibs new file mode 100644 index 0000000..325b721 --- /dev/null +++ b/sqmail-4.3.07/conf-qlibs @@ -0,0 +1,3 @@ +/usr/local/qlibs + +# This is the path to your qlibs directory (-I not required here) diff --git a/sqmail-4.3.07/conf-qmq b/sqmail-4.3.07/conf-qmq new file mode 100644 index 0000000..10d773f --- /dev/null +++ b/sqmail-4.3.07/conf-qmq @@ -0,0 +1,8 @@ +# conf-qmq environment settings +# Here, you may change some global settings + +# Note: The multiple-queue script is provisionally only + +export SKELETON_CONCURRENCYREMOTE="120" +export SKELETON_QUEUELIFETIME="1440" # 24 Hours +export SKELETON_PORT="1000" # high ports for QMQ diff --git a/sqmail-4.3.07/conf-spawn b/sqmail-4.3.07/conf-spawn new file mode 100644 index 0000000..35950a3 --- /dev/null +++ b/sqmail-4.3.07/conf-spawn @@ -0,0 +1,6 @@ +120 + +# This is a silent concurrency limit. You can't set it above 1024. +# On some systems you can't set it above 124 or you need to adjust +# kernel parameters. +# s/qmail will refuse to compile if the limit is too high. diff --git a/sqmail-4.3.07/conf-split b/sqmail-4.3.07/conf-split new file mode 100644 index 0000000..45989a9 --- /dev/null +++ b/sqmail-4.3.07/conf-split @@ -0,0 +1,15 @@ +23 + +# This is the queue subdirectory split. + +127 + +# This can ben benefial for a server handling 100k messages/day. + +521 + +# This helps for ~ 500k messages/day. + +1223 + +# You should think about splitting your service. diff --git a/sqmail-4.3.07/conf-svcdir b/sqmail-4.3.07/conf-svcdir new file mode 100644 index 0000000..9722552 --- /dev/null +++ b/sqmail-4.3.07/conf-svcdir @@ -0,0 +1,5 @@ +/service + +# This is the daemontools supervise '/service' high-level directory. +# s/qmail services are installed in specific subdirectories here under, +# typically together with their logging companions. diff --git a/sqmail-4.3.07/conf-ucspissl b/sqmail-4.3.07/conf-ucspissl new file mode 100644 index 0000000..024382d --- /dev/null +++ b/sqmail-4.3.07/conf-ucspissl @@ -0,0 +1,3 @@ +/package/host/superscript.com/net/ucspi-ssl + +# Define here the path to UCSPI-SSL diff --git a/sqmail-4.3.07/conf-users b/sqmail-4.3.07/conf-users new file mode 100644 index 0000000..42a5276 --- /dev/null +++ b/sqmail-4.3.07/conf-users @@ -0,0 +1,15 @@ +alias +qmaild +qmaill +root +qmailp +qmailq +qmailr +qmails + +# The s/qmail system is heavily partitioned for security; it does almost +# nothing as root (except for authentication based on PAMs). + +# 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. diff --git a/sqmail-4.3.07/man/Makefile b/sqmail-4.3.07/man/Makefile new file mode 100644 index 0000000..1422378 --- /dev/null +++ b/sqmail-4.3.07/man/Makefile @@ -0,0 +1,515 @@ +# Don't edit Makefile! Use ../conf-* for configuration. + +SHELL=/bin/sh + +default: modules docs dns + +addresses.0: \ +addresses.5 + nroff -man addresses.5 > addresses.0 + +bouncesaying.0: \ +bouncesaying.1 + nroff -man bouncesaying.1 > bouncesaying.0 + +columnt.0: \ +columnt.1 + nroff -man columnt.1 > columnt.0 + +condredirect.0: \ +condredirect.1 + nroff -man condredirect.1 > condredirect.0 + +dns:\ +dnscname.0 dnsfq.0 dnsip.0 dnsmxip.0 dnsptr.0 dnstxt.0 \ +hostname.0 ipmeprint.0 + +dnscname.0: \ +dnscname.8 + nroff -man dnscname.8 > dnscname.0 + +dnsfq.0: \ +dnsfq.8 + nroff -man dnsfq.8 > dnsfq.0 + +dnsip.0: \ +dnsip.8 + nroff -man dnsip.8 > dnsip.0 + +dnsmxip.0: \ +dnsmxip.8 + nroff -man dnsmxip.8 > dnsmxip.0 + +dnsptr.0: \ +dnsptr.8 + nroff -man dnsptr.8 > dnsptr.0 + +dnstxt.0: \ +dnstxt.8 + nroff -man dnstxt.8 > dnstxt.0 + +datetime.0: \ +datetime.3 + nroff -man datetime.3 > datetime.0 + +docs:\ +addresses.0 dot-qmail.0 envelopes.0 forgeries.0 mbox.0 maildir.0 \ +qmail-command.0 qmail-control.0 qmail-header.0 qmail-limits.0 \ +tcp-environ.0 + +dot-qmail.0: \ +dot-qmail.5 + nroff -man dot-qmail.5 > dot-qmail.0 + +dot-qmail.5: \ +dot-qmail.9 ../conf-home ../conf-break ../conf-spawn + cat dot-qmail.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPAWN}"`head -1 ../conf-spawn`"}g \ + > dot-qmail.5 + +envelopes.0: \ +envelopes.5 + nroff -man envelopes.5 > envelopes.0 + +except.0: \ +except.1 + nroff -man except.1 > except.0 + +fastforward.0: \ +fastforward.1 + nroff -man fastforward.1 > fastforward.0 + +forgeries.0: \ +forgeries.7 + nroff -man forgeries.7 > forgeries.0 + +forward.0: \ +forward.1 + nroff -man forward.1 > forward.0 + +hostname.0: \ +hostname.8 + nroff -man hostname.8 > hostname.0 + +ipmeprint.0: \ +ipmeprint.8 + nroff -man ipmeprint.8 > ipmeprint.0 + +maildir.0: \ +maildir.5 + nroff -man maildir.5 > maildir.0 + +maildir2mbox.0: \ +maildir2mbox.1 + nroff -man maildir2mbox.1 > maildir2mbox.0 + +maildirmake.0: \ +maildirmake.1 + nroff -man maildirmake.1 > maildirmake.0 + +maildirwatch.0: \ +maildirwatch.1 + nroff -man maildirwatch.1 > maildirwatch.0 + +mailsubj.0: \ +mailsubj.1 + nroff -man mailsubj.1 > mailsubj.0 + +matchup.0: \ +matchup.1 + nroff -man matchup.1 > matchup.0 + +mbox.0: \ +mbox.5 + nroff -man mbox.5 > mbox.0 + +modules: \ +qmail-local.0 qmail-lspawn.0 qmail-getpw.0 qmail-remote.0 qmail-smtpam.0 \ +qmail-todo.0 qmail-vmailuser.0 qmail-authuser.0 qmail-postgrey.0 \ +qmail-rspawn.0 qmail-clean.0 qmail-send.0 qmail-start.0 splogger.0 spfquery.0 \ +qmail-queue.0 qmail-inject.0 mailsubj.0 qmail-showctl.0 qmail-newu.0 qmail-qmaint.0 \ +qmail-badmimetypes.0 qmail-badloadertypes.0 qmail-recipients.0 qmail-mfrules.0 \ +qmail-pw2u.0 qmail-qread.0 qmail-qstat.0 qmail-tcpto.0 qmail-tcpok.0 \ +qmail-pop3d.0 qmail-popup.0 qmail-qmqpc.0 qmail-qmqpd.0 qmail-qmtpd.0 \ +qmail-smtpd.0 qmail-newmrh.0 qmail-mrtg.0 qmail-users.0 qreceipt.0 qbiff.0 \ +forward.0 preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \ +maildir2mbox.0 maildirwatch.0 sqmail.0 tai64nfrac.0 \ +columnt.0 matchup.0 xqp.0 xrecipient.0 xsender.0 newaliases.0 newinclude.0 \ +fastforward.0 printforward.0 printmaillist.0 setforward.0 setmaillist.0 \ +srsforward.0 srsreverse.0 \ +qmail-dkim.0 qmail-dksign.0 qmail-dkverify.0 \ + +newaliases.0: \ +newaliases.1 + nroff -man newaliases.1 > newaliases.0 + +newinclude.0: \ +newinclude.1 + nroff -man newinclude.1 > newinclude.0 + +preline.0: \ +preline.1 + nroff -man preline.1 > preline.0 + +printforward.0: \ +printforward.1 + nroff -man printforward.1 > printforward.0 + +printmaillist.0: \ +printmaillist.1 + nroff -man printmaillist.1 > printmaillist.0 + +qbiff.0: \ +qbiff.1 + nroff -man qbiff.1 > qbiff.0 + +qmail-clean.0: \ +qmail-clean.8 + nroff -man qmail-clean.8 > qmail-clean.0 + +qmail-authuser.0: \ +qmail-authuser.8 + nroff -man qmail-authuser.8 > qmail-authuser.0 + +qmail-authuser.8: \ +qmail-authuser.9 ../conf-home + cat qmail-authuser.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-authuser.8 + +qmail-badmimetypes.0: \ +qmail-badmimetypes.8 + nroff -man qmail-badmimetypes.8 > qmail-badmimetypes.0 + +qmail-badmimetypes.8: \ +qmail-badmimetypes.9 ../conf-home + cat qmail-badmimetypes.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-badmimetypes.8 + +qmail-badloadertypes.0: \ +qmail-badloadertypes.8 + nroff -man qmail-badloadertypes.8 > qmail-badloadertypes.0 + +qmail-badloadertypes.8: \ +qmail-badloadertypes.9 ../conf-home + cat qmail-badloadertypes.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-badloadertypes.8 + +qmail-command.0: \ +qmail-command.8 + nroff -man qmail-command.8 > qmail-command.0 + +qmail-control.0: \ +qmail-control.5 + nroff -man qmail-control.5 > qmail-control.0 + +qmail-control.5: \ +qmail-control.9 ../conf-home + cat qmail-control.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-control.5 + +qmail-dkim.0: \ +qmail-dkim.8 + nroff -man qmail-dkim.8 > qmail-dkim.0 + +qmail-dksign.0: \ +qmail-dksign.8 + nroff -man qmail-dksign.8 > qmail-dksign.0 + +qmail-dksign.8: \ +qmail-dksign.9 ../conf-home + cat qmail-dksign.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-dksign.8 + +qmail-dkverify.0: \ +qmail-dkverify.8 + nroff -man qmail-dkverify.8 > qmail-dkverify.0 + +qmail-getpw.0: \ +qmail-getpw.8 + nroff -man qmail-getpw.8 > qmail-getpw.0 + +qmail-getpw.8: \ +qmail-getpw.9 ../conf-home + cat qmail-getpw.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + > qmail-getpw.8 + +qmail-header.0: \ +qmail-header.5 + nroff -man qmail-header.5 > qmail-header.0 + +qmail-inject.0: \ +qmail-inject.8 + nroff -man qmail-inject.8 > qmail-inject.0 + +qmail-limits.0: \ +qmail-limits.7 + nroff -man qmail-limits.7 > qmail-limits.0 + +qmail-limits.7: \ +qmail-limits.9 ../conf-home ../conf-break ../conf-spawn + cat qmail-limits.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPAWN}"`head -1 ../conf-spawn`"}g \ + > qmail-limits.7 + +qmail-local.0: \ +qmail-local.8 + nroff -man qmail-local.8 > qmail-local.0 + +qmail-log.0: \ +qmail-log.5 + nroff -man qmail-log.5 > qmail-log.0 + +qmail-lspawn.0: \ +qmail-lspawn.8 + nroff -man qmail-lspawn.8 > qmail-lspawn.0 + +qmail-mfrules.0: \ +qmail-mfrules.8 + nroff -man qmail-mfrules.8 > qmail-mfrules.0 + +qmail-mfrules.8: \ +qmail-mfrules.9 ../conf-home + cat qmail-mfrules.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-mfrules.8 + +qmail-mrtg.0: \ +qmail-mrtg.8 + nroff -man qmail-mrtg.8 > qmail-mrtg.0 + +qmail-newmrh.0: \ +qmail-newmrh.8 + nroff -man qmail-newmrh.8 > qmail-newmrh.0 + +qmail-newmrh.8: \ +qmail-newmrh.9 ../conf-home + cat qmail-newmrh.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-newmrh.8 + +qmail-newu.0: \ +qmail-newu.8 + nroff -man qmail-newu.8 > qmail-newu.0 + +qmail-newu.8: \ +qmail-newu.9 ../conf-home + cat qmail-newu.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-newu.8 + +qmail-pop3d.0: \ +qmail-pop3d.8 + nroff -man qmail-pop3d.8 > qmail-pop3d.0 + +qmail-popup.0: \ +qmail-popup.8 + nroff -man qmail-popup.8 > qmail-popup.0 + +qmail-postgrey.0: \ +qmail-postgrey.8 + nroff -man qmail-postgrey.8 > qmail-postgrey.0 + +qmail-pw2u.0: \ +qmail-pw2u.8 + nroff -man qmail-pw2u.8 > qmail-pw2u.0 + +qmail-pw2u.8: \ +qmail-pw2u.9 ../conf-home + cat qmail-pw2u.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + > qmail-pw2u.8 + +qmail-qmqpc.0: \ +qmail-qmqpc.8 + nroff -man qmail-qmqpc.8 > qmail-qmqpc.0 + +qmail-qmqpd.0: \ +qmail-qmqpd.8 + nroff -man qmail-qmqpd.8 > qmail-qmqpd.0 + +qmail-qmtpd.0: \ +qmail-qmtpd.8 + nroff -man qmail-qmtpd.8 > qmail-qmtpd.0 + +qmail-qread.0: \ +qmail-qread.8 + nroff -man qmail-qread.8 > qmail-qread.0 + +qmail-qstat.0: \ +qmail-qstat.8 + nroff -man qmail-qstat.8 > qmail-qstat.0 + +qmail-qmaint.0: \ +qmail-qmaint.8 + nroff -man qmail-qmaint.8 > qmail-qmaint.0 + +qmail-queue.0: \ +qmail-queue.8 + nroff -man qmail-queue.8 > qmail-queue.0 + +qmail-recipients.0: \ +qmail-recipients.8 + nroff -man qmail-recipients.8 > qmail-recipients.0 + +qmail-recipients.8: \ +qmail-recipients.9 ../conf-home + cat qmail-recipients.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-recipients.8 + +qmail-remote.0: \ +qmail-remote.8 + nroff -man qmail-remote.8 > qmail-remote.0 + +qmail-rspawn.0: \ +qmail-rspawn.8 + nroff -man qmail-rspawn.8 > qmail-rspawn.0 + +qmail-send.0: \ +qmail-send.8 + nroff -man qmail-send.8 > qmail-send.0 + +qmail-send.8: \ +qmail-send.9 ../conf-home + cat qmail-send.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + > qmail-send.8 + +qmail-showctl.0: \ +qmail-showctl.8 + nroff -man qmail-showctl.8 > qmail-showctl.0 + +qmail-smtpam.0: \ +qmail-smtpam.8 + nroff -man qmail-smtpam.8 > qmail-smtpam.0 + +qmail-smtpd.0: \ +qmail-smtpd.8 + nroff -man qmail-smtpd.8 > qmail-smtpd.0 + +qmail-start.0: \ +qmail-start.8 + nroff -man qmail-start.8 > qmail-start.0 + +qmail-start.8: \ +qmail-start.9 ../conf-home ../conf-break ../conf-spawn + cat qmail-start.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPAWN}"`head -1 ../conf-spawn`"}g \ + > qmail-start.8 + +qmail-tcpok.0: \ +qmail-tcpok.8 + nroff -man qmail-tcpok.8 > qmail-tcpok.0 + +qmail-tcpto.0: \ +qmail-tcpto.8 + nroff -man qmail-tcpto.8 > qmail-tcpto.0 + +qmail-todo.0: \ +qmail-todo.8 + nroff -man qmail-todo.8 > qmail-todo.0 + +qmail-users.0: \ +qmail-users.5 + nroff -man qmail-users.5 > qmail-users.0 + +qmail-users.5: \ +qmail-users.9 ../conf-home + cat qmail-users.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-users.5 + +qmail-vmailuser.0: \ +qmail-vmailuser.8 + nroff -man qmail-vmailuser.8 > qmail-vmailuser.0 + +qmail-vmailuser.8: \ +qmail-vmailuser.9 ../conf-home + cat qmail-vmailuser.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-vmailuser.8 + +qreceipt.0: \ +qreceipt.1 + nroff -man qreceipt.1 > qreceipt.0 + +setforward.0: \ +setforward.1 + nroff -man setforward.1 > setforward.0 + +setmaillist.0: \ +setmaillist.1 + nroff -man setmaillist.1 > setmaillist.0 + +spfquery.0: \ +spfquery.8 + nroff -man spfquery.8 > spfquery.0 + +splogger.0: \ +splogger.8 + nroff -man splogger.8 > splogger.0 + +sqmail.0: \ +sqmail.7 + nroff -man sqmail.7 > sqmail.0 + +sqmail.7: \ +sqmail.9 ../package/version + cat sqmail.9 \ + | sed s}VERSION}"`head -1 ../package/version`"}g \ + > sqmail.7 + +srsforward.0: \ +srsforward.1 + nroff -man srsforward.1 > srsforward.0 + +srsreverse.0: \ +srsreverse.8 + nroff -man srsreverse.8 > srsreverse.0 + +srsreverse.8: \ +srsreverse.9 ../conf-home + cat srsreverse.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > srsreverse.8 + +tai64nfrac.0: \ +tai64nfrac.5 + nroff -man tai64nfrac.5 > tai64nfrac.0 + +tcp-environ.0: \ +tcp-environ.5 + nroff -man tcp-environ.5 > tcp-environ.0 + +xqp.0: \ +xqp.1 + nroff -man xqp.1 > xqp.0 + +xrecipient.0: \ +xrecipient.1 + nroff -man xrecipient.1 > xrecipient.0 + +xsender.0: \ +xsender.1 + nroff -man xsender.1 > xsender.0 + +clean: \ +TARGETS + rm -f `cat TARGETS` +# gzip -q -d *.gz + diff --git a/sqmail-4.3.07/man/Makefile.mandoc b/sqmail-4.3.07/man/Makefile.mandoc new file mode 100644 index 0000000..3369cbb --- /dev/null +++ b/sqmail-4.3.07/man/Makefile.mandoc @@ -0,0 +1,512 @@ +# Don't edit Makefile! Use ../conf-* for configuration. + +SHELL=/bin/sh + +default: modules docs dns + +addresses.0: \ +addresses.5 + mandoc -man addresses.5 > addresses.0 + +bouncesaying.0: \ +bouncesaying.1 + mandoc -man bouncesaying.1 > bouncesaying.0 + +columnt.0: \ +columnt.1 + mandoc -man columnt.1 > columnt.0 + +condredirect.0: \ +condredirect.1 + mandoc -man condredirect.1 > condredirect.0 + +dns:\ +dnscname.0 dnsfq.0 dnsip.0 dnsmxip.0 dnsptr.0 dnstxt.0 \ +hostname.0 ipmeprint.0 + +dnscname.0: \ +dnscname.8 + mandoc -man dnscname.8 > dnscname.0 + +dnsfq.0: \ +dnsfq.8 + mandoc -man dnsfq.8 > dnsfq.0 + +dnsip.0: \ +dnsip.8 + mandoc -man dnsip.8 > dnsip.0 + +dnsmxip.0: \ +dnsmxip.8 + mandoc -man dnsmxip.8 > dnsmxip.0 + +dnsptr.0: \ +dnsptr.8 + mandoc -man dnsptr.8 > dnsptr.0 + +dnstxt.0: \ +dnstxt.8 + mandoc -man dnstxt.8 > dnstxt.0 + +datetime.0: \ +datetime.3 + mandoc -man datetime.3 > datetime.0 + +docs:\ +addresses.0 dot-qmail.0 envelopes.0 forgeries.0 mbox.0 maildir.0 \ +qmail-command.0 qmail-control.0 qmail-header.0 qmail-limits.0 \ +tcp-environ.0 + +dot-qmail.0: \ +dot-qmail.5 + mandoc -man dot-qmail.5 > dot-qmail.0 + +dot-qmail.5: \ +dot-qmail.9 ../conf-home ../conf-break ../conf-spawn + cat dot-qmail.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPAWN}"`head -1 ../conf-spawn`"}g \ + > dot-qmail.5 + +envelopes.0: \ +envelopes.5 + mandoc -man envelopes.5 > envelopes.0 + +except.0: \ +except.1 + mandoc -man except.1 > except.0 + +fastforward.0: \ +fastforward.1 + mandoc -man fastforward.1 > fastforward.0 + +forgeries.0: \ +forgeries.7 + mandoc -man forgeries.7 > forgeries.0 + +forward.0: \ +forward.1 + mandoc -man forward.1 > forward.0 + +hostname.0: \ +hostname.8 + mandoc -man hostname.8 > hostname.0 + +ipmeprint.0: \ +ipmeprint.8 + mandoc -man ipmeprint.8 > ipmeprint.0 + +maildir.0: \ +maildir.5 + mandoc -man maildir.5 > maildir.0 + +maildir2mbox.0: \ +maildir2mbox.1 + mandoc -man maildir2mbox.1 > maildir2mbox.0 + +maildirmake.0: \ +maildirmake.1 + mandoc -man maildirmake.1 > maildirmake.0 + +maildirwatch.0: \ +maildirwatch.1 + mandoc -man maildirwatch.1 > maildirwatch.0 + +mailsubj.0: \ +mailsubj.1 + mandoc -man mailsubj.1 > mailsubj.0 + +matchup.0: \ +matchup.1 + mandoc -man matchup.1 > matchup.0 + +mbox.0: \ +mbox.5 + mandoc -man mbox.5 > mbox.0 + +modules: \ +qmail-local.0 qmail-lspawn.0 qmail-getpw.0 qmail-remote.0 qmail-smtpam.0 \ +qmail-todo.0 qmail-vmailuser.0 qmail-authuser.0 qmail-postgrey.0 \ +qmail-rspawn.0 qmail-clean.0 qmail-send.0 qmail-start.0 splogger.0 spfquery.0 \ +qmail-queue.0 qmail-inject.0 mailsubj.0 qmail-showctl.0 qmail-newu.0 qmail-qmaint.0 \ +qmail-badmimetypes.0 qmail-badloadertypes.0 qmail-recipients.0 qmail-mfrules.0 \ +qmail-pw2u.0 qmail-qread.0 qmail-qstat.0 qmail-tcpto.0 qmail-tcpok.0 \ +qmail-pop3d.0 qmail-popup.0 qmail-qmqpc.0 qmail-qmqpd.0 qmail-qmtpd.0 \ +qmail-smtpd.0 qmail-newmrh.0 qmail-mrtg.0 qmail-users.0 qreceipt.0 qbiff.0 \ +forward.0 preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \ +maildir2mbox.0 maildirwatch.0 sqmail.0 tai64nfrac.0 \ +columnt.0 matchup.0 xqp.0 xrecipient.0 xsender.0 newaliases.0 newinclude.0 \ +fastforward.0 printforward.0 printmaillist.0 setforward.0 setmaillist.0 \ +srsforward.0 srsreverse.0 \ +qmail-dkim.0 qmail-dksign.0 qmail-dkverify.0 \ + +newaliases.0: \ +newaliases.1 + mandoc -man newaliases.1 > newaliases.0 + +newinclude.0: \ +newinclude.1 + mandoc -man newinclude.1 > newinclude.0 + +preline.0: \ +preline.1 + mandoc -man preline.1 > preline.0 + +printforward.0: \ +printforward.1 + mandoc -man printforward.1 > printforward.0 + +printmaillist.0: \ +printmaillist.1 + mandoc -man printmaillist.1 > printmaillist.0 + +qbiff.0: \ +qbiff.1 + mandoc -man qbiff.1 > qbiff.0 + +qmail-clean.0: \ +qmail-clean.8 + mandoc -man qmail-clean.8 > qmail-clean.0 + +qmail-authuser.0: \ +qmail-authuser.8 + mandoc -man qmail-authuser.8 > qmail-authuser.0 + +qmail-authuser.8: \ +qmail-authuser.9 ../conf-home + cat qmail-authuser.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-authuser.8 + +qmail-badmimetypes.0: \ +qmail-badmimetypes.8 + mandoc -man qmail-badmimetypes.8 > qmail-badmimetypes.0 + +qmail-badmimetypes.8: \ +qmail-badmimetypes.9 ../conf-home + cat qmail-badmimetypes.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-badmimetypes.8 + +qmail-badloadertypes.0: \ +qmail-badloadertypes.8 + mandoc -man qmail-badloadertypes.8 > qmail-badloadertypes.0 + +qmail-badloadertypes.8: \ +qmail-badloadertypes.9 ../conf-home + cat qmail-badloadertypes.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-badloadertypes.8 + +qmail-command.0: \ +qmail-command.8 + mandoc -man qmail-command.8 > qmail-command.0 + +qmail-control.0: \ +qmail-control.5 + mandoc -man qmail-control.5 > qmail-control.0 + +qmail-control.5: \ +qmail-control.9 ../conf-home + cat qmail-control.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-control.5 + +qmail-dkim.0: \ +qmail-dkim.8 + mandoc -man qmail-dkim.8 > qmail-dkim.0 + +qmail-dksign.0: \ +qmail-dksign.8 + mandoc -man qmail-dksign.8 > qmail-dksign.0 + +qmail-dksign.8: \ +qmail-dksign.9 ../conf-home + cat qmail-dksign.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-dksign.8 + +qmail-dkverify.0: \ +qmail-dkverify.8 + mandoc -man qmail-dkverify.8 > qmail-dkverify.0 + +qmail-getpw.0: \ +qmail-getpw.8 + mandoc -man qmail-getpw.8 > qmail-getpw.0 + +qmail-getpw.8: \ +qmail-getpw.9 ../conf-home + cat qmail-getpw.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-getpw.8 + +qmail-header.0: \ +qmail-header.5 + mandoc -man qmail-header.5 > qmail-header.0 + +qmail-inject.0: \ +qmail-inject.8 + mandoc -man qmail-inject.8 > qmail-inject.0 + +qmail-limits.0: \ +qmail-limits.7 + mandoc -man qmail-limits.7 > qmail-limits.0 + +qmail-limits.7: \ +qmail-limits.9 ../conf-home ../conf-break ../conf-spawn + cat qmail-limits.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPAWN}"`head -1 ../conf-spawn`"}g \ + > qmail-limits.7 + +qmail-local.0: \ +qmail-local.8 + mandoc -man qmail-local.8 > qmail-local.0 + +qmail-log.0: \ +qmail-log.5 + mandoc -man qmail-log.5 > qmail-log.0 + +qmail-lspawn.0: \ +qmail-lspawn.8 + mandoc -man qmail-lspawn.8 > qmail-lspawn.0 + +qmail-mfrules.0: \ +qmail-mfrules.8 + mandoc -man qmail-mfrules.8 > qmail-mfrules.0 + +qmail-mfrules.8: \ +qmail-mfrules.9 ../conf-home + cat qmail-mfrules.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-mfrules.8 + +qmail-mrtg.0: \ +qmail-mrtg.8 + mandoc -man qmail-mrtg.8 > qmail-mrtg.0 + +qmail-newmrh.0: \ +qmail-newmrh.8 + mandoc -man qmail-newmrh.8 > qmail-newmrh.0 + +qmail-newmrh.8: \ +qmail-newmrh.9 ../conf-home + cat qmail-newmrh.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-newmrh.8 + +qmail-newu.0: \ +qmail-newu.8 + mandoc -man qmail-newu.8 > qmail-newu.0 + +qmail-newu.8: \ +qmail-newu.9 ../conf-home + cat qmail-newu.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-newu.8 + +qmail-pop3d.0: \ +qmail-pop3d.8 + mandoc -man qmail-pop3d.8 > qmail-pop3d.0 + +qmail-popup.0: \ +qmail-popup.8 + mandoc -man qmail-popup.8 > qmail-popup.0 + +qmail-postgrey.0: \ +qmail-postgrey.8 + mandoc -man qmail-postgrey.8 > qmail-postgrey.0 + +qmail-pw2u.0: \ +qmail-pw2u.8 + mandoc -man qmail-pw2u.8 > qmail-pw2u.0 + +qmail-pw2u.8: \ +qmail-pw2u.9 ../conf-home + cat qmail-pw2u.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-pw2u.8 + +qmail-qmqpc.0: \ +qmail-qmqpc.8 + mandoc -man qmail-qmqpc.8 > qmail-qmqpc.0 + +qmail-qmqpd.0: \ +qmail-qmqpd.8 + mandoc -man qmail-qmqpd.8 > qmail-qmqpd.0 + +qmail-qmtpd.0: \ +qmail-qmtpd.8 + mandoc -man qmail-qmtpd.8 > qmail-qmtpd.0 + +qmail-qread.0: \ +qmail-qread.8 + mandoc -man qmail-qread.8 > qmail-qread.0 + +qmail-qstat.0: \ +qmail-qstat.8 + mandoc -man qmail-qstat.8 > qmail-qstat.0 + +qmail-qmaint.0: \ +qmail-qmaint.8 + mandoc -man qmail-qmaint.8 > qmail-qmaint.0 + +qmail-queue.0: \ +qmail-queue.8 + mandoc -man qmail-queue.8 > qmail-queue.0 + +qmail-recipients.0: \ +qmail-recipients.8 + mandoc -man qmail-recipients.8 > qmail-recipients.0 + +qmail-recipients.8: \ +qmail-recipients.9 ../conf-home + cat qmail-recipients.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-recipients.8 + +qmail-remote.0: \ +qmail-remote.8 + mandoc -man qmail-remote.8 > qmail-remote.0 + +qmail-rspawn.0: \ +qmail-rspawn.8 + mandoc -man qmail-rspawn.8 > qmail-rspawn.0 + +qmail-send.0: \ +qmail-send.8 + mandoc -man qmail-send.8 > qmail-send.0 + +qmail-send.8: \ +qmail-send.9 ../conf-home + cat qmail-send.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-send.8 + +qmail-showctl.0: \ +qmail-showctl.8 + mandoc -man qmail-showctl.8 > qmail-showctl.0 + +qmail-smtpam.0: \ +qmail-smtpam.8 + mandoc -man qmail-smtpam.8 > qmail-smtpam.0 + +qmail-smtpd.0: \ +qmail-smtpd.8 + mandoc -man qmail-smtpd.8 > qmail-smtpd.0 + +qmail-start.0: \ +qmail-start.8 + mandoc -man qmail-start.8 > qmail-start.0 + +qmail-start.8: \ +qmail-start.9 ../conf-home ../conf-break ../conf-spawn + cat qmail-start.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPAWN}"`head -1 ../conf-spawn`"}g \ + > qmail-start.8 + +qmail-tcpok.0: \ +qmail-tcpok.8 + mandoc -man qmail-tcpok.8 > qmail-tcpok.0 + +qmail-tcpto.0: \ +qmail-tcpto.8 + mandoc -man qmail-tcpto.8 > qmail-tcpto.0 + +qmail-todo.0: \ +qmail-todo.8 + mandoc -man qmail-todo.8 > qmail-todo.0 + +qmail-users.0: \ +qmail-users.5 + mandoc -man qmail-users.5 > qmail-users.0 + +qmail-users.5: \ +qmail-users.9 ../conf-home + cat qmail-users.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-users.5 + +qmail-vmailuser.0: \ +qmail-vmailuser.8 + mandoc -man qmail-vmailuser.8 > qmail-vmailuser.0 + +qmail-vmailuser.8: \ +qmail-vmailuser.9 ../conf-home + cat qmail-vmailuser.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > qmail-vmailuser.8 + +qreceipt.0: \ +qreceipt.1 + mandoc -man qreceipt.1 > qreceipt.0 + +setforward.0: \ +setforward.1 + mandoc -man setforward.1 > setforward.0 + +setmaillist.0: \ +setmaillist.1 + mandoc -man setmaillist.1 > setmaillist.0 + +spfquery.0: \ +spfquery.8 + mandoc -man spfquery.8 > spfquery.0 + +splogger.0: \ +splogger.8 + mandoc -man splogger.8 > splogger.0 + +sqmail.0: \ +sqmail.7 + mandoc -man sqmail.7 > sqmail.0 + +sqmail.7: \ +sqmail.9 ../package/version + cat sqmail.9 \ + | sed s}VERSION}"`head -1 ../package/version`"}g \ + > sqmail.7 + +srsforward.0: \ +srsforward.1 + mandoc -man srsforward.1 > srsforward.0 + +srsreverse.0: \ +srsreverse.8 + mandoc -man srsreverse.8 > srsreverse.0 + +srsreverse.8: \ +srsreverse.9 ../conf-home + cat srsreverse.9 \ + | sed s}SQMAIL}"`head -1 ../conf-home`"}g \ + > srsreverse.8 + +tai64nfrac.0: \ +tai64nfrac.5 + mandoc -man tai64nfrac.5 > tai64nfrac.0 + +tcp-environ.0: \ +tcp-environ.5 + mandoc -man tcp-environ.5 > tcp-environ.0 + +xqp.0: \ +xqp.1 + mandoc -man xqp.1 > xqp.0 + +xrecipient.0: \ +xrecipient.1 + mandoc -man xrecipient.1 > xrecipient.0 + +xsender.0: \ +xsender.1 + mandoc -man xsender.1 > xsender.0 + +clean: \ +TARGETS + rm -f `cat TARGETS` +# gzip -q -d *.gz + diff --git a/sqmail-4.3.07/man/TARGETS b/sqmail-4.3.07/man/TARGETS new file mode 100644 index 0000000..89773bb --- /dev/null +++ b/sqmail-4.3.07/man/TARGETS @@ -0,0 +1,105 @@ +addresses.0 +bouncesaying.0 +columnt.0 +condredirect.0 +datetime.0 +dot-qmail.0 +dot-qmail.5 +dnscname.0 +dnsfq.0 +dnsip.0 +dnsptr.0 +dnsmxip.0 +dnstxt.0 +envelopes.0 +except.0 +fastforward.0 +forgeries.0 +forward.0 +hostname.0 +ipmeprint.0 +maildir.0 +maildir2mbox.0 +maildirmake.0 +maildirwatch.0 +mailsubj.0 +matchup.0 +mbox.0 +newaliases.0 +newinclude.0 +preline.0 +printforward.0 +printmaillist.0 +qbiff.0 +qmail-authuser.0 +qmail-authuser.8 +qmail-badloadertypes.0 +qmail-badloadertypes.8 +qmail-badmimetypes.0 +qmail-badmimetypes.8 +qmail-clean.0 +qmail-command.0 +qmail-dksign.0 +qmail-dksign.8 +qmail-dkim.0 +qmail-dkverify.0 +qmail-getpw.0 +qmail-getpw.8 +qmail-header.0 +qmail-inject.0 +qmail-limits.0 +qmail-limits.7 +qmail-local.0 +qmail-lspawn.0 +qmail-mfrules.0 +qmail-mfrules.8 +qmail-mrtg.0 +qmail-newmrh.0 +qmail-newmrh.8 +qmail-newu.0 +qmail-newu.8 +qmail-pop3d.0 +qmail-popup.0 +qmail-postgrey.0 +qmail-pw2u.0 +qmail-pw2u.8 +qmail-qmqpc.0 +qmail-qmqpd.0 +qmail-qmtpd.0 +qmail-qread.0 +qmail-qstat.0 +qmail-qmaint.0 +qmail-queue.0 +qmail-recipients.0 +qmail-recipients.8 +qmail-remote.0 +qmail-rspawn.0 +qmail-send.0 +qmail-send.8 +qmail-showctl.0 +qmail-smtpam.0 +qmail-smtpd.0 +qmail-start.0 +qmail-start.8 +qmail-tcpok.0 +qmail-tcpto.0 +qmail-todo.0 +qmail-users.0 +qmail-users.5 +qmail-vmailuser.0 +qmail-vmailuser.8 +qreceipt.0 +setforward.0 +setmaillist.0 +spfquery.0 +splogger.0 +sqmail.0 +sqmail.7 +srsforward.0 +srsreverse.0 +srsreverse.8 +tai64nfrac.0 +tcp-environ.0 +xqp.0 +xrecipient.0 +xsender.0 diff --git a/sqmail-4.3.07/man/addresses.5 b/sqmail-4.3.07/man/addresses.5 new file mode 100644 index 0000000..72a234f --- /dev/null +++ b/sqmail-4.3.07/man/addresses.5 @@ -0,0 +1,260 @@ +.TH s/qmail: 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) diff --git a/sqmail-4.3.07/man/bouncesaying.1 b/sqmail-4.3.07/man/bouncesaying.1 new file mode 100644 index 0000000..9f46b67 --- /dev/null +++ b/sqmail-4.3.07/man/bouncesaying.1 @@ -0,0 +1,71 @@ +.TH s/qmail: bouncesaying 1 +.SH NAME +bouncesaying \- perhaps bounce each incoming message +.SH SYNOPSIS +in +.BR .qmail : +.B |bouncesaying +.I error +[ +.I program +[ +.I arg ... +] +] +.SH DESCRIPTION +.B bouncesaying +feeds each new mail message to +.I program +with the given arguments. +If +.I program +exits 0, +.B bouncesaying +prints +.I error +and bounces the message. + +If +.I program +exits 111, +.B bouncesaying +exits 111, +so delivery will be retried later. + +If +.I program +exits anything else +(or does not exist), +.B bouncesaying +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. + +If +.I program +is not supplied, +.B bouncesaying +always bounces the message: + +.EX + |bouncesaying 'This address no longer accepts mail.' +.EE + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR bouncesaying , +make sure to also add a line specifying delivery to your normal mailbox. +.SH "SEE ALSO" +condredirect(1), +except(1), +dot-qmail(5), +qmail-command(8) diff --git a/sqmail-4.3.07/man/columnt.1 b/sqmail-4.3.07/man/columnt.1 new file mode 100644 index 0000000..24eeeef --- /dev/null +++ b/sqmail-4.3.07/man/columnt.1 @@ -0,0 +1,29 @@ +.TH s/qmail: columnt 1 +.SH NAME +columnt \- align columns in a table +.SH SYNTAX +.B columnt +.SH DESCRIPTION +.B columnt +reads a table of whitespace-separated lines. + +.B columnt +then prints the table, +changing the spacing so that +the first column takes the same amount of space in every line, +the second column takes the same amount of space in every line, +etc. + +In the +.B columnt +output, +all columns except the last are right-justified; +the last column is left-justified. +There are two spaces between adjacent columns. + +.B columnt +needs enough memory to read the entire input. +Other than this, +it has no limits on line length or on the number of columns. +.SH "SEE ALSO" +column(1) diff --git a/sqmail-4.3.07/man/condredirect.1 b/sqmail-4.3.07/man/condredirect.1 new file mode 100644 index 0000000..b9418db --- /dev/null +++ b/sqmail-4.3.07/man/condredirect.1 @@ -0,0 +1,63 @@ +.TH s/qmail: 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. + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR condredirect , +make sure to also add a line specifying delivery to your normal mailbox. +.SH "SEE ALSO" +bouncesaying(1), +except(1), +dot-qmail(5), +qmail-command(8), +qmail-queue(8) diff --git a/sqmail-4.3.07/man/datetime.3 b/sqmail-4.3.07/man/datetime.3 new file mode 100644 index 0000000..f62c02d --- /dev/null +++ b/sqmail-4.3.07/man/datetime.3 @@ -0,0 +1,73 @@ +.TH s/qmail: 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) diff --git a/sqmail-4.3.07/man/dnscname.8 b/sqmail-4.3.07/man/dnscname.8 new file mode 100644 index 0000000..7fd3889 --- /dev/null +++ b/sqmail-4.3.07/man/dnscname.8 @@ -0,0 +1,35 @@ +.TH s/qmail: dnscname 8 +.SH NAME +dnscname +.SH SYNOPSIS +.B dnscname +.I fqdn +.SH DESCRIPTION +.B dnscame +takes the given +.I fqdn +for a host and employs an one-stage +.I CNAME +DNS lookup for +.IR fqdn . +The retrieved DNS name could instead be an alias, +rather than a \fIcanonical name\fR. +Use +.B dnsfq +to evaluate the entire +.I CNAME +chain. +.SH "EXIT CODES" +.B dnscname +return +.I 0 +on success, +.I 1 +in case no CNAME was found, and +.I 111 +in case of memory errors. +.SH "SEE ALSO" +dnsfq(8), +dnsmxip(8), +dnsptr(8), +dnstxt(8). diff --git a/sqmail-4.3.07/man/dnsfq.8 b/sqmail-4.3.07/man/dnsfq.8 new file mode 100644 index 0000000..4773fcb --- /dev/null +++ b/sqmail-4.3.07/man/dnsfq.8 @@ -0,0 +1,34 @@ +.TH s/qmail: dnsfq 8 +.SH NAME +dnsfq +.SH SYNOPSIS +.B dnsfq +.I fqdn +.SH DESCRIPTION +.B fqdns +takes the given +.I fqdn +for a host and employs a +.I CNAME +DNS lookup while finally retrieving the +.I AAAA +and +.I A +record following the chain of potential alias names. +On output, the entire chain of nested DNS information +is displayed together with the retrieved IP(v4|v6) +addresses. +.SH "EXIT CODES" +.B dnsfq +returns +.I 0 +on success, +.I 1 +if DNS query errors did occure, and +.I 111 +in case of memory errors. +.SH "SEE ALSO" +dnscname(8), +dnsmxip(8), +dnsptr(8), +dnstxt(8). diff --git a/sqmail-4.3.07/man/dnsip.8 b/sqmail-4.3.07/man/dnsip.8 new file mode 100644 index 0000000..eaa9930 --- /dev/null +++ b/sqmail-4.3.07/man/dnsip.8 @@ -0,0 +1,31 @@ +.TH s/qmail: dnsip 8 +.SH NAME +dnsip +.SH SYNOPSIS +.B dnsip +.I fqdn . +.SH DESCRIPTION +.B dnsip +does a DNS +.I AAAA +and +.I A +lookup and displays the retrieved +.I IPv6 +and +.I IPv4 +addresses on one line for the given +.IR fqdn . +.SH "EXIT CODES" +.B dnsip +always returns +.I 0 +except of +.I 111 +in case of memory errors. +.SH "SEE ALSO" +dnscname(8), +dnsmxip(8), +dnsfq(8), +dnsptr(8), +dnstxt(8). diff --git a/sqmail-4.3.07/man/dnsmxip.8 b/sqmail-4.3.07/man/dnsmxip.8 new file mode 100644 index 0000000..cc3250d --- /dev/null +++ b/sqmail-4.3.07/man/dnsmxip.8 @@ -0,0 +1,42 @@ +.TH s/qmail: dnsmxip 8 +.SH NAME +dnsmxip +.SH SYNOPSIS +.B dnsmxip +.I fqdn +.SH DESCRIPTION +.B dnsmxip +takes the given +.I fqdn +as domain name and employs a +.I MX +lookup for +.I fqdn +while evaluating for the retrieved MX host(s) the respective +.I AAAA +and +.I A +address(es). + +On output, for each MX +.I host +its provided +.I weight +and the respective +.I AAAA +and +.I A +addresses (in perenthesis) are displayed on separate lines. +.SH "EXIT CODES" +.B dnsmxip +returns +.I 0 +and eventually +.I 1 +in case of DNS query errors. +.SH "SEE ALSO" +dnscname(8), +dnsip(8), +dnsfq(8), +dnsptr(8), +dnstxt(8). diff --git a/sqmail-4.3.07/man/dnsptr.8 b/sqmail-4.3.07/man/dnsptr.8 new file mode 100644 index 0000000..c3df614 --- /dev/null +++ b/sqmail-4.3.07/man/dnsptr.8 @@ -0,0 +1,27 @@ +.TH s/qmail: dnsptr 8 +.SH NAME +dnsptr +.SH SYNOPSIS +.B dnsptr +.I IPv4 +or +.IR IPv6 . +.SH DESCRIPTION +.B dnsptr +does a DNS +.I PTR +lookup and displays the retrieved +.IR fqdn . +.SH "EXIT CODES" +.B dnsptr +always returns +.I 0 +except for wrong IP address +formats while returning +.IR 100 . +.SH "SEE ALSO" +dnscname(8), +dnsmxip(8), +dnsfq(8), +dnsip(8), +dnstxt(8). diff --git a/sqmail-4.3.07/man/dnstlsa.8 b/sqmail-4.3.07/man/dnstlsa.8 new file mode 100644 index 0000000..879ed39 --- /dev/null +++ b/sqmail-4.3.07/man/dnstlsa.8 @@ -0,0 +1,51 @@ +.TH s/qmail: dnstlsa 8 +.SH NAME +dnstlsa +.SH SYNOPSIS +.B dnstlsa +.I [-v] [-p port] [-u(dp)|-t(cp)] fqdn +.SH DESCRIPTION +.B dnstlsa +uses the +.I fqdn +for a host employing a +DNS query for a synthesized hostname given as +.I _port._[tcp|udp].fqdn +while doing an initial CNAME resolution +followed by a TLSA query +and displays the result(s). +If +.I -p\ port +is missing +.I port\ 25 +is assumed. +If either +.I -u +or +.I -t +is omitted, +.I tcp +is used. +Each entry is shown on one line, telling +.IR Usage , +.IR Selector , +.IR Matching\ Type +together with the hex-encoded fingerprint or certificate. + +In verbose mode +.I -v +the synthezised record is displayed as well. +.SH "EXIT CODES" +.B dnstlsa +returns +.I 0 +on success, +.I 1 +for DNS query errors, and +.I 111 +in case of memory errors. +.SH "SEE ALSO" +dnstxt(8), +dnsfq(8), +dnsmxip(8), +dnsptr(8). diff --git a/sqmail-4.3.07/man/dnstxt.8 b/sqmail-4.3.07/man/dnstxt.8 new file mode 100644 index 0000000..933f06f --- /dev/null +++ b/sqmail-4.3.07/man/dnstxt.8 @@ -0,0 +1,29 @@ +.TH s/qmail: dnstxt 8 +.SH NAME +dnstxt +.SH SYNOPSIS +.B dnstxt +.I fqdn +.SH DESCRIPTION +.B dnstxt +takes the given +.I fqdn +for a host employing a +.I TXT +DNS lookup for +.I fqdn +and displays the result(s). +.SH "EXIT CODES" +.B dnstxt +returns +.I 0 +on success, +.I 1 +for DNS query errors, and +.I 111 +in case of memory errors. +.SH "SEE ALSO" +dnscname(8), +dnsfq(8), +dnsmxip(8), +dnsptr(8). diff --git a/sqmail-4.3.07/man/dot-qmail.9 b/sqmail-4.3.07/man/dot-qmail.9 new file mode 100644 index 0000000..f01f24e --- /dev/null +++ b/sqmail-4.3.07/man/dot-qmail.9 @@ -0,0 +1,396 @@ +.TH s/qmail: dot-qmail 5 +.SH NAME +dot-qmail \- control the delivery of mail messages +.SH DESCRIPTION +Normally the +.B qmail-local +program delivers each incoming message to your system mailbox, +.IR homedir\fB/Mailbox , +where +.I homedir +is your home directory. + +It can instead +write the mail to a different file or directory, +forward it to another address, +distribute it to a mailing list, +or even execute programs, +all under your control. +.SH "THE QMAIL FILE" +To change +.BR qmail-local 's +behavior, set up a +.B .qmail +file in your home directory. + +.B .qmail +contains one or more lines. +Each line is a delivery instruction. +.B qmail-local +follows each instruction in turn. +There are five types of delivery instructions: +(1) comment; (2) program; (3) forward; (4) mbox; (5) maildir. +.TP 5 +(1) +A comment line begins with a number sign: + +.EX + # this is a comment +.EE + +.B qmail-local +ignores the line. +.TP 5 +(2) +A program line begins with a vertical bar: + +.EX + |preline /usr/ucb/vacation djb +.EE + +.B qmail-local +takes the rest of the line as a command to supply to +.BR sh . +See +.B qmail-command(8) +for further information. +.TP 5 +(3) +A forward line begins with an ampersand: + +.EX + &me@new.job.com +.EE + +.B qmail-local +takes the rest of the line as a mail address; +it uses +.B qmail-queue +to forward the message to that address. +The address must contain a fully qualified domain name; +it must not contain extra spaces, angle brackets, or comments: + +.EX + # the following examples are WRONG +.br + &me@new +.br + &<me@new.job.com> +.br + & me@new.job.com +.br + &me@new.job.com (New Address) +.EE + +If the address begins with a letter or number, +you may leave out the ampersand: + +.EX + me@new.job.com +.EE + +Note that +.B qmail-local +omits its new +.B Return-Path +line when forwarding messages. +.TP 5 +(4) +An +.I mbox +line begins with a slash or dot, +and does not end with a slash: + +.EX + /home/djb/Mailbox.sos +.EE + +.B qmail-local +takes the entire line as a filename. +It appends the mail message to that file, +using +.BR flock -style +file locking if possible. +.B qmail-local +stores the mail message in +.I mbox +format, as described in +.BR mbox(5) . + +.B WARNING: +On many systems, +anyone who can read a file can +.B flock +it, and thus hold up +.BR qmail-local 's +delivery forever. +Do not deliver mail to a publicly accessible file! + +If +.B qmail-local +is able to lock the file, but has trouble writing to it +(because, for example, the disk is full), +it will truncate the file back to its original length. +However, it cannot prevent mailbox corruption if the system +crashes during delivery. +.TP 5 +(5) +A +.I maildir +line begins with a slash or dot, +and ends with a slash: + +.EX + /home/djb/Maildir/ +.EE + +.B qmail-local +takes the entire line as the name of a directory in +.I maildir +format. +It reliably stores the incoming message in that directory. +See +.B maildir(5) +for more details. +.PP +If +.B .qmail +has the execute bit set, +it must not contain any +program lines, +.I mbox +lines, +or +.I maildir +lines. +If +.B qmail-local +sees any such lines, +it will stop and indicate a temporary failure. + +If +.B .qmail +is completely empty (0 bytes long), or does not exist, +.B qmail-local +follows the +.I defaultdelivery +instructions set by your system administrator; +normally +.I defaultdelivery +is +.BR ./Mailbox , +so +.B qmail-local +appends the mail message to +.B Mailbox +in +.I mbox +format. + +.B .qmail +may contain extra spaces and tabs at the end of a line. +Blank lines are allowed, but not for the first line of +.BR .qmail . + +If +.B .qmail +is world-writable or group-writable, +.B qmail-local +stops and indicates a temporary failure. +.SH "SAFE QMAIL EDITING" +Incoming messages can arrive at any moment. +If you want to safely edit your +.B .qmail +file, first set the sticky bit on your home directory: + +.EX + chmod +t $HOME +.EE + +.B qmail-local +will temporarily defer delivery of any message to you +if your home directory is sticky +(or group-writable or other-writable, +which should never happen). +Make sure to + +.EX + chmod -t $HOME +.EE + +when you are done! +It's a good idea to test your new +.B .qmail +file as follows: + +.EX + qmail-local -n $USER ~ $USER '' '' '' '' ./Mailbox +.EE + +.SH "EXTENSION ADDRESSES" +In the +.B qmail +system, +you control all local addresses of the form +.IR user\fBBREAK\fIanything , +as well as the address +.I user +itself, +where +.I user +is your account name. +Delivery to +.I user\fBBREAK\fIanything +is controlled by the file +.IR homedir/\fB.qmail\-\fIanything . +(These rules may be changed by the system administrator; +see +.BR qmail-users (5).) + +The +.B alias +user controls all other addresses. +Delivery to +.I local +is controlled by the file +.IR homedir/\fB.qmail\-\fIlocal , +where +.I homedir +is +.BR alias 's +home directory. + +In the following description, +.B qmail-local +is handling a message addressed to +.IR local@domain , +where +.I local +is controlled by +.BR .qmail\-\fIext . +Here is what it does. + +If +.B .qmail\-\fIext +is completely empty, +.B qmail-local +follows the +.I defaultdelivery +instructions set by your system administrator. + +If +.B .qmail\-\fIext +doesn't exist, +.B qmail-local +will try some default +.B .qmail +files. +For example, +if +.I ext +is +.BR foo-bar , +.B qmail-local +will try first +.BR .qmail-foo-bar , +then +.BR .qmail-foo-default , +and finally +.BR .qmail-default . +If none of these exist, +.B qmail-local +will bounce the message. +(Exception: for the basic +.I user +address, +.B qmail-local +treats a nonexistent +.B .qmail +the same as an empty +.BR .qmail .) + +.B WARNING: +For security, +.B qmail-local +replaces any dots in +.I ext +with colons before checking +.BR .qmail\-\fIext . +For convenience, +.B qmail-local +converts any uppercase letters in +.I ext +to lowercase. + +When +.B qmail-local +forwards a message as instructed in +.B .qmail\-\fIext +(or +.BR .qmail-default ), +it checks whether +.B .qmail\-\fIext\fB-owner\fP +exists. +If so, +it uses +.I local\fB-owner@\fIdomain +as the envelope sender for the forwarded message. +Otherwise it retains the envelope sender of the original message. +Exception: +.B qmail-local +always retains the original envelope sender +if it is the empty address or +.BR #@[] , +i.e., if this is a bounce message. + +.B qmail-local +also supports +.B variable envelope return paths +(VERPs): +if +.B .qmail\-\fIext\fB-owner\fP +and +.B .qmail\-\fIext\fB-owner-default\fP +both exist, it uses +.I local\fB\-owner\-@\fIdomain\fB-@[] +as the envelope sender. +This will cause a recipient +.I recip\fB@\fIreciphost +to see an envelope sender of +.IR local\fB\-owner\-\fIrecip\fB=\fIreciphost\fB@\fIdomain . +.SH "ERROR HANDLING" +If a delivery instruction fails, +.B qmail-local +stops immediately and reports failure. +.B qmail-local +handles forwarding after all other instructions, +so any error in another type of delivery will prevent all forwarding. + +If a program returns exit code 99, +.B qmail-local +ignores all succeeding lines in +.BR .qmail , +but it still pays attention to previous forward lines. + +To set up independent instructions, +where a temporary or permanent failure in one instruction +does not affect the others, +move each instruction into a separate +.B .qmail\-\fIext +file, and set up a central +.B .qmail +file that forwards to all of the +.BR .qmail\-\fIext s. +Note that +.B qmail-local +can handle any number of forward lines simultaneously. + +.SH "SEE ALSO" +envelopes(5), +maildir(5), +mbox(5), +qmail-users(5), +qmail-local(8), +qmail-command(8), +qmail-queue(8), +qmail-lspawn(8) diff --git a/sqmail-4.3.07/man/envelopes.5 b/sqmail-4.3.07/man/envelopes.5 new file mode 100644 index 0000000..9f06ed7 --- /dev/null +++ b/sqmail-4.3.07/man/envelopes.5 @@ -0,0 +1,231 @@ +.TH s/qmail: envelopes 5 +.SH "NAME" +envelopes \- sender/recipient lists attached to messages +.SH "INTRODUCTION" +Electronic mail messages are delivered in +.IR envelopes . + +An envelope lists a +.I sender +and one or more +.IR recipients . +Usually these +envelope addresses are the same +as the addresses listed in the message header: + +.EX + (envelope) from djb to root +.br + From: djb +.br + To: root +.EE + +In more complicated situations, though, +the envelope addresses may differ from the header addresses. +.SH "ENVELOPE EXAMPLES" +When a message is delivered to +several people at different locations, +it is first photocopied +and placed into several envelopes: + +.EX + (envelope) from djb to root +.br + From: djb Copy #1 of message +.br + To: root, god@brl.mil +.EE + +.EX + (envelope) from djb to god@brl.mil +.br + From: djb Copy #2 of message +.br + To: root, god@brl.mil +.EE + +When a message is delivered +to several people at the same location, +the sender doesn't have to photocopy it. +He can instead stuff it into +one envelope with several addresses; +the recipients will make the photocopy: + +.EX + (envelope) from djb to god@brl.mil, angel@brl.mil +.br + From: djb +.br + To: god@brl.mil, angel@brl.mil, joe, frde +.EE + +Bounced mail is sent back to the envelope sender address. +The bounced mail doesn't list an envelope sender, +so bounce loops are impossible: + +.EX + (envelope) from <> to djb +.br + From: MAILER-DAEMON +.br + To: djb +.br + Subject: unknown user frde +.EE + +The recipient of a message may make another copy +and forward it in a new envelope: + +.EX + (envelope) from djb to joe +.br + From: djb Original message +.br + To: joe +.EE + +.EX + (envelope) from joe to fred +.br + From: djb Forwarded message +.br + To: joe +.EE + +A mailing list works almost the same way: + +.EX + (envelope) from djb to sos-list +.br + From: djb Original message +.br + To: sos-list +.EE + +.EX + (envelope) from sos-owner to god@brl.mil +.br + From: djb Forwarded message +.br + To: sos-list to recipient #1 +.EE + +.EX + (envelope) from sos-owner to frde +.br + From: djb Forwarded message +.br + To: sos-list to recipient #2 +.EE + +Notice that the mailing list is set up +to replace the envelope sender with something new, +.BR sos-owner . +So bounces will come back to +.BR sos-owner : + +.EX + (envelope) from <> to sos-owner +.br + From: MAILER-DAEMON +.br + To: sos-owner +.br + Subject: unknown user frde +.EE + +It's a good idea to set up an extra address, +.BR sos-owner , +like this: +the original envelope sender (\fBdjb\fP) +has no way to fix bad +.B sos-list +addresses, +and of course bounces must not be sent to +.B sos-list +itself. +.SH "HOW ENVELOPE ADDRESSES ARE STORED" +Envelope sender and envelope recipient addresses +are transmitted and recorded in several ways. + +When a user injects mail through +.BR qmail-inject , +he can supply a +.B Return-Path +line or a +.B \-f +option for the envelope sender; +by default the envelope sender is his login name. +The envelope recipient addresses can be taken +from the command line or from various header fields, +depending on the options to +.BR qmail-inject . +Similar comments apply to +.BR sendmail . + +When a message is transferred from one machine to another through SMTP, +the envelope sender is given in a +.B MAIL FROM +command, +the envelope recipients are given in +.B RCPT TO +commands, +and the message is supplied separately by a +.B DATA +command. + +When a message is delivered by +.B qmail +to a single local recipient, +.B qmail-local +records the recipient in +.B Delivered-To +and the envelope sender in +.BR Return-Path . +It uses +.B Delivered-To +to detect mail forwarding loops. + +.B sendmail +normally records the envelope sender in +.BR Return-Path . +It does not record envelope recipient addresses, +on the theory that they are redundant: +you received the mail, +so you must have been one of the envelope recipients. + +Note that, +if the header doesn't have any recipient addresses, +.B sendmail +will move envelope recipient addresses back into the header. +This situation occurs if all addresses were originally listed as +.BR Bcc , +since +.B Bcc +is automatically removed. +When +.B sendmail +sees this, it creates a new +.B Apparently-To +header field with the envelope recipient addresses. +This has the strange effect that each blind-carbon-copy recipient will see +a list of all recipients on the same machine. + +When a message is stored in +.B mbox +format, +the envelope sender is recorded at the top of the message +as a UUCP-style +.B From +(no colon) line. +Note that this line is less reliable than the +.B Return-Path +line added by +.B qmail-local +or +.B sendmail\fP. +.SH "SEE ALSO" +qmail-header(5), +qmail-local(8), +qmail-inject(8) diff --git a/sqmail-4.3.07/man/except.1 b/sqmail-4.3.07/man/except.1 new file mode 100644 index 0000000..336bc1a --- /dev/null +++ b/sqmail-4.3.07/man/except.1 @@ -0,0 +1,33 @@ +.TH s/qmail: except 1 +.SH NAME +except \- reverse the exit code of a program +.SH SYNOPSIS +.B except +.I program +[ +.I arg ... +] +.SH DESCRIPTION +.B except +runs +.I program +with the given arguments. + +If +.I program +exits 0, +.B except +exits 100. +If +.I program +exits 111, +.B except +exits 111. +If +.I program +exits anything else, +.B except +exits 0. +.SH "SEE ALSO" +bouncesaying(1), +condredirect(1) diff --git a/sqmail-4.3.07/man/fastforward.1 b/sqmail-4.3.07/man/fastforward.1 new file mode 100644 index 0000000..d56e7dc --- /dev/null +++ b/sqmail-4.3.07/man/fastforward.1 @@ -0,0 +1,123 @@ +.TH s/qmail: fastforward 1 +.SH NAME +fastforward \- forward mail according to a cdb database +.SH SYNOPSIS +in +.BR .qmail-default : +.B | fastforward +[ +.B \-nNpPdD +] +.I cdb +.SH DESCRIPTION +.B fastforward +forwards each incoming message +according to instructions in +.I cdb +created by +.BR setforward . + +If there is no forwarding instruction in +.I cdb +for the incoming recipient address, +.B fastforward +will bounce the message. + +You can override +.B .qmail-default +with a specific +.BR .qmail-\fIrecipient ; +see +.BR dot-qmail (5). + +Warning to system administrators: +Messages do not reach +.B ~alias/.qmail-default +unless they are controlled by the +.B alias +user. +See +.BR qmail-getpw (8). + +.B SECURITY WARNING: +If +.I cdb +includes instructions pointing to a mailing list owned by another user, +that user gains some amount of control over +.BR fastforward 's +behavior. +In particular, he can force +.B fastforward +to open any file that you can access, +and to read any world-readable file that you own, +even if the file is in a world-inaccessible directory. +.SH "OPTIONS" +.TP 5 +.B \-n +No delivery. +.B fastforward +will print a description of its actions, +but will not actually read or forward a message. +.TP +.B \-N +(Default.) +Forward a message as usual. +.TP +.B \-p +Pass through. +If +.B fastforward +does not find the recipient in +.IR cdb , +it exits 0, +giving the message to further commands in +.BR .qmail-default . +If +.B fastforward +finds the recipient, +it forwards the message and exits 99, +so that further commands are skipped. +.TP +.B \-P +(Default.) +Do not pass through. +If +.B fastforward +finds the recipient, +it forwards the message and exits 0. +Otherwise it bounces the message. +.TP +.B \-d +Use +.B $DEFAULT@$HOST +as the recipient address, or +.B $EXT@$HOST +if +.B $DEFAULT +is not set. +.TP +.B \-D +(Default.) +Use +.B $RECIPIENT +as the recipient address. +.SH VERSION +The original +.B fastforward +verion is 0.51, and the respective +.B fastforward +home page is +.BR http://pobox.com/~djb/fastforward.html . +However, this version is tightly integrated into +.BR s/qmail . + +.SH "SEE ALSO" +newaliases(1), +printforward(1), +setforward(1), +dot-qmail(5), +qmail-command(8), +qmail-local(8), +qmail-recpients(8), +qmail-authuser(8), +qmail-getpw(8) diff --git a/sqmail-4.3.07/man/forgeries.7 b/sqmail-4.3.07/man/forgeries.7 new file mode 100644 index 0000000..85cc947 --- /dev/null +++ b/sqmail-4.3.07/man/forgeries.7 @@ -0,0 +1,104 @@ +.TH s/qmail: forgeries 7 +.SH "NAME" +forgeries \- how easy it is to forge mail +.SH "SUMMARY" +An electronic mail message can easily be forged. +Almost everything in it, +including the return address, +is completely under the control of the sender. + +An electronic mail message can be manually traced to its origin +if (1) all system administrators of intermediate machines +are both cooperative and competent, +(2) the sender did not break low-level TCP/IP security, +and +(3) all intermediate machines are secure. + +Users of +.I cryptography +can automatically ensure the integrity and secrecy +of their mail messages, as long as +the sending and receiving machines are secure. +.SH "FORGERIES" +Like postal mail, +electronic mail can be created entirely at the whim of the sender. +.BR From , +.BR Sender , +.BR Return-Path , +and +.BR Message-ID +can all contain whatever information the sender wants. + +For example, if you inject a message through +.B sendmail +or +.B qmail-inject +or +.BR SMTP , +you can simply type in a +.B From +field. +In fact, +.B qmail-inject +lets you set up +.BR MAILUSER , +.BR MAILHOST , +and +.B MAILNAME +environment variables +to produce your desired +.B From +field on every message. +.SH "TRACING FORGERIES" +Like postal mail, +electronic mail is postmarked when it is sent. +Each machine that receives an electronic mail message +adds a +.B Received +line to the top. + +A modern +.B Received +line contains quite a bit of information. +In conjunction with the machine's logs, +it lets a competent system administrator +determine where the machine received the message from, +as long as the sender did not break low-level TCP/IP security +or security on that machine. + +Large multi-user machines often come with inadequate logging software. +Fortunately, a system administrator can easily obtain a copy of a +931/1413/Ident/TAP server, such as +.BR pidentd . +Unfortunately, +some system administrators fail to do this, +and are thus unable to figure out which local user +was responsible for generating a message. + +If all intermediate system administrators are competent, +and the sender did not break machine security or low-level TCP/IP security, +it is possible to trace a message backwards. +Unfortunately, some traces are stymied by intermediate system +administrators who are uncooperative or untrustworthy. +.SH "CRYPTOGRAPHY" +The sender of a mail message may place his message into a +.I cryptographic +envelope stamped with his seal. +Strong cryptography guarantees that any two messages with the same seal +were sent by the same cryptographic entity: +perhaps a single person, perhaps a group of cooperating people, +but in any case somebody who knows a secret originally held +only by the creator of the seal. +The seal is called a +.I public key\fR. + +Unfortunately, the creator of the seal is often an insecure machine, +or an untrustworthy central agency, +but most of the time seals are kept secure. + +One popular cryptographic program is +.BR pgp . +.SH "SEE ALSO" +pgp(1), +identd(8), +qmail-header(8) diff --git a/sqmail-4.3.07/man/forward.1 b/sqmail-4.3.07/man/forward.1 new file mode 100644 index 0000000..76d56e7 --- /dev/null +++ b/sqmail-4.3.07/man/forward.1 @@ -0,0 +1,24 @@ +.TH s/qmail: forward 1 +.SH NAME +forward \- forward new mail to one or more addresses +.SH SYNOPSIS +in +.BR .qmail : +.B |forward +.I address ... +.SH DESCRIPTION +.B forward +forwards each new mail message to the specified list of addresses. +It is a simple wrapper around +.BR qmail-queue . +It achieves the same results as listing each +.I address +separately in +.BR .qmail , +but it is more programmable since +.I address +can be constructed on the fly. +.SH "SEE ALSO" +dot-qmail(5), +qmail-command(8), +qmail-queue(8) diff --git a/sqmail-4.3.07/man/hostname.8 b/sqmail-4.3.07/man/hostname.8 new file mode 100644 index 0000000..9276f1e --- /dev/null +++ b/sqmail-4.3.07/man/hostname.8 @@ -0,0 +1,14 @@ +.TH s/qmail: hostname 8 + +.SH NAME +hostname +.SH SYNOPSIS +.B hostname +.SH DESCRIPTION +.B hostname +evaluates from the system its +.I hostname +employing a DNS lookup while erhaps including the domain +and displays it as \fIFull Qualified Domain Name\fR (\fBFQDN\fR). +.SH "SEE ALSO" +ipmeprint(8). diff --git a/sqmail-4.3.07/man/ipmeprint.8 b/sqmail-4.3.07/man/ipmeprint.8 new file mode 100644 index 0000000..473d83e --- /dev/null +++ b/sqmail-4.3.07/man/ipmeprint.8 @@ -0,0 +1,15 @@ +.TH s/qmail: ipmeprint 8 + +.SH NAME +ipmeprint +.SH SYNOPSIS +.B ipmeprint +.SH DESCRIPTION +.B ipmeprints +reads the kernel's bindings to +.I IPv4 +and +.IP IPv6 +addresses and displays those one per line. +.SH "SEE ALSO" +hostname(9). diff --git a/sqmail-4.3.07/man/maildir.5 b/sqmail-4.3.07/man/maildir.5 new file mode 100644 index 0000000..49b2b23 --- /dev/null +++ b/sqmail-4.3.07/man/maildir.5 @@ -0,0 +1,239 @@ +.TH s/qmail: maildir 5 +.SH "NAME" +maildir \- directory for incoming mail messages +.SH "INTRODUCTION" +.I maildir +is a structure for +directories of incoming mail messages. +It solves the reliability problems that plague +.I mbox +files and +.I mh +folders. +.SH "RELIABILITY ISSUES" +A machine may crash while it is delivering a message. +For both +.I mbox +files and +.I mh +folders this means that the message will be silently truncated. +Even worse: for +.I mbox +format, if the message is truncated in the middle of a line, +it will be silently joined to the next message. +The mail transport agent will try again later to deliver the message, +but it is unacceptable that a corrupted message should show up at all. +In +.IR maildir , +every message is guaranteed complete upon delivery. + +A machine may have two programs simultaneously delivering mail +to the same user. +The +.I mbox +and +.I mh +formats require the programs to update a single central file. +If the programs do not use some locking mechanism, +the central file will be corrupted. +There are several +.I mbox +and +.I mh +locking mechanisms, +none of which work portably and reliably. +In contrast, in +.IR maildir , +no locks are ever necessary. +Different delivery processes never touch the same file. + +A user may try to delete messages from his mailbox at the same +moment that the machine delivers a new message. +For +.I mbox +and +.I mh +formats, the user's mail-reading program must know +what locking mechanism the mail-delivery programs use. +In contrast, in +.IR maildir , +any delivered message +can be safely updated or deleted by a mail-reading program. + +Many sites use Sun's +.B Network F\fPa\fBil\fPur\fBe System +(NFS), +presumably because the operating system vendor does not offer +anything else. +NFS exacerbates all of the above problems. +Some NFS implementations don't provide +.B any +reliable locking mechanism. +With +.I mbox +and +.I mh +formats, +if two machines deliver mail to the same user, +or if a user reads mail anywhere except the delivery machine, +the user's mail is at risk. +.I maildir +works without trouble over NFS. +.SH "THE MAILDIR STRUCTURE" +A directory in +.I maildir +format has three subdirectories, +all on the same filesystem: +.BR tmp , +.BR new , +and +.BR cur . + +Each file in +.B new +is a newly delivered mail message. +The modification time of the file is the delivery date of the message. +The message is delivered +.I without +an extra UUCP-style +.B From_ +line, +.I without +any +.B >From +quoting, +and +.I without +an extra blank line at the end. +The message is normally in RFC 822 format, +starting with a +.B Return-Path +line and a +.B Delivered-To +line, +but it could contain arbitrary binary data. +It might not even end with a newline. + +Files in +.B cur +are just like files in +.BR new . +The big difference is that files in +.B cur +are no longer new mail: +they have been seen by the user's mail-reading program. +.SH "HOW A MESSAGE IS DELIVERED" +The +.B tmp +directory is used to ensure reliable delivery, +as discussed here. + +A program delivers a mail message in six steps. +First, it +.B chdir()\fPs +to the +.I maildir +directory. +Second, it +.B stat()s +the name +.BR tmp/\fItime.pid.host , +where +.I time +is the number of seconds since the beginning of 1970 GMT, +.I pid +is the program's process ID, +and +.I host +is the host name. +Third, if +.B stat() +returned anything other than ENOENT, +the program sleeps for two seconds, updates +.IR time , +and tries the +.B stat() +again, a limited number of times. +Fourth, the program +creates +.BR tmp/\fItime.pid.host . +Fifth, the program +.I NFS-writes +the message to the file. +Sixth, the program +.BR link() s +the file to +.BR new/\fItime.pid.host . +At that instant the message has been successfully delivered. + +The delivery program is required to start a 24-hour timer before +creating +.BR tmp/\fItime.pid.host , +and to abort the delivery +if the timer expires. +Upon error, timeout, or normal completion, +the delivery program may attempt to +.B unlink() +.BR tmp/\fItime.pid.host . + +.I NFS-writing +means +(1) as usual, checking the number of bytes returned from each +.B write() +call; +(2) calling +.B fsync() +and checking its return value; +(3) calling +.B close() +and checking its return value. +(Standard NFS implementations handle +.B fsync() +incorrectly +but make up for it by abusing +.BR close() .) +.SH "HOW A MESSAGE IS READ" +A mail reader operates as follows. + +It looks through the +.B new +directory for new messages. +Say there is a new message, +.BR new/\fIunique . +The reader may freely display the contents of +.BR new/\fIunique , +delete +.BR new/\fIunique , +or rename +.B new/\fIunique +as +.BR cur/\fIunique:info . +See +.B http://pobox.com/~djb/proto/maildir.html +for the meaning of +.IR info . + +The reader is also expected to look through the +.B tmp +directory and to clean up any old files found there. +A file in +.B tmp +may be safely removed if it +has not been accessed in 36 hours. + +It is a good idea for readers to skip all filenames in +.B new +and +.B cur +starting with a dot. +Other than this, readers should not attempt to parse filenames. +.SH "ENVIRONMENT VARIABLES" +Mail readers supporting +.I maildir +use the +.B MAILDIR +environment variable +as the name of the user's primary mail directory. +.SH "SEE ALSO" +mbox(5), +qmail-local(8) diff --git a/sqmail-4.3.07/man/maildir2mbox.1 b/sqmail-4.3.07/man/maildir2mbox.1 new file mode 100644 index 0000000..c63a6a8 --- /dev/null +++ b/sqmail-4.3.07/man/maildir2mbox.1 @@ -0,0 +1,53 @@ +.TH s/qmail: maildir2mbox 1 +.SH NAME +maildir2mbox \- move mail from a maildir to an mbox +.SH SYNOPSIS +.B maildir2mbox +.SH DESCRIPTION +.B maildir2mbox +moves mail from a +.IR maildir -format +directory to an +.IR mbox -format +file. + +You must supply three environment variables to +.BR maildir2mbox : +.B MAILDIR +is the name of your +.I maildir +directory; +.B MAIL +is the name of your +.I mbox +file; +and +.B MAILTMP +is a temporary file that +.B maildir2mbox +can overwrite. +.B MAILTMP +and +.B MAIL +must be on the same filesystem. + +.B maildir2mbox +is reliable: +it will not remove messages +from +.B MAILDIR +until the messages have been successfully appended to +.BR MAIL . + +.B maildir2mbox +locks +.B MAIL +to protect against simultaneous access by a mail reader. +This locking system does not protect against simultaneous access +by another +.BR maildir2mbox ; +you should run only one +.B maildir2mbox +at a time. +.SH "SEE ALSO" +maildir(5) diff --git a/sqmail-4.3.07/man/maildirmake.1 b/sqmail-4.3.07/man/maildirmake.1 new file mode 100644 index 0000000..875ab50 --- /dev/null +++ b/sqmail-4.3.07/man/maildirmake.1 @@ -0,0 +1,15 @@ +.TH s/qmail: maildirmake 1 +.SH NAME +maildirmake \- create a maildir for incoming mail +.SH SYNOPSIS +.B maildirmake +.I dir +.SH DESCRIPTION +.B maildirmake +makes a new directory, +.IR dir , +in +.B maildir +format. +.SH "SEE ALSO" +maildir(5) diff --git a/sqmail-4.3.07/man/maildirwatch.1 b/sqmail-4.3.07/man/maildirwatch.1 new file mode 100644 index 0000000..c33b17e --- /dev/null +++ b/sqmail-4.3.07/man/maildirwatch.1 @@ -0,0 +1,23 @@ +.TH s/qmail: maildirwatch 1 +.SH NAME +maildirwatch \- look for new mail in a maildir +.SH SYNOPSIS +.B maildirwatch +.SH DESCRIPTION +.B maildirwatch +watches your +.I maildir +for new mail. +You must supply a +.B MAILDIR +environment variable +with the name of your +.I maildir +directory. + +.B maildirwatch +prints a new mail summary twice per minute. +It is designed to run inside a (VT100-compatible) window; +it clears the window before each summary. +.SH "SEE ALSO" +maildir(5) diff --git a/sqmail-4.3.07/man/mailsubj.1 b/sqmail-4.3.07/man/mailsubj.1 new file mode 100644 index 0000000..ed4772d --- /dev/null +++ b/sqmail-4.3.07/man/mailsubj.1 @@ -0,0 +1,38 @@ +.TH s/qmail: mailsubj 1 +.SH NAME +mailsubj \- send a mail message with a subject line +.SH SYNOPSIS +.B mailsubj +.I subject +.I recip ... +.SH DESCRIPTION +.B mailsubj +inserts +.I subject +and the list of +.IR recip s +into a mail message: + +.EX + Subject: subject +.br + To: recip ... +.br + +.br + body +.EE + +.B mailsubj +reads the body of the message from its standard input. +Then it sends the message. + +Note that +.I subject +and +.I recip +must be quoted properly for the message header. +.SH "SEE ALSO" +addresses(5), +qmail-header(8), +qmail-inject(8) diff --git a/sqmail-4.3.07/man/matchup.1 b/sqmail-4.3.07/man/matchup.1 new file mode 100644 index 0000000..1a3fbf0 --- /dev/null +++ b/sqmail-4.3.07/man/matchup.1 @@ -0,0 +1,111 @@ +.TH s/qmail: matchup 1 +.SH NAME +matchup \- collect information on messages and deliveries +.SH SYNTAX +.B matchup +.SH DESCRIPTION +.B matchup +reads a series of lines from +.BR qmail-send , +with a numeric timestamp in front of each line. +.B matchup +matches the end of each delivery attempt with the start of the delivery attempt +and with the relevant message information; +it replaces +.BR qmail-send 's +message reports and delivery reports +with message lines and delivery lines in the format described below. + +.B matchup +exits after it sees end of file. +It prints pending messages and deliveries on descriptor 5, +in a format suitable for input to a future invocation of +.BR matchup : + +.EX + <log.1 matchup >out.1 5>pending.2 +.br + cat pending.2 log.2 | matchup >out.2 5>pending.3 +.br + cat pending.3 log.3 | matchup >out.3 5>pending.4 +.EE + +Note that the 5> notation does not work with csh. +.SH "MESSAGE LINES" +A message line summarizes the delivery results for a message +that has left the queue: + +.EX + m \fIbirth\fR \fIdone\fR \fIbytes\fR \fInk\fR \fInz\fR \fInd\fR <\fIsender\fR> \fIqp\fR \fIuid\fR +.EE + +Here +.I birth +and +.I done +are timestamps, +.I bytes +is the number of bytes in the message, +.I nk +is the number of successful deliveries, +.I nz +is the number of deferred delivery attempts, +.I nd +is the number of failed delivery attempts, +.I sender +is the message's return path, +.I qp +is the message's long-term queue identifier, +and +.I uid +is the userid of the user that queued the message. + +Note that +.B matchup +converts +.I sender +to lowercase. +This can lose information, +since a few hosts pay attention to the case in the box part of an address. +.SH "DELIVERY LINES" +A delivery line shows the result of a single delivery attempt: + +.EX + d \fIresult\fR \fIbirth\fR \fIdstart\fR \fIddone\fR \fIbytes\fR +.br + <\fIsender\fR> \fIchan\fR.\fIrecip\fR \fIqp\fR \fIuid\fR \fIreason\fR +.EE + +Here +.IR birth , +.IR bytes , +.IR sender , +.IR qp , +and +.I uid +are message information as above; +.I chan +is the channel for this delivery; +.I recip +is the recipient address for this delivery; +.I dstart +and +.I ddone +are timestamps; +.I result +is the letter k for success, z for deferral, d for failure; +and +.I reason +is a more detailed explanation of the delivery result. + +.B matchup +converts +.I recip +to lowercase. +.SH "SEE ALSO" +xqp(1), +xrecipient(1), +xsender(1), +accustamp(1), +qmail-log(5), +splogger(8) diff --git a/sqmail-4.3.07/man/mbox.5 b/sqmail-4.3.07/man/mbox.5 new file mode 100644 index 0000000..e9860e4 --- /dev/null +++ b/sqmail-4.3.07/man/mbox.5 @@ -0,0 +1,235 @@ +.TH s/qmail: mbox 5 +.SH "NAME" +mbox \- file containing mail messages +.SH "INTRODUCTION" +The most common format for storage of mail messages is +.I mbox +format. +An +.I mbox +is a single file containing zero or more mail messages. +.SH "MESSAGE FORMAT" +A message encoded in +.I mbox +format begins with a +.B From_ +line, continues with a series of +.B \fRnon-\fBFrom_ +lines, +and ends with a blank line. +A +.B From_ +line means any line that begins with the characters +F, r, o, m, space: + +.EX + From god@heaven.af.mil Sat Jan 3 01:05:34 1996 +.br + Return-Path: <god@heaven.af.mil> +.br + Delivered-To: djb@silverton.berkeley.edu +.br + Date: 3 Jan 1996 01:05:34 -0000 +.br + From: God <god@heaven.af.mil> +.br + To: djb@silverton.berkeley.edu (D. J. Bernstein) +.br + +.br + How's that mail system project coming along? +.br + +.EE + +The final line is a completely blank line (no spaces or tabs). +Notice that blank lines may also appear elsewhere in the message. + +The +.B From_ +line always looks like +.B From +.I envsender +.I date +.IR moreinfo . +.I envsender +is one word, without spaces or tabs; +it is usually the envelope sender of the message. +.I date +is the delivery date of the message. +It always contains exactly 24 characters in +.B asctime +format. +.I moreinfo +is optional; it may contain arbitrary information. + +Between the +.B From_ +line and the blank line is a message in RFC 822 format, +as described in +.BR qmail-header(5) , +subject to +.B >From quoting +as described below. +.SH "HOW A MESSAGE IS DELIVERED" +Here is how a program appends a message to an +.I mbox +file. + +It first creates a +.B From_ +line given the message's envelope sender and the current date. +If the envelope sender is empty (i.e., if this is a bounce message), +the program uses +.B MAILER-DAEMON +instead. +If the envelope sender contains spaces, tabs, or newlines, +the program replaces them with hyphens. + +The program then copies the message, applying +.B >From quoting +to each line. +.B >From quoting +ensures that the resulting lines are not +.B From_ +lines: +the program prepends a +.B > +to any +.B From_ +line, +.B >From_ +line, +.B >>From_ +line, +.B >>>From_ +line, +etc. + +Finally the program appends a blank line to the message. +If the last line of the message was a partial line, +it writes two newlines; +otherwise it writes one. +.SH "HOW A MESSAGE IS READ" +A reader scans through an +.I mbox +file looking for +.B From_ +lines. +Any +.B From_ +line marks the beginning of a message. +The reader should not attempt to take advantage of the fact that every +.B From_ +line (past the beginning of the file) +is preceded by a blank line. + +Once the reader finds a message, +it extracts a (possibly corrupted) envelope sender +and delivery date out of the +.B From_ +line. +It then reads until the next +.B From_ +line or end of file, whichever comes first. +It strips off the final blank line +and +deletes the +quoting of +.B >From_ +lines and +.B >>From_ +lines and so on. +The result is an RFC 822 message. +.SH "COMMON MBOX VARIANTS" +There are many variants of +.I mbox +format. +The variant described above is +.I mboxrd +format, popularized by Rahul Dhesi in June 1995. + +The original +.I mboxo +format quotes only +.B From_ +lines, not +.B >From_ +lines. +As a result it is impossible to tell whether + +.EX + From: djb@silverton.berkeley.edu (D. J. Bernstein) +.br + To: god@heaven.af.mil +.br + +.br + >From now through August I'll be doing beta testing. +.br + Thanks for your interest. +.EE + +was quoted in the original message. +An +.I mboxrd +reader will always strip off the quoting. + +.I mboxcl +format is like +.I mboxo +format, but includes a Content-Length field with the +number of bytes in the message. +.I mboxcl2 +format is like +.I mboxcl +but has no +.B >From +quoting. +These formats are used by SVR4 mailers. +.I mboxcl2 +cannot be read safely by +.I mboxrd +readers. +.SH "UNSPECIFIED DETAILS" +There are many locking mechanisms for +.I mbox +files. +.B qmail-local +always uses +.B flock +on systems that have it, otherwise +.BR lockf . + +The delivery date in a +.B From_ +line does not specify a time zone. +.B qmail-local +always creates the delivery date in GMT +so that +.I mbox +files can be safely transported from one time zone to another. + +If the mtime on a nonempty +.I mbox +file is greater than the atime, +the file has new mail. +If the mtime is smaller than the atime, +the new mail has been read. +If the atime equals the mtime, +there is no way to tell whether the file has new mail, +since +.B qmail-local +takes much less than a second to run. +One solution is for a mail reader to artificially set the +atime to the mtime plus 1. +Then the file has new mail if and only if the atime is +less than or equal to the mtime. + +Some mail readers place +.B Status +fields in each message to indicate which messages have been read. +.SH "SEE ALSO" +maildir(5), +qmail-header(5), +qmail-local(8) diff --git a/sqmail-4.3.07/man/newaliases.1 b/sqmail-4.3.07/man/newaliases.1 new file mode 100644 index 0000000..a51ff64 --- /dev/null +++ b/sqmail-4.3.07/man/newaliases.1 @@ -0,0 +1,366 @@ +.TH s/qmail: newaliases 1 +.SH NAME +newaliases \- create a forwarding database from /etc/aliases +.SH SYNOPSIS +.B newaliases +.SH DESCRIPTION +.B newaliases +reads a table of +sendmail-style +forwarding instructions from +.B /etc/aliases +and converts them into a forwarding database in +.BR /etc/aliases.cdb . +The forwarding database can be used by +.BR fastforward . + +For safety, +.B newaliases +writes the forwarding database to +.B /etc/aliases.tmp +and then moves +.B /etc/aliases.tmp +to +.BR /etc/aliases.cdb . +If there is a problem creating +.BR /etc/aliases.tmp , +.B newaliases +complains and leaves +.B /etc/aliases.cdb +alone. +Deliveries can continue using +.B /etc/aliases.cdb +in the meantime. + +.B newaliases +always creates +.B /etc/aliases.cdb +world-readable. + +.B newaliases +makes no attempt to protect against +simultaneous updates of +.BR /etc/aliases.cdb . +.SH "INSTRUCTION FORMAT" +.B newaliases +imitates +sendmail's +handling of +.BR /etc/aliases . +For example, + +.EX + root: alice, bill +.EE + +says that mail for +.B root +should be forwarded to +.B alice +and +.BR bill . + +.B COMPATIBILITY WARNING: +.B newaliases +does not support file deliveries. +You can use the file delivery mechanism described in +.B dot-qmail(5) +instead. +.SH "SIMPLE ALIASES" +The simplest type of forwarding instruction +is a line of the form + +.EX + alias: recip +.EE + +Any message sent to +.I alias +will be forwarded to the recipient address +.IR recip . +Addresses are compared to +.I alias +without regard to case. + +Forwarding instructions are cumulative. +If +.I recip +is itself an alias, +messages to +.I alias +will be forwarded the same way as +messages to +.IR recip . +For example, with the following instructions, +messages to +.B postmaster@heaven.af.mil +or +.B root@heaven.af.mil +will be delivered to Bob: + +.EX + postmaster@heaven.af.mil: bob@heaven.af.mil +.EE +.br +.EX + root@heaven.af.mil: postmaster@heaven.af.mil +.EE + +.B COMPATIBILITY WARNING: +With +sendmail, +entries in +.B /etc/aliases +can override usernames. +With +.BR s/qmail , +if you install +.B fastforward +in +.BR ~alias/.qmail-default , +it will not see addresses that are controlled by other users. +See +.BR qmail-getpw (8). +To change this, see +.BR qmail-users (5). + +.B COMPATIBILITY WARNING: +Various versions of +sendmail +do various strange things with circular alias definitions. +See +.BR setforward (1) +for details on +.BR fastforward 's +behavior. + +.B COMPATIBILITY WARNING: +If there are several forwarding instructions for a single +.IR alias , +sendmail +will complain; +.B fastforward +will silently use the first instruction. +.SH "WILDCARDS" +.I alias +can have the form +.I user@host.dom +for one user at one host, +.I @host.dom +for all users at one host, or +.I user +for one user at all hosts. + +.B COMPATIBILITY WARNING: +sendmail +supports only +.IR user ; +it does not support per-host aliases. +It accepts +.I user@host.dom +if +.I host.dom +is a local host, +but it then treats it the same way as +.IR user , +applying to all local hosts and virtual domains. +.SH "ADDRESS FORMATS" +Addresses in +.B /etc/aliases +are parsed the same way as addresses in RFC 822 message headers. +Parenthesized comments and bracketed addresses are permitted: + +.EX + root: bob (Bob, the postmaster) + joe: Joe Shmoe <shmoe@heaven.af.mil> +.EE + +Addresses with special characters must be quoted: + +.EX + fred: "spaced out mailbox"@heaven.af.mil +.EE + +Address groups are not permitted, +since colons have a different use in +.BR /etc/aliases . + +Any recipient address without a fully qualified domain name is +fed through the +.BR defaulthost , +.BR defaultdomain , +and +.B plusdomain +mechanisms described in +.BR qmail-header (5). + +.B COMPATIBILITY WARNING: +sendmail's +handling of quotes and backslashes violates RFC 821 and RFC 822, +and is not supported by +.BR newaliases . +The +.B qmail-local +delivery mechanism +lets each user manage several addresses, +so there is no need for a special syntax to get around forwarding. +.SH "MULTIPLE RECIPIENTS" +An instruction may list more than one recipient address: + +.EX + alias: recip1, recip2, recip3 +.EE + +Any message sent to +.I alias +will be forwarded to all of the addresses. + +A forwarding instruction may be split across several lines. +Each line past the first must either (1) begin with space or tab +or (2) be empty: + +.EX + hostmaster: +.EE +.br +.EX + fred, +.EE +.br +.EX + joe +.EE + +.B COMPATIBILITY WARNING: +sendmail +requires the colon to be on the first line +of a multi-line forwarding instruction. +.B newaliases +doesn't care whether the colon is present at all. + +.B COMPATIBILITY WARNING: +sendmail +does not permit blank lines in the middle of continuations. +This has the undesirable effect that a blank line behaves differently +from a line containing a single space. +.SH "COMMENTS" +Any line in +.B /etc/aliases +that begins with # is ignored: + +.EX + # this is a comment +.EE + +A comment may be split across several lines. +Each line past the first must either (1) begin with space or tab +or (2) be empty. + +.B COMPATIBILITY WARNING: +sendmail +does not permit continuations of comment lines. +.SH "PROGRAMS" +If a recipient address does not contain a domain name, +and begins with a vertical bar, +.B newaliases +takes the rest of the address as a program to run: + +.EX + weather: "|weather-server" +.EE + +.B fastforward +will run +.B weather-server +when a message arrives for +.BR weather . + +.B COMPATIBILITY WARNING: +Internet addresses can legitimately start with +a slash or vertical bar. +.B newaliases +treats anything with an unquoted @ as an address. +sendmail appears to have various problems +coping with these addresses, +and with commands that contain @ signs. + +.B COMPATIBILITY WARNING: +.B newaliases +does not allow a vertical bar before double quotes. +.SH "INCLUDE FILES" +A recipient address of the form +.B :include:\fIfile +means ``every address listed in +.IR file .'' +(Actually +.B fastforward +reads +.IR file\fB.bin ; +see +.BR newinclude (1) +for further details.) + +Note that +.I file +is read by +.BR fastforward , +not +.BR newaliases , +so the system administrator does not have to run +.B newaliases +every time +.I file +changes. +.I file +must be world-readable +and accessible to +.BR fastforward . + +.B COMPATIBILITY WARNING: +If an +.B :include: +file is unreadable or nonexistent, +sendmail +skips it; +.B fastforward +defers delivery of the message. + +.B COMPATIBILITY WARNING: +sendmail +does not permit spaces inside the literal text +.BR :include: . +.B newaliases +does. + +.B COMPATIBILITY WARNING: +Versions of +sendmail +before V8 did not strip quotes from +.B :include: +filenames. +.SH "ALIAS OWNERS" +If there is an alias for +.BR owner-\fIlist , +any message forwarded through +.I list +will have its envelope sender set to +.BR owner-\fIlist , +so that bounces go back to +.BR owner-\fIlist . + +.B COMPATIBILITY WARNING: +When an alias includes the same recipient both inside and outside +a mailing list, +.B fastforward +sends the message twice, +once with each envelope sender. +sendmail +sends the message only once; +its choice of envelope sender for that recipient +depends on the phase of the moon. +.SH "SEE ALSO" +fastforward(1), +setforward(1), +newinclude(1), +printforward(1), +dot-qmail(5) diff --git a/sqmail-4.3.07/man/newinclude.1 b/sqmail-4.3.07/man/newinclude.1 new file mode 100644 index 0000000..44edb9d --- /dev/null +++ b/sqmail-4.3.07/man/newinclude.1 @@ -0,0 +1,88 @@ +.TH s/qmail: newinclude 1 +.SH NAME +newinclude \- create a binary mailing list from an :include: file +.SH SYNOPSIS +.B newinclude +.I list +.SH DESCRIPTION +.B newinclude +reads a +sendmail-style +.B :include: +file, +.IR list , +and converts it into a binary format in +.I list\fB.bin +for use by +.BR fastforward . + +.B newinclude +first writes the mailing list to +.IR list\fB.tmp , +and then moves it to +.IR list\fB.bin . +If there is any problem creating +.IR list\fB.tmp , +.B newinclude +leaves +.I list\fB.bin +alone. + +.B newinclude +always creates +.I list\fB.bin +world-readable. + +.B COMPATIBILITY WARNING: +sendmail +reads +.I list +directly; +.B fastforward +needs +.IR list\fB.bin . +sendmail's strategy is a disaster if you save +.I list +to disk at the same moment that +sendmail +reads it; +the list will be truncated at a random spot, +perhaps in the middle of an address. +Furthermore, if the system crashes while you are writing +.IR list , +.I list +could be filled with all sorts of garbage. +.SH "LIST FORMAT" +.I list +may contain any number of lines; +each line may contain any number of addresses +or further +.B :include: +files. +See +.BR newaliases (1) +for details on the address format. +Any line in +.I file +beginning with # is ignored. + +.B COMPATIBILITY WARNING: +.B newinclude +does not support file or program deliveries in +.B :include: +files. +You can use the secure delivery mechanisms described in +.B dot-qmail(5) +instead. + +.B COMPATIBILITY WARNING: +Versions of +sendmail +before V8 did not allow comments in +.B :include: +files. +.SH "SEE ALSO" +fastforward(1), +newaliases(1), +setmaillist(1), +dot-qmail(5) diff --git a/sqmail-4.3.07/man/preline.1 b/sqmail-4.3.07/man/preline.1 new file mode 100644 index 0000000..d324ff8 --- /dev/null +++ b/sqmail-4.3.07/man/preline.1 @@ -0,0 +1,57 @@ +.TH s/qmail: preline 1 +.SH NAME +preline \- prepend lines to message +.SH SYNOPSIS +in +.BR .qmail\fIext : +.B | preline \fIcommand +.SH DESCRIPTION +.B preline +feeds each incoming mail message through +.IR command . +At the top of each message it inserts +a UUCP-style +.B From_ +line, a +.B Return-Path +line, and a +.B Delivered-To +line. + +.B preline +is useful for +.B procmail +and +ELM's +.BR filter , +which +do not understand the +.B qmail-command +environment variables. +.SH OPTIONS +.TP +.B \-d +Do not include the +.B Delivered-To +line. You should use this option when the +recipient of the incoming mail message is actually under remote control, +but was sent here through +.B control/virtualdomains +for manual routing. +.TP +.B \-f +Do not include the +.B From_ +line. You should use this option except for +.IR command s +that create +.I mbox +files. +.TP +.B \-r +Do not include the +.B Return-Path +line. +.SH "SEE ALSO" +mbox(5), +qmail-command(8) diff --git a/sqmail-4.3.07/man/printforward.1 b/sqmail-4.3.07/man/printforward.1 new file mode 100644 index 0000000..f4beaa0 --- /dev/null +++ b/sqmail-4.3.07/man/printforward.1 @@ -0,0 +1,16 @@ +.TH s/qmail: printforward 1 +.SH NAME +printforward \- print the instructions in a forwarding database +.SH SYNOPSIS +.B printforward +.SH DESCRIPTION +.B printforward +reads a forwarding database from its standard input +and prints all the forwarding instructions +in a format accepted by +.BR setforward . +.SH "SEE ALSO" +fastforward(1), +newaliases(1), +printmaillist(1), +setforward(1) diff --git a/sqmail-4.3.07/man/printmaillist.1 b/sqmail-4.3.07/man/printmaillist.1 new file mode 100644 index 0000000..803cdab --- /dev/null +++ b/sqmail-4.3.07/man/printmaillist.1 @@ -0,0 +1,15 @@ +.TH s/qmail: printmaillist 1 +.SH NAME +printmaillist \- print the contents of a binary mailing list +.SH SYNOPSIS +.B printmaillist +.SH DESCRIPTION +.B printmaillist +reads a binary mailing list from its standard input +and prints all the forwarding instructions +in a format accepted by +.BR setmaillist . +.SH "SEE ALSO" +newinclude(1), +printforward(1), +setmaillist(1) diff --git a/sqmail-4.3.07/man/qbiff.1 b/sqmail-4.3.07/man/qbiff.1 new file mode 100644 index 0000000..085d97e --- /dev/null +++ b/sqmail-4.3.07/man/qbiff.1 @@ -0,0 +1,31 @@ +.TH s/qmail: qbiff 1 +.SH NAME +qbiff \- announce new mail the moment it arrives +.SH SYNOPSIS +in +.BR .qmail : +.B |qbiff +.SH DESCRIPTION +.B qbiff +writes a message to your screen +whenever a new mail message is delivered, +if you ran +.B biff y +after logging in. + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR qbiff , +make sure to also add a line specifying delivery to your normal mailbox. +For example: + +.EX + /home/joe/Mailbox +.br + |qbiff +.EE +.SH "SEE ALSO" +biff(1), +dot-qmail(5) diff --git a/sqmail-4.3.07/man/qmail-authuser.9 b/sqmail-4.3.07/man/qmail-authuser.9 new file mode 100644 index 0000000..d2e89d8 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-authuser.9 @@ -0,0 +1,490 @@ +.TH s/qmail: qmail-authuser 8 + +.SH "NAME" +qmail-authuser \- user authentication +.SH "SYNOPSIS" +.B qmail-authuser +[ +.I program maildirname +| +.I [-s authsocket [-x service=authmethod]] +] +.I subprogram [ args ] +.SH "DESCRIPTION" +.B qmail-authuser +is a versatile authentication PAM for SMTP, POP3 and IMAP services +providing four different operation modes depending on the input +of the configuration file +.I SQMAIL/users/authuser +and the given arguments. +It can be used as substitude for the authentication modules +.IR checkpassword , +.IR cmd5checkpw , +.IR checkvpw +(vmailmgr), +and +.I vchkpw +(vpopmail) +supporting the same arguments on call. +.TP 5 +Native mode: +.B qmail-authuser +reads +.I SQMAIL/users/authuser +and uses the information as local authentication database. +.TP 5 +System mode: +.B qmail-authuser +accesses the Unix +.I /etc/password +file (or it's shadow companion) as authentication source. +.TP 5 +Virtual user mode: +.B qmail-authuser +calls either the virtual domain auth handler +.B vchkpw +or +.BR checkvpw . +.TP 5 +Dovecot mode: +.B qmail-authuser +queries +.B dovecot +as authentication provider. +.SH "USE CASES" +.B qmail-authuser +can be used for +.TP 5 +authentication only: +.B qmail-authuser +is called as a PAM typically by +.B qmail-smtpd +and verifies the user's credentials +(userid/password) +as given by the client. +.I subprogram +is typically +.BR true . +.TP 5 +mailbox interrogation: +Called by +.B qmail-popup +or +.BR bincimap-up , +upon successfull authentication +.B qmail-authuser +switches to the home directory of +.I userid +and hands over operations to +.I program +provided as +.B qmail-pop3d +or +.BR bincimpad . + +Note: +.I maildirname +has to start with \'mail\' or \'mbox\' +irrespective of case. +.SH "INTERFACE DESCRIPTION" +.B qmail-authuser +can be called by +.BR qmail-smtpd , +.BR qmail-popup , +or +.B bincimap-up +while following the +.BR checkpassword 's +interface specification and enabling +LOGIN, PLAIN, and CRAM-MD5 authentication for SMTP +as well as USER and APOP for POP3 and +LOGIN and PLAIN for IMAP. + +The information supplied on descriptor 3 +is an \fIauthuser\fR name terminated by \e0, +a \fIpassword\fR or \fIresponse\fR terminated by \e0, +and a \fIchallenge\fR for CRAM-MD5 or APOP +authentication terminated by \e0. +There must be at most 512 bytes of data before end of file. + +In case +.I authuser +and +.I password +match, +.B qmail-authuser +calls +.B pathexec +to run +.B subprogram +with the given arguments and perhaps setting up the user environment. +The use of +.B program +is required and can be expressed as +.B /bin/true +or +.B /usr/bin/true +for compliance reasons. + +.SH "FILES" +.I SQMAIL/users/authuser +contains pairs of +.I authuser +and +.I password +tokens separated by a colon (":"). +Both tokens may include white spaces (if supported by the OS) and may +use special characters for certain actions. The provided +.I password +token should have a significant length (> 2 characters). + +Lines starting with the \'#\' sign are regarded as comment. +Trailing empty spaces in lines are removed prior of evaluation. +.SH "AUTHUSER" +The +.I authuser +token is the public part of the identity and +may include a composit information, typically the +.I userid +and the +.I domain +respectively, described as +.IR userid@domain . +.B qmail-authuser +may treat both parts independently. +Domain specific authentication may be considered using the +.I @domain +part within the +.I authuser +token. However, as an abbreviation, +this may be provided simply as +.IR @ , +telling +.B qmail-authuser +to consider all unspecified authusers solely and transparently +as \'virtual users\'. +On the other hand, the +.I authuser +token may be wildcarded as +.IR * . +Now, +.B qmail-authuser +is instructed to query the local Unix system as identity provider. + +More specific +.I authuser +tokens have precedence over less specific, irrespectively of their order. +System mode has precedence over virtual user mode. +Particular users and domains can be disabled from authentication +prepending the name with a \'!\' overruling acceptance: +.IR !authuser . + +Note: Virtual Domain Managers require to include the domain within +.I authuser +in order to identify the domain the user belongs to. +.SH "NATIVE MODE" +.B qmail-authuser +recalculates the digest using the provided challenge +and the passwords from +.IR SQMAIL/users/authuser +and compares it with response (2nd parameter). + +If no challenge is provided, +.B qmail-authuser +compares the supplied password with the stored +.I password +token in +.IR SQMAIL/users/authuser . +Thus, +.B qmail-authuser +can be used as PAM identity provider for +PLAIN, LOGIN, CRAM-MD5 and APOP auth methods. +.SH "SYSTEM MODE" +.B qmail-authuser +may also been used as a replacement for the +.B checkpassword +PAM, allowing to evaluate the +.I /etc/passwd +and +.I shadow +files for the auth methods USER, PLAIN & LOGIN +while only considerung the user part in +.IR authuser . +In this case, +.B qmail-authuser +has to be \'sticky\' and running as +.IR root . +.SH "VIRTUAL USER MODE" +.B qmail-authuser +includes the call of both +.IR vpopmail 's +.B vchkpw +and +.IR vmailmgr 's +.B checkvpw +(which need to be in the path) +and transfers the received authentication information transparently to those. +.SH "DOVECOT MODE" +.B qmail-authuser +is also capabable to connect to a Unix socket created for authentication by +.IR Dovecot . +.SH "POP3 AND APOP" +Calling +.B qmail-authuser +for POP3 authentication with the option +.I qmail-pop3d +together with the format of the mailbox given as +.IR maildirname , +which is typically +.I Maildir +or +.IR mbox . +The required environment variables +\fIUSER\fR, \fIHOME\fR, and \fISHELL\fR +for the respective user are evaluated from +.IR /etc/passwd . +APOP authentication is possible for a given user, if +.I authuser +and the +.I password +is included in +.IR SQMAIL/users/authuser . +Upon successful authentication +.B qmail-authuser +changes to $\fIHOME\fR. +.SH "QUERY AND STORAGE MODES" +The first character +.I X +of the +.I password +token is used to indicate the password's query and storage method. +The following cases may be considered: + +.EX + (1a) authuser:clearpwd + (1b) authuser:%pwdhash + (2a) authuser:? + (2b) *:? + (3a) authuser:+ + (3b) @domain:+ + (3c) @:+ + (3d) authuser:& + (3e) @domain:& + (3f) @:& + (4a) authuser:= + (4b) @domain:= + (4c) @:= +.EE + +(1) Local query/storage: +Here, together with the +.I authuser +plaintext (1a) or hashed passwords (1b) +may be provisioned in the +.I SQMAIL/users/authuser +control file. +In case of +.IR %pwdhash , +the password is stored as MD5, SHA1, or SHA256 hash prepended with the \'%\'. +If the plaintext password is given as +.I password +this means that the following password is taken literally +though allwowing a leading \'%\'. + +(2) Unix system query/storage: +In case the +.I password +token consists of +.IR '?' , +the received authentication information is used to emulate a +standard Unix user login taking the +.I userid +information as system user account. Therefore, no particular +.I password +token is required here. +The inclusion of any specific +.I authuser +information can be avoided in case +.I '*' +is used as shortcut within +.I SQMAIL/users/authuser +followed by +.I '?' +as +.I password +token. Now, the received +.I userid +and password is taken from the Unix system for authentication (crypt). + +(3) Virtual domain query/storage: +Alternatively, +.B qmail-authuser +may call either +.B checkvpw +once a +.I '+' +or +.B vchkpw +in case +.I '&' +is given as +.I password +token. + +(4) Dovecot as Identity Provider: +.B Dovecot +can be used as authentication backend in case a +.I '=' +is included as +.I password +token. Assuming +.B doveadm +is in the path, a particular +.B auth-qmail +listener (socket) is tested by +.I doveadm +with the arguments +.I \'auth test -a\' +provided the socket is available via +.IR \'-s\ authsocket\' . + + +The definition of the auth socket +needs to be included in +.BR Dovecot 's +control file in the following way: + +.EX +service auth { + unix_listener /var/run/dovecot/auth-qmail { + mode = 0600 + user = qmaild + group = nofiles + } +} +.EE + +Reversely, this socket has to be +specified as calling argument for +.B qmail-authuser +providing +.I -s /var/run/dovecot/auth-sqmail +together with an additional executable (true). +The name of the auth socket can +be freely chosen. + +A particular authentication method +can be specified by means of +.I -x service=authmethod +in the call of +.BR qmail-authuser . +Check the +.b doveadmn +documentation for particular authentication methods, +typically available as \fIsmtp\fR and \fIpop3\fR. + +Note: All authentication storage and query mechanism +can be used concurrently, depending on the settings +of the +.I authuser +and +.I password +token in +.IR SQMAIL/users/authuser . +.SH "SECURITY" +.B qmail-authuser +is invoked in the environment of +.BR qmail-smtpd , +.BR qmail-popup , +or +.B bincimap-up +which is typically run as user +.IR qmaild . +The file +.I SQMAIL/users/authuser +shall be +.I qmaild +owned and belonging to the group +.I sqmail +and SHOULD NOT be readble by the \fIworld\fR. + +Since the given +.I authuser +token is visible in the email, it could be typically chosen as +.I user@domain +making it usable for virtual domain managers and allowing +a common +.I password +for ESMTP/IMAP4/POP3 services. + +The included +.I password +token shall solely be used for ESMTP/IMAP4/POP3 authentication +and should possess enough entropy. + +A sticky and root-owned +.B qmail-authuser +is a potential security risk. +.SH "PASSWORD HASHES" +Instead of plaintext passwords, additionally +MD5, SHA1, or SHA256 hashes of the passwords may be used. However, +in spite of rainbow tables this requires none-trivial passwords. +.SH "AUTH METHODS" +In case hashed passwords or the UNIX passwords are used, +only the auth methods USER, PLAIN, and LOGIN are working. +Those methods are only secure on encrypted +connections or otherwise are an easy victim of an eavesdropper. +Challenge/Response methods - like CRAM-MD5 and APOP - +require having access to the plain-text passwords. For +.B vchkpw +C/R is possible querying the local \'vpopmail\' database. +.SH "EXIT CODES" +In case the provided +.I authuser +or +.I userid +does not exist, or the digest and the response, +or the passwords +differ, +.B qmail-authuser +exits 1. +If +.B qmail-authuser +is misused, it may instead exit 2. +In case +.I SQMAIL/users/authuser +is not readeable, +.B qmail-authuser +exits 110. +If there is a temporary problem checking the password, +.B qmail-authuser +exits 111. +.SH "ENVIRONMENT VARIABLES SET" +Upon call, +.B qmail-authuser +clears the environment variable +.I USER +and sets to the +.I userid +irrespective whether authentication was successful or not. +Since +.I USER +may be used by other authentication PAMs called in the chain, +additionally +.I AUTHUSER +is set keeping the original +.I userid +information for logging purpose. +.SH "CREDITS" +The MD5 implementation originates from RSA though now supporting a +64 bit OS as well. SHA1 has been created by Steve Reid, and +SHA256 was done by Brad Conte, all released in the Public Domain. +Drew Wells receives credits for putting me into the current direction. +.SH "SEE ALSO" +qmail-popup(8), +qmail-smtpd(8), +checkpassword(8), +vchkpw(8), +checkvpw(8), +doveadm(1), +doveadm-auth(1). diff --git a/sqmail-4.3.07/man/qmail-badloadertypes.9 b/sqmail-4.3.07/man/qmail-badloadertypes.9 new file mode 100644 index 0000000..daf07cf --- /dev/null +++ b/sqmail-4.3.07/man/qmail-badloadertypes.9 @@ -0,0 +1,48 @@ +.TH s/qmail: qmail-badloadertypes 8 + +.SH "NAME" +qmail-badloadertypes \- prepare badloadertypes for qmail-smtpd +.SH SYNOPSIS +.B qmail-badloadertypes + +.SH "DESCRIPTION" +.B qmail-badloadertypes +reads the instructions in +.B SQMAIL/control/badloadertypes +and writes them into +.B SQMAIL/control/badloadertypes.cdb +in a binary format suited +for quick access by +.BR qmail-smtpd . + +If there is a problem with +.BR control/badloadertypes , +.B qmail-badloadertypes +complains and leaves +.B control/badloadertypes.cdb +alone. + +.B qmail-badloadertypes +ensures that +.B SQMAIL/control/badloadertypes.cdb +is updated atomically, +so +.B qmail-smtpd +never has to wait for +.B qmail-badloadertypes +to finish. +However, +.B qmail-badloadertypes +makes no attempt to protect against two simultaneous updates of +.BR control/badloadertypes.cdb . +For convenience, +.B qmail-badloadertypes +allows comments (lines starting with '#') and +copies only the significant leading characters to +.BR control/badloadertypes.cdb . + +The binary +.B control/badloadertypes.cdb +format is portable across machines. +.SH "SEE ALSO" +qmail-smtpd(8) diff --git a/sqmail-4.3.07/man/qmail-badmimetypes.9 b/sqmail-4.3.07/man/qmail-badmimetypes.9 new file mode 100644 index 0000000..b9dab16 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-badmimetypes.9 @@ -0,0 +1,46 @@ +.TH s/qmail: qmail-badmimetype 8 +.SH NAME +qmail-badmimetypes \- prepare badmimetypes for qmail-smtpd +.SH SYNOPSIS +.B qmail-badmimetype +.SH DESCRIPTION +.B qmail-badmimetypes +reads the instructions in +.B SQMAIL/control/badmimetypes +and writes them into +.B SQMAIL/control/badmimetypes.cdb +in a binary format suited +for quick access by +.BR qmail-smtpd . + +If there is a problem with +.BR control/badmimetypes , +.B qmail-badmimetypes +complains and leaves +.B control/badmimetypes.cdb +alone. + +.B qmail-badmimetypes +ensures that +.B control/badmimetypes.cdb +is updated atomically, +so +.B qmail-smtpd +never has to wait for +.B qmail-badmimetypes +to finish. +However, +.B qmail-badmimetypes +makes no attempt to protect against two simultaneous updates of +.BR control/badmimetypes.cdb . +For convenience, +.B qmail-badmimetypes +allows comments (lines starting with '#') and +copies only the significant leading characters to +.BR control/badmimetypes.cdb . + +The binary +.B control/badmimetypes.cdb +format is portable across machines. +.SH "SEE ALSO" +qmail-smtpd(8) diff --git a/sqmail-4.3.07/man/qmail-clean.8 b/sqmail-4.3.07/man/qmail-clean.8 new file mode 100644 index 0000000..b4cbc1d --- /dev/null +++ b/sqmail-4.3.07/man/qmail-clean.8 @@ -0,0 +1,13 @@ +.TH s/qmail: qmail-clean 8 +.SH NAME +qmail-clean \- clean up the queue directory +.SH SYNOPSIS +.B qmail-clean +.SH DESCRIPTION +.B qmail-clean +reads a cleanup command from descriptor 0, +performs the cleanup, +prints the results to descriptor 1, +and repeats. +.SH "SEE ALSO" +qmail-send(8) diff --git a/sqmail-4.3.07/man/qmail-command.8 b/sqmail-4.3.07/man/qmail-command.8 new file mode 100644 index 0000000..33f28d7 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-command.8 @@ -0,0 +1,149 @@ +.TH s/qmail: qmail-command 8 +.SH NAME +qmail-command \- user-specified mail delivery program +.SH SYNOPSIS +in +.BR .qmail\fIext : +.B |\fIcommand +.SH DESCRIPTION +.B qmail-local +will, upon your request, +feed each incoming mail message through a program of your choice. + +When a mail message arrives, +.B qmail-local +runs +.B sh -c \fIcommand +in your home directory. +It makes the message available on +.IR command 's +standard input. + +.B WARNING: +The mail message does not begin with +.BR qmail-local 's +usual +.B Return-Path +and +.B Delivered-To +lines. + +Note that +.B qmail-local +uses the same file descriptor for every delivery +in your +.B .qmail +file, so it is not safe for +.I command +to fork a child that +reads the message in the background while the parent exits. +.SH "EXIT CODES" +.IR command 's +exit codes are interpreted as follows: +0 means that the delivery was successful; +99 means that the delivery was successful, +but that +.B qmail-local +should ignore all further delivery instructions; +100 means that the delivery failed permanently (hard error); +111 means that the delivery failed but should be tried again +in a little while (soft error). + +Currently 64, 65, 70, 76, 77, 78, and 112 are considered hard errors, +and all other codes are considered soft errors, +but +.I command +should avoid relying on this. +.SH "ENVIRONMENT VARIABLES" +.B qmail-local +supplies several useful environment variables to +.IR command . +.B WARNING: +These environment variables are not quoted. +They may contain special characters. +They are under the control of a possibly malicious remote user. + +.B SENDER +is the envelope sender address. +.B NEWSENDER +is the forwarding envelope sender address, +as setup in +.BR dot-qmail(5) . +.B RECIPIENT +is the envelope recipient address, +.IR local@domain . +.B USER +is +.IR user . +.B HOME +is your home directory, +.IR homedir . +.B HOST +is the +.I domain +part of the recipient address. +.B LOCAL +is the +.I local +part. +.B EXT +is the +address extension, +.IR ext . + +.B HOST2 +is the portion of +.B HOST +preceding the last dot; +.B HOST3 +is the portion of +.B HOST +preceding the second-to-last dot; +.B HOST4 +is the portion of +.B HOST +preceding the third-to-last dot. + +.B EXT2 +is the portion of +.B EXT +following the first dash; +.B EXT3 +is the portion +following the second dash; +.B EXT4 +is the portion +following the third dash. +.B DEFAULT +is the portion +corresponding to the +.B default +part of the +.BR .qmail\- ... +file name; +.B DEFAULT +is not set if +the file name does not end with +.BR default . + +.B DTLINE +and +.B RPLINE +are the usual +.B Delivered-To +and +.B Return-Path +lines, +including newlines. +.B UFLINE +is the UUCP-style +.B From_ +line that +.B qmail-local +adds to +.IR mbox -format +files. +.SH "SEE ALSO" +dot-qmail(5), +envelopes(5), +qmail-local(8) diff --git a/sqmail-4.3.07/man/qmail-control.9 b/sqmail-4.3.07/man/qmail-control.9 new file mode 100644 index 0000000..5aa1de6 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-control.9 @@ -0,0 +1,110 @@ +.TH s/qmail: qmail-control 5 +.SH "NAME" +qmail-control \- qmail configuration files +.SH "INTRODUCTION" +You can change the behavior of the +.B qmail +system by modifying +.BR s/qmail 's +.I control files +in +.BR SQMAIL/control . + +.B s/qmail +can survive with just one control file, +.IR me , +containing the +fully-qualified name of the current host. +This file is used as the default for +other hostname-related control files. + +Comments (\'# comment\') are allowed +in +.IR badmailfrom , +.IR badmimetypes , +.IR badloadertypes , +.IR dkimdomains , +.IR locals , +.IR percenthack , +.IR qmqpservers , +.IR rcpthosts , +.IR smtproutes , +.IR srsdomains , +.IR tlsdestinations , +and +.IR virtualdomains . +Trailing spaces and tabs are allowed in any control. + +The following table lists all control files +other than +.IR me . +See the corresponding man pages for further details. + +.RS +.nf +.ta 5c 10c +control default used by + +.I authsenders \fR(none) \fRqmail-remote +.I badhelo \fR(none) \fRqmail-smtpd +.I badmailfrom \fR(none) \fRqmail-smtpd +.I badmimetypes \fR$BADMIMETYPE \fRqmail-smtpd +.I badloadertypes \fR$BADLOADERTYPE \fRqmail-smtpd +.I badrcptto \fR(none) \fRqmail-smtpd +.I bouncefrom \fRMAILER-DAEMON \fRqmail-send +.I bouncehost \fIme \fRqmail-send +.I bouncemaxbytes \fI0 \fRqmail-send +.I concurrencylocal \fR10 \fRqmail-send +.I concurrencyremote \fR20 \fRqmail-send +.I dkimdomains \fR(none) \fRqmail-dksign +.I domaincerts \fR(none) \fRqmail-remote +.I domainips \fR(none) \fRqmail-remote, \frqmail-smtpam +.I defaultdomain \fIme \fRqmail-inject +.I defaulthost \fIme \fRqmail-inject +.I databytes \fR$DATABYTES \fRqmail-smtpd +.I doublebouncehost \fIme \fRqmail-send +.I doublebounceto \fRpostmaster \fRqmail-send +.I envnoathost \fIme \fRqmail-send +.I helohost \fIme \fRqmail-remote +.I idhost \fIme \fRqmail-inject +.I localiphost \fIme \fRqmail-smtpd +.I locals \fIme \fRqmail-send +.I morercpthosts \fR(none) \fRqmail-smtpd +.I mailfromrules \fR(none) \fRqmail-smtpd +.I percenthack \fR(none) \fRqmail-send +.I plusdomain \fIme \fRqmail-inject +.I qmqpservers \fR(none) \fRqmail-qmqpc +.I qmtproutes \fR(none) \fRqmail-remote +.I queuelifetime \fR604800 \fRqmail-send +.I rcpthosts \fR(none) \fRqmail-smtpd +.I recipients \fR(none) \fRqmail-smtpd +.I spfexplain \fRSPF_DEFEXP \fRqmail-smtpd +.I spflocalrules \fR(none) \fRqmail-smtpd +.I srsdomains \fR(none) \fRsrsforward, \fRsrsreverse +.I smtpgreeting \fIme \fRqmail-smtpd +.I smtproutes \fR(none) \fRqmail-remote +.I timeoutconnect \fR60 \fRqmail-remote, \fRqmail-smtpam +.I timeoutremote \fR1200 \fRqmail-remote, \fRqmail-smtpam +.I timeoutsmtpd \fR1200 \fRqmail-smtpd +.I tlsdestinations \fR(none) \fRqmail-remote, \fRqmail-smtpam +.I virtualdomains \fR(none) \fRqmail-send +.fi + +.RE +.IR Defaultvalues +following a $ sign (ie. $RELAYCLIENT) depend on the +corresponding environment variable. + +.IR Use +.BR qmail-showctl +to display actual settings. + +.SH "SEE ALSO" +srsforward(1), +qmail-dksgin(8), +qmail-inject(8), +qmail-qmqpc(8), +qmail-remote(8), +qmail-send(8), +qmail-showctl(8), +qmail-smtpd(8). diff --git a/sqmail-4.3.07/man/qmail-dkim.8 b/sqmail-4.3.07/man/qmail-dkim.8 new file mode 100644 index 0000000..53463e9 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-dkim.8 @@ -0,0 +1,217 @@ +.TH s/qmail: qmail-dkim 8 +.SH "NAME" +qmail-dkim \- libdkim implementation for s/qmail +.SH "SYNOPSIS" +.B qmail-dkim +[ +.I -h +.I -v +.I -V +.I -s[ecckey] +.I -b[1|2|3] +.I -c[s|t|u] +.I -d domain +.I -i identity +.I -l +.I -q +.I -t +.I -x expire_time +.I -y selector +.I -Y selector2 +.I -z[1|2|3|4|5] +] +.I in_message +.I RSA_private_key +.I out_message +.I Ed25519_private_key +.SH "DESCRIPTION" +.B qmail-dkim +is the implementation of +.B libdkim +for s/qmail providing API compatibility +and supporting RSA and Ed25519 DKIM signatures +in single or hybrid mode. +In hybrid mode, two +.I private keys +and two +.I selectors +need to be provided. +.B qmail-dkim +supports distinct operations: +.TP 5 +.B qmail-dkim \fI-s in_message RSA_private_key out_message\fR +DKIM signes +.I in_message +with the given +.I private_key +and returns +.IR out_message . +.TP 5 +.B qmail-dkim \fI-s in_message RSA_private_key out_message Ed255_private_key\fR +signs +.I in_message +with both a RSA +.I RSA_private_key +and a +.IR Ed25519_private_key. +Here, the RSA default selector is \fIdefault\fR and the +Ed25519 default selector is \fIeddy\fR; both subject of change. +.TP 5 +.B qmail-dkim \fI-v in_message\fR +verifies the +.IR in_message . +.SH "DKIM FORMATS" +DKIM needs a common understanding of the attributes +subject for signing and verification. +The following attributes can be set: +.TP 5 +-c +is the 'canonicalization', thus how a validiation client +should deal with signature verification of the +message headers and/or body. Here, the choices are given +via an appended character: +.I r +relax on header, +.I s +simple (strict) on message body, +.I t +relax/simple, or eventually +.I u +simple relaxed. +Finally, the hash function to be used in the signature +can be given as +.TP 5 +-z +following either with +.I 1 +using sha1, or +.I 2 +using sha256, or finally as default +.I 3 +providing both signature values in the mail header. +.I 4 +telling +.B qmail-dkim +to use the Ed25519 signature scheme. +.I 5 +allows +.B qmail-dkim +to attach both a +.I RSA-SHA256 +as well as a +.I Ed25519 +signature to the message, which considered to be a +.I hybrid +mode. + +.SH "DKIM SIGNING" +.B qmail-dkim +will include (several) message headers detailing the +.B DKIM signature +with at least the following fields: +.TP 3 +a +=<signature type> +.TP 3 +c +=<used canoncicalization> +.TP 3 +s +=<selector> +.TP 3 +d +=<identity> +.TP 3 +i +=<identifier> +.TP 3 +h +=<included header1:header2:...> +.TP 3 +bh +=<hash of the canonicalized body until its upper limit length; if given> +.TP 3 +b +=<base64 encoded signature> +.P +Additional settings can be achieved using the following options: +.TP 5 +.I -d domain +is the signer's domain name and together with the prepended +.TP 5 +.I -y selector +it is used for the DNS TXT lookup of the public key; supporting +mainly key roll-over. The first selector is used for RSA signatures. +.TP 5 +.I -Y selector2 +Same as \fI-y\fR but now for Ed25519 signatures. +.TP 5 +.I -I identifier +giving an additional hint about the agent or identifier +responsible for the signing like 'postmaster@domain'; defaults to +.IR domain . +.TP 5 +.I -t expire_time +given in seconds, tells how log the signature is valid. +It defaults to +.I 604800 +secconds (seven days). +.P +Further, some more option fields can be displayed in the header: +.TP 5 +.I -l +include a body length tag. +.TP 5 +.I -q +include the query method tag. + +.SH "DKIM VERIFICATION" +.B qmail-dkim +as invoked by +.B qmail-dkverify +extracting the received DKIM header fields, +and following the signature verification procedure +as given here, while fetching the signer's +.I public key +using a DNS TXT lookup. +Now, the respective header lines, and/or +the message body will be hashed and compared +against the values taken from the signatures. + +The results will be indicated by either return code +.I 0 +in case of success, +.I 1 +in case of mismatch, or +.I -1 +if other failures were encountered. + +Given the call argument +.TP 3 +-v +.B qmail-dkim +will provide the DKIM results +.I pass +or +.I fail +including verbose reasons on the commmand line. +This is the legacy mode. + +.RE +Rather, invoking +.B qmail-dkim +with argument +.TP 3 +-V +it communicates the results over a file interface +to be picked up by +.IR qmail-dkverify . + +.SH "SEE ALSO" +qmail-queue(8), +qmail-remote(8), +qmail-dksign(8), +qmail-dkverify(8), +qmail-send(8), +qmail-log(8). + diff --git a/sqmail-4.3.07/man/qmail-dksign.9 b/sqmail-4.3.07/man/qmail-dksign.9 new file mode 100644 index 0000000..08d310e --- /dev/null +++ b/sqmail-4.3.07/man/qmail-dksign.9 @@ -0,0 +1,336 @@ +.TH s/qmail: qmail-dksign 8 +.SH "NAME" +qmail-dksign \- DKIM sign outgoing messages +.SH "SYNOPSIS" +.B qmail-dksign +.I host +.I sender +.I recip +[ +.I recip ... +] +.SH "DESCRIPTION" +.B qmail-dksign +is a stub routine to be invoked by +.B qmail-spawn +in place of +.B qmail-remote +and is required to customize the signing policy +for outgoing emails according to RFC 6893/8463 by means of +.B qmail-dkim +and finally to invoke +.B qmail-remote +for subsequent message delivery. + +.B qmail-dksign +is also an extension to +.B qmail-queue +(with comparable permissions) using +.I queue/dkim/<n>/<m> +to provide a temporary but persistent staging +area for outgoing messages to be DKIM signed. +.SH "CONTROL FILE" +.B qmail-dksign +will be only called by +.B qmail-rspawn +if +.I SQMAIL/control/dkimdomains +is present. + +.IR dkimdomains : +\'domain:selector[,selector2]|sdid|[auid|~]|expire|c:z:l\' +allows multitenant and hybrid DKIM signing settings per sending +.IR domain . + +.I domain +is the sender's envelope domain in order to fetch the +individually tailored DKIM signing paramaters for these. + +The following DKIM parameters can be specified: +.TP 5 +.I selector +is used as prepending name label for +.IR domain : +.IR selector._domainkey.domain . +If not explicitely given, it defaults to +.I default +and is mostly used to support the key roll-over. +.TP 5 +.I selector,selector2 +defines a hybrid selector and allows to provide +two different selectors together +with their private keys for concurrently signing of messages +according to both the RSA-SHA256 and the Ed25519 algorithm. +.TP 5 +.I sdid +Here, you can overwrite the 'Signing Domain Identifier' (SDID), +thus decouple the information given in the DKIM header from +the envelope domain sender. This allows to setup common DNS +public keys for several domains irrespectively of the sending +.IR domain . +.TP 5 +.I auid +is the 'Agent/User Identifier' of the signer, +in case it is not the sending +.IR domain . +In most cases it can be neglected and is obsolete. +Rather, you can specifiy that the +.I auid +is always included as +.I originator +of the mail while providing the tilde symbol +.I ~ +here as generic substitude. +.TP 5 +.I expire +determins the validity period of the signature in DKIM signed +message. Due to the assumed key-rollover, it is limited +and defaults to +.I 604800 +secs since the email was signed. +.TP 5 +.I c +is the 'canonicalization'; thus how a validation client +should deal with signature verification of the received +message header and/or body. Here, the choices are +.I r +relax (allow mangling of whitespaces and cases; default) +.I s +simple (=strict) +.I t +relax on header, simple on body, +.I u +simple on header, relax on body. +.TP 5 +.I z +The signature algorithm can be specified as +.I 1 +RSA with sha1, +.I 2 +RSA with sha256 (as default), or +.I 3 +providing both signature values in the mail header; +.I 4 +Ed25519 ECC signatures. +.I 5 +tells +.B qmail-dksign +to include both +.I RSA-SHA256 +and +.I Ed25519 +signatures in the mail header. +Here, you need two different +.I selectors +and +.IR private\ keys. +Finally, setting +.TP 5 +.I l +(literal) advices +.I qmail-dkim +to include the body hash length (after canonicalization) +to the DKIM header. This might be useful to cope with programs +like mailing list servers adding a 'footer' to the mail +after the signing operation has been completed. + +.RE +RSA and Ed25519 signatures can now be used simultaneously +while providing different keys available as distinct selectors. +Those settings are handed-over to +.B qmail-dkim +to provide the signing of emails. +.B qmail-dksign +calls +.B qmail-dkim +to automatically include the query method +.I q=dns/txt +in the DKIM header. +.SH "SELECTING DOMAINS FOR SIGNING" +.B qmail-dksign +can be instructed to sign all outgoing mails with the +MTA's private key. This is achieved by simply using +.I *: +in +.IR control/dkimdomains . +Rather, the signing operation can be restricted for domains +.B s/qmail +has responsibility for, as given in +.IR rcpthosts . +This is commanded via +.IR =: . +Alternatively, in multitenant mode +.B qmail-dksign +may use domain specific DKIM settings and private keys +for the sending domains and permitting parenting. +Particular domains for which outgoing emails shall +not be DKIM signed can be given as: +.IR !nodkim.org . + +.EE + *: + =:default,eddy||~||:5 + .heaven.com:||me@devil.com|500000|r:3 + cloud1.com:january|postmaster@cloud.com|||t::l + cloud7.com:february|postmaster@cloud.com|||u:1 + mybuddy.org:eddy||||:4 + !nodkim.org: +.EX + +Note: The owner of the crypto material (public and private keys) is +.IR qmailq . +.SH "CRYPTO MATERIAL" +.B qmail-dksign +follows the conventions from +.B qmail-remote +to use the directory +.I SQMAIL/ssl/domainkeys +to store public and private keys. + +Each +.I domain +may have its own key material resulting in a structure +.IR SQMAIL/ssl/domainkeys/<domain>/ , +where the following keyfiles are expected: +.TP 5 +.IR <selector>\ (default:\ 'default') +is a mandatory symbolic link to +.I [rsa|ed25519].private_<selector> +used for signing. +.TP 5 +.I rsa.public_<selector> +is the DER-header enriched and base64 encoded RSA public key. +.TP 5 +.I ed25519.public_<selector> +is the 'naked' base64 encoded Ed25519 public key. + +.RE +Here, +.I <selector> +is the name of the current +.IR selector . +After having generated keys and providing a new +.IR selector , +this name has to be included as +.I selector +for the given domain in +.I SQMAIL/control/dkimdomains +in order to become active for signing. + +In case of +.I hybrid\ signatures +different selectors need to be given for the +RSA and the Ed25519 keys each. +They have to be provided concatinated by a colon in +.IR dkimdomains . +White spaces are not allowed. If the RSA selector is +.IR default , +it can be omitted while followed by the colon and the +Ed25519 selector name. + +.SH "SHARING KEYS FOR DIFFERENT DOMAINS" +Different +.I domains +may however share common keys for signing and verification. +In order to allow a common private key for signing, simply +create symlinks for the others domains under +.I SQMAIL/ssl/domainkeys/ +to the master one. +.B qmail-dksign +will now pick up those and use the provided key for signing. + +However, in general this reqires to deploy DKIM records +for those domains sharing the same public key but require +different domain names as distinguished DNS TXT records. + +Rather, you may want to publish just one +DKIM DNS TXT record which is commonly shared for all +concerning domains. Since the +.I sending\ domain +is used as default for the +.IR SDID , +you need now to provide the same +.I SDID +explicitely for each domain of concern in +.IR control/dkimdomains . + +The '<selector>' - and not the SDID - +together with the literal +.I ._domainkey. +and the domain name defines the binding of the +private key with the DKIM TXT record: +.IR <selector>._domainkey.<domain> . + +.SH "GNERATING CRYPTO MATERIAL" +Public/private keys can be generated by +.I OpenSSL +or +.I LibreSSL +or compatible TLS implementations and +shall be provided in canonical format. +The directory +.I SQMAIL/ssl/domainkeys/ +and the resulting key needs to be readable by +.IR qmailq , +the user +.B qmail-dksign +and +.B qmail-dkim +runs under. The private key shall +.B NEVER +exposed to the public. + +The script +.B mkdkimkey +is enabled to generate +.I RSA +or +.I Ed25519 +private and public keys in the required format +together with a +.I BIND +compliant DKIM DNS TXT record. +.SH "RESPONSES" +.B qmail-dksign +may provide the following responses indicating an error: +.TP 5 +Z +Unable to switch to target directory. +.TP 5 +Z +Unable to create DKIM stage file: <file> +.TP 5 +Z +Unable to unlink DKIM stage file. +.TP 5 +Z +Unable to read control files. +.TP 5 +Z +Unable to read message. +.TP 5 +D +SMTP cannot transfer messages with partial final lines. +.TP 5 +K +can't read private file: <file> continue without signing. +.TP 5 +Z +unable to run qmail-remote. (=> configuration/permission error) +.SH "SYSTEM IMPACT" +.B qmail-dksign +makes heavy use of system file descriptors. +Given a high +.I concurrencyremote +you may run out of file descriptors which thus need to be enhanced +either system-wide or for the specific users +.I qmailr +and +.IR qmails . +.SH "SEE ALSO" +qmail-queue(8), +qmail-remote(8), +qmail-dkim(8), +qmail-dkverify(8), +qmail-log(8). + diff --git a/sqmail-4.3.07/man/qmail-dkverify.8 b/sqmail-4.3.07/man/qmail-dkverify.8 new file mode 100644 index 0000000..eb56952 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-dkverify.8 @@ -0,0 +1,137 @@ +.TH s/qmail: qmail-dkverify 8 +.SH "NAME" +qmail-dkverify \- verification of DKIM signatures in messages upon receipt +.SH "SYNOPSIS" +.B qmail-dkverify +.SH "DESCRIPTION" +.B qmail-dkverify +is invoked faciliting the +.I QMAILQUEUE(_EXTRA) +mechanism. + +.SH "CALLING CHAIN" +Verifying DKIM signatures upon receipt involves the +following calling chain: + +1. +.B qmail-smtpd +called from +.B sslserver +/ +.BR tcpserver. + +2. +.B qmail-dkverify +called by the +.I QMAILQUEUE(_EXTRA) +mechanism as (first) replacement for +.B qmail-queue +as a stub. +The incoming message is enhanced by the required CR +characters line-by-line and stored in +.IR queue/dkim/[split]/xyz . + +3. +.B qmail-dkim +is called by +.B qmail-dkverify +as a child performing the actual verification on +.I queue/dkim/[split]/xyz +while using a DNS TXT lookup for the sender's public key +given in the DKIM message header and +calling the fehQlibs DNS routines. +The verification results are persisted at +.IR queue/dkim/[split]/zyx . + +4. +.B qmail-dkverify +(as parent) reading the evaluated DKIM information from +.B qmail-dkim +and assembling a DKIM header line with the results +prepended to the message. + +5. +.B qmail-queue +is finally called to queue the message for delivery. + +.SH "INVOCATION AND USAGE" +In order to invoke +.B qmail-dkverify +the environment variable +.I QMAILQUEUE="bin/qmail-dkverify" +has to be populated in the context of +.BR qmail-smtpd . + +Since +.B qmail-smtpd +is typically called by means of +.B sslserver +or +.BR tcpserver , +the +.I tcpd.smtp.cdb +database as compiled by +.B tcprules +can be enhanced to include a line like +.I :alllow:QMAILQUEUE="bin/qmail-dkverify" +making use of the QMAILQUEUE_EXTRA mechanism. + +Alternatively, this environment variable could be +defined as part of +.BR qmail-smtpd 's +start script which would now enable to +provide DKIM signature checking for all +SMTP sessions irrespectively of their origin. + +Usually, +.B qmail-dkverify +works in annotation mode only. + +However, setting additionally the environment variable +.I DKIM="+" +would command +.B qmail-dkverify +to reject mails failing the +DKIM signature verification. +In case of a rejection, the +.B qmail-smtpd +log shows the following message: +.IR Reject::DKIM::Signature . + +Note: +.B qmail-dkverify +shall not be used for authenticated +SMTP sessions, typically provided on the +.I Submission +port. + +.SH "LOGGING" +No particular logging is currently forseen. +Rather, each individual RFC 822 message is enhanced by +the following header line in case a DKIM signature +is recognized: + +.I X-Authentication-Results: sender dkim=[pass|fail (verbose error message)] MTA +including the +.I sender +and the evaluating +.I MTA +as given in +.IR control/me . +In case of a \fIfail\fR, the verbose reason +follows in parenthesis. + +.SH "SYSTEM IMPACT" +.B qmail-dkverify +does several reads and writes on the +received messages. Apart from the cryptographic +operations, this will slow down message exchange +and increase the load on the system. + +.SH "SEE ALSO" +qmail-queue(8), +qmail-remote(8), +qmail-dkim(8), +qmail-dksign(8), +qmail-log(8). + diff --git a/sqmail-4.3.07/man/qmail-getpw.9 b/sqmail-4.3.07/man/qmail-getpw.9 new file mode 100644 index 0000000..c246b0e --- /dev/null +++ b/sqmail-4.3.07/man/qmail-getpw.9 @@ -0,0 +1,114 @@ +.TH s/qmail: qmail-getpw 8 +.SH NAME +qmail-getpw \- give addresses to users +.SH SYNOPSIS +.B qmail-getpw +.I local +.SH DESCRIPTION +In +.BR s/qmail , +each user controls a vast array of local addresses. +.B qmail-getpw +finds the user that controls a particular address, +.IR local . +It prints six pieces of information, +each terminated by NUL: +.IR user ; +.IR uid ; +.IR gid ; +.IR homedir ; +.IR dash ; +and +.IR ext . +The user's account name is +.IR user ; +the user's uid and gid in decimal are +.I uid +and +.IR gid ; +the user's home directory is +.IR homedir ; +and messages to +.I local +will be handled by +.IR homedir\fB/.qmail\fIdashext . + +In case of trouble, +.B qmail-getpw +exits nonzero without printing anything. + +.B WARNING: +The operating system's +.B getpwnam +function, which is at the heart of +.BR qmail-getpw , +is inherently unreliable: +it fails to distinguish between temporary errors and nonexistent users. +Future versions of +.B getpwnam +should return ETXTBSY to indicate temporary errors +and ESRCH to indicate nonexistent users. +.SH "RULES" +.B qmail-getpw +considers an account in +.B /etc/passwd +to be a user if +(1) the account has a nonzero uid, +(2) the account's home directory exists (and is visible to +.BR qmail-getpw ), +and +(3) the account owns its home directory. +.B qmail-getpw +ignores account names containing uppercase letters. +.B qmail-getpw +also assumes that all account names are shorter than 32 characters. + +.B qmail-getpw +gives each user +control over the basic +.I user +address and +all addresses of the form +.IR user\fBBREAK\fIanything . +When +.I local +is +.IR user , +.I dash +and +.I ext +are both empty. +When +.I local +is +.IR user\fBBREAK\fIanything , +.I dash +is a hyphen and +.I ext +is +.IR anything . +.I user +may appear in any combination of uppercase and lowercase letters +at the front of +.IR local . + +A catch-all user, +.BR alias , +controls all other addresses. +In this case +.I ext +is +.I local +and +.I dash +is a hyphen. + +You can override all of +.BR qmail-getpw 's +decisions with the +.B qmail-users +mechanism, which is reliable, highly configurable, and much faster than +.BR qmail-getpw . +.SH "SEE ALSO" +qmail-users(5), +qmail-lspawn(8) diff --git a/sqmail-4.3.07/man/qmail-header.5 b/sqmail-4.3.07/man/qmail-header.5 new file mode 100644 index 0000000..7142364 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-header.5 @@ -0,0 +1,332 @@ +.TH s/qmail: qmail-header 5 +.SH NAME +qmail-header \- format of a mail message +.SH OVERVIEW +At the top of every mail message is a +highly structured +.BR header . +Many programs expect the header to carry certain information, +as described below. +The main function of +.B qmail-inject +is to make sure that each outgoing message has an appropriate header. + +For more detailed information, see +.BR http://pobox.com/~djb/proto/immhf.html . +.SH "MESSAGE STRUCTURE" +A message contains a series of +.I header fields\fR, +a blank line, +and a +.IR body : + +.EX + Received: (qmail-queue invoked by uid 666); +.br + 30 Jul 1996 11:54:54 -0000 +.br + From: djb@silverton.berkeley.edu (D. J. Bernstein) +.br + To: fred@silverton.berkeley.edu +.br + Date: 30 Jul 1996 11:54:54 -0000 +.br + Subject: Go, Bears! +.br + +.br + I've got money on this one. How about you? +.br + +.br + ---Dan (this is the third line of the body) +.EE + +Each header field has a +.IR name , +a colon, +some +.IR contents , +and a newline: + +.EX + Subject: Go, Bears! +.EE + +The field contents may be folded across several lines. +Each line past the first must begin with a space or tab: + +.EX + Received: (qmail-queue invoked by uid 666); +.br + 30 Jul 1996 11:54:54 -0000 +.EE + +The field name must not contain spaces, tabs, or colons. +Also, an empty field name is illegal. +.B qmail-inject +does not allow field names with unprintable characters. + +Case is irrelevant in field names: +.B subject +and +.B SUBJECT +and +.B SuBjEcT +have the same meaning. +.SH "ADDRESS LISTS" +Certain fields, such as +.BR To , +contain +.I address lists\fR. + +An address list contains some number of +.I addresses +or +.I address groups\fR, +separated by commas: + +.EX + a@b, c@d (Somebody), A Person <e@f>, +.br + random group: g@h, i@j;, k@l +.EE + +An +.I address group +has some text, a colon, a list of addresses, +and a semicolon: + +.EX + random group: g@h, i@j; +.EE + +An address can appear in several forms. +The most common form is +.IR box@host . + +Every address must include a host name. +If +.B qmail-inject +sees a lone box name +it adds the +.I default host name\fR. + +All host names should be fully qualified. +.B qmail-inject +appends the +.I default domain name +to any name without dots: + +.EX + djb@silverton -> djb@silverton.berkeley.edu +.EE + +It appends the +.I plus domain name +to any name +that ends with a plus sign: + +.EX + eric@mammoth.cs+ -> eric@mammoth.cs.berkeley.edu +.EE + +A host name may be a dotted-decimal address: + +.EX + djb@[128.32.183.163] +.EE + +RFC 822 allows mailbox names inside angle brackets +to include +.I source routes\fR, +but +.B qmail-inject +strips all source routes out of addresses. +.SH "SENDER ADDRESSES" +.B qmail-inject +looks for sender address lists in the following fields: +.BR Sender , +.BR From , +.BR Reply-To , +.BR Return-Path , +.BR Return-Receipt-To , +.BR Errors-To , +.BR Resent-Sender , +.BR Resent-From , +.BR Resent-Reply-To . + +If there is no +.B From +field, +.B qmail-inject +adds a new +.B From +field with the name of the user invoking +.B qmail-inject. + +RFC 822 requires that certain sender fields contain +only a single address, but +.B qmail-inject +does not enforce this restriction. +.SH "RECIPIENT ADDRESSES" +.B qmail-inject +looks for recipient address lists in the following fields: +.BR To , +.BR Cc , +.BR Bcc , +.BR Apparently-To , +.BR Resent-To , +.BR Resent-Cc , +.BR Resent-Bcc . + +Every message must contain at least one +.B To +or +.B Cc +or +.BR Bcc . +.B qmail-inject +deletes any +.B Bcc +field. +If there is no +.B To +or +.B Cc +field, +.B qmail-inject +adds a line + +.EX + Cc: recipient list not shown: ; +.EE + +This complies with RFC 822; +it also works around some strange +.B sendmail +behavior, in case the message is passed through +.B sendmail +on another machine. +.SH STAMPS +Every message must contain a +.B Date +field, with the date in a strict format defined by RFC 822. +If necessary +.B qmail-inject +creates a new +.B Date +field with the current date (in GMT). + +Every message should contain a +.B Message-Id +field. +The field contents are a unique worldwide identifier for this message. +If necessary +.B qmail-inject +creates a new +.B Message-Id +field. + +Another important field is +.BR Received . +Every time the message is sent from one system to another, +a new +.B Received +field is added to the top of the message. +.B qmail-inject +does not create any +.B Received +fields. +.SH "RESENT MESSAGES" +A message is +.I resent +if it contains any of the following fields: +.BR Resent-Sender , +.BR Resent-From , +.BR Resent-Reply-To , +.BR Resent-To , +.BR Resent-Cc , +.BR Resent-Bcc , +.BR Resent-Date , +.BR Resent-Message-ID . + +If a message is resent, +.B qmail-inject +changes its behavior as follows. + +It deletes any +.B Resent-Bcc +field (as well as any +.B Bcc +field); +if there are no +.B Resent-To +or +.B Resent-Cc +fields, +.B qmail-inject +adds an appropriate +.B Resent-Cc +line. +It does +.I not +add a +.B Cc +line, +even if neither +.B To +nor +.B Cc +is present. + +If there is no +.B Resent-From +field, +.B qmail-inject +adds a new +.B Resent-From +field. +It does +.I not +add a new +.B From +field. + +.B qmail-inject +adds +.B Resent-Date +if one is not already present; +same for +.BR Resent-Message-Id . +It does +.I not +add new +.B Date +or +.B Message-Id +fields. +.SH "OTHER FEATURES" +Addresses are separated by commas, not spaces. +When +.B qmail-inject +sees an illegal space, +it inserts a comma: + +.EX + djb fred -> djb, fred +.EE + +.B qmail-inject +removes all +.B Return-Path +header fields. + +.B qmail-inject +also removes any +.B Content-Length +fields. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-inject(8) diff --git a/sqmail-4.3.07/man/qmail-inject.8 b/sqmail-4.3.07/man/qmail-inject.8 new file mode 100644 index 0000000..33d37e2 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-inject.8 @@ -0,0 +1,309 @@ +.TH s/qmail: qmail-inject 8 +.SH NAME +qmail-inject \- preprocess and send a mail message +.SH SYNOPSIS +.B qmail-inject +[ +.B \-nNaAhH +] [ +.B \-f\fIsender +] [ +.I recip ... +] +.SH DESCRIPTION +.B qmail-inject +reads a mail message from its standard input, +adds appropriate information to the message header, +and invokes +.B qmail-queue +to send the message +to one or more recipients. + +See +.B qmail-header(5) +for information on how +.B qmail-inject +rewrites header fields. + +.B qmail-inject +normally exits 0. +It exits 100 if it was invoked improperly +or if there is a severe syntax error in the message. +It exits 111 for temporary errors. +.SH "ENVIRONMENT VARIABLES" +For the convenience of users who do not run +.B qmail-inject +directly, +.B qmail-inject +takes many options through environment variables. + +The user name in the +.B From +header field is set by +.BR QMAILUSER , +.BR MAILUSER , +.BR USER , +or +.BR LOGNAME , +whichever comes first. + +The host name is normally set by the +.I defaulthost +control +but can be overridden with +.B QMAILHOST +or +.BR MAILHOST . + +The personal name is +.BR QMAILNAME , +.BR MAILNAME , +or +.BR NAME . + +The default envelope sender address is the same as the +default +.B From +address, +but it can be overridden with +.B QMAILSUSER +and +.BR QMAILSHOST . +It may also be modified by the +.B r +and +.B m +letters described below. +Bounces will be sent to this address. + +If +.B QMAILMFTFILE +is set, +.B qmail-inject +reads a list of mailing list addresses, +one per line, +from that file. +If To+Cc includes one of those addresses (without regard to case), +.B qmail-inject +adds a Mail-Followup-To field +with all the To+Cc addresses. +.B qmail-inject +does not add Mail-Followup-To +to a message that already has one. + +The +.B QMAILINJECT +environment variable +can contain any of the following letters: +.TP +.B c +Use address-comment style for the +.B From +field. +Normally +.B qmail-inject +uses name-address style. +.TP +.B s +Do not look at any incoming +.B Return-Path +field. +Normally, if +.B Return-Path +is supplied, it sets the envelope sender address, +overriding all environment variables. +.B Return-Path +is deleted in any case. +.TP +.B f +Delete any incoming +.B From +field. +Normally, if +.B From +is supplied, it overrides the usual +.B From +field created by +.BR qmail-inject . +.TP +.B i +Delete any incoming +.B Message-ID +field. +Normally, if +.B Message-ID +is supplied, it overrides the usual +.B Message-ID +field created by +.BR qmail-inject . +.TP +.B r +Use a per-recipient VERP. +.B qmail-inject +will append each recipient address to the envelope sender +of the copy going to that recipient. +.TP +.B m +Use a per-message VERP. +.B qmail-inject +will append the current date and process ID to the envelope sender. +.SH OPTIONS +.TP +.B \-a +Send the message to all addresses given as +.I recip +arguments; +do not use header recipient addresses. +.TP +.B \-h +Send the message to all header recipient addresses. +For non-forwarded messages, this means +the addresses listed under +.BR To , +.BR Cc , +.BR Bcc , +.BR Apparently-To . +For forwarded messages, this means +the addresses listed under +.BR Resent-To , +.BR Resent-Cc , +.BR Resent-Bcc . +Do not use any +.I recip +arguments. +.TP +.B \-A +(Default.) +Send the message to all addresses given as +.I recip +arguments. +If no +.I recip +arguments are supplied, +send the message to all header recipient addresses. +.TP +.B \-H +Send the message to all header recipient addresses, +and to all addresses given as +.I recip +arguments. +.TP +.B \-f\fIsender +Pass +.I sender +to +.B qmail-queue +as the envelope sender address. +This overrides +.B Return-Path +and all environment variables. +.TP +.B \-N +(Default.) +Feed the resulting message to +.BR qmail-queue . +.TP +.B \-n +Print the message rather than feeding it to +.BR qmail-queue . +.SH "CONTROL FILES" +.TP 5 +.I defaultdomain +Default domain name. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR defaultdomain , +which is probably not what you want. +.B qmail-inject +adds this name to any host name without dots, +including +.I defaulthost +if +.I defaulthost +does not have dots. +(Exception: see +.IR plusdomain .) + +The +.B QMAILDEFAULTDOMAIN +environment variable +overrides +.IR defaultdomain . +.TP 5 +.I defaulthost +Default host name. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR defaulthost , +which is probably not what you want. +.B qmail-inject +adds this name to any address without a host name. +.I defaulthost +need not be the current host's name. +For example, +you may prefer that outgoing mail show +just your domain name. + +The +.B QMAILDEFAULTHOST +environment variable overrides +.IR defaulthost . +.TP 5 +.I idhost +Host name for Message-IDs. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR idhost , +which is certainly not what you want. +.I idhost +need not be the current host's name. +For example, you may prefer to use fake +host names in Message-IDs. +However, +.I idhost +must be a fully-qualified name within your domain, +and each host in your domain should use a different +.IR idhost . + +The +.B QMAILIDHOST +environment variable overrides +.IR idhost . +.TP 5 +.I plusdomain +Plus domain name. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR plusdomain , +which is probably not what you want. +.B qmail-inject +adds this name to any host name that ends with a plus sign, +including +.I defaulthost +if +.I defaulthost +ends with a plus sign. +If a host name does not have dots but ends with a plus sign, +.B qmail-inject +uses +.IR plusdomain , +not +.IR defaultdomain . + +The +.B QMAILPLUSDOMAIN +environment variable overrides +.IR plusdomain . +.SH "SEE ALSO" +addresses(5), +qmail-control(5), +qmail-header(5), +qmail-queue(8) diff --git a/sqmail-4.3.07/man/qmail-limits.9 b/sqmail-4.3.07/man/qmail-limits.9 new file mode 100644 index 0000000..47f81f4 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-limits.9 @@ -0,0 +1,33 @@ +.TH s/qmail: qmail-limits 7 +.SH "NAME" +qmail-limits \- artificial limits in the qmail system + +.SH "DESCRIPTION" +The +.B qmail +system is able to handle messages of any size, +addresses of any size, mailing lists of any size, and so on, +except as limited by the available memory and disk space. + +However, it imposes certain artificial limits: +.TP 5 +1. +.B qmail-lspawn +silently limits the number of simultaneous local deliveries to SPAWN. +.B qmail-rspawn +silently limits the number of simultaneous remote deliveries to SPAWN. +.TP 5 +2. +.B qmail-queue +rejects any message with an envelope address longer than 1000 characters. +.TP 5 +3. +.B qmail-lspawn +truncates any overly long error report from a delivery program. +It appends a note saying that it did so. + +.SH "SEE ALSO" +qmail-lspawn(8), +qmail-queue(8), +qmail-rspawn(8), +ulimit(3). diff --git a/sqmail-4.3.07/man/qmail-local.8 b/sqmail-4.3.07/man/qmail-local.8 new file mode 100644 index 0000000..9074d4e --- /dev/null +++ b/sqmail-4.3.07/man/qmail-local.8 @@ -0,0 +1,99 @@ +.TH s/qmail: qmail-local 8 +.SH NAME +qmail-local \- deliver or forward a mail message +.SH SYNOPSIS +.B qmail-local +[ +.B \-nN +] +.I user +.I homedir +.I local +.I dash +.I ext +.I domain +.I sender +.I defaultdelivery +.SH DESCRIPTION +.B qmail-local +reads a mail message +and delivers it to +.I user +by the procedure described in +.BR dot-qmail(5) . + +The message's envelope recipient is +.IR local@domain . +.B qmail-local +records +.I local@domain +in a new +.B Delivered-To +header field without the virtual user name extension. +If exactly the same +.B Delivered-To: \fIlocal@domain +already appears in the header, +.B qmail-local +bounces the message, +to prevent mail forwarding loops. + +The message's envelope sender is +.IR sender . +.B qmail-local +records +.I sender +in a new +.B Return-Path +header field. + +.I homedir +is the user's home directory. +It must be an absolute directory name. + +.I dash +and +.I ext +identify the +.B .qmail\fIdashext +file used by +.BR qmail-local ; +see +.BR dot-qmail(5) . +Normally +.I dash +is either empty or a lone hyphen. +If it is empty, +.B qmail-local +treats a nonexistent +.B .qmail\fIext +the same way as an empty +.BR .qmail\fIext : +namely, following the delivery instructions in +.IR defaultdelivery . + +The standard input for +.B qmail-local +must be a seekable file, +so that +.B qmail-local +can read it more than once. +.SH "OPTIONS" +.TP +.B \-n +Instead of reading and delivering the message, +print a description of the delivery instructions. +.TP +.B \-N +(Default.) Read and deliver the message. +.SH "EXIT CODES" +0 if the delivery is completely successful; +nonzero if any delivery instruction failed. +Exit code 111 +indicates temporary failure. +.SH "SEE ALSO" +dot-qmail(5), +envelopes(5), +qmail-command(8), +qmail-queue(8), +qmail-send(8), +qmail-lspawn(8) diff --git a/sqmail-4.3.07/man/qmail-log.5 b/sqmail-4.3.07/man/qmail-log.5 new file mode 100644 index 0000000..a7584e1 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-log.5 @@ -0,0 +1,448 @@ +.TH s/qmail: qmail-log 5 +.SH NAME +qmail-log \- s/qmail activity record +.SH DESCRIPTION +.B qmail-send +prints a series of lines describing its activities. +Each possible line is described below. +.SH "STATUS" +.TP +.B status: local \fIl\fR/\fIL\fR remote \fIr\fR/\fIR\fR ... +.B qmail-send +is waiting for +.I l +local deliveries +and +.I r +remote deliveries. +The concurrency limits are +.I L +and +.IR R . +.TP +.B status: exiting +.B qmail-send +is done. +.SH "FATAL PROBLEMS" +.TP +.B alert: cannot start: ... +.B qmail-send +is unable to prepare itself for delivering messages; +it is giving up. +This normally indicates a serious configuration error, +but it can be caused by a temporary lack of resources. +.TP +.B alert: oh no! lost ... +One of the other daemons has died. +.B qmail-send +will exit as soon as possible. +.SH "SERIOUS PROBLEMS" +.TP +.B alert: unable to append to bounce message... +.B qmail-send +is unable to record a permanent failure, +usually because the disk is full. +This is a very serious problem; +.B qmail-send +cannot proceed without recording the results. +It will try again in ten seconds. +.TP +.B alert: out of memory... +.B qmail-send +tried to allocate more memory and failed. +It will try again in ten seconds. +.TP +.B alert: unable to opendir... +.B qmail-send +is having trouble reading a file list from disk, +usually because the system's file descriptor table is full, +but possibly because permissions are set incorrectly. +It will try again in ten seconds. +.TP +.B alert: unable to switch back... +.B qmail-send +was sent SIGHUP, +and it is unable to reenter the queue directory. +This is a very serious problem; +.B qmail-send +cannot proceed outside the queue directory. +It will try again in ten seconds. +.TP +.B alert: unable to reread... +.B qmail-send +was sent SIGHUP, +but it is unable to read the new controls. +It will continue operating with the original controls. +.SH "MESSAGES" +.TP +.B new msg \fIm\fR +.B qmail-send +is going to preprocess a queued message. +The message number, +.IR m , +is its disk inode number. +After a message is removed from the queue, +its number can be reused immediately. +.TP +.B info msg \fIm\fR: bytes \fIb\fR from <\fIs\fR> qp \fIq\fR uid \fIu\fR +Message +.I m +contains +.I b +bytes; +its envelope sender is +.IR s ; +it was queued by a user with user ID +.IR u . +.I q +is a long-term queue identifier, +the process ID of the +.B qmail-queue +that queued the message. +.TP +.B bounce msg \fIm\fR qp \fIq\fR +Message +.I m +had some delivery failures. +The long-term queue identifier of the bounce (or double-bounce) message +is +.IR q . +.TP +.B double bounce: discarding ... +Message +.I m +was discarded due to an \'empty\' recipient in +. +.IR doublebounceto . +.TP +.B triple bounce: discarding ... +Message +.I m +had some delivery failures, +but it is already a double-bounce message, +so it must be thrown away. +Triple-bounce messages do not exist. +.TP +.B end msg \fIm\fR +.B qmail-send +is about to remove +message +.I m +from the queue. +.SH "DELIVERIES" +.TP +.B starting delivery \fId\fR: msg \fIm\fR to ... +.B qmail-send +is telling +.B qmail-lspawn +or +.B qmail-rspawn +to deliver message +.I m +to one recipient. +The delivery number, +.IR d , +starts at 1 and increases by 1 for each new delivery. +.TP +.B delivery \fId\fR: success: ... +Delivery +.I d +was successful. +.TP +.B delivery \fId\fR: failure: ... +Delivery +.I d +failed permanently. +The message will bounce. +.TP +.B delivery \fId\fR: deferral: ... +Delivery +.I d +failed temporarily. +This recipient will be retried later. +.TP +.B delivery \fId\fR: report mangled, will defer +There is a serious bug in +.B qmail-lspawn +or +.BR qmail-rspawn . +This recipient will be retried later. +.SH "WARNINGS" +.TP +.B internal error: delivery report out of range +.B qmail-lspawn +or +.B qmail-rspawn +has supplied a report on a nonexistent delivery. +This is a serious bug. +.TP +.B qmail-clean unable to clean up ... +For some reason +.B qmail-clean +is unable to remove the indicated file. +It will try again later. +.TP +.B trouble fsyncing ... +.B qmail-send +was unable to write to disk the results of preprocessing a queued message. +It will try again later. +.TP +.B trouble in select +There is an operating system bug. +.TP +.B trouble injecting bounce message... +.B qmail-send +was unable to queue a bounce message, +usually because the disk is full. +It will try again later. +.TP +.B trouble marking ... +.B qmail-send +was unable to record the result of a successful or permanently +unsuccessful delivery. +This means that the delivery will be tried again later. +.TP +.B trouble opening ... +.B qmail-send +was unable to open the list of local or remote recipients +for a message. +It will try again later. +.TP +.B trouble reading ... +Either +.B qmail-send +is unable to read a recipient list, +or it is unable to read the envelope of a queued +message, or it is out of memory. +Whatever it was doing, it will try again later. +.TP +.B trouble writing to ... +.B qmail-send +was unable to preprocess a queued message, +usually because the disk is full. +It will try again later. +.TP +.B unable to create ... +.B qmail-send +was unable to preprocess a queued message, +usually because the disk is out of inodes. +It will try again later. +.TP unable to create .... [info,delivery] +.B qmail-send +could not setup a valid file descriptor. +This is a fatal error. +.TP +.B unable to open ... +.B qmail-send +is unable to read the envelope of a queued message +for preprocessing. +It will try again later. +.TP +.B unable to start qmail-queue... +.B qmail-send +is unable to queue a bounce message, +usually because the machine is almost out of memory. +It will try again later. +This can also be caused by incorrect settings of +.B $QMAILQUEUE +or errors in a program or script which +.B $QMAILQUEUE ++points to. +.TP +.B unable to stat ... +.B qmail-send +is unable to obtain information about a file that should exist. +It will try again later. +.TP +.B unable to unlink ... +.B qmail-send +is unable to remove a file. +It will try again later. +.TP +.B unable to utime ... +.B qmail-send +is about to exit, +and it is unable to record on disk +the next scheduled delivery time for a message. +The message will be retried as soon as +.B qmail-send +is restarted. +.TP +.B unknown record type in ... +There is a serious bug in either +.B qmail-queue +or +.BR qmail-send . + +.SH "UNIFIED SMTPD/POP3D LOGGING" +.B qmail-smtpd +and +.B qmail-popup +log additional information in a unified extensible format +\fIAction::Type::Condition\fR \fIInformation\fR. + +.B Action +is either +.IR Reject , +.IR Accept , +or additionally +.IR Info . + +The +.B Type +belongs to the following information: +.TP +.I SNDR +the client's hostname, +.TP +.I SPF +indicating SPF validation, +.TP +.I TLS +labeling TLS connections, +.TP +.I AUTH +for Authenticated sessions. Further +.TP +.I ORIG +relates to the return path \fIF:<Return-Path>\fR, and +.TP +.I RCTP +to the forwarding path \fIT:<Forwarding-Path>\fR, and finally +.TP +.I DATA +to the message. + +.TP 0 +The following \fBConditions\fR are provided: +.TP 4 +.I Bad_Helo +the client's HELO/EHLO greeting string was found in +.IR badhelo +or rejected because of one of the following conditions indicated +in the information section: '!' (HELO/EHLO not provided/empty) +, '\.'/'*' (HELO/EHLO rejected due to a direct/wildmat match with entries in +.IR badhelo ). +.TP +.I Bad_Loader +the content of a base64 encoded MIME part matched an +entry in +.IR badloadertypes.cdb . +.TP +.I Bad_MIME +a base64 encoded MIME part matched an entry n +.IR badmimetypes.cdb . +.TP +.I Bad_Mailfrom +the provided <Return-Path> matched an entry in +.I badmailfrom +additionally with the rejection conditions: '@' (address), '*' +(wildmat), '-' (badmailfromunknown), and '+' (spoofing). +.TP +.I Bad_Rcptto +the provided <Forwarding-Path> matched an entry in +.IR badrcptto . +.TP +.I DNS_Helo +the client's HELO/EHLO greeting did not match it's +FQDN or no DNS A/MX RR was found as indicated with the +following symbols: '=' (HELO/EHLO does not match +.BR TCPREMOTEHOST ) +, 'A' (DNS A-Name lookup failed for HELO/EHLO) +, 'M' (DNS MX lookup failed for HELO/EHLO). +.TP +.I DNS_MF +no DNS MX RR was found for the <Return-Path>. +.TP +.I Failed_Rcptto +the <Forwarding-Path> did not match entry in the provdided +cdbs as per +.IR recipients . +.TP +.I Invalid_Relay +the none-RELAYCLIENT provided a <Forwarding-Path> not +allowed as per +.I rcpthosts +or +.IR morercpthosts.cdb . +.TP +.I Invalid_Sender +the <Return-Path> of a RELAYCLIENT did not match the +provided value of LOCALMFCHECK or did not match against +.I mailfromrules.cdb +or was not found in +.I rcpthosts +or +.IR morercpthosts.cdb . +.TP +.I Invalid_Size +the message size exceeded the maximum as provided by +DATEBYTES or +.IR databytes . +.TP +.I Toomany_Rcptto +the number of Recipients ('RCPT TO:') exaggerated the +value provided as MAXRECPIENTS. +.TP +.I Cipher +TLS session used this cipher. +.TP +.I Missing +depending on the context, either the required +Start-TLS or AUTH s/qmail: is not granted. +.TP +.I Pam +SMTP authentication was granted by pam. +.TP +.I Recipients_Rcptto +the <Forwarding-Path> matched an entry in the cdbs available per +.IR reccients . +.TP +.I Recipients_Verp +the Forwarding-Path was recogized as VERP and matched an entry +in the cdbs available per +.IR recipients . +.TP +.I Recipients_Domain +the Forwarding-Path matched a wildcard domain entry in the cdbs +available per +.IR recipients . +.TP +.I Rcpthosts_Rcptto +the domain part of the <Forwarding-Path> matched an entry in +.I rcpthosts +or +.IR morercpthosts.cdb . + +.TP 0 +The displayed \fBInformation\fR: + +.TP 4 +.I P:protocol +the effective SMTP or POP3 protocol in use. +.TP +.I S:IP:FQDN +the sender's IP and FQDN address available via +TCPREMOTEIP(6) and TCPREMOTEHOST. +.TP +.I H:string +the client's HELO/EHLO greeting string. +.TP +.I F:Return-Path +the provided 'MAIL FROM:' address (if any). +.TP +.I T:Forwarding-Path +the given 'RCPT TO:' address. +.TP +.I ?~ 'userid' +in case of authentication the provided userid. +.TP +.I != 'DN' +in case of a TLS session, the presented client's +\'Subject\' Distinguished Name (DN) - if available +(otherwise \'unknown\'). + +.SH "SEE ALSO" +qmail-send(8), +qmail-smtpd(8), +qmail-control(9) diff --git a/sqmail-4.3.07/man/qmail-lspawn.8 b/sqmail-4.3.07/man/qmail-lspawn.8 new file mode 100644 index 0000000..e97a93d --- /dev/null +++ b/sqmail-4.3.07/man/qmail-lspawn.8 @@ -0,0 +1,46 @@ +.TH s/qmail: qmail-lspawn 8 +.SH NAME +qmail-lspawn \- schedule local deliveries +.SH SYNOPSIS +.B qmail-lspawn +.I defaultdelivery +.SH DESCRIPTION +.B qmail-lspawn +reads a series of local delivery commands from descriptor 0, +invokes +.B qmail-local +to perform the deliveries, +and prints the results to descriptor 1. +It passes +.I defaultdelivery +to +.B qmail-local +as the default delivery instruction. + +.B qmail-lspawn +invokes +.B qmail-local +asynchronously, +so the results may not be in the same order as the commands. + +For each recipient address, +.B qmail-lspawn +finds out which local user controls that address. +It first checks the +.B qmail-users +mechanism; if the address is not listed there, it invokes +.BR qmail-getpw . +.B qmail-lspawn +then runs +.B qmail-local +under the user's uid and gid. +It does not set up any supplementary groups. + +.B qmail-lspawn +treats an empty mailbox name as a trash address. +.SH "SEE ALSO" +envelopes(5), +qmail-users(5), +qmail-getpw(8), +qmail-send(8), +qmail-local(8) diff --git a/sqmail-4.3.07/man/qmail-mfrules.9 b/sqmail-4.3.07/man/qmail-mfrules.9 new file mode 100644 index 0000000..17d575f --- /dev/null +++ b/sqmail-4.3.07/man/qmail-mfrules.9 @@ -0,0 +1,108 @@ +.TH s/qmail: qmail-mfrules 8 +.SH "NAME" +qmail-mfrules \- prepare mfrules for qmail-smtpd +.SH SYNOPSIS +.B qmail-mfrules + +.SH "DESCRIPTION" +.B qmail-mfrules +reads the addresses provided in +.BR SQMAIL/control/mailfromrules , +converts them into lowercase, and writes them into +.B SQMAIL/control/mailfromrules.cdb +in a binary format suited +for quick access by +.BR qmail-smtpd . + +If there is a problem with +.BR control/mailfromrules , +.B qmail-mfrules +complains and leaves +.B control/mailfromrules.cdb +alone. + +.B qmail-mfrules +ensures that +.B control/mailfromrules.cdb +is updated atomically, +so +.B qmail-smtpd +never has to wait for +.B qmail-mfrules +to finish. +However, +.B qmail-mfrules +makes no attempt to protect against two simultaneous updates of +.BR control/mailfromrules.cdb . + +The binary +.B control/mailfromrules.cdb +format is portable across machines. + +.SH "RULE FORMAT" +A rule is one line. A file containing rules may also contain comments: lines +beginning with # are ignored. All addresses are evaluated case-insensitive. + +Each rule contains an address, an ampersend sign '&', and a list of strings separated by +commas to be used for 'Mail From: Address Verification' (MAV). When +.BR qmail-smtpd (8) +receives a connection from that address, it checks whether the received +envelope sender address correspondes with a MAV string (from the right +to the left). +The MAV string for an address may be NULL in order to allow any envelope +sender address. NULLSENDER envelope addresses are not subject of the MAV. + +.SH "RULE BASE" +.BR qmail-smtpd (8) +looks for rules with various addresses in the following order: +.IP 1 +$TCPREMOTEINFO, if $TCPREMOTEINFO is set (e.g. by SMTP Authentication); +.IP 2. +$TCPREMOTEINFO@$TCPREMOTEIP, if $TCPREMOTEINFO is set; +.IP 3. +$TCPREMOTEINFO@=$TCPREMOTEHOST, if $TCPREMOTEINFO is set and $TCPREMOTEHOST is +set; +.IP 4. +the dotted decimal $TCPREMOTEIP address; +.IP 5. +the compactified $TCPREMOTEIP6 address; +.IP 6. +=$TCPREMOTEHOST, if $TCPREMOTEHOST is set; +.IP 7. +shorter and shorter prefixes of $TCPREMOTEIP ending with a dot; +.IP 8. +shorter and shorter values of $TCPREMOTEIP6 ending with a colon; +.IP 9. +shorter and shorter suffixes of $TCPREMOTEHOST starting with a dot, preceded +by =, if $TCPREMOTEHOST is set; and finally +.IP 10. +=, if $TCPREMOTEHOST is set. +.P +.B qmail-smtpd +employes the first matching rule for the MAV check. You should use the +.B -p +option to +.BR sslserver +if you rely on $TCPREMOTEHOST here. + +For example, here are some rules: + +.EX + jsmith@virtualdomain.com&john.smith@virtualdomain.com + joe@18.23.0.32&joe@example.com + 18.23&@example.com + =.heaven.mil&God@heaven.mil,st.peter@heaven.mil,-angles@heaven.mil + fe80:&user@myhost.local + 2001::feh:abc9:&me@fehnet.com +.EE + +.SH "IP-ADDRESSES" +.B qmail-mfrules +recognizes the dotted-decimal IPv4 and the compactified +IPv6 addresses tokenized by the 'dot' or the 'colon' character +and compares the respective parts from right to left. +However, the CIDR address format is not supported (yet). + + +.SH "SEE ALSO" +qmail-smtpd(8) diff --git a/sqmail-4.3.07/man/qmail-mrtg.8 b/sqmail-4.3.07/man/qmail-mrtg.8 new file mode 100644 index 0000000..165c0d5 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-mrtg.8 @@ -0,0 +1,145 @@ +.TH s/qmail: qmail-mrtg 8 + +.SH NAME +qmail-mrtg \- prepare s/qmail logs for MRTG analysis +.SH SYNOPSIS +.B qmail-mrtg [ -1 | -2 | -3 | -4 | -5 | -6 | -a | -b | -c | -d | -e | -f | -g | -h | -i | -j | k | -z | -A | -B ] [time] + +.SH DESCRIPTION +.B qmail-mrtg +reads the +.B multilog +tagged +.B s/qmail +logs with TAI64N timestamps on standard input +to produce a counter for specifc +.B s/qmail +events and display them on standard output +suitable for MRTG processing. + +.SH USAGE +.B qmail-mrtg +can be used to analyse +.BR qmail-send , +.BR qmail-smtpd , +and +.B qmail-pop3d +logs in order to feed the results into MRTG. + +Typically, +.B qmail-mrtg +is called by the +.B crontab +facility together with a configuration files telling +.B qmail-mrtg +what to analyse. + +.SH ARGUMENTS +.B qmail-mrtg +posses three different sets of commands. +Reading +.B qmail-send +logs: +.I -1 +Deliveries/TLS transmitted, +.I -2 +Message KBytes enqueued, +.I -3 +Local/Remote Concurrency, +.I -4 +Failure/Deferred Messages, +.I -5 +Bounces/Triple bounces, +.I -6 +qmtp/qmtps Messages. + +.B qmail-smtpd +logs: +.I -a +total sessions, +.I -b +accepted/rejected sessions, +.I -c +rejected sessions (MTA), +.I -d +rejected originator, +.I -e +rejected recipient, +.I -f +rejected data (Mime + Loader), +.I -g +rejected data (Virus + Spam), +.I -h +authenticated sessions, +.I -i +accepted/rejected TLS sessions, +.I -j +recognized/rejected SPF sessions. +.I -k +deferred SMTP sessions (greylisted). +Summaries are provided by +.I -z +total sessions, including +.B qmail-smtpd +and +.BR tcpserver / +.BR sslserver / +.BR rblsmtpd . + +.BR qmail-pop3d / +.B qmail-popup +logs: +.I -A +accepted/rejected POP3 user, +.I -B +.BR qmail-pop3d / +.BR tcpserver / +.B sslserver +connections. + +The intervals to evaluate the information given on STDIN +defaults to +.IR 305\ secs +and can be changed by the second argument for +.B qmail-mrtg +providing a value as +.I minutes +increased by an offset of 5 sec to cover a roll-over +cut-off by +.BR crontab . +.SH "CONFIGURATION FILES" +.B qmail-mrtg +depends on a configuration file for each service. +Sample configuration files are provided. + +.SH "CRON INVOCATION" +Since +.B qmail-mrtg +typically is invoked by the +.B cron +facility, additional information neeeds to be supplied: + +.EX + */5 * * * * env LANG=C mrtg /etc/qmail-mrtg.send.cfg &>/dev/null + */5 * * * * env LANG=C mrtg /etc/qmail-mrtg.smtpd.cfg &>/dev/null + */5 * * * * env LANG=C mrtg /etc/qmail-mrtg.pop3d.cfg &>/dev/null +.EE + +Note: The default interval of +.IR 305\ secs +allows a certain overlap for cron not to loose events at the very +edge. + +.SH "CREDITS" +.B MRTG +is a program created by Tobias Oetiker and Dave Rand +(http://oss.oetiker.ch/mrtg/). + +.SH "SEE ALSO" +mrtg(1), +crontab(5), +cron(8), +qmail-log(8), +qmail-send(8), +qmail-smtpd(8), +qmail-popup(8). diff --git a/sqmail-4.3.07/man/qmail-newmrh.9 b/sqmail-4.3.07/man/qmail-newmrh.9 new file mode 100644 index 0000000..941dc03 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-newmrh.9 @@ -0,0 +1,41 @@ +.TH s/qmail: qmail-newmrh 8 +.SH NAME +qmail-newmrh \- prepare morercpthosts for qmail-smtpd +.SH SYNOPSIS +.B qmail-newmrh +.SH DESCRIPTION +.B qmail-newmrh +reads the instructions in +.B SQMAIL/control/morercpthosts +and writes them into +.B SQMAIL/control/morercpthosts.cdb +in a binary format suited +for quick access by +.BR qmail-smtpd . + +If there is a problem with +.BR control/morercpthosts , +.B qmail-newmrh +complains and leaves +.B control/morercpthosts.cdb +alone. + +.B qmail-newmrh +ensures that +.B control/morercpthosts.cdb +is updated atomically, +so +.B qmail-smtpd +never has to wait for +.B qmail-newmrh +to finish. +However, +.B qmail-newmrh +makes no attempt to protect against two simultaneous updates of +.BR control/morercpthosts.cdb . + +The binary +.B control/morercpthosts.cdb +format is portable across machines. +.SH "SEE ALSO" +qmail-smtpd(8) diff --git a/sqmail-4.3.07/man/qmail-newu.9 b/sqmail-4.3.07/man/qmail-newu.9 new file mode 100644 index 0000000..a030794 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-newu.9 @@ -0,0 +1,43 @@ +.TH s/qmail: qmail-newu 8 +.SH NAME +qmail-newu \- prepare address assignments for qmail-lspawn +.SH SYNOPSIS +.B qmail-newu +.SH DESCRIPTION +.B qmail-newu +reads the assignments in +.B SQMAIL/users/assign +and writes them into +.B SQMAIL/users/assign.cdb +in a binary format suited +for quick access by +.BR qmail-lspawn . + +If there is a problem with +.BR users/assign , +.B qmail-newu +complains and leaves +.B users/assign.cdb +alone. + +.B qmail-newu +ensures that +.B users/assign.cdb +is updated atomically, +so +.B qmail-lspawn +never has to wait for +.B qmail-newu +to finish. +However, +.B qmail-newu +makes no attempt to protect against two simultaneous updates of +.BR users/assign.cdb . + +The binary +.B users/assign.cdb +format is portable across machines. +.SH "SEE ALSO" +qmail-users(5), +qmail-lspawn(8), +qmail-pw2u(8) diff --git a/sqmail-4.3.07/man/qmail-pop3d.8 b/sqmail-4.3.07/man/qmail-pop3d.8 new file mode 100644 index 0000000..14afa93 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-pop3d.8 @@ -0,0 +1,46 @@ +.TH s/qmail: qmail-pop3d 8 +.SH NAME +qmail-pop3d \- provide mail via POP3 +.SH SYNOPSIS +.B qmail-pop3d +.I maildirname +.SH DESCRIPTION +.B qmail-pop3d +lets a user read and delete his mail through the network. + +Mail is stored in a +.B maildir +called +.IR maildirname , +normally +.BR Maildir , +in the user's home directory. + +.B qmail-pop3d +is normally invoked +under +.BR qmail-popup , +which reads a username and password, +and +.BR qmail-authuser , +which checks the password and sets up environment variables. + +.B qmail-pop3d +has a 20-minute idle timeout. + +.B qmail-pop3d +supports TOP, USER, UIDL, STLS, and LAST. + +.B qmail-pop3d +appends an extra blank line to every message +to work around serious bugs in certain clients. + +.B qmail-pop3d +is based on a program contributed by Russ Nelson. + +.SH "SEE ALSO" +maildir(5), +qmail-authuser(8), +qmail-local(8), +qmail-popup(8), +qmail-log(8). diff --git a/sqmail-4.3.07/man/qmail-popup.8 b/sqmail-4.3.07/man/qmail-popup.8 new file mode 100644 index 0000000..bc4aeef --- /dev/null +++ b/sqmail-4.3.07/man/qmail-popup.8 @@ -0,0 +1,131 @@ +.TH s/qmail: qmail-popup 8 +.SH NAME +qmail-popup \- read a POP username and password +.SH SYNOPSIS +.B qmail-popup +.I hostname +.I subprogram +.SH DESCRIPTION +.B qmail-popup +reads a POP username and password from the network. +It then runs +.IR subprogram . + +.B qmail-popup +expects descriptor 0 to read from the network +and descriptor 1 to write to the network. +It reads a username and password from descriptor 0 +in POP's USER-PASS style or APOP style. +File descriptor 5 is used to provide additional logging. +It invokes +.IR subprogram , +with the same descriptors 0 and 1; +descriptor 2 writing to the network; +and descriptor 3 reading the username, a 0 byte, the password, +another 0 byte, +an APOP timestamp derived from +.IR hostname , +and a final 0 byte. +.B qmail-popup +then waits for +.I subprogram +to finish. +It prints an error message if +.I subprogram +crashes or exits nonzero. + +.B qmail-popup +has a 20-minute idle timeout. + +.SH "AUTHENTICATION" +.B qmail-popup +supports both username/password and APOP authentication. +This latter is invoked, once the +environment variable +.I POP3AUTH='apop' +or +.I POP3AUTH='+apop' +is set. +In this case, you need to provide a +APOP-capable PAM, eg. +.BR qmail-authuser . + +.B qmail-popup +should be used only within a secure network. +Otherwise an eavesdropper can steal passwords. +Even if you use APOP, +an active attacker can still take over the connection +and wreak havoc. + +.SH "STLS/POP3S SUPPORT" +.B qmail-popup +can be adviced to work on a TLS encrypted connection. + +At first, using +.B sslserver +and binding +.BR qmail-popup , +.B qmail-pop3d +on (in particular) the POP3S port +.I 995 +provides mandatory TLS encryption. + +Second, in case you provide +the environment variable +.I UCSPITLS='' +together with +.BR sslserver , +.B qmail-popup +communicates with the +.B sslserver +program interface through a control socket, +a reading and a writing pipe created dynamically +during the session start after announcing +.I STLS +to the client, thus allowing TLS encryption on request. +In case +.IR UCSPITLS='!' +is set, STLS is required; while setting +.IR UCSPITLS='-' +disables STLS. + +.SH "LOGGING" +.B qmail-popup +provides logging of accepted and rejected POP3 sessions +using about the same format as +.BR qmail-smtpd . +The authentication mechanism is indicated via +.I User +in case the userid/password method was used, and +.I Apop +if APOP challenge/response was applicable. +The communication protocol may be either +.I POP3 +or +.I POP3S +for of a STLS/POP3S secured connection. +The +.I username +provided for authentication is displayed after the +sequence +.IR '?~' . +In case +.B qmail-popup +is setup requiring STLS by means of +.IR UCSPITLS='!' , +the log displays 'Any' as auth method +and 'unknown' as username. + + +The log is available on file descriptor 5. +In order to display the result use the redirection '5>&1'. + +.B qmail-popup +is based on a program contributed by Russ Nelson. + +.SH "SEE ALSO" +maildir(5), +qmail-authuser(8), +qmail-pop3d(8), +qmail-log(8). + diff --git a/sqmail-4.3.07/man/qmail-postgrey.8 b/sqmail-4.3.07/man/qmail-postgrey.8 new file mode 100644 index 0000000..b2532ce --- /dev/null +++ b/sqmail-4.3.07/man/qmail-postgrey.8 @@ -0,0 +1,90 @@ +.TH s/qmail: qmail-postgrey 8 +.SH NAME +qmail-postgrey \- send SMTP connection data to greylisting server +.SH SYNOPSIS +.B qmail-postgrey ip%netid;port Mail From: Rcpt To: TCPREMOTEIP TCPREMOTEHOST +.SH DESCRIPTION +.B qmail-postgrey +is usually invoked by +.B qmail-smtpd +automatically provissioning the SMTP connection information +.IR Mail\ From: , +.IR Rcpt\ To: , +.IR TCPREMOTEIP +and +.I TCPREMOTEHOST +to a greylising server given by +.IR IPv4|IPv6%netid;port . +.I port +defaults to +.I 60000 +and thus can be omitted. +IPv6 LLU addresses can be specified +adding the +.I netid +name following the percentage sign. +.SH "GREYLISTING SERVER" +Since there is neither a formal API defined for the +greylisting lookup nor for the behavior and return +codes of the greylisting server, +.B qmail-postgrey +only works well with +.I David\ Schweikert's +.B postgrey +implementation. + +Here, the server's response upon recognizing the triple +.RI CLIENT_IP , +.I (SMTP\ envelope)\ SENDER +and +.I (SMTP\ envelope)\ RECIPIENT +is either +.IR action=DUNNO , +.I action=PREPEND +or +.I action=DEFER_IF_PERMIT +and in case of the last, +.B qmail-postgrey +returns with +.I 10 +telling +.B qmail-smtpd +to respond to the client with a SMTP +.I 450\ greylisted +reply code. Otherwise +.B qmail-postgrey +returns +.IR 0 . +.SH "INVOCATION" +Unlike for testing reasons, +.B qmail-postgrey +is called directly from +.B qmail-smtpd +in case the environment variable +.I POSTGREY +is defined and provissioned with the greylisting +server's IP address (and perhaps netid and port) +listening there. + +The environment variable +.I POSTGREY +is typically defined within +.B sslserver\'s +.IR cdb . +Additionally, +.I REPLY_GREYLISTED +can be used as environment variable +to provide some more descriptive +information to the sending MTA which will eventually +be visible in a bounce message. +.SH "CREDITS" +.B qmail-postgrey +and its integration into +.B qmail-smtpd +is based on +.I Jan\ Mojzis +implementation and used by permission. +.SH "SEE ALSO" +qmail-control(5), +qmail-smtpd(8), +https://postgrey.schweikert.ch diff --git a/sqmail-4.3.07/man/qmail-pw2u.9 b/sqmail-4.3.07/man/qmail-pw2u.9 new file mode 100644 index 0000000..269d1f4 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-pw2u.9 @@ -0,0 +1,241 @@ +.TH s/qmail: qmail-pw2u 8 +.SH NAME +qmail-pw2u \- build address assignments from a passwd file +.SH SYNOPSIS +.B qmail-pw2u +[ +.B \-/ohHuUC +] +[ +.B \-c\fIchar +] +.SH DESCRIPTION +.B qmail-pw2u +reads a V7-format passwd file from standard input +and prints a +.BR qmail-users -format +assignment file. + +A V7-format passwd file is a series of lines. +Each line has the format + +.EX + user:password:uid:gid:gecos:home:shell +.EE + +where +.I user +is an account name, +.I uid +and +.I gid +are the user id and group id of that account, +and +.I home +is the account's home directory. +.IR password , +.IR gecos , +and +.I shell +are ignored by +.BR qmail-pw2u . + +If you put the output of +.B qmail-pw2u +into +.BR SQMAIL/users/assign , +and then run +.BR qmail-newu , +.B qmail-lspawn +will obey the assignments printed by +.BR qmail-pw2u . +.B WARNING: +After changing any users, uids, gids, or home directories +in your passwd file, +you must run +.B qmail-pw2u +and +.B qmail-newu +again if you want +.B qmail-lspawn +to see the changes. +.SH RULES +By default, +.B qmail-pw2u +follows the same rules as +.BR qmail-getpw . +It skips +.I user +if (1) +.I uid +is zero, +(2) +.I home +does not exist, +(3) +.I user +does not own +.IR home , +or +(4) +.I user +contains uppercase letters. +It then gives each remaining +.I user +control over the basic +.I user +address and +all addresses of the form +.IR user\fBBREAK\fIanything . +A catch-all user, +.BR alias , +controls all other addresses. + +You may change these rules by setting up files in +.BR SQMAIL/users : +.TP +.B include +Allowed users, one per line. +If +.B include +exists, and +.I user +is not listed in +.BR include , +.I user +is ignored. +.TP +.B exclude +Ignored users, one per line. +If +.B exclude +exists, and +.I user +is listed in +.BR exclude , +.I user +is ignored. +.TP +.B mailnames +Replacement names for users. +Each line has the form + +.EX + user:mailname1:mailname2:... +.EE + +The addresses +.I mailname1 +and +.I mailname1\fBBREAK\fIext +and +.I mailname2 +and so on will be delivered +to +.IR user . + +.B WARNING: +The addresses +.I user +and +.I user\fBBREAK\fIext +will not be delivered to +.I user +unless +.I user +is listed as one of the +.IR mailname s. + +A line in +.B mailnames +is silently ignored if the user does not exist. +.TP +.B subusers +Extra addresses. +Each line has the form + +.EX + sub:user:pre: +.EE + +.I sub +will be handled by +.IR home\fB/.qmail\-\fIpre , +where +.I home +is +.IR user 's +home directory; +.I sub\fBBREAK\fIext +will be handled by +.IR home\fB/.qmail\-\fIpre\fB\-\fIext . +.TP +.B append +Extra assignments, +printed at the end of +.BR qmail-pw2u 's +output. +.SH OPTIONS +.TP +.B \-o +(Default.) +Skip +.I user +if +.I home +does not exist (or is not visible to +.BR qmail-pw2u ). +Skip +.I user +if +.I home +is not owned by +.IR user . +.TP +.B \-h +Stop if +.I home +does not exist. +This is appropriate if every user is supposed to have a home directory. +Skip +.I user +if +.I home +is not owned by +.IR user . +.TP +.B \-H +Do not check the existence or ownership of +.IR home . +.TP +.B \-U +(Default.) +Skip +.I user +if there are any uppercase letters in +.IR user . +.TP +.B \-u +Allow uppercase letters in +.IR user . +.TP +.B \-c\fIchar +Use +.I char +as the user-extension delimiter +in place of +.BR BREAK . +.TP +.B \-C +Disable the user-extension mechanism. +.TP +.B \-/ +Use +.IR home\fB/.qmail\-/ ... +instead of +.IR home\fB/.qmail\- ... +.SH "SEE ALSO" +qmail-users(5), +qmail-lspawn(8), +qmail-newu(8), +qmail-getpw(8) diff --git a/sqmail-4.3.07/man/qmail-qmaint.8 b/sqmail-4.3.07/man/qmail-qmaint.8 new file mode 100644 index 0000000..54342b4 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-qmaint.8 @@ -0,0 +1,65 @@ +.TH s/qmail: qmail-qmaint 8 +.SH NAME +qmail-qmaint \- queue maintenance +.SH SYNOPSIS +.B qmail-qmaint +[ +.I -i +] +| +[ +.I -d messid +] +.SH DESCRIPTION +.B qmail-qmaint +inspects +.B s/qmail's +queue and validates its consistancy. +In +.I -i +interactive mode, individual fixes +can be commanded. +Queue maintanence also allows to remove +particular messages from the queue referencing their +.I messid +as given by +.B qmail-qread +(without the leading pound sign '#') by means of +.IR -d\ messid . +Here, only pre-processed and bounce messages are taken +into consideration. + +.B qmail-qmaint +must be run either as root or with user id +.I qmails +and group id +.IR sqmail . +.SH "WARNING" +It is strongly advised to use +.B qmail-qmaint +only in case +.B qmail-send +was shut down before. Queue inspection on a `sane` queue +is however none-destructive. +.SH "EXIT CODES" +.B qmail-qmaint +unlike +.B qmail-queue +prints diagnostics messages. +It exits +0 if +it has successfully inspected the queue +or the message has been deleted. +It may exit +99 in case of a warning, or +100 if an operation can not be completed, or +110 if a directory can not be accessed. +.SH "SEE ALSO" +qmail-qstat(8), +qmail-qread(8), +qmail-send(8), +qmail-queue(9) +.SH "CREDITS" +.B qmail-qmaint +is based on the program 'queue-fix' +written be Eric Huss. diff --git a/sqmail-4.3.07/man/qmail-qmqpc.8 b/sqmail-4.3.07/man/qmail-qmqpc.8 new file mode 100644 index 0000000..5a04e38 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-qmqpc.8 @@ -0,0 +1,37 @@ +.TH s/qmail: qmail-qmqpc 8 +.SH NAME +qmail-qmqpc \- queue a mail message via QMQP +.SH SYNOPSIS +.B qmail-qmqpc +.SH DESCRIPTION +.B qmail-qmqpc +offers the same interface as +.BR qmail-queue , +but it gives the message to a QMQP server +instead of storing it locally. + +In a +.B mini-qmail +installation, +.B qmail-queue +is replaced with a symbolic link to +.BR qmail-qmqpc . +.SH "CONTROL FILES" +.TP 5 +.I qmqpservers +IP addresses of QMQP servers, one address per line and eventually +include the name of the interface to bind to for IPv6 LLUs: + +.EX + 192.168.1.1 + 2001:fefe::31 + fe80::fefe:1%eth0 +.EE + +.B qmail-qmqpc +will try each address in turn until it establishes a QMQP connection +or runs out of addresses. +.SH "SEE ALSO" +qmail-control(5), +qmail-queue(8), +qmail-qmqpd(8) diff --git a/sqmail-4.3.07/man/qmail-qmqpd.8 b/sqmail-4.3.07/man/qmail-qmqpd.8 new file mode 100644 index 0000000..1913a7e --- /dev/null +++ b/sqmail-4.3.07/man/qmail-qmqpd.8 @@ -0,0 +1,25 @@ +.TH s/qmail: qmail-qmqpd 8 +.SH NAME +qmail-qmqpd \- receive mail via QMQP +.SH SYNOPSIS +.B qmail-qmqpd +.SH DESCRIPTION +.B qmail-qmqpd +receives mail messages via the Quick Mail Queueing Protocol (QMQP) +and invokes +.B qmail-queue +to deposit them into the outgoing queue. +.B qmail-qmqpd +must be supplied several environment variables; +see +.BR tcp-environ(5) . + +.B qmail-qmqpd +will relay messages to any destination. +It should be invoked only for connections from preauthorized users. +.SH "SEE ALSO" +tcpserver(1), +sslserver(1), +tcp-environ(5), +qmail-qmqpc(8), +qmail-queue(8) diff --git a/sqmail-4.3.07/man/qmail-qmtpd.8 b/sqmail-4.3.07/man/qmail-qmtpd.8 new file mode 100644 index 0000000..545ea8c --- /dev/null +++ b/sqmail-4.3.07/man/qmail-qmtpd.8 @@ -0,0 +1,36 @@ +.TH s/qmail: qmail-qmtpd 8 +.SH NAME +qmail-qmtpd \- receive mail via QMTP/QMTPS +.SH SYNOPSIS +.B qmail-qmtpd +.SH DESCRIPTION +.B qmail-qmtpd +receives mail messages via the Quick Mail Transfer Protocol (QMTP) +or the TLS secured QMTP (QMTPS) version +and invokes +.B qmail-queue +to deposit them into the outgoing queue. +.B qmail-qmtpd +must be supplied several environment variables; +see +.BR tcp-environ(5) . +In case a valid X.509 client certificate is recognized, +QMTPS enables +.I relaying +of mail messages. + +.B qmail-qmtpd +supports the +.IR rcpthosts , +.IR morercpthosts , +.BR RELAYCLIENT , +.IR databytes , +and +.B DATABYTES +mechanisms described in +.BR qmail-smtpd(8) . +.SH "SEE ALSO" +tcp-environ(5), +qmail-control(5), +qmail-queue(8), +qmail-smtpd(8) diff --git a/sqmail-4.3.07/man/qmail-qread.8 b/sqmail-4.3.07/man/qmail-qread.8 new file mode 100644 index 0000000..5774f6b --- /dev/null +++ b/sqmail-4.3.07/man/qmail-qread.8 @@ -0,0 +1,25 @@ +.TH s/qmail: qmail-qread 8 +.SH NAME +qmail-qread \- list outgoing messages and recipients +.SH SYNOPSIS +.B qmail-qread +.SH DESCRIPTION +.B qmail-qread +scans the outgoing queue of messages. +For each message it prints various human-readable information, +including the date the message entered the queue, +the number of bytes in the message, +the message sender, +and all the recipients still under consideration. + +.B qmail-qread +must be run either as +.B root +or with user id +.B qmails +and group id +.BR sqmail . +.SH "SEE ALSO" +qmail-qstat(8), +qmail-qmaint(8), +qmail-send(8) diff --git a/sqmail-4.3.07/man/qmail-qstat.8 b/sqmail-4.3.07/man/qmail-qstat.8 new file mode 100644 index 0000000..e21068a --- /dev/null +++ b/sqmail-4.3.07/man/qmail-qstat.8 @@ -0,0 +1,18 @@ +.TH s/qmail: qmail-qstat 8 +.SH NAME +qmail-qstat \- summarize status of mail queue +.SH SYNOPSIS +.B qmail-qstat +.SH DESCRIPTION +.B qmail-qstat +gives a human-readable breakdown +of the number of messages at various spots in the mail queue. + +.B qmail-qstat +must be run either as +.B root +or with group id +.BR sqmail . +.SH "SEE ALSO" +qmail-qread(8), +qmail-send(8) diff --git a/sqmail-4.3.07/man/qmail-queue.8 b/sqmail-4.3.07/man/qmail-queue.8 new file mode 100644 index 0000000..b025c95 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-queue.8 @@ -0,0 +1,199 @@ +.TH s/qmail: qmail-queue 8 +.SH NAME +qmail-queue \- queue a mail message for delivery +.SH SYNOPSIS +.B qmail-queue +.SH DESCRIPTION +.B qmail-queue +reads a mail message from descriptor 0. +It then reads envelope information from descriptor 1. +It places the message into the outgoing queue +for future delivery by +.BR qmail-send . + +The envelope information is +an envelope sender address +followed by a list of envelope recipient addresses. +The sender address is preceded by the letter F +and terminated by a 0 byte. +Each recipient address is preceded by the letter T +and terminated by a 0 byte. +The list of recipient addresses is terminated by an extra 0 byte. +If +.B qmail-queue +sees end-of-file before the extra 0 byte, +it aborts without placing the message into the queue. + +Every envelope recipient address +should contain a username, +an @ sign, +and a fully qualified domain name. + +.B qmail-queue +always adds a +.B Received +line to the top of the message. +Other than this, +.B qmail-queue +does not inspect the message +and does not enforce any restrictions on its contents. +However, the recipients probably expect to see a proper header, +as described in +.BR qmail-header(5) . + +Programs included with qmail which invoke +.B qmail-queue +will invoke the contents of +.B QMAILQUEUE +instead, if that environment variable is set. +.SH "FILESYSTEM RESTRICTIONS" +.B qmail-queue +imposes two constraints on the queue structure: +each +.B mess +subdirectory must be in the same filesystem as the +.B pid +directory; and each +.B todo +subdirectory must be in the same filesystem as the +.B intd +directory. +.SH "EXIT CODES" +.B qmail-queue +does not print diagnostics. +It exits +0 if +it has successfully queued the message. +It exits between 1 and 99 if +it has failed to queue the message. + +All +.B qmail-queue +error codes between 11 and 40 +indicate permanent errors: +.TP 5 +.B 11 +Address too long. +.TP +.B 31 +Mail server permanently refuses to send the message to any recipients. +(Not used by +.BR qmail-queue), +.TP +.B 32 +Mail server does not accept the message. +(The message includes an identified virus.) +.TP +.B 33 +Mail server does not accept the message. +(The message is identified as spam.) +.TP +.B 34 +Mail server does not accept the message. +(The message carries an invalid MIME attachment.) +.PP +All other +.B qmail-queue +error codes indicate temporary errors: +.TP 5 +.B 51 +Out of memory. +.TP +.B 52 +Timeout. +.TP +.B 53 +Write error; e.g., disk full. +.TP +.B 54 +Unable to read the message or envelope. +.TP +.B 55 +Unable to read a configuration file. +The virus scanner called via the +.BR QHPSI +returned with return code other then +0 or QHPSIRC. +.TP +.B 56 +Problem making a network connection from this host. +(Not used by +.BR qmail-queue .) +.TP +.B 61 +Problem with the qmail home directory. +.TP +.B 62 +Problem with the queue directory. +.TP +.B 63 +Problem with queue/pid. +.TP +.B 64 +Problem with queue/mess. +.TP +.B 65 +Problem with queue/intd. +.TP +.B 66 +Problem with queue/todo. +.TP +.B 71 +Mail server temporarily refuses to send the message to any recipients. +(Not used by +.BR qmail-queue .) +.TP +.B 72 +Connection to mail server timed out. +(Not used by +.BR qmail-queue .) +.TP +.B 73 +Connection to mail server rejected. +(Not used by +.BR qmail-queue .) +.TP +.B 74 +Connection to mail server succeeded, +but communication failed. +(Not used by +.BR qmail-queue .) +.TP +.B 81 +Internal bug; e.g., segmentation fault. +.TP +.B 91 +Envelope format error. +.SH "QHPSI ARGUMENTS" +The Qmail High Performance Scanner interface QHPSI allows +.B qmail-queue +to read up to seven arguments taken from the environment to be used +as a call-interface for an external virus scanner: +.TP 5 +.B QHPSI +is set to the file name of the virus scanner, ie. QHPSI='/usr/local/bin/clamscan'. +The path can be omitted, if the virus scanner is in the default path. +.TP +.B QHPSIARG1...3 +Optional additional arguments can be included here, ie. QHPSIARG1="--verbose". +Useful to suppress output in case an email is +clean and to enable mailbox support for the virus scanner. +.TP +.B QHPSIRC +To specify the return code of the virus scanner in case of an infection; default is 1. +.TP +.B QHPSIMINSIZE +The minimal size of the message to invoke the virus scanner; default is 0. +A typical choice would be QHPSIMINSIZE=10000 (~10k). +.TP +.B QHPSIMAXSIZE +The maximal size of the message to invoke the virus scanner; default is unrestricted. +A typical choice would be QHPSIMAXSIZE=1000000 (~1M). +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-header(5), +qmail-inject(8), +qmail-qmqpc(8), +qmail-send(8), +qmail-smtpd(8) diff --git a/sqmail-4.3.07/man/qmail-recipients.9 b/sqmail-4.3.07/man/qmail-recipients.9 new file mode 100644 index 0000000..04974fe --- /dev/null +++ b/sqmail-4.3.07/man/qmail-recipients.9 @@ -0,0 +1,48 @@ +.TH s/qmail: qmail-recipients 8 +.SH NAME +qmail-recipients \- prepare recipients for qmail-smtpd +.SH SYNOPSIS +.B qmail-recipients +.SH DESCRIPTION +.B qmail-recipients +reads the addresses provided in +.BR SQMAIL/users/recipients , +converting into lowercase, and writes them into +.B SQMAIL/users/recipients.cdb +in a binary format suited +for quick access by +.BR qmail-smtpd . + +If there is a problem with +.BR users/recipients , +.B qmail-recipients +complains and leaves +.B users/recipients.cdb +alone. + +.B qmail-recipients +ensures that +.B users/recipients.cdb +is updated atomically, +so +.B qmail-smtpd +never has to wait for +.B qmail-recipients +to finish. +However, +.B qmail-recipients +makes no attempt to protect against two simultaneous updates of +.BR users/recipients.cdb . + +The binary +.B users/recipients.cdb +is compatible with +.B setforward +generated \'fastforward\' cdbs and it's +format is portable across machines. + +.SH "SEE ALSO" +qmail-smtpd(8), +qmail-vmailusr(8), +setforward(8), +fastforward(8). diff --git a/sqmail-4.3.07/man/qmail-remote.8 b/sqmail-4.3.07/man/qmail-remote.8 new file mode 100644 index 0000000..363c972 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-remote.8 @@ -0,0 +1,806 @@ +.TH s/qmail: qmail-remote 8 +.SH NAME +qmail-remote \- send mail via SMTP(S) or QMTP(S) +.SH SYNOPSIS +.B qmail-remote +.I host +.I sender +.I recip +[ +.I recip ... +] +.SH DESCRIPTION +.B qmail-remote +reads a mail message from its input +and sends the message +to one or more recipients +at a remote host. + +The remote host is +.BR qmail-remote 's +first argument, +.IR host . +.B qmail-remote +sends the message to +.IR host , +or to a mail exchanger for +.I host +listed in the Domain Name System, +via the Simple Mail Transfer Protocol (SMTP/ESMTP) +perhaps encrypted via STARTTLS/TLS +or the Quick Mail Transfer Protocol (QMTP/QMTPS). +Prior of setting up a TLS connection, +.B qmail-remote +will lookup automatically the corresponding TLSA +record in the DNS and uses this for X.509 certificate +validation. +.I host +can be either a fully-qualified domain name: + +.EX + silverton.berkeley.edu +.EE + +or an IPv4 or IPv6 address enclosed in brackets: + +.EX + [128.32.183.163] + [2001::163] +.EE + +In case the primary mail exchanger for that Domain +will issue a 5xy reply message during the connection, +.B qmail-remote +will contact all responsible mail exchangers in turn +in order to deliver the message anyway. + +The envelope recipient addresses are listed as +.I recip +arguments to +.BR qmail-remote . +The envelope sender address is listed as +.I sender\fP. + +In case the remote host issues the EHLO SIZE extension, +.I qmail-remote +will handover the size of the message (in byte) +prior of transmission and respects the remote host's reply code. + +Note that +.B qmail-remote +does not take options +and does not follow the +.B getopt +standard. +.SH "TRANSPARENCY" +End-of-file in SMTP is encoded as dot CR LF. +A dot at the beginning of a line is encoded as dot dot. +It is impossible in SMTP to send a message that does not end with a newline. +.B qmail-remote +respects SMTPUTF8 and EAI addresses +and converts the UNIX newline convention into the +SMTP newline convention by inserting CR before each LF. + +.SH "RESULTS" +.B qmail-remote +prints some number of +.I recipient reports\fP, +followed by a +.I message report\fR. +Each report is terminated by a 0 byte. +Each report begins with a single letter: +.TP 5 +r +Recipient report: acceptance. +.TP 5 +h +Recipient report: permanent rejection. +.TP 5 +s +Recipient report: temporary rejection. +.TP 5 +K +Message report: success. +.I host +has taken responsibility for delivering the message to each +acceptable recipient. +.TP 5 +Z +Message report: greylisted or temporary failure. +.TP 5 +D +Message report: permanent failure. +.PP +After this letter comes a human-readable description of +what happened. + +.B qmail-remote +may use SMTP Authenticaton to connect to remote hosts. +The following reports are provided: +.TP 5 +K +no supported AUTH s/qmail: method found, continuing without authentication. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (AUTH s/qmail: PLAIN). +.TP 5 +Z +Connected to +.I host +but unable to base64encode (plain). +.TP 5 +Z +Connected to +.I host +but authentication was rejected (plain). +.TP 5 +Z +Connected to +.I host +but authentication was rejected (AUTH s/qmail: LOGIN). +.TP 5 +Z +Connected to +.I host +but unable to base64encode user. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (username). +.TP 5 +Z +Connected to +.I host +but unable to base64encode pass. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (AUTH s/qmail: CRAM-MD5). +.TP 5 +Z +Connected to +.I host +but unable to base64decode challenge. +.TP 5 +Z +Connected to +.I host +but unable to base64encode username+digest. +.TP 5 +Z +Connected to +.I host +but authentication was rejected (username+digest). +.PP +The recipient reports will always be printed in the same order as +.BR qmail-remote 's +.I recip +arguments. +Note that in failure cases there may be fewer +recipient reports +than +.I recip +arguments. +.PP +In case a CNAME can not be resovled +.B qmail-remote +issues the following message: +.TP 5 +Z +CNAME lookup failed temporarily for: +.IR host . +.PP +If a SMTP connection is bound to a none-existing IP address +.B qmail-remote +will complain with the message: +.TP 5 +Z +System resources temporarily unavailable. +.TP 5 +Z +System can't bind to local ip address: +.IR ip . +.PP +In case a QMTP connection can not be established +.B qmail-remote +will issue the error message: +.TP 5 +Z +recipient +.I host +did not talk proper QMTP. +.PP +On demand +.B qmail-remote +supports TLS/STARTTLS and will log the following notifications: +.TP 5 +K +TLS transmitted message accepted +.TP 5 +K +TLS (verfied CA) transmitted message accepted +.TP 5 +K +TLS (verified CA+DN*) transmitted message accepted +.TP 5 +K +TLS (verified CA+DN) transmitted message accepted +.TP 5 +K +TLS (CERT pinning) transmitted message accepted +.TP 5 +K +TLS (TLSA validated) transmitted message accepted +.PP +.B qmail-remote +needs to read some X.509 certificates and key files +prior of setting up a TLS connection. Failures are indicated as: +.TP 5 +Z +Can't load X.509 certificate: +.IR certfile . +.TP 5 +Z +Can't load X.509 private key: +.IR keyfile . +.TP 5 +Z +Keyfile does not match X.509 certificate: +.IR password . +.TP 5 +Z +I wasn't able to process the TLS ciphers: +.IR ciphers . +.TP 5 +Z +I wasn't able to setup CAFILE: +.I cafile +or CADIR: +.I cadir +for TLS. +.PP +Connection problems for TLS are not uncommon. +Here, +.I host +is the domain or host to connect with and +.I remotehost +is the corresponding MX. +.B +qmail-remote +provides the following diagnostic messages: +.TP 5 +Z +I wasn't able to create TLS context for: +.I host +at +.IR remotehost . +.TP 5 +Z +I wasn't able to establish a TLS connection with: +.I remotehost +for +.IR host . +.TP 5 +Z +TLS connection/protocol error with host: +.I remotehost +for +.IR host . +.TP 5 +Z +I wasn't able to negotiate a StartTLS connection with: +.I remotehost +for +.IR host . +.PP +For each MX to reach via TLS, +.B qmail-remote +performs an automatic TLSA lookup comparing the received +X.509 fingerprints with the issued cert during the TLS handshake. +X.509 certificate checks can also been performed. Failures here +are given as: +.TP 5 +Z +Unable to obtain X.500 certificate from: +.I remotehost +for +.IR host . +.TP 5 +Z +Unable to validate X.500 certificate Subject for: +.I host +at +.IR remotehost . +.TP 5 +Z +TLSA X.509 cert required but missing from: +.I remotehost +for +.IR host . +.TP 5 +Z +Received X.500 certificate from: +.I remotehost +for +.I host +does not match provided fingerprint: +.IR hashvalue . +.TP 5 +Z +Received X.500 certificate from: +.I remotehost +for +.I host +posses an unknown digest method. +.PP +.SH "CONTROL FILES" +.TP 5 +.I authsenders +Authenticated sender. +For each +.I sender +included in +.IR authsenders : +.I sender\fB:\fIrelay\fB;\fI[s]port\fB|\fIuser\fB|\fIpassword +.B qmail-remote +will try SMTP Authentication +of type CRAM-MD5, LOGIN, or PLAIN +with the provided user name +.I user +and password +.I password +(the authentication information) +and eventually relay the +mail through +.I relay +on port +.IR port . +If +.I port +is given als or prepended with +.I s +like +.I s587 +\'implicit TLS\' is used omitting StartTLS upon connection. +The use of +.I relay +and +.I port +follows the same rules as for +.IR smtproutes +Note: In case +.I sender +is empty, +.B qmail-remote +will try to deliver each outgoing mail +SMTP authenticated. If the authentication +information is missing, the mail is +delivered none-authenticated. +.I authsenders +can be constructed as follows: + +.EX + @example.com:relay.example.com|user|passwd + info@example.com:relay.example.com;26|infouser|infopasswd + :mailrelay.example.com|e=mc2|testpass +.EE +.TP 5 +.I domaincerts +In case +.B qmail-remote +needs to present a client certificate to the server +(for authentication purposes) the PEM encoded +X.509 certificate can be provided per sending domain: +.IR domain\fB:\fIcertificate\fB|\fIkeyfile\fB|\fIpassword . +If +.I domain +equals '*' this +.I certificate +is used as default. +The file +.I certificate +may include the private key, thus +.I keyfile +can be omitted. Additionally, the private key can be protected with a +.IR password . + +.TP 5 +.I domainips +IP addresses to be used for outgoing connections. +Each line has the form +.IR domain\fB:\fIlocalip(%ifname)\fB|\fIhelohost , +without any extra spaces. +If +.I domain +matches the domain part in +.IR sender , +.B qmail-remote +will bind to +.IR localip +when connecting to +.IR host . +LLU IPv6 addresses need to be appended with the binding +.IR ifname +following +.IR localip +with a '%'. +If it matches, it will set the provided HELO string as greeting; +otherwise, it will use the default. +.I domain +can be the wildcard +.I * +in which case +.B qmail-remote +binds to the provided address for any sender domain name. +.TP 5 +.I helohost +Current host name, +for use solely in saying ehlo/hello to the remote SMTP server. +Default: +.IR me , +if that is supplied; +otherwise +.B qmail-remote +refuses to run. +.TP 5 +.I qmtproutes +Additional QMTP routes which have precedence over +.IR smtproutes . +QMTP routes should obey the form +.IR domain\fB:\fIrelay\fB;\fIport , +without any extra spaces. +.I qmtproutes +follows the same syntax as +.IR smtproutes . +By default, +.B qmail-remote +connects to QMTP service port 209. However +you can chose a dedicated high-port for QMTP communication +as defined in +.IR qmtproutes . +In case the QMTP port is chosen to be +.I 6209 +the TLS secured QMTPS protocol will be used, +irrespectively of the settings in +.IR tlsdestinations . +.TP 5 +.I smtproutes +Artificial SMTP routes. +Each route has the well-known form +.I domain\fB:\fIrelay +or the enhanced syntax +.I domain\fB:\fIrelay;\fI[s]port\fB|\fIuser\fB|\fIpassword|localip +without any extra spaces. +If +.I domain +matches +.IR host , +.B qmail-remote +will connect to +.IR relay , +as if +.I host +had +.I relay +as its only MX. +(It will also avoid doing any CNAME lookups on +.IR recip .) +.I host +may include a semi-colon and a port number to use instead of the +normal SMTP port, 25. +If +.I port +is given as or prepended with +.I s +\'implicit TLS\' is assumed. +In case, a userid and password is +present, +.B qmail-remote +will try a SMTP authenticated session: + +.EX + inside.af.mil:firewall.af.mil;26 + :submission.myrelay.com;s587|myuserid|mypasswd +.EE + +However, +.I authsenders +routes have precedence. + +.I relay +may be empty; +this tells +.B qmail-remote +to look up MX records as usual. +.I smtproutes +may include wildcards: + +.EX + .af.mil: + :heaven.af.mil +.EE + +Here +any address ending with +.B .af.mil +(but not +.B af.mil +itself) +is routed by its MX records; +any other address is artificially routed to +.BR heaven.af.mil . + +The outgoing IP address used by +.B qmail-remote +can be specified: + +.EX + :bouncehost.org||10.1.1.0 + :partnermx.net;42||2001::fefe +.EE + +Note: +.I localip +can be private IP address subject of NAT'ing. + +Additionally, +.I smtproutes +allows to forward bounces (with a 'Nullsender' MAIL FROM: <>) +literally expressed as '!@' +to a particular bounce host: + +.EX + !@:bouncehost.af.mil;27 +.EE + +The +.B qmail +system does not protect you if you create an artificial +mail loop between machines. +However, +you are always safe using +.I smtproutes +if you do not accept mail from the network. +.TP 5 +.I timeoutconnect +Number of seconds +.B qmail-remote +will wait for the remote SMTP server to accept a connection. +Default: 60. +The kernel normally imposes a 75-second upper limit. +.TP 5 +.I timeoutremote +Number of seconds +.B qmail-remote +will wait for each response from the remote SMTP server. +Default: 1200. +.TP 5 +.I tlsdestinations +If present, this file advices +.B qmail-remote +to use TLS (optionally or mandatory) encryption for specific destination domains +as provided by the forward-path and to validate/verify +the server certificate perhaps for a particular sender's domain: +.I destination:cafile|ciphers|verifydepth;[s]port|domain +or +.IR destination:=fingerprint|ciphers|verifydepth;[s]port|domain . +Unless explicitely configured, +.B qmail-remote +accepts any or no certificate provided by the server (opportunistic encryption) +using the following (single character) rules: + +.EX + (0) *: # Enable TLS but fallback to NOTLS (default); + server authentication is optional, given further settings +.EE + +Special settings: + +.EX + (1) ?: # fallthru to no TLS in case of TLS protocol errors (exceptional) + (2) -: # allow anonymous connections + (3) /: # disable TLSA lookup and verification +.EE + +Double character rules instruct +.B qmail-remote +to require a STARTTLS or SMTPS connection (mandatory TLS): + +.EX + (4) -*: # at least anonymous connections + (5) +*: # require and validate X.509 certs + (6) ~*: # cert + validate SAN/DN, however accept wildcard certs and partial matching + (7) =*: # cert + validate SAN/DN against FQDN + (8) /*: # don't do TSLA lookup and X.509 matching +.EE + +Additionally, +.B qmail-remote +can be told to use per-domain connection settings: + +.EX + (9) example.com: + (10) securityfirst.com:/etc/ssl/cafile|!SSLv2:HIGH + (11) remote.com:/etc/ssl/certdir/||3;465 + (12) mx.partner.com:/etc/ssl/partnerca||2|mydomain.net + (13) =mx.myfriend.com:/etc/ssl/cacert||4 + (14) ~wildneighbor.net: + (15) -adhonlydomain.com:||aNULL:!kRSA + (16) %peer.partner.com:=E44194C56EF..... + (17) !nosslhost.example.com: + (18) hiddenpartner.org:;35 + (19) ?tlsold.net: + (20) /nodane.org: +.EE + +The ninth line requires from +.B qmail-remote +to demand a STARTTLS connection for any destination +address targeting domain +.IR example.com . + +The tenth line accepts STARTTLS connections +for +.I securityfirst.com +only, if the X.509 certificate can be verified against +the CA cert as provided via +.I /etc/ssl/cafile +and with the acceptable ciphers +.IR SSLv2:HIGH . + +Line number eleven tells +.B qmail-remote +to use a +.I SMTPS +connection on port +.I 465 +to any host at +.I remote.com +and accept this host only, if the peer's cert +can be validated against the CA certs available +in +.I /etc/ssl/certdir/ +and does not exceed a verification depth of +.IR 3 . + +Line twelve shows an example, how +.I tlsdestinations +can be bound exclusively to a sender domain. In the shown case, +only if +.I mx.mydomain.net +is used as sender domain, +a connection for the destination address +.I mx.partner.com +is mandatory secured by TLS with a CA cert available as +.I /etc/ssl/partnerca +with a verification depth of +.IR 2 . + +Furthermore, the sample on line thirteen demonstrates the case where +.B qmail-remote +sees a destination address concatinated with +.IR = . +Now it will only accept the certificate, +if the X.509's DN can be validated +against the FQDN of the server (by means of a DNS lookup) +and it verifies against the +.IR cacert +CA certificate and does not exceed a verification depth of +.IR 1 . + +In case a certain +.I destination +may use 'wildcard' domain names in the SAN/DN, +.B qmail-remote +can cope with this (line fourteeen) +prepending the destination with a '~': +.IR ~wildneighor.net . +This mechanism also supports partial matching +of SAN/DN and domain name. + +In the same sense (line fiveteen), +.B qmail-remote +may accept TLS connections based on Anonymous DH (ADH) +- where the server does not provide a cert for authentication - +once the domain name is prepended with a +.I - +as key encryption cipher and discards +.I !RSA +for authentication if told so. + +Certificate pinning for a particular +.I %host +indicated by the leading character '%' is shown on line sixteen. +Instead of the CA file, now the +.I =fingerprint +of the peer host certificate needs to be provided. +The X.509 fingerprint +should prepended with an equal sign ('=') and to +be stripped from additional colons (':'). The fingerprint +string is evaluated case-insensitive. +.BR qmail-remote 's +certificate pinning supports SHA1, SHA224, SHA256, and SHA512 +digests, determined by the length of the fingerprint given. + +Note, that in this case, no TLSA validation is performed; +it is thus a 'silent' verification'. +.B qmail-remote +can be instructed to omit the STARTTLS command for the recipient address +.I nosslhost.example.com +as indicated with a leading +.I ! +as shown on line seventeen. This behavior can be relaxed (line nineteen) using +.I ? +followed by a colon, a host, or domain name. Now +.B qmail-remote +will initally try a TLS connection by however is alllowed to switch back +to none-encryption mode, in case this is not possible due +protocol reasons. + +.B qmail-remote +allows an \'implicit TLS\' connection on any port, if +.I port +is prended with an +.I s +even without providing the port. + +In case, no particular ciphers or CA certs are +required, a colon/semi-colon ':;' can be used as shortcut (line eighteen). +Generally, any port can be provided after the semi-colon. +If however, +.I port +equals +.IR 465 , +SMTPS will be used instead of STARTTLS and if +.I port +equals +.IR 6209 , +QMTPS is the chosen transport protocol. +The settings here overrule previous instructions. + +Finally, TLSA lookups can be disabled, prepending a +domain name with +.I / +for the target domain as shown on line twenty. + +Note that 'destination' is subject of the +forwarding rules as provided by +.IR authsenders , +.IR qmtproutes , +and +.IR smtproutes . +.SU "ADDENDUM" +.B qmail-remote +needs to read the message from a file in order +to announce the +.I SIZE +in the SMTP dialogue. +However, if called through a pipe, it will not +provide this information to the receiving MTA. +More severe, a delivery over +.I QMTP(S) +will fail. +.SH "RETURN CODES" +.B qmail-remote +always exits +.I 0 +for SMTP(S) delivery. +In case of QMTP(S) +.I 1 +is returned in case a buffer feed fails and +.I 0 +otherwise. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-control(5), +qmail-send(8), +qmail-smtpd(8), +qmail-smtpam(8), +qmail-dksign(8), +qmail-dkim(8), +qmail-tcpto(8) diff --git a/sqmail-4.3.07/man/qmail-rspawn.8 b/sqmail-4.3.07/man/qmail-rspawn.8 new file mode 100644 index 0000000..71a43d7 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-rspawn.8 @@ -0,0 +1,21 @@ +.TH s/qmail: qmail-rspawn 8 +.SH NAME +qmail-rspawn \- schedule remote deliveries +.SH SYNOPSIS +.B qmail-rspawn +.SH DESCRIPTION +.B qmail-rspawn +reads a series of remote delivery commands from descriptor 0, +invokes +.B qmail-remote +to perform the deliveries, +and prints the results to descriptor 1. + +.B qmail-rspawn +invokes +.B qmail-remote +asynchronously, +so the results may not be in the same order as the commands. +.SH "SEE ALSO" +qmail-send(8), +qmail-remote(8) diff --git a/sqmail-4.3.07/man/qmail-send.9 b/sqmail-4.3.07/man/qmail-send.9 new file mode 100644 index 0000000..334bfa9 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-send.9 @@ -0,0 +1,265 @@ +.TH s/qmail: qmail-send 8 +.SH NAME +qmail-send \- deliver mail messages from the queue +.SH SYNOPSIS +.B qmail-send +.SH DESCRIPTION +.B qmail-send +handles messages placed into the outgoing queue by +.BR qmail-queue . +It uses +.B qmail-lspawn +to deliver messages to local recipients and +.B qmail-rspawn +to deliver messages to remote recipients. +If a message is temporarily undeliverable to one or more addresses, +.B qmail-send +leaves it in the queue and tries the addresses again later. + +.B qmail-send +prints a readable record of its activities to descriptor 0. +It writes commands to +.BR qmail-lspawn , +.BR qmail-rspawn , +and +.B qmail-clean +on descriptors 1, 3, and 5, +and reads responses from descriptors 2, 4, and 6. +Communication with +.B qmail-todo +is based on decriptors 7 and 8. +.B qmail-send +is responsible for avoiding deadlock. + +If +.B qmail-send +receives a TERM signal, +it will exit cleanly, after waiting +(possibly more than a minute) +for current delivery attempts to finish. + +If +.B qmail-send +receives an ALRM signal, +it will reschedule every message in the queue for immediate delivery. + +.SH "CONTROL FILES" +.B WARNING: +.B qmail-send +reads its control files only when it starts. +If you change the control files, +you must stop and restart +.BR qmail-send . +Exception: +If +.B qmail-send +receives a HUP signal, +it will reread +.IR locals , +.IR virtualdomains , +as well as +.IR concurrencylocal , +.IR concurrencyremote , +and in addition +.IR queuelifetime . +.TP 5 +.I bouncefrom +Bounce username. +Default: +.BR MAILER-DAEMON . +.TP 5 +.I bouncehost +Bounce host. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR bouncehost , +which is probably not what you want. +If a message is permanently undeliverable, +.B qmail-send +sends a +.B single-bounce +notice back to the message's envelope sender. +The notice is +.B From: \fIbouncefrom\fB@\fIbouncehost\fR, +although its envelope sender is empty. +.TP 5 +.I bouncemaxbytes +Maximum size (in bytes) of bounce messages. +Bounce messages exceeding this limit will be truncated. +Default is 0; which means no limit. +.TP 5 +.I concurrencylocal +Maximum number of simultaneous local delivery attempts. +Default: 10. +If 0, local deliveries will be put on hold. +.I concurrencylocal +is limited at compile time to +SPAWN. +.TP 5 +.I concurrencyremote +Maximum number of simultaneous remote delivery attempts. +Default: 20. +If 0, remote deliveries will be put on hold. +.I concurrencyremote +is limited at compile time to +SPAWN. +.TP 5 +.I doublebouncehost +Double-bounce host. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR doublebouncehost , +which is probably not what you want. +.TP 5 +.I doublebounceto +User to receive double-bounces. +Default: +.BR postmaster . +If a single-bounce notice is permanently undeliverable, +.B qmail-send +sends a +.B double-bounce +notice to +.IR doublebounceto\fB@\fIdoublebouncehost . +(If that bounces, +.B qmail-send +gives up.) +As a special case, if the first line of +.IR doublebounceto +contains a '@' or an empty line +.B qmail-send +will discard all double-bounces. +.TP 5 +.I envnoathost +Presumed domain name for addresses without @ signs. +Default: +.IR me , +if that is supplied; +otherwise the literal name +.BR envnoathost , +which is probably not what you want. +If +.B qmail-send +sees an envelope recipient address without an @ sign, +it appends +.B @\fIenvnoathost\fR. +.TP 5 +.I locals +List of domain names that the current host +receives mail for, +one per line. +Default: +.IR me , +if that is supplied; +otherwise +.B qmail-send +refuses to run. +An address +.I user@domain +is considered local if +.I domain +is listed in +.IR locals . +.TP 5 +.I percenthack +List of domain names where the percent hack is applied. +If +.I domain +is listed in +.IR percenthack , +any address of the form +.I user%fqdn@domain +is rewritten as +.IR user@fqdn . +.I user +may contain %, +so the percent hack may be applied repeatedly. +.B qmail-send +handles +.I percenthack +before +.IR locals . +.TP 5 +.I queuelifetime +Number of seconds +a message can stay in the queue. +Default: 604800 (one week). +After this time expires, +.B qmail-send +will try the message once more, +but it will treat any temporary delivery failures as +permanent failures. +.TP 5 +.I virtualdomains +List of virtual users or domains, one per line. +A virtual user has the form +.IR user\fB@\fIdomain\fB:\fIprepend , +without any extra spaces. +When +.B qmail-send +sees the recipient address +.IR user\fB@\fIdomain , +it converts it to +.I prepend\fB-\fIuser\fB@\fIdomain +and treats it as local. + +A virtual domain has the form +.IR domain\fB:\fIprepend . +It applies to any recipient address at +.IR domain . +For example, if + +.EX + nowhere.mil:joeBREAKfoo +.EE + +is in +.IR virtualdomains , +and a message arrives for +.BR info@nowhere.mil , +.B qmail-send +will rewrite the recipient address as +.B joeBREAKfoo-info@nowhere.mil +and deliver the message locally. + +.I virtualdomains +may contain wildcards: + +.EX + .fax:uucpBREAKfax + :aliasBREAKcatchall + .nowhere.mil:joeBREAKfoo-host +.EE + +.I virtualdomains +may also contain exceptions: +an empty +.I prepend +means that +.I domain +is not a virtual domain. + +.B qmail-send +handles +.I virtualdomains +after +.IR locals : +if a domain is listed in +.IR locals , +.I virtualdomains +does not apply. +.SH "SEE ALSO" +nice(1), +addresses(5), +envelopes(5), +qmail-control(5), +qmail-log(5), +qmail-todo(8), +qmail-queue(8), +qmail-clean(8), +qmail-lspawn(8), +qmail-rspawn(8) diff --git a/sqmail-4.3.07/man/qmail-showctl.8 b/sqmail-4.3.07/man/qmail-showctl.8 new file mode 100644 index 0000000..ddd90d7 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-showctl.8 @@ -0,0 +1,12 @@ +.TH s/qmail: qmail-showctl 8 +.SH NAME +qmail-showctl \- analyze the qmail configuration files +.SH SYNOPSIS +.B qmail-showctl +.SH DESCRIPTION +.B qmail-showctl +explains the current +.B s/qmail +configuration. +.SH "SEE ALSO" +qmail-control(8) diff --git a/sqmail-4.3.07/man/qmail-smtpam.8 b/sqmail-4.3.07/man/qmail-smtpam.8 new file mode 100644 index 0000000..9fe8e90 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-smtpam.8 @@ -0,0 +1,110 @@ +.TH s/qmail: qmail-smtpam 8 +.SH NAME +qmail-smtpam \- SMTP client PAM +.SH SYNOPSIS +.B qmail-smtpam +.I host +.I [s]port +.SH DESCRIPTION +.B qmail-smtpam +reads an email address from FD 3 +and tries to verify this +connecting to the remote +.IR host +on +.IR port . +If +.I port +starts is +.I s +\'implicit TLS\' ist used on that port. +In a standard SMTP dialog, +.B qmail-smtpam +supplies the HELO greeting, +a MAIL FROM: <> address, and +the purported RCPT TO: <address>. +.SH "CONTROL FILES" +.TP 5 +.I domainips +IP addresses to be used on outgoing connections. +Each line has the form +.IR domain\fB:\fIlocalip(%ifname)\fB|\fIhelohost , +without any extra spaces. +If +.I domain +matches the domain part in +.IR sender , +.B qmail-smtpam +will bind to +.IR localip +when connecting to +.IR host . +LLU IPv6 addresses need to be appended with the binding +.IR ifname +following +.IR localip +with a '%'. +If it matches, it will set the provided HELO string as greeting; +otherwise, it will use the default. +.TP 5 +.I helohost +Current host name, +for use solely in saying hello to the remote SMTP server. +Default: +.IR me , +if that is supplied; +otherwise +.B qmail-smtpam +refuses to run. +.TP 5 +.I timeoutconnect +Number of seconds +.B qmail-smtpam +will wait for the remote SMTP server to accept a connection. +Default: 60. +The kernel normally imposes a 75-second upper limit. +.TP 5 +.I timeoutremote +Number of seconds +.B qmail-smtpam +will wait for each response from the remote SMTP server. +Default: 1200. +.TP 5 +.I tlsdestinations +If present, this file advices +.B qmail-smtpam +to use TLS encryption for specific destination domains +as provided by the forward-path and perhaps to validate/verify +the domain's server certificate: +.IR destination:cafile|verifydepth;[s]port|ciphers|domain . +If +.I port +is give as or prepended with +.I s +\'implict TLS\' is used; omitting StartTLS. +Unless explicitely configured, +.B qmail-smtpam +accepts any or no certificate provided by the server, +thus uses TLS for encryption only. +.B qmail-smtpam +uses the same certificate validation/verification +mechanism as +.B qmail-remote +except for distinguishing among the sender's domain information. +.SH "RETURN CODES" +.B qmail-smtpam +exits +.I 0 +if the remote server +replies with '250', otherwise +.IR 1 . +In case the control files can not +be read or a communication problem has +occured, it exits +.IR 111 . +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-control(5), +qmail-remote(8), +qmail-smtpd(8) diff --git a/sqmail-4.3.07/man/qmail-smtpd.8 b/sqmail-4.3.07/man/qmail-smtpd.8 new file mode 100644 index 0000000..393ec28 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-smtpd.8 @@ -0,0 +1,1018 @@ +.TH s/qmail: qmail-smtpd 8 +.SH "NAME" +qmail-smtpd \- receive mail via SMTP +.SH "SYNOPSIS" +.B qmail-smtpd +[ +.I checkprogram +.I subprogram +] +.SH "DESCRIPTION" +.B qmail-smtpd +receives mail messages via the Simple Mail Transfer Protocol (SMTP) +and invokes +.B qmail-queue +to deposit them into the outgoing queue. +.B qmail-smtpd +must be supplied with several environment variables; +see +.BR tcp-environ(5) . + +.B qmail-smtpd +is responsible for counting hops. +It rejects any message with 100 or more +.B Received +or +.B Delivered-To +header fields. + +.B qmail-smtpd +supports ESMTP and offers 8BITMIME, DATA, PIPELINING, SIZE, AUTH, STARTTLS, and SMTPUTF8 options. +.B qmail-smtpd +includes a 'Mail From:' parameter parser and obeys 'Auth', 'Size', and 'SMTPUTF8' advertisements. +.B qmail-smtpd +supports SMTPUTF8 SMTP envelope addresses and provides 8 bit clean message transmission. +.B qmail-smtpd +STARTTLS and SMTPS implementation requires the use of +.B sslserver +from ucspi-ssl. + +Authentication is facilitated in case the environment variable +SMTPAUTH is set which tells +.B qmail-smtpd +to accept LOGIN, PLAIN, and eventually CRAM-MD5 Auth types +and if additionally a PAM +.I checkprogram +is available which reads on file descriptor 3 the username, a 0 byte, +the password or CRAM-MD5 digest/response derived from the SMTP client, +another 0 byte, a CRAM-MD5 challenge (if applicable to the Auth type), +and a final 0 byte. +.I checkprogram +invokes +.I subprogram +upon successful authentication, which should return 0 to +.BR qmail-smtpd , +effectively setting the environment variables RELAYCLIENT and +TCPREMOTEINFO or TCP6REMOTEINFO +(any supplied value replaced with the authenticated username). +.B qmail-smtpd +will reject the authentication attempt if it receives a nonzero return +value from +.I checkprogram +or +.IR subprogram . + +STARTTLS support is enabled setting the environment variable UCSPITLS. +In this case, +.B qmail-smtpd +communicates with the +.B sslserver +program interface through a control socket, a reading and a writing pipe, dynamically +defined during the session start to be used for transport layer encryption. +.B qmail-smtpd +provides mutual authentication based on X.509 client certs and relaying +with additional SMTP Return-Path validation. + +.B qmail-smtpd +may employ additional DNS look-ups for the 'Mail From:' envelope sender +address and/or the HELO/EHLO greeting string from the MTA client. + +.B qmail-smtpd +implements a SPF record check for the domain part of the received +.I Mail-From:\ <return-path> +address or +the +.I HELO/EHLO +statement in case the domain information is missing. +This behavior is triggered by the environment variable +.BR SPF . + +.B qmail-smtpd +can be advised to communicate with a Greylisting server prior of acceptance, like +.BR postgrey , +submitting the connection information +.IR Mail\ From: , +.IR Rcpt\ To: , +.IR TCPREMOTEIP +and +.I TCPREMOTEHOST +given its IPv4/IPv6 address as environment variable +.IR POSTGREY +and potentially including the port number (60000 is default) +following the IP address separated by a semi-colon. +For IPv6 LLU addresses the interface name followwing a percent sign can be included: +.IR fe80::1%eth0;60000 . +A return value of +.I 10 +will advise +.B qmail-smtpd +to defer the SMTP connection providing a +.I 450\ greylisted\ (#4.3.0) +response to the connecting MTA, which can be tailored (see below). +This mechanism shall not be used for SMTP connections on the +.I Submission +port. +Setting +.I POSTGREY='-' +disables the lookup. + +.SH "TRANSPARENCY" +.B qmail-smtpd +converts the SMTP newline convention into the UNIX newline convention +by converting CR LF into LF. +Usually, it returns a temporary error and drops the connection on bare LFs. + +.B qmail-smtpd +accepts messages that contain long lines or non-ASCII characters +and thus is initially capable for SMTPUTF8 support. + +.SH "CONTROL FILES" +.TP 5 +.IR badhelo +Unacceptable HELO/EHLO greeting strings. +.B qmail-smtpd +will reject every connection attempt +if the client MTA's HELO/EHLO greeting compares with +a wildmat pattern provided in +.IR badhelo +in case the environment variable +.B HELOCHECK +is set. +.IR badhelo +checks have precedence over DNS lookups. +DNS lookups can be avoided, if the announced +HELO/EHLO greeting string is concatinated +with a trailing '!' and included in +.IR badhelo : + +.EX + localhost + localhost.localdomain + 127.0.0.1 + mygreetingstring + [192.168.1.2]! +.EE + +.TP 5 +.I badmailfrom +Unacceptable envelope sender addresses. +.B qmail-smtpd +will reject every recipient address for a message +if the envelope sender address is listed in +.IR badmailfrom . +A line in +.I badmailfrom +may be of the form +.BR @\fIhost , +meaning every address at +.IR host . +Additionally, any envelope sender address can be filtered +with a wildmat check: + +.EX + *@earthlink.net + !fred@earthlink.net + [0-9][0-9][0-9][0-9][0-9]@[0-9][0-9][0-9].com + answerme@save* + *%* + @yahoo.com- + @hotmail.com= + @mydomain.tld+ + ~yahoo.com + ?nobody@example.com +.EE + +A +.I badmailfrom +file with this contents reject all mail from Earthlink except from +fred@earthlink.net. It also rejects all mail with addresses like: +12345@123.com and answerme@savetrees.com. Further, any mail with +a sender address containing a percent sign (%) is rejected. + +This implementation recognises 'extended' addresss in +.I badmailfrom +allowing to reject mails with particluar spoofed domain addresses: + +(1) The address is appended with a '-'. +Now, if +.I TCP(6)REMOTEHOST +equals 'unknown', mails with the corresponding address are rejected +(badmailfromunknown). + +(2) The address is appended with a '='. +In case +.I TCP(6)REMEOTEHOST +is set mails, whose domain part of the envelope addresses +.B not +matching +the corresponding entry are rejected (badmailfromwellknown). + +(3) The address is appended with a '+'. +If +.I RELAYCLIENT +is not set and the sender address matches a corresponding entry +(anti-spoofing for internal addresses). + +(4) The address is enhanced with a leading '~'. +This requires a (left to right partial) matching of +.I TCP(6)REMOTEHOST +with the domain part of the envelope address. +Thus, this specific entry in +.I badmailfrom +uses +.I TCP(6)REMOTEHOST +in the first place (badmailfrommismachteddomains). + +(5) The address is enhanced with a leading '?'. +Emails with the corresponding sender address pass by all further +.I badmailfrom +tests including the +.I MFDNSCHECK +check. + +Note: The 'enhanced' addresses are not subject of the wildmat check +and are evaluated in lower-case. + +The wildmat check is done in the order: +Least significant to most significant. +Example: + +.EX + * + ! + !*@*.* + *viagra* +.EE + +.TP 5 +.I badloadertypes.cdb +Unacceptable base64 loader types in the message. +.B qmail-smtpd +will reject every message if 5 significant +characters (eg. +.BR Mi5kb) +anyware in the base64 encoded attachment is identical +to those compiled into +.IR badloadertypes.cdb . +Use +.B qmail-badloadertypes +to derive +.I badloadertypes.cdb +from +.IR badloadertypes . +In order to make the search efficient, all bad loader +types have to start with the same character (eg. 'M'). +The control file +.I badloadertypes.cdb +is evaluated if the environment variable BADLOADERTYPE +is set to the first character according to the contents of +.IR badloadertypes . +.TP +.I badmimetypes.cdb +Unacceptable base64 encoded MIME types in message. +.B qmail-smtpd +will reject every message if the first 9 significant +characters (eg. +.BR TVqQAAMAA ) +of any of it's embedded MIME types is identical with one +compiled into +.IR badmimetypes.cdb . +Use +.B qmail-badmimetypes +to derive +.I badmimetypes.cdb +from +.IR badmimetypes . +The control file +.I badmimetypes.cdb +is evaluated if the environment variable +.I BADMIMETYPE +is set. +In addition, irregular BASE64 attachments carrying whitespaces can +be rejected defining +.IR BADMIMETYPE='!' . +.TP 5 +.I badrcptto +Unacceptable envelope recipient addresses. +.B qmail-smtpd +will reject every incoming message +if the envelope recipient address is listed in +.IR badrcptto . +This control file is complementary to +.IR badmailfrom . +A line in +.I badrcptto +may be of the form +.BR @\fIhost , +meaning every address at +.IR host . +.I badrcptto +employes the same filtering logic for the envelope recipient as +.IR badmailfrom . +Effectively, +.IR badrcptto +allows a 'whitelisting' of envelope recipient addresses: + +.EX + * + !user1@mydomain.com + !user2@mydomain.com + !*@anotherdomain.com +.EE + +.IR badrcptto +allows to tag recipient addresses to be reachable from +authorized clients only (aka relayclients), prepending it +in +.IR badrcptto +with +.IR + . + +.EX + +localaddress@mydomain.com +.EE + +.TP 5 +.I databytes +Maximum number of bytes allowed in a message, +or 0 for no limit. +Default: 0. +If a message exceeds this limit, +.B qmail-smtpd +returns a permanent error code to the client; +in contrast, if +the disk is full or +.B qmail-smtpd +hits a resource limit, +.B qmail-smtpd +returns a temporary error code. + +.I databytes +counts bytes as stored on disk, not as transmitted through the network. +It does not count the +.B qmail-smtpd +Received line, the +.B qmail-queue +Received line, or the envelope. + +If the environment variable DATABYTES +is set, it overrides +.IR databytes . +.TP 5 +.I localiphost +Replacement host name for local IP addresses. +Default: +.IR me , +if that is supplied. +.B qmail-smtpd +is responsible for recognizing native IPv4/IPv6 addresses for the +current host. +When it sees a recipient address of the form +.I box@[d.d.d.d] +or +.IR box@[a:b:c:d:e:f:g:h] , +where +.I d.d.d.d +or +.IR a:b:c:d:e:f:g:h +is a local IPv4/IPv6 address, +it replaces +.I [d.d.d.d] +or +.IR [a:b:c:d:e:f:g:h] +with +.IR localiphost . +This is done before +.IR rcpthosts . +.TP 5 +.I morercpthosts +Extra allowed RCPT domains. +If +.I rcpthosts +and +.I morercpthosts +both exist, +.I morercpthosts +is effectively appended to +.IR rcpthosts . + +You must run +.B qmail-newmrh +whenever +.I morercpthosts +changes. + +Rule of thumb for large sites: +Put your 50 most commonly used domains into +.IR rcpthosts , +and the rest into +.IR morercpthosts . +.TP 5 +.I mailfromrules +Acceptable 'Mail From:' addresses for +RELAYCLIENTs are included here. Use +.B qmail-mfrules +to derive +.TP 5 +.I mailfromrules.cdb +from +.IR mailfromrules . +.TP 5 +.I rcpthosts +Allowed RCPT domains. +If +.I rcpthosts +is supplied, +.B qmail-smtpd +will reject +any envelope recipient address with a domain not listed in +.IR rcpthosts . + +Exception: +If the environment variable RELAYCLIENT is set, +.B qmail-smtpd +will ignore +.IR rcpthosts , +and will append the value of RELAYCLIENT +to each incoming recipient address. + +.I rcpthosts +may include wildcards: + +.EX + heaven.af.mil + .heaven.af.mil +.EE + +Envelope recipient addresses without @ signs are +always allowed through. +.TP 5 +.I recipients +List of external resources providing acceptable, +full-qualified envelope addresses +(\'RCPT to: <recip@domain>\') +to be used for recipient verification +during the SMTP session. + +The external sources can be either +.B fastforward +compliant cdbs including the envelope addresses, +where the path to a cdb has to be referenced +relative to Qmail's home directory, or a +.B qmail-users +build cdb available as +.IR users/assign.cdb , +or a +.B checkpassword +compatible Plugable Authentication Modules +(PAM), receiving the envelope address on FD 3 +as 'recip@domain\\0\\0\\0' and returning '0' +in a case of success and '1' in case of failure. +The use of a PAM is indicated with a delimiting '|' and +it will be called with up to five additional parameters; +while a cdb follows a ':', which can be omitted. + +The list of external sources is consulted line-by-line for each +recipient envelope address until the first positive answer, +or a final negative response is encountered. +Which external source to be queried, depends on the domain part of the +recipient envelope address specified on the left side of the +.I recipients +file, while the external resource is provided right from the delimitor. + +The addresses' domain part is evaluated in lower-case. +An exact domain match can be encompassed by means of a leading '@'. +The '*' is a generic wildcard for all domains. +Specific domains can be excluded from the lookup by means of a +leading '!'; thus all recipient addresses are accepted for this domain. +Additionally, a '!*' can be used as wildcard for all domains not encountered +before in +.I recipients +(pass-thru). + +A +.I recipients +file is always constructed like 'domain:cdb','domain|pam', +or simply 'cdb': + +.EX + !nocheck.com + mydomain.com:users/recipients.cdb + @mx.mydomain.com:= + example.com|bin/qmail-smtpam mx.example.com + *:etc/fastforward.cdb + *|PATH/ldapam ldapserver host port DN passwd + !* +.EE + +.B qmail-smtpd +will semi-automatically consult +.I users/assign.cdb +generated by +.B qmail-newu +in case the domain name is +followed by a colon and the equal sign '='. +Now, the received \'Rcpt to:\' address +is compared against each local part address +(starting with a '=') in +.IR users/assign.cdb . +However, no VERP addresses are considered, +which are indicated therein via a '+'. + +Lagacy format: + +.EX + users/recipients.cdb + etc/fastforward.cdb +.EE + +Note: Excluded domains starting with a '!' +should be placed in the beginning of the +.I recipients +file for performance reasons, while the pass-thru +statement '!*' has to be on the last line. +The recipients check is applied after the +.I rcpthosts +evaluation. + +.B qmail-recipients +may be used to construct a +.I users/recipients.cdb +from +.IR users/recipients . + +The +.B qmail-smtpd +recipients mechanism supports Qmail's address extension (VERP). +Unqualified envelope recipients are appended with \'@localhost\'. +.TP 5 +.I smtpgreeting +SMTP greeting message. +Default: +.IR me , +if that is supplied; +otherwise +.B qmail-smtpd +will refuse to run. +The first word of +.I smtpgreeting +should be the current host's name. +.TP 5 +.I spfexplain +An additional SPF explanation can be given here to provide more +specific information for the sender in case of a reject. +SPF macro expansion is possible. It will override the default one, e.g.: + +.EE +See https://example.com/spfrules.html (#5.7.1) +.EX +.TP 5 +.I spflocalrules +As 'last resort', it is possible to include SPF local rules here +(on one line), that will be applied before other SPF rules would fail. +This can be used to allow certain MX to send mails anyway. Example: + +.EE +include:spf.trusted-forwarder.org +.EX +.TP 5 +.I timeoutsmtpd +Number of seconds +.B qmail-smtpd +will wait for each new buffer of data from the remote SMTP client. +Default: 1200. + +.SH "CONDITIONAL CONTROL FILES" +The control files \fIrcpthosts\fR, \fImorecpthosts\fR, +\fIrecipients\fR, \fIbadhelo\fR +are 'conditional' control files and evaluated +only if the environment variable RELAYCLIENT is not set. +On the other hand, +\fImailfromrules.cdb\fR is only taken into account, if +RELAYCLIENT is set. +This allows +.B qmail-smtpd +to relay mail messages from local clients and to filter +mails with certain SMTP envelope conditions +originating from particular clients ('Split Horizon'). +Other conditional control files are +\fIbadloadertypes\fR, +\fIbadmimetypes\fR +which depend on the setting of the corresponding +environment variables. + +Further, the control files \fIspfexplain\fR and +\fIspflocalrules\fR are only evaluated if the +environment variable +.I SPF +is defined and greater than 0 and +.I RELAYCLIENT +is not set. + +.SH "ENVIRONMENT VARIABLES READ" +Environment variables may be defined globally in the +.B qmail-smtpd +startup script and/or individually as part of the +.BR sslserver 's +cdb database. +The environment variables may be quoted ("variable", or 'variable') and +in case of global use, have to be exported. +.B qmail-smtpd +supports the following legacy environment variables, typically +provided by +.B sslserver +or +.B tcpserver: +.IR TCP(6)REMOTEIP , +.IR TCP(6)REMOTEHOST +.IR TCP(6)REMOTEINFO +and +.IR TCPLOCALPORT +as well as +.IR RELAYCLIENT . +Additionally, +.B qmail-smtpd +may use several environment variables for different purposes. +.P +Controlling the SMTP HELO/EHLO: +.IP +.TP 5 +.I HELOCHECK='' +enables a check of the provided HELO/EHLO greeting against +the content of the control file +.IR badhelo . +In case no HELO/EHLO greeting is given, SMTP +connections can be rejected, if +.I HELOCHECK='!' +is set. Checks on the presence and the content of +the HELO/EHLO greeting string is facilitated, setting +.IR HELOCHECK='.' . +To enforce the match of the HELO/EHLO greeting with +the remote host's FQDN ( +.IR TCP(6)REMOTEHOST ), +use +.IR HELOCHECK='=' . +.TP 5 +.I HELOCHECK='A' | HELOCHECK='M' +enable DNS A/MX lookup for the HELO/EHLO greeting string. +In addition, the HELO/EHLO string is checked against +the content of +.IR badhelo . +.TP 5 +.I UTF8 +display the +.I SMTPUTF8 +greeting string. This is off by default. +.p +Since +.B qmail-smtpd +is 8 bit clean, setting of +.I UTF8 +has no real consequences except for displaying this +setting in the log as +.IR ESMTP[SA]UTF8 . +.P +Controlling the SMTP Mail From: +.IP +.TP 5 +.I LOCALMFCHECK +is used to enable a 'Mail From:' address Verification (MAV) for RELAYCLIENTs. +Thus, the domain part of the 'Mail From:' envelope sender address +has to match an entry in +.IR rcpthosts +or +.IR morercpthosts +control files, if not explicitly defined otherwise. + +If LOCALMFCHECK='!' is set, the control file +.I mailfromrules.cdb +is evaluated and the MAV is facilitated employing the environment variables +.IR TCP(6)REMOTEINFO , +.IR TCP(6)REMOTIP , +or +.I TCP(6)REMOTEHOST +as a key. +However, if LOCALMFCHECK='=' is provided, +.IR TCP(6)REMOTEINFO +(i.e. set by Auth) has to match the 'Mail From:' +envelope address (case insensitive). +Alternativley, using LOCALMFCHECK='?' the email address +embedded in the DN of a X.509 client is used and compared +against the 'Mail From:' envelope address. +Of course, this requires +.B sslserver +to request a client cert for mutual authentication. + +Note: Adding a qualifier to LOCALMFCHCEK, +the domain part of the 'Mail From:' address is compared +against the provided string. +.TP 5 +.IR MFDNSCHECK +enable DNS MX lookup for the domain part of the 'Mail From:' envelope sender address. +.TP 5 +.I SPF='0'|'1'|'2'|'3'|'4'|'5'|'6' +SPF Records will be evaluated for the current SMTP session in case +.B SPF +is defined. The value of +.B SPF +may be given between 1 and 6 to enable SPF checks. +.I 1 +selects 'annotate-only' mode, where +.B qmail-smtpd +will annotate incoming email with a +.B Received-SPF +header, but will not reject any messages. +.I 2 +will produce temporary failures on DNS lookup problems +so you can be sure always to have a meaningful Received-SPF header. +.I 3 +selects 'reject' mode, where incoming mail will be rejected +if the SPF record says 'fail'. +.I 4 +selects a more stricter rejection mode, which is like 'reject' mode, +except that incoming mail will also be rejected, when the SPF record +says 'softfail'. Further, +.I 5 +will reject when the SPF record says 'neutral', and +.I 6 +rejects, if no SPF records are available at all +(or a syntax error was encountered). +If +.B SPF +is given as +.IR 0 , +SPF checks are disabled. + +Note: Additional control files are +.I spfexplain +and +.IR spflocalrules . + +.P +Controlling the SMTP RCPT TO: +.IP +.TP 5 +.I MAXRECIPIENTS +is the number of Rcpt To:'s +.B qmail-smtpd +will accept in a SMTP session. +If MAXRECIPIENTS ist not set, any number is allowed. +.TP 5 +.IR TARPITCOUNT +is the number of Rcpt To: +.B qmail-smtpd +accepts before it starts tarpitting. +Default: 0 which means no tarpitting. +.TP 5 +.IR TARPITDELAY +tarpitdelay is the time in seconds of delay +to be introduced after each subsequent Rcpt To:. + +Smart Rejection Notes: +If +.IR TARPITCOUNT +is set and +.IR TARPITDELAY += 0 (default) +.B qmail-smtpd +will issue after recognising +.IR TARPITCOUNT +invalid Rcpt To: a Recipient failure; +thus additional Rcpt Tos will not be accepted. +If, however +.IR TARPITCOUNT +is set and +.IR TARPITDELAY += 999 +.B qmail-smtpd +will issue after +.IR TARPITCOUNT +invalid Rcpt To: a Recipient failure +.TP 5 +.I RECIPIENTS450 +tells +.b qmail-smtpd +to issue a SMTP reply '450' (temporary rejection) +instead the default '550' +in case the recipient was not listed in any +.I recipients +cdb. + +.P +Controlling the email body: +.IP +.TP 5 +.I BADLOADERTYPE='c' +tells +.B qmail-smtpd +to evaluate the control file +.I badloadertypes.cdb +with the starting string 'c'. +If +.I BADLOADERTYPE='-' +is set, the check is disabled. +In case +.I BADLOADERTYPE='+' +is defined, the check is disabled for +.IR RELAYCLIENTS . +.TP 5 +.I BADMIMETYPE +see control file +.IR badmimetypes.cdb . +In case +.I BADMIMETYPE='-' +is set; +.I badmimetypes.cdb +is not considered; thus the check is disabled. +Setting +.I BADMIMTETYPE='!' +the mime type is rejected if it includes whitespaces; +even without the control file +.IR badmimetypes.cdb . +Providing +.I BADMIMTETYPE='+' +the check is disabled if in addition +.IR RELAYCLIENTS +are recognized. + +.TP 5 +.I BASE64 +tells QHPSI to enable virus checking only if a base64 encoded +attachment was identified. +.TP 5 +.I DATABYTES +see control file +.IR databytes . +.TP 5 +.I QHPSI +is used by +.B qmail-smtpd +to supply the name of the virus scanner and it's path. +.P +Environment variables for SMTP authentication: +.IP +.TP 5 +.I SMTPAUTH +is used to enable SMTP Authentication for the +Auth types +LOGIN and PLAIN. +In case +.TP 5 +.I SMTPAUTH='+cram' +is defined, +.B qmail-smtpd +honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication. +Simply +.TP 5 +.I SMTPAUTH='cram' +restricts authentication just to CRAM-MD5. +If however +.TP 5 +.I SMTPAUTH='!' +starts with an exclamation mark, Auth is required. +You can enforce 'Submission' using this option +and binding +.B qmail-smtpd +to the SUBMISSION port \'587'\. +In particular, +.TP 5 +.I SMTPAUTH='!cram' +may be useful. +In opposite, if +.TP 5 +.I SMTPAUTH='-' +starts with a dash, Auth disabled for particular +connections. +Note: The use of 'cram' requires a CRAM-MD5 enabled PAM. +.P +Setting up the TLS/STARTTLS environment: +.IP +.TP 5 +.I UCSPITLS +enables encrypted SMTP communication +via STARTTLS in case +.B sslserver +is provided. +If +.I UCSPITLS='!' +is set, STARTTLS is required; while setting +.I UCSPITLS='-' +disables STARTTLS. +Further, +.I UCSPITLS='?' +may be used to force the client to present a X.509 cert +for authentication purpose which may be refined +requesting +.I UCSPITLS='@' +to additionally fetch the email address +from the client's cert to be perhaps subject of +.IR LOCALMFCHECK . +.P +Other environment variables used: +.IP +.TP 5 +.I DELIVERTO +mail address for special recipients. +.TP 5 +.I RBLSMTPD +feed from +.B rblsmtpd +including the information received from the +inquired RBL hosts and displayed as +.I X-RBL-Info: +message header. +.TP 5 +.I POSTGREY +triggering the call of +.B qmail-postgrey +and feeding it with the IP address and port of the +.I greylisting +server. If +.I POSTGREY +is set to +.I - +no lookup is performed. + +.SH "CUSTOMIZABLE RETURN MESSAGES" +In case of rejected or defered SMTP connections +.B qmail-smtpd +can provide additional informations in the SMTP reply message +which are sandwiched between the reply code and the EMMSC. +.B qmail-smtpd +recognizes these environment variables: +.TP 5 +.I REPLY_GREYLISTED +following 450 greylisting +.TP 5 +.I REPLY_HELO +following 550 Bad Helo +.TP 5 +.I REPLY_MAILBOX +following 550 mailbox not existing +.TP 5 +.I REPLY_MAXSIZE +following 552 message size to large +.TP 5 +.I REPLY_BADMAILFROM +following 553 badmail from +.TP 5 +.I REPLY_BADRCPTTO +following 553 badrcpt to +.TP 5 +.I REPLY_SENDEREXIST +following 553 SMTP sender DNS +.TP 5 +.I REPLY_NOGATEWAY +following 553 No gateway +.TP 5 +.I REPLY_SENDERINVALID +following 553 SMTP sender invalid +.TP 5 +.I REPLY_CONTENT +following 554 Message content invalid + +.SH "ENVIRONMENT VARIABLES SET" +By means of the following environment variables, +the SMTP session can be interrogated: +.TP 5 +.I HELOHOST +the HELO/EHLO greeting of the SMTP client. +.TP 5 +.I AUTHPROTOCOL +the ESMTPA protocol used for authentication. +.TP 5 +.I AUTHUSER +the supplied username for authentication. +.TP 5 +.I MAILFROM +containes the received 'Mail From:' address. +.TP 5 +.I RCPTTO +containes all received 'Rcpt To:' addresses separated by blanks. +.TP 5 +.I TCP(6)REMOTEINFO +in authentication mode set to the accepted username. +.TP 5 +.I SSL_* +information from +.BR sslserver , +if applicable. + +.SH "SEE ALSO" +tcp-environ(5), +qmail-control(5), +qmail-inject(8), +qmail-newmrh(8), +qmail-newbmt(8), +qmail-authuser(8), +qmail-recipients(8), +qmail-postgrey(8), +qmail-smtpam(8), +qmail-mfrules(8), +qmail-queue(8), +qmail-remote(8), +qmail-send(8), +qmail-log(8), +tcpserver(8), +sslserver(8). + diff --git a/sqmail-4.3.07/man/qmail-start.9 b/sqmail-4.3.07/man/qmail-start.9 new file mode 100644 index 0000000..b801ac2 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-start.9 @@ -0,0 +1,94 @@ +.TH s/qmail: qmail-start 8 +.SH NAME +qmail-start \- turn on mail delivery +.SH SYNOPSIS +.B qmail-start +[ +.I defaultdelivery +[ +.I logger arg ... +] +] +.SH DESCRIPTION +.B qmail-start +invokes +.BR qmail-send , +.BR qmail-lspawn , +.BR qmail-rspawn , +and +.BR qmail-clean , +under the proper uids and gids. +These four daemons cooperate to deliver messages from the queue. + +.B qmail-start +arranges for +.BR qmail-send 's +activity record to be sent to +.BR qmail-start 's +output. +See +.B qmail-log(5) +for the format of the activity record. +Other than this, +.B qmail-start +does not print anything, even on failure. + +If +.I defaultdelivery +is supplied, +.B qmail-start +passes it to +.BR qmail-lspawn . + +If +.I logger +is supplied, +.B qmail-start +invokes +.I logger +with the given arguments, +and feeds +.BR qmail-send 's +activity record through +.IR logger . + +Environment variables given to +.B qmail-start +will eventually be passed on to +.BR qmail-local , +so make sure to clean up the environment if you run +.B qmail-start +manually: + +.EX + # env - PATH="HOME/bin:$PATH" +.br + qmail-start ./Mailbox splogger qmail & +.br + (all on one line) +.EE + +Resource limits, controlling ttys, et al. are also passed from +.B qmail-start +to +.BR qmail-local . + +Note that +.B qmail-send +normally juggles several simultaneous deliveries. +To reduce +.BR qmail-send 's +impact on other programs, +you can run +.B qmail-start +with a low priority. +.SH "SEE ALSO" +logger(1), +splogger(1), +nice(1), +qmail-log(5), +qmail-local(8), +qmail-clean(8), +qmail-lspawn(8), +qmail-rspawn(8), +qmail-send(8) diff --git a/sqmail-4.3.07/man/qmail-tcpok.8 b/sqmail-4.3.07/man/qmail-tcpok.8 new file mode 100644 index 0000000..3052c96 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-tcpok.8 @@ -0,0 +1,24 @@ +.TH s/qmail: qmail-tcpok 8 +.SH NAME +qmail-tcpok \- clear TCP timeout table +.SH SYNOPSIS +.B qmail-tcpok +.SH DESCRIPTION +.B qmail-tcpok +erases +.BR qmail-remote 's +current list of timeouts, +so that +.B qmail-remote +does not make any assumptions about failing addresses. + +.B qmail-tcpok +must be run either as +.B root +or with user id +.B qmailr +and group id +.BR sqmail . +.SH "SEE ALSO" +qmail-remote(8), +qmail-tcpto(8) diff --git a/sqmail-4.3.07/man/qmail-tcpto.8 b/sqmail-4.3.07/man/qmail-tcpto.8 new file mode 100644 index 0000000..ed44617 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-tcpto.8 @@ -0,0 +1,30 @@ +.TH s/qmail: qmail-tcpto 8 +.SH NAME +qmail-tcpto \- print TCP timeout table +.SH SYNOPSIS +.B qmail-tcpto +.SH DESCRIPTION +After an SMTP connection attempt times out, +.B qmail-remote +records the relevant IP address. +If the same address fails again (after at least two minutes with +no intervening successful connections), +.B qmail-remote +assumes that further attempts will fail for at least another hour. + +.B qmail-tcpto +prints +.BR qmail-remote 's +current list of timeouts. + +.B qmail-tcpto +must be run either as +.B root +or with user id +.B qmailr +and group id +.BR sqmail . +.SH "SEE ALSO" +qmail-qread(8), +qmail-remote(8), +qmail-tcpok(8) diff --git a/sqmail-4.3.07/man/qmail-todo.8 b/sqmail-4.3.07/man/qmail-todo.8 new file mode 100644 index 0000000..740f5b3 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-todo.8 @@ -0,0 +1,128 @@ +.TH s/qmail: qmail-todo 8 +.SH NAME +qmail-todo \- schedule state change of message for delivery +.SH SYNOPSIS +.B qmail-todo +.SH DESCRIPTION +.B s/qmail +with a high local and remote concurrency number +is able to deliver a tremendous amount of messages (throughput). +Depending on the provided resources however, +often this can not be achieved since +.B qmail-send +becomes a bottleneck on delivery. + +.B qmail-send +preprocesses all new messages before deploying them for +.I local +or for +.I remote +delivering. In a particulur run, +.B qmail-send +does one 'todo' processing, but has the ability to close multiple jobs. +Due to this layout, potentially +.B qmail-send +can not feed all the new available (local/remote) delivery slots +and therefore, it is not possible to achieve the maximum throughput. + +This is a minor problem, given +.B qmail-send +is able to complete this in short time; but due to +many file system calls (fsync and (un)link) a 'todo' +run is expensive and throttles the throughput. + +.B qmail-todo +solves this 'silly qmail (queue) problem' +which is apparent only on system with high injection rates, +delegating the scheduling of 'todo' runs to a dedicated process. + +.SH "COMMUNICATION" +.B qmail-todo +interfaces with +.B qmail-send +on file descriptors \fI[1,8]\fR on sending +and \fI[7,0]\fR for receiving. +.B qmail-todo +communicates with +.B qmail-clean +on file descriptors \fI[2,0]\fR for sending +and \fI[3,1]\fR for receiving. + +.B qmail-todo +and +.B qmail-send +share an extended and peristent message exchange format: + +.EX +D[LRB]<mesgid>\0 + Start delivery for new message with id <messid>. + The character L, R or B defines the type + of delivery: Local, Remote, or Both, respectively. +.EE + +.EX +L<string>\0 + Dump string to the logger without adding additional + '\\n' or similar. +.EE + +.B qmail-todo +sends "\\0" terminated messages, whereas +.B qmail-send +just sends one character to +.BR qmail-todo . + +.SH "BIG PICTURE" +.EX + +-------+ +-------+ + | clean | | clean | + +--0-1--+ +--0-1--+ +-----------+ + trigger ^ | ^ | +->0,1 lspawn | + | | v | v / +-----------+ + +-------+ v +--2-3--+ +--5-6--+ / + | | | | 0<--7 1,2<-+ + | queue |--+--| todo | | send | + | | | | 1-->8 3,4<-+ + +-------+ +-------+ +---0---+ \\ + | \\ +-----------+ + v +->0,1 rspwan | + +---0---+ +-----------+ + | logger| + +-------+ +.EE + +.SH "EXIT CODES" +.B qmail-todo +exits +.I 0 +if the messages have been processed successfully. +It exits +.I 1 +in case there is a communication problem with +.BR qmail-send . +The exit code +.I 111 +together with a diagnostic message is facilitated by +.B qmail-todo +in case it failes reading the required control files. + +.SH "DIAGNOSTICS" +.B qmail-todo +provides additional diagnostic messages to +.B qmail-send +to be displayed in the logs. In particular, in +case of problems creating and (un)linking files. + +.SH "CREDITS" +.B qmail-todo +included in +.B s/qmail +has been created by Andre Oppermann (http://www.nrg4u.com) +as part of this LDAP patch for +.BR qmail . +This man-page uses parts of his EXTERNAL discription. + + +.SH "SEE ALSO" +qmail-send(8), +qmail-queue(8). diff --git a/sqmail-4.3.07/man/qmail-users.9 b/sqmail-4.3.07/man/qmail-users.9 new file mode 100644 index 0000000..6ef5548 --- /dev/null +++ b/sqmail-4.3.07/man/qmail-users.9 @@ -0,0 +1,117 @@ +.TH s/qmail: qmail-users 5 +.SH NAME +qmail-users \- assign mail addresses to users +.SH OVERVIEW +The file +.B SQMAIL/users/assign +assigns the local part of mail addresses to users. For example, + +.EX + =joe.shmoe:joe:503:78:/home/joe::: +.EE + +says that mail for +.B joe.shmoe +should be delivered to user +.BR joe , +with uid 503 and gid 78, +as specified by +.BR /home/joe/.qmail . + +Assignments fed to +.B qmail-newu +will be used by +.B qmail-lspawn +to control +.BR qmail-local 's +deliveries. +Use +.B qmail-newu (8) +to generate +.I users/assign.cdb +from +.IR users/assign . +A change to +.B SQMAIL/users/assign +will have no effect until +.B qmail-newu +is run. +.SH STRUCTURE +.B SQMAIL/users/assign +is a series of assignments, one per line. +It ends with a line containing a single dot. +Lines must not contain NUL. +.SH "SIMPLE ASSIGNMENTS" +A simple assignment is a line of the form + +.EX + =local:user:uid:gid:homedir:dash:ext: +.EE + +Here +.I local +is an address; +.IR user , +.IR uid , +and +.I gid +are the account name, uid, and gid +of the user in charge of +.IR local ; +and messages to +.I local +will be controlled by +.IR homedir\fB/.qmail\fIdashext . + +If there are several assignments for the same +.I local +address, +.B qmail-lspawn +will use the first one. + +.I local +is interpreted without regard to case. +.SH "WILDCARD ASSIGNMENTS" +A wildcard assignment is a line of the form + +.EX + +loc:user:uid:gid:homedir:dash:pre: +.EE + +This assignment applies to any address beginning with +.IR loc , +including +.I loc +itself. +It means the same as + +.EX + =locext:user:uid:gid:homedir:dash:preext: +.EE + +for every string +.IR ext . + +A more specific wildcard assignment overrides a less specific +assignment, and a simple assignment overrides any wildcard assignment. +For example: + +.EX + +:alias:7790:2108:SQMAIL/alias:-:: + +joe-:joe:507:100:/home/joe:-:: + =joe:joe:507:100:/home/joe::: +.EE + +The address +.B joe +is handled by the third line; +the address +.B joe-direct +is handled by the second line; +the address +.B bill +is handled by the first line. +.SH "SEE ALSO" +qmail-pw2u(8), +qmail-newu(8), +qmail-lspawn(8) diff --git a/sqmail-4.3.07/man/qmail-vmailuser.9 b/sqmail-4.3.07/man/qmail-vmailuser.9 new file mode 100644 index 0000000..e19898d --- /dev/null +++ b/sqmail-4.3.07/man/qmail-vmailuser.9 @@ -0,0 +1,108 @@ +.TH s/qmail: qmail-vmailuser 8 + +.SH "NAME" +qmail-vmailuser \- recipient maildir validation + +.SH "SYNOPSIS" +.B qmail-vmailuser +.I [homedir] +.I [-C] +.SH "DESCRIPTION" +.B qmail-vmailuser +is a maildir verification PAM supporting +.I VMailMgr +and +.I Vpopmail +users for virtual domains. +Invoked via +.BR qmail-smtpd 's +recipient mechanism, it checks the +existence of the recipient directory +for the provisioned virtual users in +.IR SQMAIL/control/virtualusers . + +.B qmail-vmailuser +follows +.BR checkpassword 's +interface specification evaluating the +SMTP forwarding path (RCPT TO:) taken from +discriptor 3 with a length of max 128 bytes. + +The forwarding path +.I vuser@domain +is tokenized to determine the +virtual user in +.I SQMAIL/control/virtualusers +given by +.I domain +in the first step and then validating for +.I vuser +the existance of (v)user's mail directory +in lower case while substituting dots by colons. +.SH "USAGE" +.B qmail-vmailuser +is called as PAM from +.BR qmail-smtpd 's +control file +.IR SQMAIL/control/recipients : + +.EX + domain|bin/qmail-vmailuser + *|bin/qmail-vmailuser /homedir -C +.EE + +No specific settings are required to support +either +.I VMailMgr +or +.IR Vpopmail , +except for the +.I homedir +and perhaps the option +.I -C +evaluating +.I vuser +in case respect mode. +Since +.I homedir +defaults mostly to +.IR /home , +this argument can be omitted. +.SH "SECURITY" +For successfull operation +.B qmail-vmailuser +requires to stat +.IR vuser 's +directory though without reading +it's actual contents. Due to +restrictions given by +.IR Vpopmail , +.B qmail-vmailuser +needs to belong to +.I vpopmail:vchkpw +or gnerally to be +root-owned and 'sticky'. +.SH "RETURN CODES" +If for the provided +.I vuser@domain +the user directory does not exist +.B qmail-vmailuser +exits 1. +If +.B qmail-vmailuser +is misused, it may instead exit 2. +If there is a temporary problem, +.B qmail-vmailuser +exits 111. +In case +.B qmail-vmailuser +can't read +.I SQMAIL/control/recipients +it exits 110. +.SH "SEE ALSO" +addresses(5), +envelopes(5), +qmail-send(8), +qmail-smtpd(8), +qmail-recipients(8), +qmail-authuser(8). diff --git a/sqmail-4.3.07/man/qreceipt.1 b/sqmail-4.3.07/man/qreceipt.1 new file mode 100644 index 0000000..37b39ed --- /dev/null +++ b/sqmail-4.3.07/man/qreceipt.1 @@ -0,0 +1,33 @@ +.TH s/qmail: qreceipt 1 +.SH NAME +qreceipt \- respond to delivery notice requests +.SH SYNOPSIS +in +.BR .qmail : +.B |qreceipt +.I youraddress +.SH DESCRIPTION +When a mail message arrives with +.I youraddress +listed in a +.B Notice-Requested-Upon-Delivery-To +header field, +.B qreceipt +sends a success notice back to the envelope sender. + +.B WARNING: +If you create a +.B .qmail +file to enable +.BR qreceipt , +make sure to also add a line specifying delivery to your normal mailbox. +For example: + +.EX + /home/joe/Mailbox +.br + |qreceipt joe@nowhere.mil +.EE +.SH "SEE ALSO" +dot-qmail(5), +envelopes(5) diff --git a/sqmail-4.3.07/man/setforward.1 b/sqmail-4.3.07/man/setforward.1 new file mode 100644 index 0000000..1c2925c --- /dev/null +++ b/sqmail-4.3.07/man/setforward.1 @@ -0,0 +1,204 @@ +.TH s/qmail: setforward 1 +.SH NAME +setforward \- create a forwarding database +.SH SYNOPSIS +.B setforward +.I cdb +.I tmp +.SH DESCRIPTION +.B setforward +reads a table of forwarding instructions from its standard input. +It converts the table into a forwarding database. +The forwarding database can be used by +.BR fastforward . + +.B setforward +writes the forwarding database to +.IR tmp ; +it then moves +.I tmp +to +.IR cdb . +.I tmp +and +.I cdb +must be on the same filesystem. + +If there is a problem creating +.IR tmp , +.B setforward +complains and leaves +.I cdb +alone. + +The forwarding database format is portable across machines. +.SH "INSTRUCTION FORMAT" +A forwarding instruction contains a +.I target\fR, +a colon, a series of commands, and a semicolon. +Each command is a +.I recipient address\fR, +.I owner address\fR, +.I external mailing list\fR, +or +.I program\fR. +Commands are separated by commas. + +For example, + +.EX + root@yp.to: god@heaven.af.mil, staff@af.mil; +.EE + +says that mail for +.B root@yp.to +should be forwarded to the recipient addresses +.B god@heaven.af.mil +and +.BR staff@af.mil . + +When +.B setforward +sees # it ignores all text from # to the end of the line: + +.EX + # this is a comment +.EE + +.B setforward +ignores all other line endings, +so you can split a forwarding instruction across lines. +It also ignores spaces and tabs. +Exception: +you can put a space (or tab or comma or whatever) +into a target or command by putting a backslash in front of it. +(However, NUL bytes are not permitted anywhere.) +.SH "TARGETS" +When +.B fastforward +sees the incoming address +.IR user@host.dom , +it tries three targets: +.IR user@host.dom , +.IR @host.dom , +and +.IR user@ . +It obeys the commands for the first target that it finds. +Target names are interpreted without regard to case. + +All the commands for a single target must be listed in a single instruction. +Exception: an owner address can be listed in a separate instruction. +.SH "RECIPIENT ADDRESSES" +If a command begins with an ampersand, +.B setforward +takes the remaining bytes in the command as a recipient address: + +.EX + boss@yp.to: &god@heaven.af.mil; +.EE + +.B fastforward +sends each incoming mail message +to the recipient address. +The recipient address must include a fully qualified domain name. +It cannot be longer than 800 bytes. + +If a recipient address is itself a target in the forwarding table, +.B fastforward +will recursively handle the instructions for that target. +Note that +.I @host.dom +and +.I user@ +wildcards do not apply here; +they apply only to the incoming address. + +If a command begins with a letter or number, +.B setforward +takes the entire command as a recipient address: + +.EX + boss@yp.to: god@heaven.af.mil; +.EE +.SH "OWNER ADDRESSES" +If a command begins with a question mark, +.B setforward +takes the remaining bytes in the command as an owner address: + +.EX + sos@heaven.af.mil: ?owner-sos@heaven.af.mil; +.EE + +.B fastforward +uses that address as the envelope sender for forwarded mail, +so bounces will go back to that address. +(Normally, if a message is forwarded to a bad address, +it will bounce back to the original envelope sender.) +.SH "EXTERNAL MAILING LISTS" +If a command begins with a dot or slash, +.B setforward +takes the entire command as the name of a binary mailing list file created by +.BR setmaillist : + +.EX + sos@heaven.af.mil: /etc/lists/sos.bin; +.EE + +.B fastforward +will read and obey the commands in that file. +The file must be world-readable +and accessible to +.BR fastforward . +.SH "PROGRAMS" +If a command begins with a vertical bar or exclamation point, +.B setforward +takes the rest of the command as the name of a program to run: + +.EX + dew@: |dew-monitor; +.EE + +For a vertical bar, +.B fastforward +feeds the message +to that program. +An exclamation point works the same way except that +.B fastforward +inserts +.BR $UFLINE , +.BR $RPLINE , +and +.B $DTLINE +in front of the message. +.SH "DUPLICATES" +When +.B fastforward +is building the recipient list for a message, +it keeps track of the recipient addresses and external mailing lists +it has used. +If the same command shows up again, it skips it. +For example: + +.EX + everybody@yp.to: programmers@yp.to, testers@yp.to; + programmers@yp.to: joe@yp.to, bob@yp.to; + testers@yp.to: joe@yp.to, fred@yp.to; +.EE + +A message to +.B everybody@yp.to +will be sent to +.B joe@yp.to +only once. +(This also means that addresses in an internal forwarding loop +are discarded.) + +Exception: +If a target has an owner address, +commands for that target are considered different +from commands for ``outside'' targets. +.SH "SEE ALSO" +newaliases(1), +preline(1), +printforward(1), +setmaillist(1) diff --git a/sqmail-4.3.07/man/setmaillist.1 b/sqmail-4.3.07/man/setmaillist.1 new file mode 100644 index 0000000..59fbf7d --- /dev/null +++ b/sqmail-4.3.07/man/setmaillist.1 @@ -0,0 +1,72 @@ +.TH s/qmail: setmaillist 1 +.SH NAME +setmaillist \- create a binary mailing list +.SH SYNOPSIS +.B setmaillist +.I bin +.I tmp +.SH DESCRIPTION +.B setmaillist +reads a mailing list from its standard input. + +.B setmaillist +writes the mailing list in a binary format to +.IR tmp ; +it then moves +.I tmp +to +.IR bin . +.I tmp +and +.I bin +must be on the same filesystem. + +If there is a problem creating +.IR tmp , +.B setmaillist +complains and leaves +.I bin +alone. + +The binary mailing list format is portable across machines. + +.B setmaillist +always creates +.I bin +world-readable. +.SH "MAILING LIST FORMAT" +The mailing list read by +.B setmaillist +is a series of lines. +NUL bytes are not allowed. + +If a line begins with a dot or slash, +.B setmaillist +takes the entire line as an include file name. + +If a line begins with an ampersand, +.B setmaillist +takes the rest of the line as a recipient address. +If a line begins with a letter or number, +.B setmaillist +takes the entire line as a recipient address. +Each recipient address must include a fully qualified domain name. +Recipient addresses longer than 800 bytes are not allowed. + +.B setmaillist +ignores blank lines +and lines beginning with #. +It also ignores spaces and tabs at the ends of lines. + +For example, + +.EX + god@heaven.af.mil + djb@silverton.berkeley.edu +.EE + +is a mailing list with two addresses. +.SH "SEE ALSO" +setforward(1), +newinclude(1), +printmaillist(1) diff --git a/sqmail-4.3.07/man/spfquery.8 b/sqmail-4.3.07/man/spfquery.8 new file mode 100644 index 0000000..4c26323 --- /dev/null +++ b/sqmail-4.3.07/man/spfquery.8 @@ -0,0 +1,147 @@ +.TH s/qmail: spfquery 8 +.SH NAME +spfquery \- SPF test program +.SH SYNOPSIS +.B spfquery +.I sender-ip +.I sender-helo +.I envelope-from +.I [local rules] +.I [-v] +.SH DESCRIPTION +.B spfquery +is a test program to allow evaluation +of +.I SPF records +fetched on demand by means of +.BR qmail-smtpd . + +.SH "ARGUMENTS" +.B spfquery +uses the given arguments +.IR sender-ip , +.IR sender-helo , +and +.I envelope-from +to perform a DNS SPF TXT lookup +and evaluates the results. +In addition, \'local-rules\' might +be included as +.IR local-rules . +By means of the (last) option +.I -v +a verbose output is provided. + +.SH "RESPONSE" +The result of +.B spfquery +shows the SPF return codes of the retrieved +information after the DNS evaluation. +Additionally, the mechanisms and +results are displayed as chain +of resulting codes. In case the option +.I -v +is given, the received DNS SPF TXT records +for the analysed domain are shown in raw +format to allow further diagnostics. + +.SH "SPF MECHANISMS" +.B spfquery +and of course +.B qmail-smtpd +support all mechanisms defined in +.IR RFC\ 7208 , +in particular: +.IR A/AAAA , +.IR IPv4 , +.IR IPv6 , +.IR MX , +.IR PTR , +.IR Exists . +Nesting of SPF records - indicated by the commands +.I include: +and +.I redirect= +- is allowed and the chain is followed. +Further, +.I exp(lanation)= +is supported. + +.SH "SPF QUALIFIERS" +SPF makes uses of command and explanation qualifiers. +Command and explanation characters are: +.I + +pass (default), +.I - +fail, +.I ~ +softfail, +.I ? +neutral. + +.SH "EXPLANATION CHARACTERS" +This implementation uses the following +additional explanation characters: +.I o +none, +.I u +unknown, +.I d +DNS problem (not used). + +.SH "MACRO EXPANSION" +Macros (keyword) expansion is supported conforming to +.IR RFC\ 7208 . + + +.SH "SPF EVALUATION" +.B spfquery +provides a brief summary of results for the evaluation: +.I S +the sending IP, +.I O +the envelope-from address, +.I C +the requested domain for lookup, +.I H +the HELO/EHLO of the contacted MTA, +.I M +the SPF lookup mechanis as explained, +.I I +the included domanin for lookup, +.I D +the (re)direct to follow, +.I P +a potential problem observed. +These letters are followed by an equal sign '=' +and detail the information. +.I R +is the lookup result obtained, followed by a +colon ':'. + +.SH "DIAGNOSTICS" +Additional DNS diagnostic routines are available: +.B dnstxt +returns the DNS TXT for +.IR host . +.B dnsptr +returns the DNS PTR for +.IR IP . +.B dnsmxip +returns the MTA IPs for +.IR domain . + +.SH "CREDITS" +The +.B spfquery +program and the SPF integration into +.B s/qmail +follows mainly the implementation of +Jana Saout (http://www.saout.de/misc/spf/) +and is used by permission. + +.SH "SEE ALSO" +qmail-control(5), +qmail-smtpd(8) +dnsmxip(8), +dnstxt(8). diff --git a/sqmail-4.3.07/man/splogger.8 b/sqmail-4.3.07/man/splogger.8 new file mode 100644 index 0000000..c9137a3 --- /dev/null +++ b/sqmail-4.3.07/man/splogger.8 @@ -0,0 +1,60 @@ +.TH s/qmail: splogger 8 +.SH NAME +splogger \- make entries in syslog +.SH SYNOPSIS +.B splogger +[ +.I tag +[ +.I fac +] +] +.SH DESCRIPTION +.B splogger +reads a series of messages and feeds them to +.BR syslog . +At the front of each message it puts +.I tag +(default: +.BR splogger ) +and a numerical timestamp. + +.B splogger +checks for +.B alert: +or +.B warning: +at the beginning of each message. +It selects a priority of +LOG_ALERT, LOG_WARNING, or LOG_INFO accordingly. + +.B splogger +logs messages with facility +.IR fac . +.I fac +(default: 2) +must be numeric. + +.B splogger +converts unprintable characters to question marks. + +.B splogger +does not log blank lines. + +.B splogger +folds messages after 800 characters, +since +.B syslog +can't handle long messages. +.B splogger +uses a + after the timestamp +to mark folded lines. + +Note that the +.B syslog +mechanism is inherently unreliable: +it does not guarantee that messages will be logged. +It is also very slow. +.SH "SEE ALSO" +syslog(3), +logger(8) diff --git a/sqmail-4.3.07/man/sqmail.9 b/sqmail-4.3.07/man/sqmail.9 new file mode 100644 index 0000000..921a95c --- /dev/null +++ b/sqmail-4.3.07/man/sqmail.9 @@ -0,0 +1,130 @@ +.TH s/qmail: s/qmail 7 +.SH "NAME" +s/qmail \- overview of s/qmail documentation +.SH "INTRODUCTION" +.B s/qmail +is a secure, encrypting, authenticating, reliable, efficient, +yet simple IPv4/IPv6 message transfer agent based on +.B qmail +and ought to be plug-in compatible. +The +.B s/qmail +software includes Dan Bernstein's +.B fastforward +and +.B qmailanalog +package in addition with other enhancements taken mainly from the +.B Spamcontrol +patch. + +The current version of +.B s/qmail +depends on the +.B fehQlibs +and +.B OpenSSL +or +.BR LibreSSL . + +Users who want to control incoming messages +should read +.BR dot-qmail (5). +Available commands for the +.B .qmail +file include +.BR qbiff (1), +.BR qreceipt (1), +.BR forward (1), +.BR fastforward (1), +.BR bouncesaying (1), +and +.BR condredirect (1). +Other helpful commands include +.BR maildirmake (1), +.BR maildir2mbox (1), +and +.BR maildirwatch (1). + +System administrators who want to control the entire +.B s/qmail +system should start with +.BR qmail-control (5), +.BR qmail-mfrules (8), +and +.BR qmail-start (8). + +There are four queue-monitoring/mangement tools: +.BR qmail-qread (8), +.BR qmail-qstat (8), +.BR qmail-qmaint (8), +and +.BR qmail-tcpto (8). +.BR qmail-mrtg (8) +allows to feed the +.B s/qmail +logs to +.BR MRTG . +Incoming SMTP connections are handled by +.BR qmail-smtpd (8) +and +.BR qmail-recipients (8) +optionally together with +.BR qmail-smtpam (8), +.BR qmail-authuser (8) +and perhaps with +.BR qmail-vmailusers (8) +if virtual mail managers like +.B vpopmail +or +.B vmailmgr +are in use. + +SRS is availalable within +.B s/qmail +by means of the additional commands +.BR srsforward (1) +and +.BR srsreverse (1). +DKIM message signing and verification is achieved with +.B qmail-dksign (8) +and +.BR qmail-dkverify (8). + +.B s/qmail +offers two command-line message-sending interfaces: +.BR qmail-inject (8) +and +.BR mailsubj (1). +For background information on Internet mail messages, +see +.BR addresses (5), +.BR envelopes (5), +.BR qmail-header (5), +and +.BR forgeries (7). + +Miscellaneous documentation includes +.BR qmail-limits (7) +and +.BR qmail-pop3d (8). + +Apart from the Internet mail message transport protocols +.I ESMTP/ESMTPS +.B s/qmail +supports +.I QMTP/QMTPS +together with the Pop Office message protocols +.IR POP3/POP3S +depending on the +.B ucspi-ssl +package for TLS support. + +This documentation describes version +VERSION +of +.BR s/qmail . +See +.B https://www.fehcom.de/djbware.html +for other +.BR s/qmail -related +software. diff --git a/sqmail-4.3.07/man/srsforward.1 b/sqmail-4.3.07/man/srsforward.1 new file mode 100644 index 0000000..930c3df --- /dev/null +++ b/sqmail-4.3.07/man/srsforward.1 @@ -0,0 +1,96 @@ +.TH s/qmail: srsforward 1 +.SH NAME +srsforward \- forward mail to one or more addresses including a SRS extension +.SH SYNOPSIS +in +.BR .qmail : +.B |srsforward +.I address ... +.SH DESCRIPTION +.B srsforward +forwards mails for dedicated recipient +.I srsdomains +to the specified list of addresses +while extending the SMTP 'RCPT TO:' envelope address with +SRS (Sender Rewriting Scheme) information. +It is a simple wrapper around +.B qmail-queue +rewriting the SMTP recipient address. The forwarded email +ought to be acceptable for SPF enabled recipient MTAs. +Additionally, it mitigates the forgery of addresses for bounces. +.SH "CONTROL FILE" +.B srsforward +reads the control file +.IR srsdomains . +Here, you can specify + +.I srsdomain:SRS_secret1 SRS_secret2 ...|[+,-,=]|[srsaddress(.)] + +.I srsdomain +is +.B s/qmail's +recipient domain; typically +.I defaultdomain +or any domain given in +.IR rcpthosts . +.I srsdomain +can be simply expressed as '*', thus the +following informations are +applicable for all +.B srsfoward +domains as default values, while +particular +.I srsdomain +settings have precedence. +Reversely, recipient +domains can be disable for SRS fowarding: +.IR !nosrsfoward.example.com: . + +.B srsforward +accepts several 'secrets' for each +.I srsdomain +separated by empty spaces. + +.BR srsfoward 's +.I delimiter +is a character chosen out of the set +.I +,-,= +with default +.I = +and thus is optional. + +.B srsforward +may include +.I srsaddress +to construct the domain part of the RCPT TO: +envelope address for SRS fowarded mails. If +.I srsaddress +ends with a dot '.', +this name is used to prepend the original +host name and typically is chosen as +.IR srs. . +Otherwise, the original host name is +used as default +.I srsaddress +for forwarding and also relevant for +potential bounces being subject of +.BR srsreverse . +.SH "ENVIRONMENT VARIABLES" +.B srsforward +reads the environment variables +.IR HOST , +which is used to determine the +.IR srsdomain , +.IR DTLINE , +and +.IR NEWSENDER . +.SH REFERENCE +.B srsforward +uses srs2.c from +.IR libsrs2 . +.SH "SEE ALSO" +srsreverse(1), +dot-qmail(5), +qmail-command(8), +qmail-queue(8), +qmail-send(8). diff --git a/sqmail-4.3.07/man/srsreverse.9 b/sqmail-4.3.07/man/srsreverse.9 new file mode 100644 index 0000000..5057330 --- /dev/null +++ b/sqmail-4.3.07/man/srsreverse.9 @@ -0,0 +1,87 @@ +.TH s/qmail: srsreverse 1 +.SH NAME +srsreverse \- reconstruct the original address from its SRS extension +and forward bounce mail +.SH SYNOPSIS +in +.BR .qmail : +.B |srsreverse +.SH DESCRIPTION +Upon reception by +.BR qmail-smtpd , +.B qmail-local +may feed a locally delivered bounce email through +.B srsrevers +in order to reconstruct the original sender from +the received SRS address provided in the local part +and to forward the bounce mail to its original address. +.SH "SRS DOMAINS" +In order to accept emails for SRS modified +return addresses, you need to setup those in +.IR rcpthosts . +If your domain is +.I example.com +in +.I rcpthosts +you probably want to set up additionally +.IR srs.example.com . +However, +.I .example.com +would be fine as well. +.SH "VIRTUAL SRS USER" +SRS can facilitate a virtual user typically named +.I srs +and thus requires an entry like +.I srs.example.com:srs +in +.IR virtualdomains . +.SH "DOT QMAIL" +.B srsreverse +is called from a +.I dot-qmail +file which could be +.IR SQMAIL/alias/.qmail-srs-default . +.SH "CONTROL FILES" +.B srsreverse +reads the control file +.I virtualdomains +to exfiltrate the (virtual) SRS user name for the received domain, +if given. With the evaluated +.IR srsdomain , +.B srsrevers +fetches the +.I SRS secret +from +.I srsdomains +in order to validate the SRS bounce address. +.SH "ENVIRONMENT VARIABLES" +.B srsrverse +reads the environment variables +.IR DTLINE , +.IR HOST , +and +.IR RECIPIENTS . +.I HOST +is used to determine the +.IR srsdomain . +The forwarding bounce address is reconstructed from +the local part of +.IR RECIPIENTS . +.SH VERP +The Sender Rewriting Scheme SRS can be considered +as tailored form of VERP: Variable Envelope Return Path. +The chosen primary delimiter +.I = +is recognized by +.BR qmail-smtpd 's +recipient extension. +.SH REFERENCE +.B srsreverse +uses srs2.c from +.IR libsrs2 . +.SH "SEE ALSO" +srsforward(1), +dot-qmail(5), +qmail-command(8), +qmail-queue(8), +qmail-send(8). diff --git a/sqmail-4.3.07/man/tai64nfrac.5 b/sqmail-4.3.07/man/tai64nfrac.5 new file mode 100644 index 0000000..6a2cc5f --- /dev/null +++ b/sqmail-4.3.07/man/tai64nfrac.5 @@ -0,0 +1,18 @@ +.TH s/qmail: tai64nfrac 5 +.SH NAME +tai64nfrac \- evaluate the TAI64 timestamp and write the fractional seconds +.SH SYNOPSIS +.B tai64nfrac + +.SH DESCRIPTION +Reads a TAI64N external format timestamp following the '@' +as first character from +.I stdin +and +writes the fractional seconds since epoch (TAI, not UTC) to +.IR stdout . +Returns the following characters after the timestamp unaltered. + +.SH "SEE ALSO" +tcpserver(1), +sslserver(1). diff --git a/sqmail-4.3.07/man/tcp-environ.5 b/sqmail-4.3.07/man/tcp-environ.5 new file mode 100644 index 0000000..244d32a --- /dev/null +++ b/sqmail-4.3.07/man/tcp-environ.5 @@ -0,0 +1,86 @@ +.TH s/qmail: tcp-environ 5 +.SH NAME +tcp-environ \- TCP-related environment variables +.SH DESCRIPTION +The following environment variables +describe a TCP connection. +They are set up by +.B tcpclient +and +.B tcpserver +as well as +.BR sslclient +and +.BR sslserver . + +Note that +.BR TCPLOCALHOST , +.BR TCP6LOCALHOST , +.BR TCPREMOTEHOST , +.BR TCP6REMOTEHOST , +and +.BR TCPREMOTEINFO , +.BR TCP6REMOTEINFO , +can contain arbitrary characters. +.TP 5 +PROTO +The string +.BR TCP , +or +.BR TCP6 . +.TP 5 +TCPLOCALHOST/TCP6LOCALHOST +The domain name of the local host, +with uppercase letters converted to lowercase. +If there is no currently available domain name +for the local IP address, +.BR TCPLOCALHOST , +.B TCP6LOCALHOST +is not set. +.TP 5 +TCPLOCALIP +The IPv4 address of the local host, in dotted-decimal form. +.TP 5 +TCP6LOCALIP +The compactified IPv6 address of the local host. +.TP 5 +TCPLOCALPORT/TCP6LOCALPORT +The local TCP port number, in decimal. +.TP 5 +TCPREMOTEHOST/TCP6RMOTEHOST +The domain name of the remote host, +with uppercase letters converted to lowercase. +If there is no currently available domain name +for the remote IP address, +.B TCPREMOTEHOST +or +.B TCP6REMOTEHOST +is not set. +.TP 5 +TCPREMOTEINFO/TCP6REMOTEINFO +A connection-specific string, perhaps a username, +supplied by the remote host +via 931/1413/IDENT/TAP. +If the remote host did not supply connection information, +.BR TCPREMOTEINFO , +.B TCP6REMOTEINFO +is not set. +.TP 5 +TCPREMOTEIP +The IPv4 address of the remote host. +.TP 5 +TCP6REMOTEIP +The IPv6 address of the remote host. +.TP 5 +TCPREMOTEPORT/TCP6REMOTEPORT +The remote TCP port number. +.TP 5 +TCP6INTERFACE +contains the interface name for IPv6 connections. + +.SH "SEE ALSO" +tcpclient(1), +tcpserver(1), +sslclient(1), +sslserver(1), +tcp(4) diff --git a/sqmail-4.3.07/man/xqp.1 b/sqmail-4.3.07/man/xqp.1 new file mode 100644 index 0000000..14bf370 --- /dev/null +++ b/sqmail-4.3.07/man/xqp.1 @@ -0,0 +1,18 @@ +.TH s/qmail: xqp 1 +.SH NAME +xqp \- locate a message given its qp +.SH SYNTAX +.B xqp +.I qp +.SH DESCRIPTION +.B xqp +reads message lines and delivery lines printed by +.BR matchup . +It prints the lines that involve messages with long-term queue identifier +.IR qp . + +Long-term queue identifiers are not permanent identifiers. +They are based on process IDs; +15-bit process IDs can easily wrap around in less than an hour on a busy system. +.SH "SEE ALSO" +matchup(1) diff --git a/sqmail-4.3.07/man/xrecipient.1 b/sqmail-4.3.07/man/xrecipient.1 new file mode 100644 index 0000000..ec58832 --- /dev/null +++ b/sqmail-4.3.07/man/xrecipient.1 @@ -0,0 +1,14 @@ +.TH s/qmail: xrecipient 1 +.SH NAME +xrecipient \- locate all deliveries to one recipient +.SH SYNTAX +.B xrecipient +.I channel.recipient +.SH DESCRIPTION +.B xrecipient +reads message lines and delivery lines printed by +.BR matchup . +It prints the delivery lines that involve messages sent to +.IR channel.recipient . +.SH "SEE ALSO" +matchup(1) diff --git a/sqmail-4.3.07/man/xsender.1 b/sqmail-4.3.07/man/xsender.1 new file mode 100644 index 0000000..f919f8a --- /dev/null +++ b/sqmail-4.3.07/man/xsender.1 @@ -0,0 +1,14 @@ +.TH s/qmail: xsender 1 +.SH NAME +xsender \- locate all messages from one sender +.SH SYNTAX +.B xsender +.I sender +.SH DESCRIPTION +.B xsender +reads message lines and delivery lines printed by +.BR matchup . +It prints the lines that involve messages with return path +.IR sender . +.SH "SEE ALSO" +matchup(1) diff --git a/sqmail-4.3.07/src/Makefile b/sqmail-4.3.07/src/Makefile new file mode 100644 index 0000000..ae4801b --- /dev/null +++ b/sqmail-4.3.07/src/Makefile @@ -0,0 +1,1542 @@ +# Don't edit Makefile! Use ../conf-* for configuration. + +SHELL=/bin/sh + +default: \ +it-analog it-base it-clients it-control it-dns \ +it-forward it-log it-mbox it-pam it-pop it-queue \ +it-user it-setup it-server it-srs it-dkim + +auto-ccld.sh: \ +../conf-cc ../conf-ld warn-auto.sh + ( cat warn-auto.sh; \ + echo CC=\'`head -1 ../conf-cc`\'; \ + echo LD=\'`head -1 ../conf-ld`\' \ + ) > auto-ccld.sh + +auto-gid: \ +load auto-gid.o qlibs.lib + ./load auto-gid `cat qlibs.lib` + +auto-gid.o: \ +compile auto-gid.c + ./compile auto-gid.c + +auto-int: \ +load auto-int.o qlibs.lib + ./load auto-int `cat qlibs.lib` + +auto-int.o: \ +compile auto-int.c + ./compile auto-int.c + +auto-int8: \ +load auto-int8.o qlibs.lib + ./load auto-int8 `cat qlibs.lib` + +auto-int8.o: \ +compile auto-int8.c + ./compile auto-int8.c + +auto-str: \ +load auto-str.o qlibs.lib + ./load auto-str `cat qlibs.lib` + +auto-str.o: \ +compile auto-str.c + ./compile auto-str.c + +auto-uid: \ +load auto-uid.o qlibs.lib + ./load auto-uid `cat qlibs.lib` + +auto-uid.o: \ +compile auto-uid.c + ./compile auto-uid.c + +auto_break.c: \ +auto-str ../conf-break + ./auto-str auto_break \ + "`head -1 ../conf-break`" > auto_break.c + +auto_break.o: \ +compile auto_break.c + ./compile auto_break.c + +auto_patrn.c: \ +auto-int8 ../conf-patrn + ./auto-int8 auto_patrn `head -1 ../conf-patrn` > auto_patrn.c + +auto_patrn.o: \ +compile auto_patrn.c + ./compile auto_patrn.c + +auto_qmail.c: \ +auto-str ../conf-home + ./auto-str auto_qmail `head -1 ../conf-home` > auto_qmail.c + +auto_qmail.o: \ +compile auto_qmail.c + ./compile auto_qmail.c + +auto_spawn.c: \ +auto-int ../conf-spawn + ./auto-int auto_spawn `head -1 ../conf-spawn` > auto_spawn.c + +auto_spawn.o: \ +compile auto_spawn.c + ./compile auto_spawn.c + +auto_split.c: \ +auto-int ../conf-split + ./auto-int auto_split `head -1 ../conf-split` > auto_split.c + +auto_split.o: \ +compile auto_split.c + ./compile auto_split.c + +auto_uids.c: \ +auto-uid auto-gid ../conf-users ../conf-groups + ( ./auto-uid auto_uida `head -1 ../conf-users` \ + &&./auto-uid auto_uidd `head -2 ../conf-users | tail -1` \ + &&./auto-uid auto_uidl `head -3 ../conf-users | tail -1` \ + &&./auto-uid auto_uido `head -4 ../conf-users | tail -1` \ + &&./auto-uid auto_uidp `head -5 ../conf-users | tail -1` \ + &&./auto-uid auto_uidq `head -6 ../conf-users | tail -1` \ + &&./auto-uid auto_uidr `head -7 ../conf-users | tail -1` \ + &&./auto-uid auto_uids `head -8 ../conf-users | tail -1` \ + &&./auto-gid auto_gidq `head -1 ../conf-groups` \ + &&./auto-gid auto_gidn `head -2 ../conf-groups | tail -1` \ + ) > auto_uids.c.tmp && mv auto_uids.c.tmp auto_uids.c + +auto_uids.o: \ +compile auto_uids.c + ./compile auto_uids.c + +auto_usera.c: \ +auto-str ../conf-users + ./auto-str auto_usera `head -1 ../conf-users` > auto_usera.c + +auto_usera.o: \ +compile auto_usera.c + ./compile auto_usera.c + +base64.o: \ +compile base64.c + ./compile base64.c + +md5c.o : \ +compile md5c.c + ./compile md5c.c + +hmac_md5.o : \ +compile hmac_md5.c + ./compile hmac_md5.c + +bouncesaying: \ +load bouncesaying.o qlibs.lib + ./load bouncesaying `cat qlibs.lib` + +bouncesaying.o: \ +compile bouncesaying.c + ./compile bouncesaying.c + +chkshsgr: \ +load chkshsgr.o + ./load chkshsgr + +chkshsgr.o: \ +compile chkshsgr.c + ./compile chkshsgr.c + +chkspawn: \ +load chkspawn.o auto_spawn.o qlibs.lib + ./load chkspawn auto_spawn.o `cat qlibs.lib` + +chkspawn.o: \ +compile chkspawn.c + ./compile chkspawn.c + +clean: \ +TARGETS + rm -f `cat TARGETS` + +columnt: \ +load columnt.o qlibs.lib + ./load columnt `cat qlibs.lib` + +columnt.o: \ +compile columnt.c + ./compile columnt.c + +commands.o: \ +compile commands.c + ./compile commands.c + +compile: \ +make-compile warn-auto.sh systype + ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \ + compile + chmod 755 compile + +condredirect: \ +load condredirect.o qmail.o auto_qmail.o qlibs.lib + ./load condredirect qmail.o auto_qmail.o `cat qlibs.lib` + +condredirect.o: \ +compile condredirect.c + ./compile condredirect.c + +config: \ +warn-auto.sh config.sh ../conf-home ../conf-break ../conf-split + cat warn-auto.sh config.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPLIT}"`head -1 ../conf-split`"}g \ + > config + chmod 755 config + +config-fast: \ +warn-auto.sh config-fast.sh ../conf-home ../conf-break ../conf-split + cat warn-auto.sh config-fast.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPLIT}"`head -1 ../conf-split`"}g \ + > config-fast + chmod 755 config-fast + +constmap.o: \ +compile constmap.c + ./compile constmap.c + +control.o: \ +compile control.c + ./compile control.c + +date822fmt.o: \ +compile date822fmt.c + ./compile date822fmt.c + +datemail: \ +warn-auto.sh datemail.sh ../conf-home ../conf-break ../conf-split + cat warn-auto.sh datemail.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPLIT}"`head -1 ../conf-split`"}g \ + > datemail + chmod 755 datemail + +datetime.a: \ +makelib datetime.o datetime_un.o + ./makelib datetime.a datetime.o datetime_un.o + +datetime.o: \ +compile datetime.c + ./compile datetime.c + +datetime_un.o: \ +compile datetime_un.c + ./compile datetime_un.c + +ddist: \ +warn-auto.sh ddist.sh ../conf-home + cat warn-auto.sh ddist.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > ddist + chmod 755 ddist + +deferrals: \ +warn-auto.sh deferrals.sh ../conf-home + cat warn-auto.sh deferrals.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > deferrals + chmod 755 deferrals + +direntry.h: \ +compile trydrent.c direntry.h1 direntry.h2 + ( ./compile trydrent.c >/dev/null 2>&1 \ + && cat direntry.h2 || cat direntry.h1 ) > direntry.h + rm -f trydrent.o + +dkim.o: \ +compile dkim.cpp + ./compile dkim.cpp + +dkimbase.o: \ +compile dkimbase.cpp + ./compile dkimbase.cpp + +dkimsign.o: \ +compile dkimsign.cpp + ./compile dkimsign.cpp + +dkimverify.o: \ +compile dkimverify.cpp + ./compile dkimverify.cpp + +dns.lib: \ +tryrsolv.c compile load + ( (./compile tryrsolv.c && \ + ./load tryrsolv -L`head -1 ../conf-qlibs` -ldnsresolv ) \ + && echo "-L`head -1 ../conf-qlibs` -ldnsresolv" || exit 0 ) > dns.lib + rm -f tryrsolv.o tryrsolv + +dns.o: \ +compile dns.c dns_tlsa.c + ./compile dns.c dns_tlsa.c + +dnscname: \ +load dnscname.o \ +dns.lib socket.lib qlibs.lib dns.o ipalloc.o + ./load dnscname dns.o ipalloc.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +dnscname.o: \ +compile dnscname.c + ./compile dnscname.c + +dnsfq: \ +load dnsfq.o \ +dns.lib socket.lib qlibs.lib dns.o ipalloc.o + ./load dnsfq dns.o ipalloc.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +dnsfq.o: \ +compile dnsfq.c + ./compile dnsfq.c + +dnsip: \ +load dnsip.o dns.o ipalloc.o \ +dns.lib socket.lib qlibs.lib + ./load dnsip dns.o ipalloc.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +dnsip.o: \ +compile dnsip.c + ./compile dnsip.c + +dnsmxip: \ +load dnsmxip.o ipalloc.o dns.o dns.lib socket.lib + ./load dnsmxip ipalloc.o dns.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +dnsmxip.o: \ +compile dnsmxip.c + ./compile dnsmxip.c + +dnsptr: \ +load dnsptr.o dns.o ipalloc.o \ +dns.lib socket.lib qlibs.lib + ./load dnsptr dns.o ipalloc.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +dnsptr.o: \ +compile dnsptr.c + ./compile dnsptr.c + +dnstlsa: \ +load dnstlsa.o dns_tlsa.o ipalloc.o dns.o \ +dns.lib socket.lib qlibs.lib + ./load dnstlsa dns_tlsa.o ipalloc.o dns.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +dnstlsa.o: \ +compile dnstlsa.c dns_tlsa.c + ./compile dnstlsa.c dns_tlsa.c + +dnstxt: \ +load dnstxt.o ipalloc.o dns.o \ +dns.lib socket.lib qlibs.lib + ./load dnstxt ipalloc.o dns.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +dnstxt.o: \ +compile dnstxt.c + ./compile dnstxt.c + +except: \ +load except.o qlibs.lib + ./load except `cat qlibs.lib` + +except.o: \ +compile except.c + ./compile except.c + +failures: \ +warn-auto.sh failures.sh ../conf-home + cat warn-auto.sh failures.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > failures + chmod 755 failures + +fastforward: \ +load fastforward.o qmail.o auto_qmail.o strset.o qlibs.lib + ./load fastforward qmail.o auto_qmail.o strset.o \ + `cat qlibs.lib` + +fastforward.o: \ +compile fastforward.c + ./compile fastforward.c + +fifo.o: \ +compile fifo.c + ./compile fifo.c + +find-systype: \ +find-systype.sh auto-ccld.sh + cat auto-ccld.sh find-systype.sh > find-systype + chmod 755 find-systype + +fmtqfn.o: \ +compile fmtqfn.c + ./compile fmtqfn.c + +forward: \ +load forward.o qmail.o auto_qmail.o qlibs.lib + ./load forward qmail.o auto_qmail.o \ + `cat qlibs.lib` + +forward.o: \ +compile forward.c + ./compile forward.c + +gfrom.o: \ +compile gfrom.c + ./compile gfrom.c + +hasflock.h: \ +tryflock.c compile load + ( ( ./compile tryflock.c && ./load tryflock ) >/dev/null \ + 2>&1 \ + && echo \#define HASFLOCK 1 || exit 0 ) > hasflock.h + rm -f tryflock.o tryflock + +hasmkffo.h: \ +trymkffo.c compile load + ( ( ./compile trymkffo.c && ./load trymkffo ) >/dev/null \ + 2>&1 \ + && echo \#define HASMKFIFO 1 || exit 0 ) > hasmkffo.h + rm -f trymkffo.o trymkffo + +hasspnam.h: \ +tryspnam.c compile load + ( ( ./compile tryspnam.c && ./load tryspnam ) >/dev/null \ + 2>&1 \ + && echo \#define HASGETSPNAM 1 || exit 0 ) > hasspnam.h + rm -f tryspnam.o tryspnam + +hasuserpw.h: \ +tryuserpw.c s.lib compile load + ( ( ./compile tryuserpw.c \ + && ./load tryuserpw `cat s.lib` ) >/dev/null 2>&1 \ + && echo \#define HASGETUSERPW 1 || exit 0 ) > hasuserpw.h + rm -f tryuserpw.o tryuserpw + +hassalen.h: \ +trysalen.c compile + ( ./compile trysalen.c >/dev/null 2>&1 \ + && echo \#define HASSALEN 1 || exit 0 ) > hassalen.h + +hassgact.h: \ +trysgact.c compile load + ( ( ./compile trysgact.c && ./load trysgact ) >/dev/null \ + 2>&1 \ + && echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h + rm -f trysgact.o trysgact + +hassgprm.h: \ +trysgprm.c compile load + ( ( ./compile trysgprm.c && ./load trysgprm ) >/dev/null \ + 2>&1 \ + && echo \#define HASSIGPROCMASK 1 || exit 0 ) > hassgprm.h + rm -f trysgprm.o trysgprm + +hasshsgr.h: \ +chkshsgr warn-shsgr tryshsgr.c compile load + ./chkshsgr || ( cat warn-shsgr; exit 1 ) + ( ( ./compile tryshsgr.c \ + && ./load tryshsgr && ./tryshsgr ) >/dev/null 2>&1 \ + && echo \#define HASSHORTSETGROUPS 1 || exit 0 ) > \ + hasshsgr.h + rm -f tryshsgr.o tryshsgr + +hasutmp.h: \ +tryutmp.c compile + ( ./compile tryutmp.c >/dev/null 2>&1 \ + && echo \#define HASUTMP 1 || exit 0 ) > hasutmp.h + rm -f tryutmp.o + +haswaitp.h: \ +trywaitp.c compile load + ( ( ./compile trywaitp.c && ./load trywaitp ) >/dev/null \ + 2>&1 \ + && echo \#define HASWAITPID 1 || exit 0 ) > haswaitp.h + rm -f trywaitp.o trywaitp + +headerbody.o: \ +compile headerbody.c + ./compile headerbody.c + +hfield.o: \ +compile hfield.c + ./compile hfield.c + +hier.o: \ +compile hier.c + ./compile hier.c + +hostname: \ +load hostname.o dns.lib socket.lib + ./load hostname `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +hostname.o: \ +compile hostname.c + ./compile hostname.c + +idn2.lib: \ +tryidn2.c compile load + ( (./compile tryidn2.c && \ + ./load tryidn2 `head -1 ../conf-idn2` -lidn2 ) >/dev/null 2>&1 \ + && echo "`head -1 ../conf-idn2` -lidn2" || exit 0 ) > idn2.lib + rm -f tryind2.o tryidn2 + +install: \ +load install.o hier.o auto_qmail.o auto_split.o auto_uids.o fifo.o qlibs.lib + ./load install hier.o auto_qmail.o auto_split.o auto_uids.o fifo.o \ + `cat qlibs.lib` + +install.o: \ +compile install.c + ./compile install.c + +instcheck: \ +load instcheck.o hier.o auto_qmail.o auto_split.o auto_uids.o qlibs.lib + ./load instcheck hier.o auto_qmail.o auto_split.o auto_uids.o \ + `cat qlibs.lib` + +instcheck.o: \ +compile instcheck.c + ./compile instcheck.c + +ipalloc.o: \ +compile ipalloc.c + ./compile ipalloc.c + +ipme.o: \ +compile ipme.c hassalen.h + ./compile ipme.c + +ipmeprint: \ +load ipmeprint.o ipme.o ipalloc.o auto_qmail.o \ +dns.lib socket.lib qlibs.lib + ./load ipmeprint ipme.o auto_qmail.o ipalloc.o \ + `cat qlibs.lib` `cat socket.lib` `cat dns.lib` + +ipmeprint.o: \ +compile ipmeprint.c + ./compile ipmeprint.c + +it-analog: \ +columnt matchup \ +ddist deferrals failures senders successes suids \ +recipients rhosts rhosts rxdelay \ +xqp xrecipient xsender \ +zddist zdeferrals zfailures zrecipients zrhosts \ +zrxdelay zsenders zsendmail zsuccesses zsuids zoverall + +it-base: \ +qmail-local qmail-rspawn qmail-lspawn qmail-send qmail-qmaint \ +qmail-clean qmail-start qmail-queue qmail-inject qmail-todo + +it-mbox: \ +forward predate preline condredirect bouncesaying except \ +datemail maildirmake maildir2mbox maildirwatch qbiff qreceipt + +it-clients: \ +mailsubj qmail-remote qmail-qmqpc sendmail + +it-dkim: \ +qmail-dkim qmail-dksign qmail-dkverify + +it-dns: \ +dnscname dnsptr dnsip dnsmxip dnsfq dnstlsa dnstxt \ +hostname ipmeprint spfquery + +it-pop: \ +qmail-popup qmail-pop3d + +it-forward: \ +fastforward forward printforward setforward newaliases \ +printmaillist setmaillist newinclude + +it-control: \ +qmail-badmimetypes qmail-badloadertypes \ +qmail-mfrules qmail-recipients qmail-showctl + +it-ldap: \ +qmail-ldapam + +it-log: \ +splogger qmail-mrtg qmail-mrtg-queue tai64nfrac + +it-pam: \ +qmail-authuser qmail-smtpam qmail-vmailuser \ +qmail-postgrey + +it-queue: \ +qmail-qread qmail-qstat qmail-tcpto qmail-tcpok qmail-upq + +it-server: \ +qmail-qmtpd qmail-qmqpd qmail-smtpd + +it-setup: \ +config config-fast install instcheck + +it-srs: \ +srsforward srsreverse + +it-user: \ +qmail-getpw qmail-newu qmail-pw2u qmail-newmrh + +ldap.lib: \ +tryldap.c compile load + ( ( ./compile tryldap.c && \ + ./load tryldap `head -2 ../conf-ldap | tail -1` ) >/dev/null 2>&1 \ + && echo "`head -2 ../conf-ldap | tail -1` -lldap" || exit 0 ) > ldap.lib +# rm -f tryldap.o tryldap + +load: \ +make-load warn-auto.sh systype + ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load + chmod 755 load + +maildir.o: \ +compile maildir.c + ./compile maildir.c + +maildir2mbox: \ +load maildir2mbox.o maildir.o prioq.o now.o myctime.o gfrom.o \ +datetime.a + ./load maildir2mbox maildir.o prioq.o now.o myctime.o \ + gfrom.o datetime.a `cat qlibs.lib` + +maildir2mbox.o: \ +compile maildir2mbox.c + ./compile maildir2mbox.c + +maildirmake: \ +load maildirmake.o + ./load maildirmake `cat qlibs.lib` + +maildirmake.o: \ +compile maildirmake.c + ./compile maildirmake.c + +maildirwatch: \ +load maildirwatch.o hfield.o headerbody.o maildir.o prioq.o now.o + ./load maildirwatch hfield.o headerbody.o maildir.o \ + prioq.o now.o `cat qlibs.lib` + +maildirwatch.o: \ +compile maildirwatch.c + ./compile maildirwatch.c + +mailsubj: \ +warn-auto.sh mailsubj.sh ../conf-home ../conf-break ../conf-split + cat warn-auto.sh mailsubj.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPLIT}"`head -1 ../conf-split`"}g \ + > mailsubj + chmod 755 mailsubj + +make-compile: \ +make-compile.sh auto-ccld.sh + cat auto-ccld.sh make-compile.sh > make-compile + chmod 755 make-compile + +make-load: \ +make-load.sh auto-ccld.sh + cat auto-ccld.sh make-load.sh > make-load + chmod 755 make-load + +make-makelib: \ +make-makelib.sh auto-ccld.sh + cat auto-ccld.sh make-makelib.sh > make-makelib + chmod 755 make-makelib + +makelib: \ +make-makelib warn-auto.sh systype + ( cat warn-auto.sh; ./make-makelib "`cat systype`" ) > \ + makelib + chmod 755 makelib + +matchup: \ +load matchup.o qlibs.lib + ./load matchup `cat qlibs.lib` + +matchup.o: \ +compile matchup.c + ./compile matchup.c + +myctime.o: \ +compile myctime.c + ./compile myctime.c + +mfrules.o: \ +compile mfrules.c + ./compile mfrules.c + +newaliases: \ +load newaliases.o auto_qmail.o token822.o control.o qlibs.lib + ./load newaliases auto_qmail.o token822.o control.o \ + `cat qlibs.lib` + +newaliases.o: \ +compile newaliases.c + ./compile newaliases.c + +newinclude: \ +load newinclude.o auto_qmail.o token822.o control.o qlibs.lib + ./load newinclude auto_qmail.o token822.o control.o \ + `cat qlibs.lib` + +newinclude.o: \ +compile newinclude.c + ./compile newinclude.c + +newfield.o: \ +compile newfield.c + ./compile newfield.c + +now.o: \ +compile now.c + ./compile now.c + +predate: \ +load predate.o datetime.a qlibs.lib + ./load predate datetime.a `cat qlibs.lib` + +predate.o: \ +compile predate.c + ./compile predate.c + +preline: \ +load preline.o qlibs.lib + ./load preline `cat qlibs.lib` + +preline.o: \ +compile preline.c + ./compile preline.c + +printforward: \ +load printforward.o qlibs.lib + ./load printforward `cat qlibs.lib` + +printforward.o: \ +compile printforward.c + ./compile printforward.c + +printmaillist: \ +load printmaillist.o qlibs.lib + ./load printmaillist `cat qlibs.lib` + +printmaillist.o: \ +compile printmaillist.c + ./compile printmaillist.c + +prioq.o: \ +compile prioq.c + ./compile prioq.c + +qbiff: \ +load qbiff.o headerbody.o hfield.o qlibs.lib + ./load qbiff headerbody.o hfield.o `cat qlibs.lib` + +qbiff.o: \ +compile hasutmp.h qbiff.c + ./compile qbiff.c + +qlibs.lib: \ +tryqlibs.c compile load + ( (./compile tryqlibs.c && \ + ./load tryqlibs -L`head -1 ../conf-qlibs` -lqlibs ) \ + && echo "-L`head -1 ../conf-qlibs` -lqlibs" || exit 0 ) > qlibs.lib + rm -f tryqlibs.o tryqlibs + +qmail-authuser: \ +load qmail-authuser.o auto_qmail.o control.o hmac_md5.o md5c.o \ +constmap.o shadow.lib sha1.o sha256.o \ +qlibs.lib shadow.lib crypt.lib s.lib + ./load qmail-authuser auto_qmail.o control.o \ + constmap.o hmac_md5.o md5c.o sha1.o sha256.o \ + `cat shadow.lib` `cat qlibs.lib` `cat crypt.lib` `cat s.lib` + +qmail-authuser.o: \ +compile qmail-authuser.c hasspnam.h hasuserpw.h + ./compile qmail-authuser.c + +qmail-clean: \ +load qmail-clean.o fmtqfn.o now.o auto_qmail.o auto_split.o qlibs.lib + ./load qmail-clean fmtqfn.o now.o auto_qmail.o auto_split.o `cat qlibs.lib` + +qmail-clean.o: \ +compile qmail-clean.c + ./compile qmail-clean.c + +qmail-dkim: \ +load qmail-dkim.o libqdkim.a dkim.o dkimbase.o dkimsign.o dkimverify.o \ + qlibs.lib dns.lib ssl.lib + ./load qmail-dkim libqdkim.a \ + -lstdc++ `cat dns.lib` `cat qlibs.lib` `cat ssl.lib` + +qmail-dkim.o: \ +compile qmail-dkim.cpp dkim.cpp dkimbase.cpp dkimsign.cpp dkimverify.cpp + ./compile qmail-dkim.cpp + +qmail-dksign: \ +load qmail-dksign.o control.o constmap.o fmtqfn.o rcpthosts.o qmail-dkim \ +auto_qmail.o auto_split.o qlibs.lib + ./load qmail-dksign control.o constmap.o fmtqfn.o rcpthosts.o \ + auto_qmail.o auto_split.o `cat qlibs.lib` + +qmail-dksign.o: \ +compile qmail-dksign.c + ./compile qmail-dksign.c + +qmail-dkverify: \ +load qmail-dkverify.o control.o fmtqfn.o qmail-dkim \ +auto_qmail.o auto_split.o qmail.o qlibs.lib + ./load qmail-dkverify qmail.o control.o fmtqfn.o \ + auto_qmail.o auto_split.o `cat qlibs.lib` + +qmail-dkverify.o: \ +compile qmail-dkverify.c + ./compile qmail-dkverify.c + +qmail-getpw: \ +load qmail-getpw.o auto_break.o auto_usera.o qlibs.lib + ./load qmail-getpw auto_break.o auto_usera.o `cat qlibs.lib` + +qmail-getpw.o: \ +compile qmail-getpw.c + ./compile qmail-getpw.c + +qmail-inject: \ +load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \ +control.o date822fmt.o qmail.o datetime.a token822.o auto_qmail.o qlibs.lib + ./load qmail-inject headerbody.o hfield.o newfield.o \ + constmap.o quote.o now.o control.o date822fmt.o qmail.o datetime.a \ + token822.o auto_qmail.o `cat qlibs.lib` + +qmail-inject.o: \ +compile qmail-inject.c + ./compile qmail-inject.c + +qmail-clean: \ +load qmail-clean.o fmtqfn.o now.o auto_qmail.o auto_split.o qlibs.lib + +qmail-ldapam: \ +load qmail-ldapam.o auto_qmail.o control.o constmap.o \ +qlibs.lib shadow.lib crypt.lib s.lib ldap.lib + ./load qmail-ldapam auto_qmail.o control.o constmap.o \ + `cat shadow.lib` `cat qlibs.lib` `cat crypt.lib` `cat s.lib` `cat ldap.lib` + +qmail-ldapam.o: \ +compile qmail-ldapam.c \ +hasspnam.h hasuserpw.h + ./compile qmail-ldapam.c + +qmail-local: \ +load qmail-local.o auto_qmail.o auto_break.o auto_patrn.o \ +qmail.o quote.o now.o gfrom.o myctime.o datetime.a socket.lib qlibs.lib + ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ + datetime.a auto_qmail.o auto_break.o auto_patrn.o \ + `cat socket.lib` `cat qlibs.lib` + +qmail-local.o: \ +compile qmail-local.c + ./compile qmail-local.c + +qmail-lspawn: \ +load qmail-lspawn.o spawn.o \ +auto_qmail.o auto_uids.o auto_spawn.o qlibs.lib + ./load qmail-lspawn spawn.o \ + auto_qmail.o auto_uids.o auto_spawn.o `cat qlibs.lib` + +qmail-lspawn.o: \ +compile qmail-lspawn.c + ./compile qmail-lspawn.c + +qmail-badmimetypes: \ +load qmail-badmimetypes.o auto_qmail.o qlibs.lib + ./load qmail-badmimetypes auto_qmail.o `cat qlibs.lib` + +qmail-badmimetypes.o: \ +compile qmail-badmimetypes.c + ./compile qmail-badmimetypes.c + +qmail-badloadertypes: \ +load qmail-badloadertypes.o auto_qmail.o qlibs.lib + ./load qmail-badloadertypes auto_qmail.o `cat qlibs.lib` + +qmail-badloadertypes.8: \ +qmail-badloadertypes.9 ../conf-break ../conf-spawn + cat qmail-badloadertypes.9 \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPAWN}"`head -1 ../conf-spawn`"}g \ + > qmail-badloadertypes.8 + +qmail-badloadertypes.o: \ +compile qmail-badloadertypes.c + ./compile qmail-badloadertypes.c + +qmail-newmrh: \ +load qmail-newmrh.o auto_qmail.o qlibs.lib + ./load qmail-newmrh auto_qmail.o `cat qlibs.lib` + +qmail-newmrh.o: \ +compile qmail-newmrh.c + ./compile qmail-newmrh.c + +qmail-recipients: \ +load qmail-recipients.o auto_qmail.o qlibs.lib + ./load qmail-recipients auto_qmail.o `cat qlibs.lib` + +qmail-recipients.o: \ +compile qmail-recipients.c + ./compile qmail-recipients.c + +qmail-vmailuser: \ +load qmail-vmailuser.o auto_qmail.o control.o constmap.o qlibs.lib + ./load qmail-vmailuser auto_qmail.o control.o constmap.o \ + `cat qlibs.lib` + +qmail-vmailuser.o: \ +compile qmail-vmailuser.c + ./compile qmail-vmailuser.c + +qmail-smtpam: \ +load qmail-smtpam.o control.o now.o dns.o constmap.o \ +ipalloc.o ipme.o quote.o auto_qmail.o tcpto.o \ +tls_timeoutio.o tls_errors.o tls_remote.o dns_tlsa.o \ +ssl.lib dns.lib socket.lib qlibs.lib ucspissl.a + ./load qmail-smtpam constmap.o control.o dns_tlsa.o \ + tcpto.o now.o dns.o ipalloc.o ipme.o quote.o auto_qmail.o \ + tls_errors.o tls_remote.o tls_timeoutio.o ucspissl.a \ + `cat ssl.lib` `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +qmail-smtpam.o: \ +compile qmail-smtpam.c + ./compile qmail-smtpam.c + +qmail-mfrules: \ +load qmail-mfrules.o auto_qmail.o qlibs.lib + ./load qmail-mfrules auto_qmail.o `cat qlibs.lib` + +qmail-mfrules.o: \ +compile qmail-mfrules.c + ./compile qmail-mfrules.c + +qmail-mrtg: \ +load qmail-mrtg.o now.o qlibs.lib + ./load qmail-mrtg now.o `cat qlibs.lib` + +qmail-mrtg.o: \ +compile qmail-mfrules.c + ./compile qmail-mrtg.c + +qmail-mrtg-queue: \ +warn-auto.sh qmail-mrtg-queue.sh ../conf-home + cat warn-auto.sh qmail-mrtg-queue.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > qmail-mrtg-queue + chmod 755 qmail-mrtg-queue + +qmail-newu: \ +load qmail-newu.o auto_qmail.o qlibs.lib + ./load qmail-newu auto_qmail.o `cat qlibs.lib` + +qmail-newu.o: \ +compile qmail-newu.c + ./compile qmail-newu.c + +qmail-pop3d: \ +load qmail-pop3d.o commands.o maildir.o prioq.o now.o socket.lib qlibs.lib + ./load qmail-pop3d commands.o maildir.o prioq.o now.o \ + `cat socket.lib` `cat qlibs.lib` + +qmail-pop3d.o: \ +compile qmail-pop3d.c + ./compile qmail-pop3d.c + +qmail-popup: \ +load qmail-popup.o commands.o now.o tls_start.o socket.lib qlibs.lib + ./load qmail-popup commands.o tls_start.o now.o \ + `cat socket.lib` `cat qlibs.lib` + +qmail-popup.o: \ +compile qmail-popup.c + ./compile qmail-popup.c + +qmail-postgrey: \ +load qmail-postgrey.o socket.lib qlibs.lib + ./load qmail-postgrey \ + `cat socket.lib` `cat qlibs.lib` + +qmail-postgrey.o: \ +compile qmail-postgrey.c + ./compile qmail-postgrey.c + +qmail-pw2u: \ +load qmail-pw2u.o constmap.o control.o auto_usera.o auto_break.o auto_qmail.o qlibs.lib + ./load qmail-pw2u constmap.o control.o \ + auto_usera.o auto_break.o auto_qmail.o `cat qlibs.lib` + +qmail-pw2u.o: \ +compile qmail-pw2u.c + ./compile qmail-pw2u.c + +qmail-qmqpc: \ +load qmail-qmqpc.o control.o auto_qmail.o socket.lib qlibs.lib + ./load qmail-qmqpc control.o auto_qmail.o `cat socket.lib` `cat qlibs.lib` + +qmail-qmqpc.o: \ +compile qmail-qmqpc.c + ./compile qmail-qmqpc.c + +qmail-qmqpd: \ +load qmail-qmqpd.o received.o now.o date822fmt.o qmail.o auto_qmail.o \ +datetime.a qlibs.lib + ./load qmail-qmqpd received.o now.o date822fmt.o datetime.a qmail.o \ + auto_qmail.o `cat qlibs.lib` + +qmail-qmqpd.o: \ +compile qmail-qmqpd.c + ./compile qmail-qmqpd.c + +qmail-qmtpd: \ +load qmail-qmtpd.o rcpthosts.o control.o constmap.o received.o \ +date822fmt.o now.o qmail.o datetime.a auto_qmail.o qlibs.lib + ./load qmail-qmtpd rcpthosts.o auto_qmail.o control.o constmap.o \ + received.o date822fmt.o now.o qmail.o datetime.a `cat qlibs.lib` + +qmail-qmtpd.o: \ +compile qmail-qmtpd.c + ./compile qmail-qmtpd.c + +qmail-qread: \ +load qmail-qread.o fmtqfn.o readsubdir.o date822fmt.o datetime.a \ +auto_qmail.o auto_split.o qlibs.lib + ./load qmail-qread fmtqfn.o readsubdir.o date822fmt.o \ + datetime.a auto_qmail.o auto_split.o `cat qlibs.lib` + +qmail-qread.o: \ +compile qmail-qread.c + ./compile qmail-qread.c + +qmail-qstat: \ +warn-auto.sh qmail-qstat.sh ../conf-home ../conf-break ../conf-split + cat warn-auto.sh qmail-qstat.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPLIT}"`head -1 ../conf-split`"}g \ + > qmail-qstat + chmod 755 qmail-qstat + +qmail-queue: \ +load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \ +datetime.a auto_qmail.o auto_split.o auto_uids.o qlibs.lib + ./load qmail-queue auto_qmail.o auto_split.o auto_uids.o \ + triggerpull.o fmtqfn.o now.o date822fmt.o datetime.a `cat qlibs.lib` + +qmail-queue.o: \ +compile qmail-queue.c + ./compile qmail-queue.c + +qmail-qmaint: \ +load qmail-qmaint.o auto_qmail.o auto_split.o auto_uids.o fifo.o \ +fmtqfn.o readsubdir.o qlibs.lib + ./load qmail-qmaint auto_qmail.o auto_split.o auto_uids.o fifo.o \ + fmtqfn.o readsubdir.o `cat qlibs.lib` + +qmail-qmaint.o: \ +compile qmail-qmaint.c + ./compile qmail-qmaint.c + +qmail-remote: \ +load qmail-remote.o control.o tcpto.o now.o dns.o ipalloc.o ipme.o \ +quote.o tls_timeoutio.o tls_errors.o tls_remote.o dns_tlsa.o \ +base64.o constmap.o md5c.o hmac_md5.o auto_qmail.o \ +ssl.lib dns.lib socket.lib qlibs.lib idn2.lib ucspissl.a + ./load qmail-remote control.o tcpto.o now.o \ + base64.o constmap.o md5c.o hmac_md5.o ipalloc.o ipme.o \ + quote.o dns.o ucspissl.a auto_qmail.o dns_tlsa.o \ + tls_errors.o tls_remote.o tls_timeoutio.o ucspissl.a \ + `cat ssl.lib` `cat dns.lib` `cat socket.lib` `cat qlibs.lib` `cat idn2.lib` + +qmail-remote.o: \ +compile qmail-remote.c + ./compile qmail-remote.c + +qmail-rspawn: \ +load qmail-rspawn.o spawn.o tcpto_clean.o now.o \ +auto_qmail.o auto_uids.o auto_spawn.o qlibs.lib + ./load qmail-rspawn spawn.o tcpto_clean.o now.o \ + auto_qmail.o auto_uids.o auto_spawn.o `cat qlibs.lib` + +qmail-rspawn.o: \ +compile qmail-rspawn.c + ./compile qmail-rspawn.c + +qmail-send: \ +load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \ +trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ +datetime.a auto_qmail.o auto_split.o qlibs.lib + ./load qmail-send qsutil.o control.o constmap.o newfield.o \ + prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ + qmail.o date822fmt.o datetime.a auto_qmail.o auto_split.o `cat qlibs.lib` + +qmail-send.o: \ +compile qmail-send.c + ./compile qmail-send.c + +qmail-showctl: \ +load qmail-showctl.o auto_uids.o control.o auto_qmail.o auto_break.o \ +auto_patrn.o auto_spawn.o auto_split.o qlibs.lib + ./load qmail-showctl auto_uids.o auto_qmail.o auto_break.o auto_patrn.o \ + auto_spawn.o auto_split.o control.o `cat qlibs.lib` + +qmail-showctl.o: \ +compile qmail-showctl.c + ./compile qmail-showctl.c + +qmail-smtpd: \ +load qmail-smtpd.o auto_break.o rcpthosts.o commands.o \ +ipme.o ipalloc.o constmap.o control.o received.o \ +recipients.o mfrules.o tls_start.o smtpdlog.o dns.o \ +date822fmt.o now.o qmail.o wildmat.o spf.o spfdnsip.o \ +datetime.a auto_qmail.o base64.o socket.lib qlibs.lib + ./load qmail-smtpd rcpthosts.o recipients.o commands.o \ + mfrules.o tls_start.o auto_break.o smtpdlog.o ipme.o \ + ipalloc.o constmap.o control.o dns.o spf.o spfdnsip.o \ + date822fmt.o now.o qmail.o wildmat.o received.o \ + base64.o datetime.a auto_qmail.o \ + `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +qmail-smtpd.o: \ +compile qmail-smtpd.c + ./compile qmail-smtpd.c + +qmail-start: \ +load qmail-start.o auto_uids.o qlibs.lib + ./load qmail-start auto_uids.o `cat qlibs.lib` + +qmail-start.o: \ +compile qmail-start.c + ./compile qmail-start.c + +qmail-tcpok: \ +load qmail-tcpok.o auto_qmail.o qlibs.lib + ./load qmail-tcpok auto_qmail.o `cat qlibs.lib` + +qmail-tcpok.o: \ +compile qmail-tcpok.c + ./compile qmail-tcpok.c + +qmail-tcpto: \ +load qmail-tcpto.o now.o auto_qmail.o qlibs.lib + ./load qmail-tcpto now.o auto_qmail.o `cat qlibs.lib` + +qmail-tcpto.o: \ +compile qmail-tcpto.c + ./compile qmail-tcpto.c + +qmail-todo: \ +load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o \ +now.o qsutil.o readsubdir.o auto_qmail.o auto_split.o qlibs.lib + ./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \ + readsubdir.o qsutil.o auto_qmail.o auto_split.o `cat qlibs.lib` + +qmail-todo.o: \ +compile qmail-todo.c + ./compile qmail-todo.c + +qmail-upq: \ +warn-auto.sh qmail-upq.sh ../conf-home ../conf-break ../conf-split + cat warn-auto.sh qmail-upq.sh \ + | sed s}QMAIL}"`head -1 ../conf-home`"}g \ + | sed s}BREAK}"`head -1 ../conf-break`"}g \ + | sed s}SPLIT}"`head -1 ../conf-split`"}g \ + > qmail-upq + chmod 755 qmail-upq + +qmail.o: \ +compile qmail.c + ./compile qmail.c + +qreceipt: \ +load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \ +auto_qmail.o qlibs.lib + ./load qreceipt headerbody.o hfield.o quote.o token822.o \ + qmail.o auto_qmail.o `cat qlibs.lib` + +qreceipt.o: \ +compile qreceipt.c + ./compile qreceipt.c + +qsutil.o: \ +compile qsutil.c + ./compile qsutil.c + +quote.o: \ +compile quote.c + ./compile quote.c + +rcpthosts.o: \ +compile rcpthosts.c + ./compile rcpthosts.c + +recipients.o: \ +compile recipients.c + ./compile recipients.c + +recipients: \ +warn-auto.sh recipients.sh ../conf-home + cat warn-auto.sh recipients.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > recipients + chmod 755 recipients + +rhosts: \ +warn-auto.sh rhosts.sh ../conf-home + cat warn-auto.sh rhosts.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > rhosts + chmod 755 rhosts + +rxdelay: \ +warn-auto.sh rxdelay.sh ../conf-home + cat warn-auto.sh rxdelay.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > rxdelay + chmod 755 rxdelay + +senders: \ +warn-auto.sh senders.sh ../conf-home + cat warn-auto.sh senders.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > senders + chmod 755 senders + +smtpdlog.o: \ +compile smtpdlog.c + ./compile smtpdlog.c + +s.lib: \ +tryslib.c compile load + ( ( ./compile tryslib.c && \ + ./load tryslib -ls ) >/dev/null 2>&1 \ + && echo -ls || exit 0 ) > s.lib + rm -f tryslib.o tryslib + +shadow.lib: \ +tryshadow.c compile load + ( ( ./compile tryshadow.c && \ + ./load tryshadow -lshadow ) >/dev/null 2>&1 \ + && echo -lshadow || exit 0 ) > shadow.lib + rm -f tryshadow.o tryshadow + +successes: \ +warn-auto.sh successes.sh ../conf-home + cat warn-auto.sh successes.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > successes + chmod 755 successes + +suids: \ +warn-auto.sh suids.sh ../conf-home + cat warn-auto.sh suids.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > suids + chmod 755 suids + +readsubdir.o: \ +compile readsubdir.c + ./compile readsubdir.c + +received.o: \ +compile received.c + ./compile received.c + +sendmail: \ +load sendmail.o auto_qmail.o qlibs.lib + ./load sendmail auto_qmail.o `cat qlibs.lib` + +sendmail.o: \ +compile sendmail.c + ./compile sendmail.c + +setforward: \ +load setforward.o qlibs.lib + ./load setforward `cat qlibs.lib` + +setforward.o: \ +compile setforward.c + ./compile setforward.c + +setmaillist: \ +load setmaillist.o qlibs.lib + ./load setmaillist `cat qlibs.lib` + +setmaillist.o: \ +compile setmaillist.c + ./compile setmaillist.c + +sha1.o: \ +compile sha1.c + ./compile sha1.c + +sha256.o : \ +compile sha256.c + ./compile sha256.c + +socket.lib: \ +trylsock.c compile load + ( ( ./compile trylsock.c && \ + ./load trylsock -lsocket -lnsl ) >/dev/null 2>&1 \ + && echo -lsocket -lnsl || exit 0 ) > socket.lib + rm -f trylsock.o trylsock + +spawn.o: \ +compile chkspawn spawn.c + ./chkspawn + ./compile spawn.c + +spfdnsip.o: \ +compile spfdnsip.c + ./compile spfdnsip.c + +spf.o: \ +compile spf.c + ./compile spf.c + +spfquery: \ +load spfquery.o spf.o ipme.o ipalloc.o now.o dns.o \ +spfdnsip.o datetime.a dns.lib qlibs.lib + ./load spfquery spf.o ipme.o ipalloc.o spfdnsip.o \ + now.o dns.o datetime.a `cat dns.lib` `cat socket.lib` `cat qlibs.lib` + +spfquery.o: \ +compile spfquery.c + ./compile spfquery.c + +splogger: \ +load splogger.o syslog.lib socket.lib qlibs.lib + ./load splogger `cat syslog.lib` `cat socket.lib` `cat qlibs.lib` + +splogger.o: \ +compile splogger.c + ./compile splogger.c + +srs2.o: \ + compile srs2.c + ./compile srs2.c + +srsforward: \ +load srsforward.o qmail.o auto_qmail.o control.o constmap.o \ +srs2.o sha1.o \ +qlibs.lib + ./load srsforward qmail.o auto_qmail.o control.o constmap.o \ + srs2.o sha1.o `cat qlibs.lib` + +srsforward.o: \ +compile srsforward.c + ./compile srsforward.c + +srsreverse: \ +load srsreverse.o qmail.o auto_break.o auto_qmail.o \ +control.o constmap.o srs2.o sha1.o qlibs.lib + ./load srsreverse qmail.o auto_break.o auto_qmail.o \ + control.o constmap.o srs2.o sha1.o \ + `cat qlibs.lib` + +srsreverse.o: \ +compile srsreverse.c + ./compile srsreverse.c + +strset.o: \ +compile strset.c + ./compile strset.c + +syslog.lib: \ +trysyslog.c compile load + ( ( ./compile trysyslog.c && \ + ./load trysyslog -lgen ) >/dev/null 2>&1 \ + && echo -lgen || exit 0 ) > syslog.lib + rm -f trysyslog.o trysyslog + +systype: \ +find-systype trycpp.c + ./find-systype > systype + +tai64nfrac: \ +load tai64nfrac.o qlibs.lib + ./load tai64nfrac `cat qlibs.lib` + +tai64nfrac.o: \ +compile tai64nfrac.c + ./compile tai64nfrac.c + +tcpto.o: \ +compile tcpto.c + ./compile tcpto.c + +tcpto_clean.o: \ +compile tcpto_clean.c + ./compile tcpto_clean.c + +tls_errors.o: \ +compile tls_errors.c + ./compile tls_errors.c + +tls_remote.o: \ +compile tls_remote.c + ./compile tls_remote.c + +tls_start.o: \ +compile tls_start.c + ./compile tls_start.c tls_errors.c + +tls_timeoutio.o: \ +compile tls_timeoutio.c + ./compile tls_timeoutio.c + +token822.o: \ +compile token822.c + ./compile token822.c + +trigger.o: \ +compile trigger.c + ./compile trigger.c + +triggerpull.o: \ +compile triggerpull.c + ./compile triggerpull.c + +wildmat.o: \ +compile wildmat.c + ./compile wildmat.c + +xqp: \ +warn-auto.sh xqp.sh ../conf-home + cat warn-auto.sh xqp.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > xqp + chmod 755 xqp + +xrecipient: \ +warn-auto.sh xrecipient.sh ../conf-home + cat warn-auto.sh xrecipient.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > xrecipient + chmod 755 xrecipient + +xsender: \ +warn-auto.sh xsender.sh ../conf-home + cat warn-auto.sh xsender.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > xsender + chmod 755 xsender + +zddist: \ +warn-auto.sh zddist.sh ../conf-home + cat warn-auto.sh zddist.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zddist + chmod 755 zddist + +zdeferrals: \ +warn-auto.sh zdeferrals.sh ../conf-home + cat warn-auto.sh zdeferrals.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zdeferrals + chmod 755 zdeferrals + +zfailures: \ +warn-auto.sh zfailures.sh ../conf-home + cat warn-auto.sh zfailures.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zfailures + chmod 755 zfailures + +zoverall: \ +warn-auto.sh zoverall.sh ../conf-home + cat warn-auto.sh zoverall.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zoverall + chmod 755 zoverall + +zrecipients: \ +warn-auto.sh zrecipients.sh ../conf-home + cat warn-auto.sh zrecipients.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zrecipients + chmod 755 zrecipients + +zrhosts: \ +warn-auto.sh zrhosts.sh ../conf-home + cat warn-auto.sh zrhosts.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zrhosts + chmod 755 zrhosts + +zrxdelay: \ +warn-auto.sh zrxdelay.sh ../conf-home + cat warn-auto.sh zrxdelay.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zrxdelay + chmod 755 zrxdelay + +zsenders: \ +warn-auto.sh zsenders.sh ../conf-home + cat warn-auto.sh zsenders.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zsenders + chmod 755 zsenders + +zsendmail: \ +warn-auto.sh zsendmail.sh ../conf-home + cat warn-auto.sh zsendmail.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zsendmail + chmod 755 zsendmail + +zsuccesses: \ +warn-auto.sh zsuccesses.sh ../conf-home + cat warn-auto.sh zsuccesses.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zsuccesses + chmod 755 zsuccesses + +zsuids: \ +warn-auto.sh zsuids.sh ../conf-home + cat warn-auto.sh zsuids.sh \ + | sed s}HOME}"`head -1 ../conf-home`"}g \ + > zsuids + chmod 755 zsuids + +# cpp sources + +SRCS = dkim.cpp dkimbase.cpp dkimsign.cpp dkimverify.cpp +OBJS = $(SRCS:.cpp=.o) + +.cpp.o: + g++ -O2 $< $* + +libqdkim.a: $(OBJS) + @rm -f libqdkim.a + ar cr libqdkim.a $(OBJS) + ranlib libqdkim.a + diff --git a/sqmail-4.3.07/src/TARGETS b/sqmail-4.3.07/src/TARGETS new file mode 100644 index 0000000..c83e46e --- /dev/null +++ b/sqmail-4.3.07/src/TARGETS @@ -0,0 +1,270 @@ +auto-ccld.sh +auto-gid +auto-gid.o +auto-int +auto-int.o +auto-int8 +auto-int8.o +auto-str +auto-str.o +auto-uid +auto-uid.o +auto_break.o +auto_patrn.c +auto_patrn.o +auto_qmail.c +auto_qmail.o +auto_spawn.c +auto_spawn.o +auto_split.c +auto_split.o +auto_uids.c +auto_uids.o +auto_usera.o +base64.o +bouncesaying +bouncesaying.o +chkspawn +chkspawn.o +columnt +columnt.o +commands.o +compile +condredirect +condredirect.o +constmap.o +control.o +date822fmt.o +datetime.a +datetime.o +datetime_un.o +ddist +deferrals +dkim.o +dkimbase.o +dkimsign.o +dkimverify.o +dns.lib +dns.o +dns_tlsa.o +dnscname +dnscname.o +dnsfq +dnsfq.o +dnsip +dnsip.o +dnsmxip +dnsmxip.o +dnsptr +dnsptr.o +dnstlsa +dnstlsa.o +dnstxt +dnstxt.o +except +except.o +failures +fastforward +fastforward.o +fifo.o +find-systype +fmtqfn.o +forward +forward.o +gfrom.o +headerbody.o +hfield.o +hier.o +hmac_md5.o +hostname +hostname.o +idedit +idedit.o +idn2.lib +install.o +instcheck.o +ipalloc.o +ipme.o +ipmeprint +ipmeprint.o +libdkim.a +load +maildir.o +maildir2mbox +maildir2mbox.o +maildirmake +maildirmake.o +maildirwatch +maildirwatch.o +mailsubj +make-compile +make-load +make-makelib +makelib +matchup +matchup.o +md5c.o +mfrules.o +myctime.o +newaliases +newaliases.o +newfield.o +newinclude +newinclude.o +now.o +predate.o +preline +preline.o +printforward +printforward.o +printmaillist +printmaillist.o +prioq.o +qbiff +qbiff.o +qlibs.lib +qmail-authuser +qmail-authuser.o +qmail-badloadertypes +qmail-badloadertypes.o +qmail-badmimetypes +qmail-badmimetypes.o +qmail-clean +qmail-clean.o +qmail-dkim +qmail-dkim.o +qmail-dksign.o +qmail-dkverify.o +qmail-getpw +qmail-getpw.o +qmail-inject +qmail-inject.o +qmail-ldapam +qmail-ldapam.o +qmail-local +qmail-local.o +qmail-lspawn +qmail-lspawn.o +qmail-mfrules +qmail-mfrules.o +qmail-mrtg +qmail-mrtg-queue +qmail-mrtg.o +qmail-newmrh +qmail-newmrh.o +qmail-newu +qmail-newu.o +qmail-pop3d +qmail-pop3d.o +qmail-popup +qmail-popup.o +qmail-postgrey +qmail-postgrey.o +qmail-pw2u +qmail-pw2u.o +qmail-qmaint +qmail-qmaint.o +qmail-qmqpc +qmail-qmqpc.o +qmail-qmqpd +qmail-qmqpd.o +qmail-qmtpd +qmail-qmtpd.o +qmail-qread +qmail-qread.o +qmail-qstat +qmail-queue +qmail-queue.o +qmail-recipients +qmail-recipients.o +qmail-remote +qmail-remote.o +qmail-rspawn +qmail-rspawn.o +qmail-send +qmail-send.o +qmail-showctl +qmail-showctl.o +qmail-smtpam +qmail-smtpam.o +qmail-smtpd +qmail-smtpd.o +qmail-start +qmail-start.o +qmail-tcpok +qmail-tcpok.o +qmail-tcpto +qmail-tcpto.o +qmail-todo +qmail-todo.o +qmail-upq +qmail-vmailuser +qmail-vmailuser.o +qmail.o +qreceipt +qreceipt.o +qsutil.o +quote.o +rcpthosts.o +readsubdir.o +received.o +recipients +recipients.o +rhosts +rxdelay +s.lib +senders +sendmail +sendmail.o +setforward +setforward.o +setmaillist +setmaillist.o +sha1.o +sha256.o +shadow.lib +smtpdlog.o +socket.lib +spawn.o +spf.o +spfdnsip.o +spfquery +spfquery.o +splogger +splogger.o +srs2.o +srsforward.o +srsreverse.o +strset.o +successes +suids +syslog.lib +systype +tai64nfrac +tai64nfrac.o +tcpto.o +tcpto_clean.o +tls_errors.o +tls_remote.o +tls_start.o +tls_timeoutio.o +token822.o +trigger.o +triggerpull.o +tryrsolv.o +trysalen.o +wildmat.o +xqp +xrecipient +xsender +zddist +zdeferrals +zfailures +zoverall +zrecipients +zrhosts +zrxdelay +zsenders +zsendmail +zsuccesses +zsuids diff --git a/sqmail-4.3.07/src/auto-gid.c b/sqmail-4.3.07/src/auto-gid.c new file mode 100644 index 0000000..c5a39df --- /dev/null +++ b/sqmail-4.3.07/src/auto-gid.c @@ -0,0 +1,47 @@ +#include <unistd.h> +#include <sys/types.h> +#include <grp.h> +#include "buffer.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char inbuf[256]; +buffer b = BUFFER_INIT(write,1,inbuf,sizeof(inbuf)); + +void outs(char *s) +{ + if (buffer_puts(&b,s) == -1) _exit(111); +} + +int main(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) { + buffer_puts(buffer_2,"fatal: unable to find group "); + buffer_puts(buffer_2,value); + buffer_puts(buffer_2,"\n"); + buffer_flush(buffer_2); + _exit(111); + } + + strnum[fmt_ulong(strnum,(unsigned long) gr->gr_gid)] = 0; + + outs("int "); + outs(name); + outs(" = "); + outs(strnum); + outs(";\n"); + if (buffer_flush(&b) == -1) _exit(111); + _exit(0); +} diff --git a/sqmail-4.3.07/src/auto-int.c b/sqmail-4.3.07/src/auto-int.c new file mode 100644 index 0000000..58f44b2 --- /dev/null +++ b/sqmail-4.3.07/src/auto-int.c @@ -0,0 +1,38 @@ +#include <unistd.h> +#include "buffer.h" +#include <unistd.h> +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char inbuf[256]; +buffer b = BUFFER_INIT(write,1,inbuf,sizeof(inbuf)); + +void out(char *s) +{ + if (buffer_puts(&b,s) == -1) _exit(111); +} + +int main(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; + + out("int "); + out(name); + out(" = "); + out(strnum); + out(";\n"); + if (buffer_flush(&b) == -1) _exit(111); + _exit(0); +} diff --git a/sqmail-4.3.07/src/auto-int8.c b/sqmail-4.3.07/src/auto-int8.c new file mode 100644 index 0000000..fd5ead6 --- /dev/null +++ b/sqmail-4.3.07/src/auto-int8.c @@ -0,0 +1,37 @@ +#include <unistd.h> +#include "buffer.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char inbuf[256]; +buffer b = BUFFER_INIT(write,1,inbuf,sizeof(inbuf)); + +void out(char *s) +{ + if (buffer_puts(&b,s) == -1) _exit(111); +} + +int main(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; + + out("int "); + out(name); + out(" = "); + out(strnum); + out(";\n"); + if (buffer_flush(&b) == -1) _exit(111); + _exit(0); +} diff --git a/sqmail-4.3.07/src/auto-str.c b/sqmail-4.3.07/src/auto-str.c new file mode 100644 index 0000000..72e93bd --- /dev/null +++ b/sqmail-4.3.07/src/auto-str.c @@ -0,0 +1,41 @@ +#include <unistd.h> +#include "buffer.h" +#include "exit.h" + +char inbuf[BUFFER_SMALL]; +buffer b = BUFFER_INIT(write,1,inbuf,sizeof(inbuf)); + +void out(char *s) +{ + if (buffer_puts(&b,s) == -1) _exit(111); +} + +int main(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); + + out("char "); + out(name); + out("[] = \"\\\n"); + + while ((ch = *value++)) { + out("\\"); + octal[3] = 0; + octal[2] = '0' + (ch & 7); ch >>= 3; + octal[1] = '0' + (ch & 7); ch >>= 3; + octal[0] = '0' + (ch & 7); + out(octal); + } + + out("\\\n\";\n"); + if (buffer_flush(&b) == -1) _exit(111); + _exit(0); +} diff --git a/sqmail-4.3.07/src/auto-uid.c b/sqmail-4.3.07/src/auto-uid.c new file mode 100644 index 0000000..21f469b --- /dev/null +++ b/sqmail-4.3.07/src/auto-uid.c @@ -0,0 +1,47 @@ +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include "buffer.h" +#include "exit.h" +#include "scan.h" +#include "fmt.h" + +char inbuf[256]; +buffer b = BUFFER_INIT(write,1,inbuf,sizeof(inbuf)); + +void outs(char *s) /* was named puts, but Solaris pwd.h includes stdio.h. dorks. */ +{ + if (buffer_puts(&b,s) == -1) _exit(111); +} + +int main(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) { + buffer_puts(buffer_2,"fatal: unable to find user "); + buffer_puts(buffer_2,value); + buffer_puts(buffer_2,"\n"); + buffer_flush(buffer_2); + _exit(111); + } + + strnum[fmt_ulong(strnum,(unsigned long) pw->pw_uid)] = 0; + + outs("int "); + outs(name); + outs(" = "); + outs(strnum); + outs(";\n"); + if (buffer_flush(&b) == -1) _exit(111); + _exit(0); +} diff --git a/sqmail-4.3.07/src/base64.c b/sqmail-4.3.07/src/base64.c new file mode 100644 index 0000000..fd38fe3 --- /dev/null +++ b/sqmail-4.3.07/src/base64.c @@ -0,0 +1,119 @@ +#include "base64.h" +#include "str.h" + +static char *b64alpha = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#define B64PAD '=' + +/* returns 0 ok, 1 illegal, -1 problem */ + +int b64decode(const unsigned char *in,int l,stralloc *out) +/* stralloc *out => not null terminated */ +{ + int p = 0; + int n; + unsigned int x; + int i, j; + char *s; + unsigned char b[3]; + + if (l == 0) { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + while (in[l-1] == B64PAD) { + p ++; + l--; + } + + n = (l + p) / 4; + i = (n * 3) - p; + if (!stralloc_ready(out,i)) return -1; + out->len = i; + s = out->s; + + for (i = 0; i < n - 1; i++) { + x = 0; + for (j = 0; j < 4; j++) { + if (in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if (in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if (in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if (in[j] == '+') + x = (x << 6) + 62; + else if (in[j] == '/') + x = (x << 6) + 63; + else if (in[j] == '=') + x = (x << 6); + else return 1; + } + + s[2] = (unsigned char)(x & 255); x >>= 8; + s[1] = (unsigned char)(x & 255); x >>= 8; + s[0] = (unsigned char)(x & 255); x >>= 8; + s += 3; in += 4; + } + + x = 0; + for (j = 0; j < 4; j++) { + if (in[j] >= 'A' && in[j] <= 'Z') + x = (x << 6) + (unsigned int)(in[j] - 'A' + 0); + else if (in[j] >= 'a' && in[j] <= 'z') + x = (x << 6) + (unsigned int)(in[j] - 'a' + 26); + else if (in[j] >= '0' && in[j] <= '9') + x = (x << 6) + (unsigned int)(in[j] - '0' + 52); + else if (in[j] == '+') + x = (x << 6) + 62; + else if (in[j] == '/') + x = (x << 6) + 63; + else if (in[j] == '=') + x = (x << 6); + else return 1; + } + + b[2] = (unsigned char)(x & 255); x >>= 8; + b[1] = (unsigned char)(x & 255); x >>= 8; + b[0] = (unsigned char)(x & 255); x >>= 8; + + for (i = 0; i < 3 - p; i++) + s[i] = b[i]; + + return 0; +} + +int b64encode(stralloc *in,stralloc *out) +{ + unsigned char a, b, c; + int i; + char *s; + + if (in->len == 0) + { + if (!stralloc_copys(out,"")) return -1; + return 0; + } + + i = in->len / 3 * 4 + 4; + if (!stralloc_ready(out,i)) return -1; + s = out->s; + + for (i = 0; i < in->len; i += 3) { + a = in->s[i]; + b = i + 1 < in->len ? in->s[i + 1] : 0; + c = i + 2 < in->len ? in->s[i + 2] : 0; + + *s++ = b64alpha[a >> 2]; + *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)]; + + if (i + 1 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[((b & 0x0f) << 2) | (c >> 6)]; + + if (i + 2 >= in->len) *s++ = B64PAD; + else *s++ = b64alpha[c & 0x3f]; + } + out->len = s - out->s; + return 0; +} diff --git a/sqmail-4.3.07/src/bouncesaying.c b/sqmail-4.3.07/src/bouncesaying.c new file mode 100644 index 0000000..416d76d --- /dev/null +++ b/sqmail-4.3.07/src/bouncesaying.c @@ -0,0 +1,38 @@ +#include <unistd.h> +#include "logmsg.h" +#include "wait.h" +#include "sig.h" +#include "exit.h" + +#define WHO "bouncesaying" + +int main(int argc,char **argv) +{ + int pid; + int wstat; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"bouncesaying error [ program [ arg ... ] ]"); + + if (argv[2]) { + pid = fork(); + if (pid == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + if (pid == 0) { + execvp(argv[2],argv + 2); + if (errno) _exit(111); + _exit(100); + } + if (wait_pid(&wstat,pid) == -1) + logmsg(WHO,111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + logmsg(WHO,111,FATAL,"child crashed"); + switch (wait_exitcode(wstat)) { + case 0: break; + case 111: logmsg(WHO,111,FATAL,"temporary child error"); + default: _exit(0); + } + } + + logmsg(WHO,100,LOG,argv[1]); +} diff --git a/sqmail-4.3.07/src/chkshsgr.c b/sqmail-4.3.07/src/chkshsgr.c new file mode 100644 index 0000000..fc752bd --- /dev/null +++ b/sqmail-4.3.07/src/chkshsgr.c @@ -0,0 +1,13 @@ +#include <grp.h> +#include <sys/types.h> +#include <unistd.h> +#include "exit.h" + +int main() +{ + short x[4]; + + x[0] = x[1] = 0; + if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1); + _exit(0); +} diff --git a/sqmail-4.3.07/src/chkspawn.c b/sqmail-4.3.07/src/chkspawn.c new file mode 100644 index 0000000..511489a --- /dev/null +++ b/sqmail-4.3.07/src/chkspawn.c @@ -0,0 +1,48 @@ +#include "buffer.h" +#include "fmt.h" +#include "select.h" +#include "exit.h" +#include "auto_spawn.h" +#define MAXSPAWN 1000 /* Silent spawn limit increased to 1000 */ + +char num[FMT_ULONG]; +fd_set fds; + +int main() +{ + unsigned long hiddenlimit; + unsigned long maxnumd; + + hiddenlimit = sizeof(fds) * 8; + maxnumd = (hiddenlimit - 5) / 2; + + if (auto_spawn < 1) { + buffer_puts(buffer_2,"Oops. You have set conf-spawn lower than 1.\n"); + buffer_flush(buffer_2); + _exit(1); + } + + if (auto_spawn > MAXSPAWN) { + buffer_puts(buffer_2,"Oops. You have set conf-spawn higher than MAXSPAWN.\n"); + buffer_flush(buffer_2); + _exit(1); + } + + if (auto_spawn > maxnumd) { + buffer_puts(buffer_2,"Oops. Your system's FD_SET() has a hidden limit of "); + buffer_put(buffer_2,num,fmt_ulong(num,hiddenlimit)); + buffer_puts(buffer_2," descriptors.\n\ +This means that the qmail daemons could crash if you set the run-time\n\ +concurrency higher than "); + buffer_put(buffer_2,num,fmt_ulong(num,maxnumd)); + buffer_puts(buffer_2,". So I'm going to insist that the concurrency\n\ +limit in conf-spawn be at most "); + buffer_put(buffer_2,num,fmt_ulong(num,maxnumd)); + buffer_puts(buffer_2,". Right now it's "); + buffer_put(buffer_2,num,fmt_ulong(num,(unsigned long) auto_spawn)); + buffer_puts(buffer_2,".\n"); + buffer_flush(buffer_2); + _exit(1); + } + _exit(0); +} diff --git a/sqmail-4.3.07/src/columnt.c b/sqmail-4.3.07/src/columnt.c new file mode 100644 index 0000000..9e4cf0e --- /dev/null +++ b/sqmail-4.3.07/src/columnt.c @@ -0,0 +1,104 @@ +#include <unistd.h> +#include "alloc.h" +#include "logmsg.h" +#include "buffer.h" +#include "stralloc.h" +#include "exit.h" +#include "readclose.h" + +#define WHO "columnt" + +#define BSIZE 4096 + +char outbuf[BSIZE]; +buffer bo = BUFFER_INIT(write,1,outbuf,sizeof(outbuf)); + +void nomem() { logmsg(WHO,111,FATAL,"out of memory"); } +void die_read() { logmsg(WHO,110,ERROR,"unable to read input: "); } +void die_write() { logmsg(WHO,110,ERROR,"unable to write output: "); } + +stralloc file = {0}; +int *width; +int maxfield = 0; + +void nothing() +{ + ; +} + +void printline() +{ + if (buffer_put(&bo,"\n",1) == -1) die_write(); +} + +void maxfield_check(int fieldnum,char *buf,int len) +{ + if (fieldnum > maxfield) maxfield = fieldnum; +} + +void width_check(int fieldnum,char *buf,int len) +{ + if (len > width[fieldnum]) width[fieldnum] = len; +} + +void width_init() +{ + int i; + + width = (int *) alloc((maxfield + 1) * sizeof(int)); + if (!width) nomem(); + for (i = 0; i <= maxfield; ++i) + width[i] = 0; +} + +void printfield(int fieldnum,char *buf,int len) +{ + int i; + + if (fieldnum < maxfield) + for (i = len; i < width[fieldnum]; ++i) + if (buffer_put(&bo," ",1) == -1) die_write(); + + if (buffer_put(&bo,buf,len) == -1) die_write(); + + if (fieldnum < maxfield) + if (buffer_put(&bo," ",2) == -1) die_write(); +} + +void split(void (*dofield)(), void (*doline)()) +{ + int i; + int j; + int fieldpos; + int fieldnum; + + for (j = i = 0; j < file.len; ++j) + if (file.s[j] == '\n') { + fieldnum = 0; + for (;;) { + while ((file.s[i] == ' ') || (file.s[i] == '\t')) ++i; + if (i == j) break; + fieldpos = i; + while ((file.s[i] != ' ') && (file.s[i] != '\t') && (file.s[i] != '\n')) ++i; + dofield(fieldnum++,file.s + fieldpos,i - fieldpos); + } + doline(); + i = j + 1; + } +} + +int main() +{ + if (readclose_append(0,&file,BSIZE) == -1) die_read(); + if (!file.len) _exit(0); + if (file.s[file.len - 1] != '\n') + if (!stralloc_append(&file,"\n")) nomem(); + + split(maxfield_check,nothing); + width_init(); + split(width_check,nothing); + split(printfield,printline); + + if (buffer_flush(&bo) == -1) die_write(); + _exit(0); +} diff --git a/sqmail-4.3.07/src/commands.c b/sqmail-4.3.07/src/commands.c new file mode 100644 index 0000000..8602f7c --- /dev/null +++ b/sqmail-4.3.07/src/commands.c @@ -0,0 +1,40 @@ +#include "commands.h" +#include "buffer.h" +#include "stralloc.h" +#include "str.h" +#include "case.h" + +static stralloc cmd = {0}; + +int commands(buffer *b,struct commands *c) +{ + int i; + char *arg; + + for (;;) { + if (!stralloc_copys(&cmd,"")) return -1; + + for (;;) { + if (!stralloc_readyplus(&cmd,1)) return -1; + i = buffer_get(b,cmd.s + cmd.len,1); + if (i != 1) return i; + if (cmd.s[cmd.len] == '\n') break; + ++cmd.len; + } + + if (cmd.len > 0) if (cmd.s[cmd.len - 1] == '\r') --cmd.len; + + cmd.s[cmd.len] = 0; + + i = str_chr(cmd.s,' '); + arg = cmd.s + i; + while (*arg == ' ') ++arg; + cmd.s[i] = 0; + + for (i = 0; c[i].text; ++i) + if (case_equals(c[i].text,cmd.s)) break; + + c[i].fun(arg); + if (c[i].flush) c[i].flush(); + } +} diff --git a/sqmail-4.3.07/src/condredirect.c b/sqmail-4.3.07/src/condredirect.c new file mode 100644 index 0000000..2e5cc11 --- /dev/null +++ b/sqmail-4.3.07/src/condredirect.c @@ -0,0 +1,81 @@ +#include <unistd.h> +#include "sig.h" +#include "exit.h" +#include "env.h" +#include "logmsg.h" +#include "wait.h" +#include "seek.h" +#include "qmail.h" +#include "buffer.h" +#include "fmt.h" + +#define WHO "condredirect" + +struct qmail qqt; + +ssize_t mywrite(int fd,char *buf,int len) +{ + qmail_put(&qqt,buf,len); + return len; +} + +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); +char outbuf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(mywrite,-1,outbuf,sizeof(outbuf)); + +char num[FMT_ULONG]; + +int main(int argc,char **argv) +{ + char *sender; + char *dtline; + int pid; + int wstat; + char *qqx; + + if (!argv[1] || !argv[2]) + logmsg(WHO,100,USAGE,"condredirect newaddress program [ arg ... ]"); + + pid = fork(); + if (pid == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + if (pid == 0) { + execvp(argv[2],argv + 2); + if (errno) _exit(111); + _exit(100); + } + if (wait_pid(&wstat,pid) == -1) + logmsg(WHO,111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + logmsg(WHO,111,FATAL,"child crashed"); + switch (wait_exitcode(wstat)) { + case 0: break; + case 111: logmsg(WHO,111,FATAL,"temporary child error"); + default: _exit(0); + } + + if (seek_begin(0) == -1) + logmsg(WHO,111,FATAL,"unable to rewind: "); + sig_pipeignore(); + + sender = env_get("SENDER"); + if (!sender) logmsg(WHO,100,ERROR,"SENDER not set"); + dtline = env_get("DTLINE"); + if (!dtline) logmsg(WHO,100,ERROR,"DTLINE not set"); + + if (qmail_open(&qqt) == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + qmail_puts(&qqt,dtline); + if (buffer_copy(&bo,&bi) != 0) + logmsg(WHO,111,FATAL,"unable to read message: "); + buffer_flush(&bo); + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + qmail_from(&qqt,sender); + qmail_to(&qqt,argv[1]); + qqx = qmail_close(&qqt); + if (*qqx) logmsg(WHO,*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + logmsg(WHO,0,LOG,B("qp ",num)); +} diff --git a/sqmail-4.3.07/src/config-fast.sh b/sqmail-4.3.07/src/config-fast.sh new file mode 100755 index 0000000..f41796c --- /dev/null +++ b/sqmail-4.3.07/src/config-fast.sh @@ -0,0 +1,35 @@ +fqdn="$1" +echo Your fully qualified host name is "$fqdn". + +echo Putting "$fqdn" into control/me... +echo "$fqdn" > HOME/control/me +chmod 644 HOME/control/me + +( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | ( + read ddom + echo Putting "$ddom" into control/defaultdomain... + echo "$ddom" > HOME/control/defaultdomain + chmod 644 HOME/control/defaultdomain +) ) + +( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | ( + read pdom + echo Putting "$pdom" into control/plusdomain... + echo "$pdom" > HOME/control/plusdomain + chmod 644 HOME/control/plusdomain +) ) + +echo Putting "$fqdn" into control/locals... +echo "$fqdn" >> HOME/control/locals +chmod 644 HOME/control/locals + +echo Putting "$fqdn" into control/rcpthosts... +echo "$fqdn" >> HOME/control/rcpthosts +chmod 644 HOME/control/rcpthosts +echo "Now qmail will refuse to accept SMTP messages except to $fqdn." +echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!' + +echo Enabling TLS "*:" into control/tlsdestinations ... +echo "*:" >> HOME/control/tlsdestinations +chmod 644 HOME/control/tlsdestinations +echo "Now qmail-remote will send TLS encrypted mails to enabled destinations." diff --git a/sqmail-4.3.07/src/config.sh b/sqmail-4.3.07/src/config.sh new file mode 100755 index 0000000..a2ecd25 --- /dev/null +++ b/sqmail-4.3.07/src/config.sh @@ -0,0 +1,64 @@ +./hostname | tr '[A-Z]' '[a-z]' | ( + if read host + then + echo Your hostname is "$host". + ./dnsfq "$host" | tr '[A-Z]' '[a-z]' | ( + if read fqdn + then + echo Your host\'s fully qualified name in DNS is "$fqdn". + echo Putting "$fqdn" into control/me... + echo "$fqdn" > HOME/control/me + chmod 644 HOME/control/me + ( echo "$fqdn" | sed 's/^\([^\.]*\)\.\([^\.]*\)\./\2\./' | ( + read ddom + echo Putting "$ddom" into control/defaultdomain... + echo "$ddom" > HOME/control/defaultdomain + chmod 644 HOME/control/defaultdomain + ) ) + ( echo "$fqdn" | sed 's/^.*\.\([^\.]*\)\.\([^\.]*\)$/\1.\2/' | ( + read pdom + echo Putting "$pdom" into control/plusdomain... + echo "$pdom" > HOME/control/plusdomain + chmod 644 HOME/control/plusdomain + ) ) + echo ' ' + echo Checking local IP addresses: + : > HOME/control/locals + chmod 644 HOME/control/locals + ( ./dnsip "$fqdn" + ./ipmeprint ) | sort -u | \ + ( + while read localip + do + echo "$localip: " | tr -d '\012' + ./dnsptr "$localip" 2>/dev/null | ( + if read local + then + echo Adding "$local" to control/locals... + echo "$local" >> HOME/control/locals + else + echo PTR lookup failed. I assume this address has no DNS name. + fi + ) + done + ) + echo ' ' + echo If there are any other domain names that point to you, + echo you will have to add them to HOME/control/locals. + echo You don\'t have to worry about aliases, i.e., domains with CNAME records. + echo ' ' + echo Copying HOME/control/locals to HOME/control/rcpthosts... + cp HOME/control/locals HOME/control/rcpthosts + chmod 644 HOME/control/rcpthosts + echo 'Now qmail will refuse to accept SMTP messages except to those hosts.' + echo 'Make sure to change rcpthosts if you add hosts to locals or virtualdomains!' + else + echo Sorry, I couldn\'t find your host\'s canonical name in DNS. + echo You will have to set up control/me yourself. + fi + ) + else + echo Sorry, I couldn\'t find your hostname. + echo You will have to set up control/me yourself. + fi +) diff --git a/sqmail-4.3.07/src/constmap.c b/sqmail-4.3.07/src/constmap.c new file mode 100644 index 0000000..ea153ea --- /dev/null +++ b/sqmail-4.3.07/src/constmap.c @@ -0,0 +1,168 @@ +#include "constmap.h" +#include "alloc.h" +#include "case.h" + +static constmap_hash hash(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(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(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; +} + +int constmap_init_char(struct constmap *cm,char *s,int len,int flagcolon,char flagchar) +{ + int i; + int j; + int k; + int pos; + constmap_hash h; + + if (!flagchar || flagchar == 0 || flagchar == '\0') { + flagchar = ':'; + } + + 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] == flagchar) 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(struct constmap *cm) +{ + alloc_free(cm->next); + alloc_free(cm->hash); + alloc_free(cm->inputlen); + alloc_free(cm->input); + alloc_free(cm->first); +} diff --git a/sqmail-4.3.07/src/control.c b/sqmail-4.3.07/src/control.c new file mode 100644 index 0000000..2558225 --- /dev/null +++ b/sqmail-4.3.07/src/control.c @@ -0,0 +1,122 @@ +#include <unistd.h> +#include "open.h" +#include "getln.h" +#include "stralloc.h" +#include "buffer.h" +#include "logmsg.h" +#include "control.h" +#include "alloc.h" +#include "scan.h" +#include "error.h" + +static char inbuf[2048]; +static stralloc line = {0}; +static stralloc me = {0}; +static int meok = 0; + +/** @file control.c +*/ + +static void striptrailingwhitespace(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(void) +{ + int r; + + r = control_readline(&me,"control/me"); + if (r == 1) meok = 1; + return r; +} + +int control_rldef(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(stralloc *sa,char *fn) +{ + buffer b; + int fd; + int match; + + fd = open_read(fn); + if (fd == -1) { if (errno == ENOENT) return 0; return -1; } + + buffer_init(&b,read,fd,inbuf,sizeof(inbuf)); + + if (getln(&b,sa,&match,'\n') == -1) { close(fd); return -1; } + + striptrailingwhitespace(sa); + + close(fd); + return 1; +} + +int control_readint(int *i,char *fn) +{ + unsigned long u; + + switch (control_readline(&line,fn)) { + case 0: return 0; + case -1: return -1; + } + if (!stralloc_0(&line)) return -1; + if (!scan_ulong(line.s,&u)) return 0; + *i = u; + + return 1; +} + +int control_readfile(stralloc *sa,char *fn,int flagme) +{ + buffer b; + int fd; + int match; + + if (!stralloc_copys(sa,"")) return -1; + + fd = open_read(fn); + if (fd == -1) { + if (errno == ENOENT) { + if (flagme && meok) { + if (!stralloc_copy(sa,&me)) return -1; + if (!stralloc_0(sa)) return -1; + return 1; + } + return 0; + } + return -1; + } + + buffer_init(&b,read,fd,inbuf,sizeof(inbuf)); + + for (;;) { + if (getln(&b,&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; +} diff --git a/sqmail-4.3.07/src/crypt.lib b/sqmail-4.3.07/src/crypt.lib new file mode 100644 index 0000000..2fd0d0c --- /dev/null +++ b/sqmail-4.3.07/src/crypt.lib @@ -0,0 +1 @@ +-lcrypt diff --git a/sqmail-4.3.07/src/date822fmt.c b/sqmail-4.3.07/src/date822fmt.c new file mode 100644 index 0000000..fc2d1f7 --- /dev/null +++ b/sqmail-4.3.07/src/date822fmt.c @@ -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(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; +} diff --git a/sqmail-4.3.07/src/datemail.sh b/sqmail-4.3.07/src/datemail.sh new file mode 100755 index 0000000..66ea257 --- /dev/null +++ b/sqmail-4.3.07/src/datemail.sh @@ -0,0 +1 @@ +exec HOME/bin/predate HOME/bin/sendmail ${1+"$@"} diff --git a/sqmail-4.3.07/src/datetime.c b/sqmail-4.3.07/src/datetime.c new file mode 100644 index 0000000..3db5f7f --- /dev/null +++ b/sqmail-4.3.07/src/datetime.c @@ -0,0 +1,53 @@ +/* 19950925 */ +#include "datetime.h" + +void datetime_tai(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; +} diff --git a/sqmail-4.3.07/src/datetime_un.c b/sqmail-4.3.07/src/datetime_un.c new file mode 100644 index 0000000..e84806d --- /dev/null +++ b/sqmail-4.3.07/src/datetime_un.c @@ -0,0 +1,35 @@ +#include "datetime.h" + +/* roughly 100x faster than mktime() */ + +datetime_sec datetime_untai(struct datetime *dt) +{ + int year; + int day; + int mon; + + year = dt->year + 1900; + + mon = dt->mon; + if (mon >= 2) { mon -= 2; } + else { mon += 10; --year; } + + day = (dt->mday - 1) * 10 + 5 + 306 * mon; + day /= 10; + + if (day == 365) { year -= 3; day = 1460; } + else { day += 365 * (year % 4); } + year /= 4; + + day += 1461 * (year % 25); + year /= 25; + + if (day == 36524) { year -= 3; day = 146096; } + else { day += 36524 * (year % 4); } + year /= 4; + + day += 146097 * (year - 5); + day += 11017; + + return ((day * 24 + dt->hour) * 60 + dt->min) * 60 + dt->sec; +} diff --git a/sqmail-4.3.07/src/ddist.sh b/sqmail-4.3.07/src/ddist.sh new file mode 100644 index 0000000..b632572 --- /dev/null +++ b/sqmail-4.3.07/src/ddist.sh @@ -0,0 +1,31 @@ + +awk '/^d k/ { print $5 - $3 }' \ +| sort -n \ +| awk ' + { x += 1; cumulative[$1] = x } + END { + if (x > 0) { + for (p = 0;p <= 100;++p) mindel[p] = -1 + for (d in cumulative) { + p = int((cumulative[d] * 100) / x) + if (mindel[p] == -1) mindel[p] = d + else if (d < mindel[p]) mindel[p] = d + totdel[p] += d + numdel[p] += 1 + } + td = 0 + nd = 0 + for (p = 0;p <= 100;++p) { + td += totdel[p] + nd += numdel[p] + if (p >= 10) + if (nd > 0) + if (mindel[p] >= 0) { + str1 = sprintf("%.2f",mindel[p]) + str2 = sprintf("%.2f",td / nd) + print str1, str2, p + } + } + } + } +' diff --git a/sqmail-4.3.07/src/deferrals.sh b/sqmail-4.3.07/src/deferrals.sh new file mode 100644 index 0000000..84b19f6 --- /dev/null +++ b/sqmail-4.3.07/src/deferrals.sh @@ -0,0 +1,14 @@ + +awk ' + /^d z/ { + reason = $11 + temp[reason] += 1 + xdelay[reason] += $5 - $4 + } + END { + for (reason in temp) { + str = sprintf("%.2f",xdelay[reason]) + print temp[reason],str,reason + } + } +' diff --git a/sqmail-4.3.07/src/direntry.h1 b/sqmail-4.3.07/src/direntry.h1 new file mode 100644 index 0000000..f737676 --- /dev/null +++ b/sqmail-4.3.07/src/direntry.h1 @@ -0,0 +1,8 @@ +#ifndef DIRENTRY_H +#define DIRENTRY_H + +#include <sys/types.h> +#include <sys/dir.h> +#define direntry struct direct + +#endif diff --git a/sqmail-4.3.07/src/direntry.h2 b/sqmail-4.3.07/src/direntry.h2 new file mode 100644 index 0000000..0302ebe --- /dev/null +++ b/sqmail-4.3.07/src/direntry.h2 @@ -0,0 +1,8 @@ +#ifndef DIRENTRY_H +#define DIRENTRY_H + +#include <sys/types.h> +#include <dirent.h> +#define direntry struct dirent + +#endif diff --git a/sqmail-4.3.07/src/dkim.cpp b/sqmail-4.3.07/src/dkim.cpp new file mode 100644 index 0000000..8f36644 --- /dev/null +++ b/sqmail-4.3.07/src/dkim.cpp @@ -0,0 +1,197 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ +#include <string.h> +#include "dkim.h" +#include "dkimsign.h" +#include "dkimverify.h" + +#define DKIMID ('D' | 'K'<<8 | 'I'<<16 | 'M'<<24) +/* taken from removed file "ressource.h" */ +#ifdef VERSION +#define VERSION_STRING VERSION +#else +#define VERSION_STRING "1.4.0" +#endif + +static void InitContext(DKIMContext* pContext,bool bSign,void* pObject) +{ + pContext->reserved1 = DKIMID; + pContext->reserved2 = bSign ? 1 : 0; + pContext->reserved3 = pObject; +} + +static void* ValidateContext(DKIMContext* pContext,bool bSign) +{ + if (pContext->reserved1 != DKIMID) + return NULL; + + if (pContext->reserved2 != (unsigned int)(bSign ? 1 : 0)) + return NULL; + + return pContext->reserved3; +} + +int DKIM_CALL DKIMSignInit(DKIMContext* pSignContext,DKIMSignOptions* pOptions) +{ + int nRet = DKIM_OUT_OF_MEMORY; + + CDKIMSign* pSign = new CDKIMSign; + + if (pSign) { + nRet = pSign->Init(pOptions); + if (nRet != DKIM_SUCCESS) + delete pSign; + } + + if (nRet == DKIM_SUCCESS) { InitContext(pSignContext,true,pSign); } + return nRet; +} + +int DKIM_CALL DKIMSignProcess(DKIMContext* pSignContext,char* szBuffer,int nBufLength) +{ + CDKIMSign* pSign = (CDKIMSign*)ValidateContext(pSignContext,true); + + if (pSign) { return pSign->Process(szBuffer,nBufLength,false); } + return DKIM_INVALID_CONTEXT; +} + +int DKIM_CALL DKIMSignGetSig2(DKIMContext* pSignContext,char* szRSAPrivKey,char* szECCPrivKey,char** pszSignature) +{ + CDKIMSign* pSign = (CDKIMSign*)ValidateContext(pSignContext,true); + + if (pSign) { return pSign->GetSig2(szRSAPrivKey,szECCPrivKey,pszSignature); } + return DKIM_INVALID_CONTEXT; +} + +void DKIM_CALL DKIMSignFree(DKIMContext* pSignContext) +{ + CDKIMSign* pSign = (CDKIMSign*)ValidateContext(pSignContext,true); + + if (pSign) { + delete pSign; + pSignContext->reserved3 = NULL; + } +} + +int DKIM_CALL DKIMVerifyInit(DKIMContext* pVerifyContext,DKIMVerifyOptions* pOptions) +{ + int nRet = DKIM_OUT_OF_MEMORY; + + CDKIMVerify* pVerify = new CDKIMVerify; + + if (pVerify) { + nRet = pVerify->Init(pOptions); + if (nRet != DKIM_SUCCESS) + delete pVerify; + } + + if (nRet == DKIM_SUCCESS) { + InitContext(pVerifyContext,false,pVerify); + } + + return nRet; +} + + +int DKIM_CALL DKIMVerifyProcess(DKIMContext* pVerifyContext,const char* const szBuffer,int nBufLength) +{ + CDKIMVerify* pVerify = (CDKIMVerify*)ValidateContext(pVerifyContext,false); + + if (pVerify) { + return pVerify->Process(szBuffer,nBufLength,false); + } + + return DKIM_INVALID_CONTEXT; +} + +int DKIM_CALL DKIMVerifyResults(DKIMContext* pVerifyContext) +{ + CDKIMVerify* pVerify = (CDKIMVerify*)ValidateContext(pVerifyContext,false); + + if (pVerify) { + return pVerify->GetResults(); + } + return DKIM_INVALID_CONTEXT; +} + +int DKIM_CALL DKIMVerifyGetDetails(DKIMContext* pVerifyContext,int* nSigCount,DKIMVerifyDetails** pDetails,char* szPractices) +{ + szPractices[0] = '\0'; + + CDKIMVerify* pVerify = (CDKIMVerify*)ValidateContext(pVerifyContext,false); + + if (pVerify) { + strcpy(szPractices,pVerify->GetPractices()); + return pVerify->GetDetails(nSigCount,pDetails); + } + + return DKIM_INVALID_CONTEXT; +} + + +void DKIM_CALL DKIMVerifyFree(DKIMContext* pVerifyContext) +{ + CDKIMVerify* pVerify = (CDKIMVerify*)ValidateContext(pVerifyContext,false); + + if (pVerify) { + delete pVerify; + pVerifyContext->reserved3 = NULL; + } +} + +const char* DKIM_CALL DKIMVersion() +{ + return VERSION_STRING; +} + +static const char* DKIMErrorStrings[-1-DKIM_MAX_ERROR] = { + "DKIM_FAIL", + "DKIM_BAD_SYNTAX", + "DKIM_SIGNATURE_BAD", + "DKIM_SIGNATURE_BAD_BUT_TESTING", + "DKIM_SIGNATURE_EXPIRED", + "DKIM_SELECTOR_INVALID", + "DKIM_SELECTOR_GRANULARITY_MISMATCH", + "DKIM_SELECTOR_KEY_REVOKED", + "DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG", + "DKIM_SELECTOR_DNS_TEMP_FAILURE", + "DKIM_SELECTOR_DNS_PERM_FAILURE", + "DKIM_SELECTOR_PUBLIC_KEY_INVALID", + "DKIM_NO_SIGNATURES", + "DKIM_NO_VALID_SIGNATURES", + "DKIM_BODY_HASH_MISMATCH", + "DKIM_SELECTOR_ALGORITHM_MISMATCH", + "DKIM_STAT_INCOMPAT", + "DKIM_UNSIGNED_FROM", + "DKIM_OUT_OF_MEMORY", + "DKIM_INVALID_CONTEXT", + "DKIM_NO_SENDER", + "DKIM_BAD_PRIVATE_KEY", + "DKIM_BUFFER_TOO_SMALL", +}; + +const char* DKIM_CALL DKIMGetErrorString(int ErrorCode) { + if (ErrorCode >= 0 || ErrorCode <= DKIM_MAX_ERROR) + return "Unknown"; + else + return DKIMErrorStrings[-1-ErrorCode]; +} diff --git a/sqmail-4.3.07/src/dkimbase.cpp b/sqmail-4.3.07/src/dkimbase.cpp new file mode 100644 index 0000000..f6abf45 --- /dev/null +++ b/sqmail-4.3.07/src/dkimbase.cpp @@ -0,0 +1,320 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ +#include <string.h> +#include <algorithm> +#include "dkim.h" +#include "dkimbase.h" + + +CDKIMBase::CDKIMBase() +{ + m_From = NULL; + m_Sender = NULL; + m_hTag = NULL; + m_hTagSize = 0; + m_hTagPos = 0; + m_Line = NULL; + m_LineSize = 0; + m_LinePos = 0; + m_InHeaders = true; +} + +CDKIMBase::~CDKIMBase() // delete +{ + Free(m_Line); + Free(m_From); + Free(m_Sender); + Free(m_hTag); +} + +int CDKIMBase::Init(void) +{ + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Alloc - allocate buffer +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::Alloc(char*& szBuffer,int nRequiredSize) +{ + szBuffer = new char[nRequiredSize]; + + return (szBuffer == NULL) ? DKIM_OUT_OF_MEMORY : DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ReAlloc - extend buffer if necessary, leaving room for future expansion +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ReAlloc(char*& szBuffer,int& nBufferSize,int nRequiredSize) +{ + if (nRequiredSize > nBufferSize) { + char* newp; + int nNewSize = nRequiredSize + BUFFER_ALLOC_INCREMENT; + + if (Alloc(newp,nNewSize) == DKIM_SUCCESS) { + if (szBuffer != NULL && nBufferSize > 0) { + memcpy(newp,szBuffer,nBufferSize); + delete[] szBuffer; + } + szBuffer = newp; + nBufferSize = nNewSize; + } else { + return DKIM_OUT_OF_MEMORY; // memory alloc error! + } + } + + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Process - split buffers into lines without any CRs or LFs at the end. +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMBase::Free(char* szBuffer) +{ + if (szBuffer) + delete[] szBuffer; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Process - split buffers into lines without any CRs or LFs at the end. +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::Process(const char* szBuffer,int nBufLength,bool bEOF) +{ const char* p = szBuffer; + const char* e = szBuffer + nBufLength; + + while (p < e) { + if (*p != '\n' || m_LinePos == 0 || m_Line[m_LinePos - 1] != '\r') { + // add char to line + if (m_LinePos >= m_LineSize) { + int nRet = ReAlloc(m_Line,m_LineSize,m_LinePos + 1); + if (nRet != DKIM_SUCCESS) return nRet; + } + m_Line[m_LinePos++] = *p; + } else { + // back up past the CR + m_LinePos--; + + if (m_InHeaders) { + // process header line + if (m_LinePos == 0) { + m_InHeaders = false; + int Result = ProcessHeaders(); + if (Result != DKIM_SUCCESS) + return Result; + } else { + // append the header to the headers list + if (m_Line[0] != ' ' && m_Line[0] != '\t') { + HeaderList.push_back(string(m_Line,m_LinePos)); +// fprintf(stderr," dkimbase.cpp:Process:Input: %s \n",m_Line); + } else { + if (!HeaderList.empty()) { + HeaderList.back().append("\r\n",2).append(m_Line,m_LinePos); + } else { + // no header to append to... + } + } + } + } else { + // process body line + int Result = ProcessBody(m_Line,m_LinePos,bEOF); + if (Result != DKIM_SUCCESS) { + m_LinePos = 0; + return Result; + } + } + + m_LinePos = 0; + } + + p++; + } + + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessFinal - process leftovers if stopping before the body or mid-line +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ProcessFinal(void) +{ + if (m_LinePos > 0) { + Process("\r\n",2,true); + } + + if (m_InHeaders) { + m_InHeaders = false; + ProcessHeaders(); + /* type conversion should be safe as length is zero */ + ProcessBody((char *)"",0,true); + } + + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessHeaders - process the headers (to be implemented by derived class) +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ProcessHeaders() +{ + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessBody - process body line (to be implemented by derived class) +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMBase::ProcessBody(char* szBuffer, int nBufLength, bool bEOF) +{ + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// RemoveSWSP - remove streaming white space from buffer/string inline +// +//////////////////////////////////////////////////////////////////////////////// + +struct isswsp +{ + bool operator()(char ch) { return(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); } +}; + +void CDKIMBase::RemoveSWSP(char* szBuffer) +{ + *remove_if(szBuffer,szBuffer + strlen(szBuffer),isswsp()) = '\0'; +} + +void CDKIMBase::RemoveSWSP(char* pBuffer,int& nBufLength) +{ + nBufLength = remove_if(pBuffer,pBuffer+nBufLength,isswsp()) - pBuffer; +} + +void CDKIMBase::RemoveSWSP(string& sBuffer) +{ + sBuffer.erase(remove_if(sBuffer.begin(),sBuffer.end(),isswsp()),sBuffer.end()); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CompressSWSP - compress streaming white space into single spaces from buffer/string inline +// +////////////////////////////////////////////////////////////////////////////////////////// + +void CDKIMBase::CompressSWSP(char* pBuffer,int& nBufLength) +{ + char* pSrc = pBuffer; + char* pDst = pBuffer; + char* pEnd = pBuffer + nBufLength; + + while (pSrc != pEnd) { + if (isswsp()(*pSrc)) { + + do { + ++pSrc; + } while (pSrc != pEnd && isswsp()(*pSrc)); + + if (pSrc == pEnd) + break; + + *pDst++ = ' '; + } + + *pDst++ = *pSrc++; + } + + nBufLength = pDst - pBuffer; +} + +void CDKIMBase::CompressSWSP(string& sBuffer) +{ + string::iterator iSrc = sBuffer.begin(); + string::iterator iDst = sBuffer.begin(); + string::iterator iEnd = sBuffer.end(); + + while (iSrc != iEnd) { + if (isswsp()(*iSrc)) { + + do { + ++iSrc; + } while (iSrc != iEnd && isswsp()(*iSrc)); + + if (iSrc == iEnd) + break; + + *iDst++ = ' '; + } + + *iDst++ = *iSrc++; + } + + sBuffer.erase(iDst, iEnd); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// RelaxHeader - relax a header field (lower case the name, remove swsp before and after :) +// +// modified 4/21/06 STB to remove white space before colon +// +////////////////////////////////////////////////////////////////////////////////////////// + +string CDKIMBase::RelaxHeader(const string& sHeader) +{ + string sTemp = sHeader; + + CompressSWSP(sTemp); + + string::size_type cpos = sTemp.find(':'); + + if (cpos == string::npos) { + // no colon?! + } else { + // lower case the header field name + for (unsigned i = 0; i < cpos; i++) { + if (sTemp[i] >= 'A' && sTemp[i] <= 'Z') + sTemp[i] += 'a'-'A'; + } + + // remove the space after the : + if (cpos + 1 < sTemp.length() && sTemp[cpos+1] == ' ') + sTemp.erase(cpos + 1, 1); + + // remove the space before the : + if (cpos > 0 && sTemp[cpos - 1] == ' ') + sTemp.erase(cpos - 1,1); + } + + return sTemp; +} diff --git a/sqmail-4.3.07/src/dkimsign.cpp b/sqmail-4.3.07/src/dkimsign.cpp new file mode 100644 index 0000000..03b03e2 --- /dev/null +++ b/sqmail-4.3.07/src/dkimsign.cpp @@ -0,0 +1,1106 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ + +#define _strnicmp strncasecmp +#define _stricmp strcasecmp +#define LOWORD(l) ((unsigned)(l) & 0xffff) +#define HIWORD(l) ((unsigned)(l) >> 16) + +#include <string.h> +#include <map> + +#include "dkim.h" +#include "dkimsign.h" + +/***************************************************************************** +* +* Generating Ed25519 signed message: +* +* 1. RSA SHA1/SHA256 signatures are generated in streaming mode together with +* their hashes. Two different 'contexts' (ctx) are used here: +* m_Hdr_shaXctx => Used for signing (EVP_Sign...) -- covering the header only +* m_[B,E]dy_shaXctx => Used for hashing (EVP_Digest..) -- covering the body only +* +* 2. Private keys +* For hybrid signing we need two distinct keys: +* - RSAKey +* - ECCKey +* These private keys needs to be passed concurrently to the signature functions. +* Given those keys, the signature operation itself is executed in one step. +* +* 3. Public keys +* The 'public keys' need to be deployed in the DNS: +* - The RSA public key is DER-header enriched base64-encoded; thus is 9 byte larger +* than the 'naked' public key, which size depends on the given parameters. +* - The Ed25519 public key is also base64-encoded with a constant length of 60 byte. +* +* 4. DKIM message preparation scheme +* According to RFC 6376 Sec. 3.7, we have a conducted hash for +* - the previously available headers in the message; +* selected and given in order by h=..., +* - any existing DKIM signature fields b=..., +* - except for previous added 'X-Authentication ...' header fields, +* - and all (new) synthezised DKIM header tokens; except of course for the +* signature itself - treated as 'null string': b="". +* All this is subject of canonicalization (adding/removing CRLF, whitespaces ...). ++ As a result, the input for further calculations depends on this order given. +* +* Results following the 'preparation scheme': +* - The message body hash is included in the DKIM header => bh=[m_[B,E]dy_shaXctx]. +* - The message signature (including the result of bh=...) => b=[m_Hdr_shaXctx] +* +* We consider SHA256 as default hash function and SHA1 as exception (on demand). +* +* 5. Generating (ECC) signatures +* According to RFC 8032 Sect 4., we have two possible Ed25519 signature schemes: +* +* a) PureEd25519, as a one shot signature calculation swallowing the +* complete message and employing a shortened SHA-512 hash input. +* b) HashEd25519 working again in 'streaming mode' and permitting a choice +* for the hash function - which is in RFC 8463 - defined to be SHA-256. +* +* RFC 8463 in Sect 3 is a bit ambiguous about the signing function: +* Ed25519-256 vs. PureEd25519. +* In fact (after consulting John Levine), it is PureEd25519. +* +* In order to allow parallel RSA/Ed25519 processing, we need to generate: +* m_Hdr_sha256ctx => Used for RSA signatures +* m_Bdy_sha256ctx => The SHA256 hash of selected header parts and body (RSA) +* m_Edy_sha256ctx => The SHA256 hash of selected header parts and body (Ed25519) +* m_Hdr_ed25519ctx => The signature of the messsage header using PureEd25519 +* following the 'preparation' scheme +* +* Now, two cryptographic informations are provided in the header: +* bh=[m_Edy_sha256ctx] => The SHA256 digest of the message (BodyHash), +* b=[m_Hdr_ed25519ctx] => The PureED25519 signature. +* including the value of bh=... (EmailSignature) +* having a length of 512 bits => 64 bytes. +* +* 6. Hybrid signatures (RSA and Ed25519) +* They involve +* m_Hdr_sha256ctx => Used for RSA signatures +* m_Hdr_ed25519ctx => PureED25519 signature +* m_Bdy_sha256ctx => SHA256 digest of the message (BodyHash) for RSA +* m_Edy_sha256ctx => SHA256 digest of the message (BodyHash) for Ed25519 +* +* The EVP_DigestFinal routine has to be replaced by EVP_DigestFinal_ex. +* However; after the first call, its content seems to be garbeled. +* A common MD for both RSA and Ed2551 seems to be infeasible. +* +* ------ +* +* The particular function and variable names chosen here do not obviously match +* what they are intended to do. However, in order to keep traceablility of the +* changes, I left those untouched. +* +*****************************************************************************/ + +CDKIMSign::CDKIMSign() +{ + m_EmptyLineCount = 0; + m_pfnHdrCallback = NULL; + +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_SignInit(&m_Hdr_sha1ctx,EVP_sha1()); + EVP_SignInit(&m_Hdr_sha256ctx,EVP_sha256()); + EVP_DigestInit(&m_Bdy_sha1ctx,EVP_sha1()); + EVP_DigestInit(&m_Bdy_sha256ctx,EVP_sha256()); +#else + m_Hdr_sha1ctx = EVP_MD_CTX_create(); + EVP_SignInit_ex(m_Hdr_sha1ctx,EVP_sha1(),NULL); + + m_Hdr_sha256ctx = EVP_MD_CTX_create(); + EVP_SignInit_ex(m_Hdr_sha256ctx,EVP_sha256(),NULL); + + m_Bdy_sha1ctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(m_Bdy_sha1ctx,EVP_sha1(),NULL); + + m_Bdy_sha256ctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(m_Bdy_sha256ctx,EVP_sha256(),NULL); + + m_Hdr_ed25519ctx = EVP_MD_CTX_create(); + + m_Edy_sha256ctx = EVP_MD_CTX_create(); + EVP_DigestInit_ex(m_Edy_sha256ctx,EVP_sha256(),NULL); +#endif +} + +CDKIMSign::~CDKIMSign() +{ +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_MD_CTX_cleanup(&m_Hdr_sha1ctx); + EVP_MD_CTX_cleanup(&m_Hdr_sha256ctx); + EVP_MD_CTX_cleanup(&m_Bdy_sha1ctx); + EVP_MD_CTX_cleanup(&m_Bdy_sha256ctx); +#else + EVP_MD_CTX_free(m_Hdr_sha1ctx); + EVP_MD_CTX_free(m_Hdr_sha256ctx); + EVP_MD_CTX_free(m_Hdr_ed25519ctx); + EVP_MD_CTX_free(m_Bdy_sha1ctx); + EVP_MD_CTX_free(m_Bdy_sha256ctx); + EVP_MD_CTX_free(m_Edy_sha256ctx); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Init - save the options +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMSign::Init(DKIMSignOptions* pOptions) +{ + int nRet = CDKIMBase::Init(); + + m_Canon = pOptions->nCanon; + + // as of draft 01, these are the only allowed signing types: + if ((m_Canon != DKIM_SIGN_SIMPLE_RELAXED) && + (m_Canon != DKIM_SIGN_RELAXED) && + (m_Canon != DKIM_SIGN_RELAXED_SIMPLE)) { + m_Canon = DKIM_SIGN_SIMPLE; + } + + sSelector.assign(pOptions->szSelector); + eSelector.assign(pOptions->szSelectorE); + + m_pfnHdrCallback = pOptions->pfnHeaderCallback; + + sDomain.assign(pOptions->szDomain); + + m_IncludeBodyLengthTag = (pOptions->nIncludeBodyLengthTag != 0); + + m_nBodyLength = 0; + + m_ExpireTime = pOptions->expireTime; + + sIdentity.assign(pOptions->szIdentity); + + m_nIncludeTimeStamp = pOptions->nIncludeTimeStamp; + m_nIncludeQueryMethod = pOptions->nIncludeQueryMethod; + m_nIncludeCopiedHeaders = pOptions->nIncludeCopiedHeaders; + + // NOTE: the following line is not backwards compatible with MD 8.0.3 + // because the szRequiredHeaders member was added after the release + //sRequiredHeaders.assign(pOptions->szRequiredHeaders); + + //make sure there is a colon after the last header in the list + if ((sRequiredHeaders.size() > 0) && + sRequiredHeaders.at(sRequiredHeaders.size() - 1) != ':') { + sRequiredHeaders.append(":"); + } + + m_nHash = pOptions->nHash; + m_bReturnedSigAssembled = false; + m_sCopiedHeaders.erase(); + + // Initializes ED25519 header fields SigHdrs +#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) + SigHdrs.assign(""); + m_SigHdrs = 0; +#endif + + return nRet; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Hash - update the hash +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::Hash(const char *szBuffer,int nBufLength,bool bHdr) +{ + + /** START DEBUG CODE ** + if (nBufLength == 2 && szBuffer[0] == '\r' && szBuffer[1] == '\n') { + printf("[CRLF]\n"); + } else { + char *szDbg = new char[nBufLength+1]; + strncpy(szDbg,szBuffer,nBufLength); + szDbg[nBufLength] = '\0'; + printf("[%s]\n",szDbg); + } *** + + if (fpdebug == NULL) { + fpdebug = fopen("canon.msg", "wb"); + } + + fwrite(szBuffer,1,nBufLength,fpdebug); + + ** END DEBUG CODE **/ + + if (bHdr) { /* Generate signature: b=... */ + if ((m_nHash == DKIM_HASH_SHA1) || + (m_nHash == DKIM_HASH_SHA1_AND_SHA256)) +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_SignUpdate(&m_Hdr_sha1ctx,szBuffer,nBufLength); +#else + EVP_SignUpdate(m_Hdr_sha1ctx,szBuffer,nBufLength); +#endif + if ((m_nHash == DKIM_HASH_SHA256) || + (m_nHash == DKIM_HASH_SHA1_AND_SHA256) || + (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_SignUpdate(&m_Hdr_sha256ctx,szBuffer,nBufLength); +#else + EVP_SignUpdate(m_Hdr_sha256ctx,szBuffer,nBufLength); +#endif +#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) + if ((m_nHash == DKIM_HASH_ED25519) || + (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) { + SigHdrs.append(szBuffer,nBufLength); + m_SigHdrs += nBufLength; + } +#endif + } else { /* lets go for body hash values: bh=... (either SHA1 or SHA256) */ + if ((m_nHash == DKIM_HASH_SHA1) || + (m_nHash == DKIM_HASH_SHA1_AND_SHA256)) +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_DigestUpdate(&m_Bdy_sha1ctx,szBuffer,nBufLength); +#else + EVP_DigestUpdate(m_Bdy_sha1ctx,szBuffer,nBufLength); +#endif + if (m_nHash != DKIM_HASH_SHA1) +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_DigestUpdate(&m_Bdy_sha256ctx,szBuffer,nBufLength); +#else + EVP_DigestUpdate(m_Bdy_sha256ctx,szBuffer,nBufLength); +#endif +#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) + if ((m_nHash == DKIM_HASH_ED25519) || + (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) + EVP_DigestUpdate(m_Edy_sha256ctx,szBuffer,nBufLength); +#endif + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// SignThisTag - return boolean whether or not to sign this tag +// +//////////////////////////////////////////////////////////////////////////////// +bool CDKIMSign::SignThisTag(const string& sTag) +{ + bool bRet = true; + + if (_strnicmp(sTag.c_str(),"X-",2) == 0 || + _stricmp(sTag.c_str(),"Authentication-Results:") == 0 || + _stricmp(sTag.c_str(),"Return-Path:") == 0) { + bRet = false; + } + + return bRet; +} + +bool ConvertHeaderToQuotedPrintable(const char* source, char* dest) +{ + bool bConvert = false; + + // do quoted printable + static unsigned char hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + + unsigned char *d = (unsigned char*)dest; + for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++) + { + if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|') { + *d++ = *s; + } else { + bConvert = true; + *d++ = '='; + *d++ = hexchars[*s >> 4]; + *d++ = hexchars[*s & 15]; + } + } + *d = '\0'; + + return bConvert; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// GetHeaderParams - Extract any needed header parameters +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::GetHeaderParams(const string &sHdr) +{ + if (_strnicmp(sHdr.c_str(),"X",1) == 0) return; + if (_strnicmp(sHdr.c_str(),"From:",5) == 0) { sFrom.assign(sHdr.c_str() + 5); } + if (_strnicmp(sHdr.c_str(),"Sender:",7) == 0) { sSender.assign(sHdr.c_str() + 7); } + + if (m_nIncludeCopiedHeaders) { + string::size_type pos = sHdr.find(':'); + + if (pos != string::npos) { + string sTag, sValue; + char *workBuffer = new char[sHdr.size() * 3 + 1]; + + sTag.assign(sHdr.substr(0,pos)); + sValue.assign(sHdr.substr(pos + 1,string::npos)); + + ConvertHeaderToQuotedPrintable(sTag.c_str(),workBuffer); + if (!m_sCopiedHeaders.empty()) { m_sCopiedHeaders.append("|"); } + m_sCopiedHeaders.append(workBuffer); m_sCopiedHeaders.append(":"); + ConvertHeaderToQuotedPrintable(sValue.c_str(),workBuffer); + m_sCopiedHeaders.append(workBuffer); + + delete[] workBuffer; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessHeaders - sign headers and save needed parameters (this is a lie) +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMSign::ProcessHeaders(void) +{ + map<string,list<string>::reverse_iterator> IterMap; + map<string,list<string>::reverse_iterator>::iterator IterMapIter; + list<string>::reverse_iterator riter; + list<string>::iterator iter; + string sTag; + bool bFromHeaderFound = false; + + // walk the header list + for (iter = HeaderList.begin(); iter != HeaderList.end(); iter++) { + sTag.assign(*iter); + + // look for a colon + string::size_type pos = sTag.find(':'); + + if (pos != string::npos) { + int nSignThisTag = 1; + + // hack off anything past the colon + sTag.erase(pos + 1,string::npos); + + // is this the From: header? + if (_stricmp(sTag.c_str(),"From:") == 0) { + bFromHeaderFound = true; + nSignThisTag = 1; + IsRequiredHeader(sTag); // remove from required header list + } + // is this in the list of headers that must be signed? + else if (IsRequiredHeader(sTag)) { + nSignThisTag = 1; + } + else { + if(m_pfnHdrCallback) { + nSignThisTag = m_pfnHdrCallback(iter->c_str()); + } else { + nSignThisTag = SignThisTag(sTag) ? 1 : 0; + } + } + + // save header parameters + GetHeaderParams(*iter); + + if (nSignThisTag > 0) { + // add this tag to h= + hParam.append(sTag); + + IterMapIter = IterMap.find(sTag); + + riter = (IterMapIter == IterMap.end()) ? HeaderList.rbegin() : IterMapIter->second; + + // walk the list in reverse looking for the last instance of this header + while (riter != HeaderList.rend()) { + if (_strnicmp(riter->c_str(),sTag.c_str(),sTag.size()) == 0) { + ProcessHeader(*riter); + + // save the reverse iterator position for this tag + riter++; + IterMap[sTag] = riter; + break; + } + riter++; + } + } + } + } + + if(!bFromHeaderFound) { + string sFrom("From:"); + hParam.append(sFrom); + IsRequiredHeader(sFrom); // remove from required header list +// Hash("\r\n",2); + } + + hParam.append(sRequiredHeaders); + +// string::size_type end = sRequiredHeaders.find(':'); +// while (end != string::npos) +// { +// Hash("\r\n",2); +// end = sRequiredHeaders.find(':', end+1); +// } + + // remove the last colon from h= + if (hParam.at(hParam.size() - 1) == ':') + hParam.erase(hParam.size() - 1,string::npos); + + return DKIM_SUCCESS; +} + +void CDKIMSign::ProcessHeader(const string &sHdr) +{ + switch (HIWORD(m_Canon)) { + case DKIM_CANON_SIMPLE: + Hash(sHdr.c_str(),sHdr.size(),true); + Hash("\r\n",2,true); + break; + + case DKIM_CANON_NOWSP: { + string sTemp = sHdr; + RemoveSWSP(sTemp); + + // convert characters before ':' to lower case + for (char *s = (char*)sTemp.c_str(); *s != '\0' && *s != ':'; s++) { + if (*s >= 'A' && *s <= 'Z') + *s += 'a' - 'A'; + } + + Hash(sTemp.c_str(),sTemp.size(),true); + Hash("\r\n",2,true); + } + break; + + case DKIM_CANON_RELAXED: { + string sTemp = RelaxHeader(sHdr); + Hash(sTemp.c_str(),sTemp.length(),true); + Hash("\r\n",2,true); + } + break; + } +} + +int CDKIMSign::ProcessBody(char *szBuffer,int nBufLength,bool bEOF) +{ + switch(LOWORD(m_Canon)) { + case DKIM_CANON_SIMPLE: + if (nBufLength > 0) { + while (m_EmptyLineCount > 0) { + Hash("\r\n",2,false); + m_nBodyLength += 2; + m_EmptyLineCount--; + } + Hash(szBuffer,nBufLength,false); + Hash("\r\n",2,false); + m_nBodyLength += nBufLength + 2; + } else { + m_EmptyLineCount++; + if (bEOF) { + Hash("\r\n",2,false); + m_nBodyLength += 2; + } + } + break; + case DKIM_CANON_NOWSP: + RemoveSWSP(szBuffer,nBufLength); + if (nBufLength > 0) { + Hash(szBuffer,nBufLength,false); + m_nBodyLength += nBufLength; + } + break; + case DKIM_CANON_RELAXED: + CompressSWSP(szBuffer,nBufLength); + if (nBufLength > 0) { + while (m_EmptyLineCount > 0) { + Hash("\r\n",2,false); + m_nBodyLength += 2; + m_EmptyLineCount--; + } + Hash(szBuffer,nBufLength,false); + m_nBodyLength += nBufLength; + if (!bEOF) { + Hash("\r\n",2,false); + m_nBodyLength += 2; + } + } else + m_EmptyLineCount++; + break; + } + + return DKIM_SUCCESS; +} + +bool CDKIMSign::ParseFromAddress(void) +{ + string::size_type pos; + string sAddress; + + if (!sFrom.empty()) { + sAddress.assign(sFrom); + } else if (!sSender.empty()) { + sAddress.assign(sSender); + } else { + return false; + } + + // simple for now, beef it up later + + // remove '<' and anything before it + pos = sAddress.find('<'); + if(pos != string::npos) + sAddress.erase(0,pos); + + // remove '>' and anything after it + pos = sAddress.find('>'); + if (pos != string::npos) + sAddress.erase(pos,string::npos); + + // look for '@' symbol + pos = sAddress.find('@'); + if (pos == string::npos) + return false; + + if (sDomain.empty()) { + sDomain.assign (sAddress.c_str() + pos + 1); + RemoveSWSP(sDomain); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// InitSig - initialize signature folding algorithm +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::InitSig(void) +{ + m_sSig.reserve(1024); + m_sSig.assign("DKIM-Signature:"); + m_nSigPos = m_sSig.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AddTagToSig - add tag and value to signature folding if necessary +// if bFold, fold at cbrk char +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddTagToSig(const char* const Tag,const string &sValue,char cbrk,bool bFold) +{ + int nTagLen = strlen(Tag); + + AddInterTagSpace((!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2); + + m_sSig.append(Tag); + m_sSig.append("="); + m_nSigPos += 1 + nTagLen; + + if (!bFold) { + m_sSig.append(sValue); + m_nSigPos += sValue.size(); + } else { + AddFoldedValueToSig(sValue,cbrk); + } + m_sSig.append(";"); + m_nSigPos++; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AddTagToSig - add tag and numeric value to signature folding if necessary +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddTagToSig(const char* const Tag,unsigned long nValue) +{ + char szValue[64]; + sprintf(szValue,"%lu",nValue); + AddTagToSig(Tag,szValue,0,false); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AddInterTagSpace - add space or fold here +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddInterTagSpace(int nSizeOfNextTag) +{ + if (m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength) { +// m_sSig.append("\r\n\t"); + m_sSig.append("\r\n "); /* s/qmail style */ + m_nSigPos = 1; + } else { + m_sSig.append(" "); + m_nSigPos++; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AddTagToSig - add value to signature folding if necessary +// if cbrk == 0 fold anywhere, otherwise fold only at cbrk +// +//////////////////////////////////////////////////////////////////////////////// +void CDKIMSign::AddFoldedValueToSig(const string &sValue,char cbrk) +{ + string::size_type pos = 0; + + if (cbrk == 0) { + // fold anywhere + while (pos < sValue.size()) { + string::size_type len = OptimalHeaderLineLength - m_nSigPos; + if (len > sValue.size() - pos) + len = sValue.size() - pos; + m_sSig.append(sValue.substr(pos,len)); + m_nSigPos += len; + pos += len; + + if (pos < sValue.size()) { +// m_sSig.append("\r\n\t"); + m_sSig.append("\r\n "); /* s/qmail style */ + m_nSigPos = 1; + } + } + } else { + // fold only at cbrk + while (pos < sValue.size()) { + string::size_type len = OptimalHeaderLineLength - m_nSigPos; + string::size_type brkpos; + + if (sValue.size() - pos < len) { + brkpos = sValue.size(); + } else { + brkpos = sValue.rfind(cbrk,pos + len); + } + + if (brkpos == string::npos || brkpos < pos) { + brkpos = sValue.find(cbrk,pos); + if (brkpos == string::npos) { + brkpos = sValue.size(); + } + } + + len = brkpos - pos + 1; + + m_sSig.append(sValue.substr(pos,len)); + + m_nSigPos += len; + pos += len; + + if (pos < sValue.size()) { +// m_sSig.append("\r\n\t"); + m_sSig.append("\r\n "); /* s/qmail style */ + m_nSigPos = 1; + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// GetSig - compute hash and return signature header in szSignature +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMSign::GetSig2(char* szRSAKey,char* szECCKey,char** pszSignature) +{ + if (szRSAKey == NULL && szECCKey == NULL) { + return DKIM_BAD_PRIVATE_KEY; + } + + if (pszSignature == NULL) { + return DKIM_BUFFER_TOO_SMALL; + } + + int nRet = AssembleReturnedSig(szRSAKey,szECCKey); + + if (nRet != DKIM_SUCCESS) + return nRet; + + *pszSignature = (char*)m_sReturnedSig.c_str(); + + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// IsRequiredHeader - Check if header in required list. If so, delete +// header from list. +// +//////////////////////////////////////////////////////////////////////////////// +bool CDKIMSign::IsRequiredHeader(const string& sTag) +{ + string::size_type start = 0; + string::size_type end = sRequiredHeaders.find(':'); + + while (end != string::npos) { + // check for a zero-length header + if(start == end) { + sRequiredHeaders.erase(start,1); + } else { + if (_stricmp(sTag.c_str(),sRequiredHeaders.substr(start,end - start + 1).c_str()) == 0) { + sRequiredHeaders.erase(start,end - start + 1); + return true; + } else { + start = end + 1; + } + } + + end = sRequiredHeaders.find(':',start); + } + + return false; +} +//////////////////////////////////////////////////////////////////////////////// +// +// ConstructSignature +// +// Here, we don't construct the 'signature' but rather the DKIM header +// multiply and indidually crafted for each distinct nSigAlg method +// +// nSigAlg: DKIM_HASH_SHA1, DKIM_HASH_SHA256, DKIM_HASH_ED25519 +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMSign::ConstructSignature(char* szPrivKey,int nSigAlg) +{ + string sSignedSig; + unsigned char* sig; + EVP_PKEY *pkey = 0; + BIO *bio, *b64; + unsigned int siglen; + int size; + int len; + char* buf; + int nSignRet; + + /* construct the DKIM-Signature: header and add to hash */ + InitSig(); + + AddTagToSig("v","1",0,false); + + switch (nSigAlg) { + case DKIM_HASH_SHA1: + AddTagToSig("a","rsa-sha1",0,false); break; + case DKIM_HASH_SHA256: + AddTagToSig("a","rsa-sha256",0,false); break; + case DKIM_HASH_ED25519: + AddTagToSig("a","ed25519-sha256",0,false); break; + } + + switch (m_Canon) { + case DKIM_SIGN_SIMPLE: + AddTagToSig("c","simple/simple",0,false); break; + case DKIM_SIGN_SIMPLE_RELAXED: + AddTagToSig("c","simple/relaxed",0,false); break; + case DKIM_SIGN_RELAXED: + AddTagToSig("c","relaxed/relaxed",0,false); break; + case DKIM_SIGN_RELAXED_SIMPLE: + AddTagToSig("c","relaxed/simple",0,false); break; + } + + AddTagToSig("d",sDomain,0,false); + if (nSigAlg == DKIM_HASH_ED25519) + AddTagToSig("s",eSelector,0,false); + else + AddTagToSig("s",sSelector,0,false); + if (m_IncludeBodyLengthTag) { AddTagToSig("l",m_nBodyLength); } + if (m_nIncludeTimeStamp != 0) { time_t t; time(&t); AddTagToSig("t",t); } + if (m_ExpireTime != 0) { AddTagToSig("x",m_ExpireTime); } + if (!sIdentity.empty()) { AddTagToSig("i",sIdentity,0,false); } + if (m_nIncludeQueryMethod) { AddTagToSig("q","dns/txt",0,false); } + + AddTagToSig("h",hParam,':',true); // copied headers follow the ':' + if (m_nIncludeCopiedHeaders) { AddTagToSig("z",m_sCopiedHeaders,0,true); } + + /* Set up context for (body) hash */ + + unsigned char Hash[4096]; + unsigned int nHashLen = 0; + + switch (nSigAlg) { + case DKIM_HASH_SHA1: +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_DigestFinal(&m_Bdy_sha1ctx,Hash,&nHashLen); break; +#else + EVP_DigestFinal_ex(m_Bdy_sha1ctx,Hash,&nHashLen); break; +#endif + case DKIM_HASH_SHA256: +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_DigestFinal(&m_Bdy_sha256ctx,Hash,&nHashLen); break; +#else + EVP_DigestFinal_ex(m_Bdy_sha256ctx,Hash,&nHashLen); break; +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + case DKIM_HASH_ED25519: + EVP_DigestFinal_ex(m_Edy_sha256ctx,Hash,&nHashLen); break; +#endif + } + + bio = BIO_new(BIO_s_mem()); + if (!bio) return DKIM_OUT_OF_MEMORY; + + b64 = BIO_new(BIO_f_base64()); + if (!b64) { + BIO_free(bio); + return DKIM_OUT_OF_MEMORY; + } + BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); + BIO_push(b64,bio); + if (BIO_write(b64,Hash,nHashLen) < (int)nHashLen) { + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + BIO_flush(b64); + + len = nHashLen * 2; + buf = new char[len]; + + if (buf == NULL) { + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + + size = BIO_read(bio,buf,len); + BIO_free_all(b64); + + // this should never happen + if (size >= len) { + delete[] buf; + return DKIM_OUT_OF_MEMORY; + } + + buf[size] = '\0'; + AddTagToSig("bh",buf,0,true); + delete[] buf; + + AddInterTagSpace(3); + + m_sSig.append("b="); + m_nSigPos += 2; + + // Force a full copy - no reference copies please + sSignedSig.assign(m_sSig.c_str()); + + // note that since we're not calling hash here, need to dump this + // to the debug file if you want the full canonical form + + string sTemp; + + if (HIWORD(m_Canon) == DKIM_CANON_RELAXED) { + sTemp = RelaxHeader(sSignedSig); + } else { + sTemp = sSignedSig.c_str(); + } + + /* Update streaming signatures */ + + switch (nSigAlg) { + case DKIM_HASH_SHA1: +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_SignUpdate(&m_Hdr_sha1ctx,sTemp.c_str(),sTemp.size()); break; +#else + EVP_SignUpdate(m_Hdr_sha1ctx,sTemp.c_str(),sTemp.size()); break; +#endif + case DKIM_HASH_SHA256: +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_SignUpdate(&m_Hdr_sha256ctx,sTemp.c_str(),sTemp.size()); break; +#else + EVP_SignUpdate(m_Hdr_sha256ctx,sTemp.c_str(),sTemp.size()); break; +#endif +#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) + case DKIM_HASH_ED25519: + SigHdrs.append(sTemp.c_str(),sTemp.size()); + m_SigHdrs += sTemp.size(); break; +#endif + } + + bio = BIO_new_mem_buf(szPrivKey, -1); + if (bio == NULL) return DKIM_OUT_OF_MEMORY; + + pkey = PEM_read_bio_PrivateKey(bio,NULL,NULL,NULL); // FIXME - done + BIO_free(bio); + + if (!pkey) { return DKIM_BAD_PRIVATE_KEY; } + siglen = EVP_PKEY_size(pkey); + + sig = (unsigned char*) OPENSSL_malloc(siglen); + if (sig == NULL) { + EVP_PKEY_free(pkey); + return DKIM_OUT_OF_MEMORY; + } + + /* Finish streaming signature and potentially go for Ed25519 signatures */ + + size_t sig_len; + unsigned char* SignMsg; + + switch (nSigAlg) { + case DKIM_HASH_SHA1: +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + nSignRet = EVP_SignFinal(&m_Hdr_sha1ctx,sig,&siglen,pkey); break; +#else + nSignRet = EVP_SignFinal(m_Hdr_sha1ctx,sig,&siglen,pkey); break; +#endif + case DKIM_HASH_SHA256: +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + nSignRet = EVP_SignFinal(&m_Hdr_sha256ctx,sig,&siglen,pkey); break; +#else + nSignRet = EVP_SignFinal(m_Hdr_sha256ctx,sig,&siglen,pkey); break; +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + case DKIM_HASH_ED25519: + EVP_DigestSignInit(m_Hdr_ed25519ctx,NULL,NULL,NULL,pkey); + SignMsg = (unsigned char*) SigHdrs.c_str(); + EVP_DigestSign(m_Hdr_ed25519ctx,NULL,&sig_len,SignMsg,m_SigHdrs); + sig = (unsigned char*) OPENSSL_malloc(sig_len); + nSignRet = EVP_DigestSign(m_Hdr_ed25519ctx,sig,&sig_len,SignMsg,m_SigHdrs); + siglen = (unsigned int) sig_len; break; +#endif + } + EVP_PKEY_free(pkey); + + if (!nSignRet) { + OPENSSL_free(sig); + return DKIM_BAD_PRIVATE_KEY; // key too small + } + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + return DKIM_OUT_OF_MEMORY; + } + + b64 = BIO_new(BIO_f_base64()); + if (!b64) { + BIO_free(bio); + return DKIM_OUT_OF_MEMORY; + } + + BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); + BIO_push(b64,bio); + + if (BIO_write(b64,sig,siglen) < (int) siglen) { + OPENSSL_free(sig); + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + BIO_flush(b64); + OPENSSL_free(sig); + + len = siglen * 2; + buf = new char[len]; + + if (buf == NULL) { + BIO_free_all(b64); + return DKIM_OUT_OF_MEMORY; + } + + size = BIO_read(bio,buf,len); + BIO_free_all(b64); + + // this should never happen + if (size >= len) { + delete[] buf; + return DKIM_OUT_OF_MEMORY; + } + + buf[size] = '\0'; + AddFoldedValueToSig(buf,0); + delete[] buf; + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// AssembleReturnSig +// +// calls ConstructSignature +// for all different hashes and signature key files +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMSign::AssembleReturnedSig(char* szRSAKey,char* szECCKey) +{ + int nRet; + + if (m_bReturnedSigAssembled) + return DKIM_SUCCESS; + + ProcessFinal(); + + if (ParseFromAddress() == false) { + return DKIM_NO_SENDER; + } + + string ed25519Sig, sha256Sig, sha1Sig; + + if ((m_nHash == DKIM_HASH_ED25519) || + (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) { + nRet = ConstructSignature(szECCKey,DKIM_HASH_ED25519); + if (nRet == DKIM_SUCCESS) { + ed25519Sig.assign(m_sSig); + } else { + return nRet; + } + } + + if ((m_nHash == DKIM_HASH_SHA256) || + (m_nHash == DKIM_HASH_SHA1_AND_SHA256) || + (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) { + nRet = ConstructSignature(szRSAKey,DKIM_HASH_SHA256); + if (nRet == DKIM_SUCCESS) { + sha256Sig.assign(m_sSig); + } else { + return nRet; + } + } + + if ((m_nHash == DKIM_HASH_SHA1) || + (m_nHash == DKIM_HASH_SHA1_AND_SHA256)) { + nRet = ConstructSignature(szRSAKey,DKIM_HASH_SHA1); + if (nRet == DKIM_SUCCESS) { + sha1Sig.assign(m_sSig); + } else { + return nRet; + } + } + +// fclose(fpdebug); +// fpdebug = NULL; + + if (!ed25519Sig.empty()) { +/* if (!m_sReturnedSig.empty()) { + m_sReturnedSig.append("\r\n"); + } + */ + m_sReturnedSig.assign(ed25519Sig); + } + + if (!sha1Sig.empty()) { + if (!m_sReturnedSig.empty()) { + m_sReturnedSig.append("\r\n"); + } + m_sReturnedSig.append(sha1Sig); + } + + if (!sha256Sig.empty()) { + if (!m_sReturnedSig.empty()) { + m_sReturnedSig.append("\r\n"); + } + m_sReturnedSig.append(sha256Sig); + } + + m_bReturnedSigAssembled = true; + return DKIM_SUCCESS; +} diff --git a/sqmail-4.3.07/src/dkimverify.cpp b/sqmail-4.3.07/src/dkimverify.cpp new file mode 100644 index 0000000..c9f1003 --- /dev/null +++ b/sqmail-4.3.07/src/dkimverify.cpp @@ -0,0 +1,1443 @@ +/***************************************************************************** +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <vector> +#include <algorithm> +#include "dkim.h" +#include "dkimverify.h" +#include "dnsgettxt.h" +extern "C" { +#include "dns.h" +#include "stralloc.h" +} + +/***************************************************************************** +* +* Verifying DKIM Ed25519 signatures: +* +* The received DKIM header includes two cryptographic relevant informations: +* +* a) The 'body hash' => bh=[sha1|sha256] +* b) The signature => b=[RSA-SHA1|RSA-SHA256|PureEd25519] +* +* Several DKIM headers (=signatures) may be present in the email. +* Here, it is limited to max. Shall we really evaluate all? +* +* Caution: Using hybrid signatures, calling the destructor will core dump +* given EVP_MD_CTX_free() upon the next call of EVP_DigestInit. +* Using the destructor with EVP_MD_CTX_reset() however works. +* +*****************************************************************************/ + +#define _strnicmp strncasecmp +#define _stricmp strcasecmp +#define MAX_SIGNATURES 10 // maximum number of DKIM signatures to process/message +#define FDLOG stderr /* writing to another FD requires a method */ + +string SigHdr; +size_t m_SigHdr; + +extern "C" int stralloc_copys(stralloc *,char const *); + +int dig_ascii(char *digascii,unsigned const char *digest,const int len) +{ + static const char hextab[] = "0123456789abcdef"; + int j; + + for (j = 0; j < len; j++) { + digascii[2 * j] = hextab[(unsigned char)digest[j] >> 4]; + digascii[2 * j + 1] = hextab[(unsigned char)digest[j] & 0x0f]; + } + digascii[2 * len] = '\0'; + + return (2 * j); // 2*len +} + + +int _DNSGetTXT(const char *szFQDN,char *Buffer,int nBufLen) +{ + stralloc out = {0}; + stralloc sa = {0}; + Buffer[0] = '\0'; // need to be initialized + + if (!stralloc_copys(&sa,szFQDN)) return -1; + + DNS_INIT + + switch (dns_txt(&out,&sa)) { + case -1: return -1; + case 0: return 0; + } + + if (nBufLen < out.len) + return -2; + + if (!stralloc_0(&out)) return -1; + memcpy(Buffer,out.s,out.len); // Return-by-value; sigh + + return out.len; +} + +int _DKIM_ReportResult(const char* ResFile,const char* result,const char* reason) +{ + int len = 0; + + FILE* out = fopen(ResFile,"wb+"); + if (out == NULL) return -1; + + if (result) { + len = strlen(result); + fwrite(result,1,len,out); + fwrite("\r",1,1,out); + } + + if (reason) { + fwrite(reason,1,strlen(reason),out); + fwrite("\r",1,1,out); + } + fclose(out); + + return len; +} + +const char* DKIM_ErrorResult(const int res) +{ + const char* errormsg = ""; + + switch (res) { + case DKIM_FAIL: + errormsg = " (verify error: message is suspicious)"; + break; + case DKIM_BAD_SYNTAX: + errormsg = " (signature error: could not parse or has bad tags/values)"; + break; + case DKIM_SIGNATURE_BAD: + errormsg = " (signature error: RSA/ED25519 verify failed)"; + break; + case DKIM_SIGNATURE_BAD_BUT_TESTING: + errormsg = " (signature error: RSA/ED25519 verify failed but testing)"; + break; + case DKIM_SIGNATURE_EXPIRED: + errormsg = " (signature error: signature x= value expired)"; + break; + case DKIM_SELECTOR_INVALID: + errormsg = " (signature error: selector doesn't parse or contains invalid values)"; + break; + case DKIM_SELECTOR_GRANULARITY_MISMATCH: + errormsg = " (signature error: selector g= doesn't match i=)"; + break; + case DKIM_SELECTOR_KEY_REVOKED: + errormsg = " (signature error: revoked p= empty)"; + break; + case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG: + errormsg = " (dns error: selector domain name too long to request)"; + break; + case DKIM_SELECTOR_DNS_TEMP_FAILURE: + errormsg = " (dns error: temporary dns failure requesting selector)"; + break; + case DKIM_SELECTOR_DNS_PERM_FAILURE: + errormsg = " (dns error: permanent dns failure requesting selector)"; + break; + case DKIM_SELECTOR_PUBLIC_KEY_INVALID: + errormsg = " (signature error: selector p= value invalid or wrong format)"; + break; + case DKIM_NO_SIGNATURES: + errormsg = " (process error: no signatures)"; + break; + case DKIM_NO_VALID_SIGNATURES: + errormsg = " (process error: no valid signatures)"; + break; + case DKIM_BODY_HASH_MISMATCH: + errormsg = " (signature verify error: message body does not hash to bh= value)"; + break; + case DKIM_SELECTOR_ALGORITHM_MISMATCH: + errormsg = " (signature error: selector h= doesn't match signature a=)"; + break; + case DKIM_STAT_INCOMPAT: + errormsg = " (signature error: incompatible v= value)"; + break; + case DKIM_UNSIGNED_FROM: + errormsg = " (signature error: not all message's From headers in signature)"; + break; + case DKIM_OUT_OF_MEMORY: + errormsg = " (internal error: memory allocation failed)"; + break; + case DKIM_INVALID_CONTEXT: + errormsg = " (internal error: DKIMContext structure invalid for this operation)"; + break; + case DKIM_NO_SENDER: + errormsg = " (signing error: Could not find From: or Sender: header in message)"; + break; + case DKIM_BAD_PRIVATE_KEY: + errormsg = " (signing error: Could not parse private key)"; + break; + case DKIM_BUFFER_TOO_SMALL: + errormsg = " (signing error: Buffer passed in is not large enough)"; + break; + } + + return errormsg; +} + +SignatureInfo::SignatureInfo(bool s) +{ + VerifiedBodyCount = 0; + UnverifiedBodyCount = 0; + +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_MD_CTX_init(&m_Hdr_ctx); + EVP_MD_CTX_init(&m_Bdy_ctx); +#else + m_Hdr_ctx = EVP_MD_CTX_new(); + m_Bdy_ctx = EVP_MD_CTX_new(); +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + m_Msg_ctx = EVP_MD_CTX_new(); +#endif + m_pSelector = NULL; + Status = DKIM_SUCCESS; + m_nHash = 0; + EmptyLineCount = 0; + m_SaveCanonicalizedData = s; +} + +SignatureInfo::~SignatureInfo() +{ +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_MD_CTX_cleanup(&m_Hdr_ctx); + EVP_MD_CTX_cleanup(&m_Bdy_ctx); +#else + /** FIXME: No free but reset ! **/ + EVP_MD_CTX_reset(m_Hdr_ctx); + EVP_MD_CTX_reset(m_Bdy_ctx); +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + EVP_MD_CTX_reset(m_Msg_ctx); +#endif +} + +inline bool isswsp(char ch) +{ + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Parse a DKIM tag-list. Returns true for success +// +//////////////////////////////////////////////////////////////////////////////// +bool ParseTagValueList(char *tagvaluelist,const char *wanted[],char *values[]) +{ + char *s = tagvaluelist; + + for (;;) { + // skip whitespace + while (isswsp(*s)) + s++; + + // if at the end of the string, return success. Note: this allows a list with no entries + if (*s == '\0') + return true; + + // get tag name + if (!isalpha(*s)) + return false; + + char *tag = s; + do { + s++; + } while (isalnum(*s) || *s == '-'); + + char *endtag = s; + + // skip whitespace before equals + while (isswsp(*s)) + s++; + + // next character must be equals + if (*s != '=') + return false; + s++; + + // null-terminate tag name + *endtag = '\0'; + + // skip whitespace after equals + while (isswsp(*s)) + s++; + + // get tag value + char *value = s; + + while (*s != ';' && ((*s == '\t' || *s == '\r' || *s == '\n') || (*s >= ' ' && *s <= '~'))) + s++; + + char *e = s; + + // make sure the next character is the null terminator (which means we're done) or a semicolon (not done) + bool done = false; + if (*s == '\0') + done = true; + else { + if (*s != ';') + return false; + s++; + } + + // skip backwards past any trailing whitespace + while (e > value && isswsp(e[-1])) + e--; + + // null-terminate tag value + *e = '\0'; + + // check to see if we want this tag + for (unsigned i = 0; wanted[i] != NULL; i++) { + if (strcmp(wanted[i],tag) == 0) { + // return failure if we already have a value for this tag (duplicates not allowed) + if (values[i] != NULL) + return false; + values[i] = value; + break; + } + } + + if (done) + return true; + } +} +//////////////////////////////////////////////////////////////////////////////// +// +// Convert hex char to value (0-15) +// +//////////////////////////////////////////////////////////////////////////////// +char Tohex(char ch) +{ + if (ch >= '0' && ch <= '9') + return (ch - '0'); + else if (ch >= 'A' && ch <= 'F') + return (ch - 'A' + 10); + else if (ch >= 'a' && ch <= 'f') + return (ch - 'a' + 10); + else { + assert(0); + return 0; + } +} +//////////////////////////////////////////////////////////////////////////////// +// +// Decode quoted printable string in-place +// +//////////////////////////////////////////////////////////////////////////////// +void DecodeQuotedPrintable(char* ptr) +{ + char *s = ptr; + while (*s != '\0' && *s != '=') + s++; + + if (*s == '\0') + return; + + char *d = s; + do { + if (*s == '=' && isxdigit(s[1]) && isxdigit(s[2])) { + *d++ = (Tohex(s[1]) << 4) | Tohex(s[2]); + s += 3; + } else { + *d++ = *s++; + } + } while (*s != '\0'); + *d = '\0'; +} +//////////////////////////////////////////////////////////////////////////////// +// +// Decode base64 string in-place, returns number of bytes output +// +//////////////////////////////////////////////////////////////////////////////// +unsigned DecodeBase64(char *ptr) +{ + static const char base64_table[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; + + unsigned char* s = (unsigned char* )ptr; + unsigned char* d = (unsigned char* )ptr; + unsigned b64accum = 0; + unsigned char b64shift = 0; + + while (*s != '\0') { + unsigned char value = base64_table[*s++]; + if ((signed char) value >= 0) { + b64accum = (b64accum << 6) | value; + b64shift += 6; + if (b64shift >= 8) { + b64shift -= 8; + *d++ = (b64accum >> b64shift); + } + } + } + + return (char* )d-ptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Match a string with a pattern (used for g= value) +// Supports a single, optional "*" wildcard character. +// +//////////////////////////////////////////////////////////////////////////////// +bool WildcardMatch(const char *p, const char *s) +{ + // special case: An empty "g=" value never matches any addresses + if (*p == '\0') + return false; + + const char* wildcard = strchr(p,'*'); + if (wildcard == NULL) { + return strcmp(s, p) == 0; + } else { + unsigned beforewildcardlen = wildcard - p; + unsigned afterwildcardlen = strlen(wildcard + 1); + unsigned slen = strlen(s); + return (slen >= beforewildcardlen + afterwildcardlen) && + (strncmp(s,p,beforewildcardlen) == 0) && strcmp(s + slen - afterwildcardlen,wildcard + 1) == 0; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Parse addresses from a string. Returns true if at least one address found +// +//////////////////////////////////////////////////////////////////////////////// +bool ParseAddresses(string str,vector<string> &Addresses) +{ + char* s = (char* )str.c_str(); + + while (*s != '\0') { + char* start = s; + char* from = s; + char* to = s; + char* lt = NULL; // pointer to less than character (<) which starts the address if found + + while (*from != '\0') { + if (*from == '(') { + // skip over comment + from++; + for (int depth = 1; depth != 0; from++) { + if (*from == '\0') + break; + else if (*from == '(') + depth++; + else if (*from == ')') + depth--; + else if (*from == '\\' && from[1] != '\0') + from++; + } + } + else if (*from == ')') { + // ignore closing parenthesis outside of comment + from++; + } else if (*from == ',' || *from == ';') { + // comma/semicolon ends the address + from++; + break; + } + else if (*from == ' ' || *from == '\t' || *from == '\r' || *from == '\n') { + // ignore whitespace + from++; + } else if (*from == '"') { + // copy the contents of a quoted string + from++; + while (*from != '\0') { + if (*from == '"') { + from++; + break; + } else if (*from == '\\' && from[1] != '\0') + *to++ = *from++; + *to++ = *from++; + } + } else if (*from == '\\' && from[1] != '\0') { + // copy quoted-pair + *to++ = *from++; + *to++ = *from++; + } else { + // copy any other char + *to = *from++; + // save pointer to '<' for later... + if (*to == '<') + lt = to; + to++; + } + } + + *to = '\0'; + + // if there's < > get what's inside + if (lt != NULL) { + start = lt+1; + char *gt = strchr(start, '>'); + if (gt != NULL) + *gt = '\0'; + } else { + // look for and strip group name + char *colon = strchr(start, ':'); + if (colon != NULL) { + char *at = strchr(start, '@'); + if (at == NULL || colon < at) + start = colon+1; + } + } + + if (*start != '\0' && strchr(start, '@') != NULL) { + Addresses.push_back(start); // save address + } + + s = from; + } + + return !Addresses.empty(); +} + +//////////////////////////////////////////////////////////////////////////////// + +CDKIMVerify::CDKIMVerify() +{ + m_pfnSelectorCallback = NULL; +// m_pfnPracticesCallback = NULL; + m_HonorBodyLengthTag = false; + m_CheckPractices = false; +// Kai: +// m_SubjectIsRequired = true; + m_SubjectIsRequired = false; + m_SaveCanonicalizedData = false; + m_AllowUnsignedFromHeaders = false; +} + +CDKIMVerify::~CDKIMVerify() {} // Destructor + +//////////////////////////////////////////////////////////////////////////////// +// +// Init - save the options +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMVerify::Init(DKIMVerifyOptions* pOptions) +{ + int nRet = CDKIMBase::Init(); + + m_pfnSelectorCallback = pOptions->pfnSelectorCallback; + // m_pfnPracticesCallback = pOptions->pfnPracticesCallback; + + m_HonorBodyLengthTag = pOptions->nHonorBodyLengthTag != 0; + m_CheckPractices = pOptions->nCheckPractices != 0; +// Kai: +// m_SubjectIsRequired = pOptions->nSubjectRequired == 0; + m_SubjectIsRequired = pOptions->nSubjectRequired == 1; + m_SaveCanonicalizedData = pOptions->nSaveCanonicalizedData != 0; + m_AllowUnsignedFromHeaders = pOptions->nAllowUnsignedFromHeaders != 0; + + return nRet; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// GetResults - return the pass/fail/neutral verification result +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMVerify::GetResults(void) +{ + // char mdi[128]; + // char digi[128]; + + ProcessFinal(); + + unsigned char *SignMsg; + unsigned SuccessCount = 0; + int TestingFailures = 0; + int RealFailures = 0; + int res = 0; + + list<string> SuccessfulDomains; // can contain duplicates + + for (list<SignatureInfo>::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { + if (i->Status == DKIM_SUCCESS) { + if (!i->BodyHashData.empty()) { // FIRST: Get the body hash + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned len = 0; + +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + res = EVP_DigestFinal(&i->m_Bdy_ctx,md,&len); +#else + res = EVP_DigestFinal_ex(i->m_Bdy_ctx,md,&len); + EVP_MD_CTX_reset(i->m_Bdy_ctx); +#endif + // dig_ascii(digi,md,32); + // dig_ascii(mdi,(unsigned const char *)i->BodyHashData.data(),32); + + if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(),md,len) != 0) { + // body hash mismatch + + // if the selector is in testing mode... + if (i->m_pSelector->Testing) { + i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING; // todo: make a new error code for this? + TestingFailures++; + } else { + i->Status = DKIM_BODY_HASH_MISMATCH; + RealFailures++; + } + continue; // next signature + } + } else { + // hash CRLF separating the body from the signature + i->Hash("\r\n",2); + } + + // SECOND: Fetch the signature + + string sSignedSig = i->Header; + string sSigValue = sSignedSig.substr(sSignedSig.find(':') + 1); + + static const char* tags[] = {"b",NULL}; + char* values[sizeof(tags)/sizeof(tags[0])] = {NULL}; + + char* pSigValue = (char* ) sSigValue.c_str(); // our signature + if (ParseTagValueList(pSigValue,tags,values) && values[0] != NULL) { + sSignedSig.erase(15 + values[0] - pSigValue,strlen(values[0])); + } + + if (i->HeaderCanonicalization == DKIM_CANON_RELAXED) { + sSignedSig = RelaxHeader(sSignedSig); + } + else if (i->HeaderCanonicalization == DKIM_CANON_NOWSP) { + RemoveSWSP(sSignedSig); + // convert "DKIM-Signature" to lower case + sSignedSig.replace(0,14,"dkim-signature",14); + } + + i->Hash(sSignedSig.c_str(),sSignedSig.length()); // include generated DKIM signature header + assert(i->m_pSelector != NULL); + + if (EVP_PKEY_base_id(i->m_pSelector->PublicKey) != EVP_PKEY_ED25519) +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + res = EVP_VerifyFinal(&i->m_Hdr_ctx,(unsigned char *)i->SignatureData.data(),i->SignatureData.length(),i->m_pSelector->PublicKey); +#else + res = EVP_VerifyFinal(i->m_Hdr_ctx,(unsigned char *)i->SignatureData.data(),i->SignatureData.length(),i->m_pSelector->PublicKey); +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + else if (EVP_PKEY_base_id(i->m_pSelector->PublicKey) == EVP_PKEY_ED25519) { + EVP_DigestVerifyInit(i->m_Msg_ctx,NULL,NULL,NULL,i->m_pSelector->PublicKey); // late initialization + + SignMsg = (unsigned char *) SigHdr.data(); + res = EVP_DigestVerify(i->m_Msg_ctx,(unsigned char *)i->SignatureData.data(),(size_t)i->SignatureData.length(), + SignMsg,m_SigHdr); + } +#endif + + if (res == 1) { + if (i->UnverifiedBodyCount == 0) + i->Status = DKIM_SUCCESS; + else + i->Status = DKIM_SUCCESS_BUT_EXTRA; + SuccessCount++; + SuccessfulDomains.push_back(i->Domain); + } else { + // if the selector is in testing mode... + if (i->m_pSelector->Testing) { + i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING; + TestingFailures++; + } else { + i->Status = DKIM_SIGNATURE_BAD; + RealFailures++; + } + } + } else if (i->Status == DKIM_SELECTOR_GRANULARITY_MISMATCH || + i->Status == DKIM_SELECTOR_ALGORITHM_MISMATCH || + i->Status == DKIM_SELECTOR_KEY_REVOKED) { + // treat these as failures + // todo: maybe see if the selector is in testing mode? + RealFailures++; + } + } // loop over signature infos done + + + // get the From address's domain if we might need it + string sFromDomain; + if (SuccessCount > 0 || m_CheckPractices) { + for (list<string>::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) { + if (_strnicmp(i->c_str(),"From",4) == 0) { + // skip over whitespace between the header name and : + const char* s = i->c_str() + 4; + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':') { + vector<string> Addresses; + if (ParseAddresses(s + 1, Addresses)) { + unsigned atpos = Addresses[0].find('@'); + sFromDomain = Addresses[0].substr(atpos + 1); + break; + } + } + } + } + } + + // if a signature from the From domain verified successfully, return success now + // without checking the author domain signing practices + if (SuccessCount > 0 && !sFromDomain.empty()) { + for (list<string>::iterator i = SuccessfulDomains.begin(); i != SuccessfulDomains.end(); ++i) { + // see if the successful domain is the same as or a parent of the From domain + if (i->length() > sFromDomain.length()) + continue; + if (_stricmp(i->c_str(),sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0) + continue; + if (i->length() == sFromDomain.length() || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] == '.') { + return SuccessCount == Signatures.size() ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS; + } + } + } + + /* Removed obsolete ADSP check */ + + // return neutral for everything else + return DKIM_NEUTRAL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Hash - update the hash or update the Ed25519 signature input +// +//////////////////////////////////////////////////////////////////////////////// +void SignatureInfo::Hash(const char* szBuffer,unsigned nBufLength,bool IsBody) +{ +#if 0 + /** START DEBUG CODE **/ + if(nBufLength == 2 && szBuffer[0] == '\r' && szBuffer[1] == '\n') + { + printf("[CRLF]\n"); + } else { + char* szDbg = new char[nBufLength+1]; + strncpy(szDbg, szBuffer, nBufLength); + szDbg[nBufLength] = '\0'; + printf("[%s]\n", szDbg); + } + /** END DEBUG CODE **/ +#endif + + if (IsBody && BodyLength != (unsigned) -1) { // trick: 2's complement + VerifiedBodyCount += nBufLength; + if (VerifiedBodyCount > BodyLength) { + nBufLength = BodyLength - (VerifiedBodyCount - nBufLength); + UnverifiedBodyCount += VerifiedBodyCount - BodyLength; + VerifiedBodyCount = BodyLength; + if (nBufLength == 0) return; + } + } + + if (IsBody && !BodyHashData.empty()) { +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_DigestUpdate(&m_Bdy_ctx,szBuffer,nBufLength); + } else { + EVP_VerifyUpdate(&m_Hdr_ctx,szBuffer,nBufLength); +#else + EVP_DigestUpdate(m_Bdy_ctx,szBuffer,nBufLength); + } else { + EVP_VerifyUpdate(m_Hdr_ctx,szBuffer,nBufLength); +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + SigHdr.append(szBuffer,nBufLength); + m_SigHdr += nBufLength; +#endif + } + + if (m_SaveCanonicalizedData) { + CanonicalizedData.append(szBuffer,nBufLength); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessHeaders - Look for DKIM-Signatures and start processing them +// look for DKIM-Signature header(s) +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMVerify::ProcessHeaders(void) +{ + for (list<string>::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) { + if (strlen(i->c_str()) < 14) continue; // too short + if (_strnicmp(i->c_str(),"DKIM-Signature",14) == 0) { + // skip over whitespace between the header name and : + const char *s = i->c_str() + 14; + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':') { + // found + SignatureInfo sig(m_SaveCanonicalizedData); + sig.Status = ParseDKIMSignature(*i,sig); + Signatures.push_back(sig); // save signature + + if (Signatures.size() >= MAX_SIGNATURES) + break; + } + } + } + + if (Signatures.empty()) + return DKIM_NO_SIGNATURES; + + bool ValidSigFound = false; + + for (list<SignatureInfo>::iterator s = Signatures.begin(); s != Signatures.end(); ++s) { + SignatureInfo &sig = *s; + if (sig.Status != DKIM_SUCCESS) continue; + SelectorInfo &sel = GetSelector(sig.Selector,sig.Domain); + sig.m_pSelector = &sel; + + if (sel.Status != DKIM_SUCCESS) { + sig.Status = sel.Status; + } else { + // check the granularity + if (!WildcardMatch(sel.Granularity.c_str(),sig.IdentityLocalPart.c_str())) + sig.Status = DKIM_SELECTOR_GRANULARITY_MISMATCH; // this error causes the signature to fail + + // check the hash algorithm + if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1) || + (sig.m_nHash == DKIM_HASH_SHA256 && !sel.AllowSHA256)) + sig.Status = DKIM_SELECTOR_ALGORITHM_MISMATCH; // causes signature to fail + + // check for same domain + if (sel.SameDomain && _stricmp(sig.Domain.c_str(),sig.IdentityDomain.c_str()) != 0) + sig.Status = DKIM_BAD_SYNTAX; + } + + if (sig.Status != DKIM_SUCCESS) continue; + + // initialize the hashes + if (sig.m_nHash == DKIM_HASH_SHA1) { +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_VerifyInit(&sig.m_Hdr_ctx,EVP_sha1()); + EVP_DigestInit(&sig.m_Bdy_ctx,EVP_sha1()); +#else + EVP_VerifyInit_ex(sig.m_Hdr_ctx,EVP_sha1(),NULL); + EVP_DigestInit_ex(sig.m_Bdy_ctx,EVP_sha1(),NULL); +#endif + } + if (sig.m_nHash == DKIM_HASH_SHA256) { +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_VerifyInit(&sig.m_Hdr_ctx,EVP_sha256()); + EVP_DigestInit(&sig.m_Bdy_ctx,EVP_sha256()); +#else + EVP_VerifyInit_ex(sig.m_Hdr_ctx,EVP_sha256(),NULL); + EVP_DigestInit_ex(sig.m_Bdy_ctx,EVP_sha256(),NULL); +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + SigHdr.assign(""); + m_SigHdr = 0; + } +#endif + + // compute the hash of the header + vector<list<string>::reverse_iterator> used; + + for (vector<string>::iterator x = sig.SignedHeaders.begin(); x != sig.SignedHeaders.end(); ++x) { + list<string>::reverse_iterator i; + for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) { + if (_strnicmp(i->c_str(),x->c_str(),x->length()) == 0) { + // skip over whitespace between the header name and : + const char* s = i->c_str()+x->length(); + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':' && find(used.begin(),used.end(),i) == used.end()) + break; + } + } + + if (i != HeaderList.rend()) { + used.push_back(i); + + // hash this header + if (sig.HeaderCanonicalization == DKIM_CANON_SIMPLE) { + sig.Hash(i->c_str(),i->length()); + } else if (sig.HeaderCanonicalization == DKIM_CANON_RELAXED) { + string sTemp = RelaxHeader(*i); + sig.Hash(sTemp.c_str(),sTemp.length()); + } else if (sig.HeaderCanonicalization == DKIM_CANON_NOWSP) { + string sTemp = *i; + RemoveSWSP(sTemp); + + // convert characters before ':' to lower case + for (char* s = (char*)sTemp.c_str(); *s != '\0' && *s != ':'; s++) { + if (*s >= 'A' && *s <= 'Z') + *s += 'a' - 'A'; + } + sig.Hash(sTemp.c_str(),sTemp.length()); + } + sig.Hash("\r\n",2); + } + } + + if (sig.BodyHashData.empty()) { + // hash CRLF separating headers from body + sig.Hash("\r\n",2); + } + + if (!m_AllowUnsignedFromHeaders) { + // make sure the message has no unsigned From headers + list<string>::reverse_iterator i; + for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) { + if (_strnicmp(i->c_str(),"From",4) == 0) { + // skip over whitespace between the header name and : + const char *s = i->c_str() + 4; + while (*s == ' ' || *s == '\t') + s++; + if (*s == ':') { + if (find(used.begin(),used.end(),i) == used.end()) { + // this From header was not signed + break; + } + } + } + } + if (i != HeaderList.rend()) { + // treat signature as invalid + sig.Status = DKIM_UNSIGNED_FROM; + continue; + } + } + + ValidSigFound = true; + } + + if (!ValidSigFound) + return DKIM_NO_VALID_SIGNATURES; + + return DKIM_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Strictly parse an unsigned integer. Don't allow spaces, negative sign, +// 0x prefix, etc. Values greater than 2^32-1 are capped at 2^32-1 +// +//////////////////////////////////////////////////////////////////////////////// +bool ParseUnsigned(const char *s, unsigned *result) +{ + unsigned temp = 0, last = 0; + bool overflowed = false; + + do { + if (*s < '0' || *s > '9') + return false; // returns false for an initial '\0' + + temp = temp * 10 + (*s - '0'); + if (temp < last) + overflowed = true; + last = temp; + + s++; + } while (*s != '\0'); + + *result = overflowed ? -1 : temp; + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// ParseDKIMSignature - Parse a DKIM-Signature header field +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMVerify::ParseDKIMSignature(const string& sHeader,SignatureInfo &sig) +{ + // for strtok_r() + char *saveptr; + + // save header for later + sig.Header = sHeader; + + string sValue = sHeader.substr(sHeader.find(':') + 1); + + static const char *tags[] = {"v","a","b","d","h","s","c","i","l","q","t","x","bh",NULL}; + char *values[sizeof(tags)/sizeof(tags[0])] = {NULL}; + + if (!ParseTagValueList((char*) sValue.c_str(),tags,values)) + return DKIM_BAD_SYNTAX; + + // check signature version + if (values[0] == NULL) return DKIM_BAD_SYNTAX; + + // signature MUST have a=, b=, d=, h=, s= + if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL || values[5] == NULL) + return DKIM_BAD_SYNTAX; + + // algorithm ('a=') can be "rsa-sha1" or "rsa-sha256" or "ed25519" + if (strcmp(values[1],"rsa-sha1") == 0) { + sig.m_nHash = DKIM_HASH_SHA1; + } else if (strcmp(values[1],"rsa-sha256") == 0) { + sig.m_nHash = DKIM_HASH_SHA256; +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + } else if (strcmp(values[1],"ed25519-sha256") == 0) { + sig.m_nHash = DKIM_HASH_SHA256; +#endif + } else { + return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown algorithm + } + + // make sure the signature data is not empty: b=[...] + unsigned SigDataLen = DecodeBase64(values[2]); + + if (SigDataLen == 0) + return DKIM_BAD_SYNTAX; + + sig.SignatureData.assign(values[2],SigDataLen); + + // check for body hash in DKIM header: bh=[...]; + unsigned BodyHashLen = DecodeBase64(values[12]); + if (BodyHashLen == 0) return DKIM_BAD_SYNTAX; + sig.BodyHashData.assign(values[12],BodyHashLen); + + // domain must not be empty + if (*values[3] == '\0') + return DKIM_BAD_SYNTAX; + sig.Domain = values[3]; + + // signed headers must not be empty (more verification is done later) + if (*values[4] == '\0') + return DKIM_BAD_SYNTAX; + + // selector must not be empty + if (*values[5] == '\0') + return DKIM_BAD_SYNTAX; + sig.Selector = values[5]; + + // canonicalization + if (values[6] == NULL) { + sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_SIMPLE; + } else { + char* slash = strchr(values[6],'/'); + if (slash != NULL) + *slash = '\0'; + + if (strcmp(values[6],"simple") == 0) + sig.HeaderCanonicalization = DKIM_CANON_SIMPLE; + else if (strcmp(values[6],"relaxed") == 0) + sig.HeaderCanonicalization = DKIM_CANON_RELAXED; + else + return DKIM_BAD_SYNTAX; + + if (slash == NULL || strcmp(slash + 1,"simple") == 0) + sig.BodyCanonicalization = DKIM_CANON_SIMPLE; + else if (strcmp(slash + 1,"relaxed") == 0) + sig.BodyCanonicalization = DKIM_CANON_RELAXED; + else + return DKIM_BAD_SYNTAX; + } + + // identity + if (values[7] == NULL) { + sig.IdentityLocalPart.erase(); + sig.IdentityDomain = sig.Domain; + } else { + // quoted-printable decode the value + DecodeQuotedPrintable(values[7]); + + // must have a '@' separating the local part from the domain + char* at = strchr(values[7],'@'); + if (at == NULL) + return DKIM_BAD_SYNTAX; + *at = '\0'; + + char* ilocalpart = values[7]; + char* idomain = at + 1; + + // i= domain must be the same as or a subdomain of the d= domain + int idomainlen = strlen(idomain); + int ddomainlen = strlen(values[3]); + + // todo: maybe create a new error code for invalid identity domain + if (idomainlen < ddomainlen) + return DKIM_BAD_SYNTAX; + if (_stricmp(idomain + idomainlen - ddomainlen,values[3]) != 0) + return DKIM_BAD_SYNTAX; + if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] != '.') + return DKIM_BAD_SYNTAX; + + sig.IdentityLocalPart = ilocalpart; + sig.IdentityDomain = idomain; + } + + // body count + if (values[8] == NULL || !m_HonorBodyLengthTag) { + sig.BodyLength = (unsigned) -1; + } else { + if (!ParseUnsigned(values[8],&sig.BodyLength)) + return DKIM_BAD_SYNTAX; + } + + // query methods + if (values[9] != NULL) { + // make sure "dns" is in the list + bool HasDNS = false; + char* s = strtok_r(values[9],":",&saveptr); + while (s != NULL) { + if (strncmp(s,"dns",3) == 0 && (s[3] == '\0' || s[3] == '/')) { + HasDNS = true; + break; + } + s = strtok_r(NULL,": \t",&saveptr); /* FIXME */ +// s = strtok_r(NULL,": ",&saveptr); /* FIXME */ + } + if (!HasDNS) + return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown query method + } + + // signature time + unsigned SignedTime = -1; + if (values[10] != NULL) { + if (!ParseUnsigned(values[10],&SignedTime)) + return DKIM_BAD_SYNTAX; + } + + // expiration time + if (values[11] == NULL) { + sig.ExpireTime = (unsigned) -1; // common trick; feh + } else { + if (!ParseUnsigned(values[11],&sig.ExpireTime)) + return DKIM_BAD_SYNTAX; + + if (sig.ExpireTime != (unsigned) -1) { + // the value of x= MUST be greater than the value of t= if both are present + if (SignedTime != (unsigned) -1 && sig.ExpireTime <= SignedTime) + return DKIM_BAD_SYNTAX; + + // todo: if possible, use the received date/time instead of the current time + unsigned curtime = time(NULL); + if (curtime > sig.ExpireTime) + return DKIM_SIGNATURE_EXPIRED; + } + } + + // parse the signed headers list + bool HasFrom = false, HasSubject = false; + RemoveSWSP(values[4]); // header names shouldn't have spaces in them so this should be ok... + char* s = strtok_r(values[4],":",&saveptr); + while (s != NULL) { + if (_stricmp(s,"From") == 0) + HasFrom = true; + else if (_stricmp(s,"Subject") == 0) + HasSubject = true; + + sig.SignedHeaders.push_back(s); + s = strtok_r(NULL,":",&saveptr); + } + + if (!HasFrom) + return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing From + if (m_SubjectIsRequired && !HasSubject) + return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing Subject + + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ProcessBody - Process message body data +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMVerify::ProcessBody(char* szBuffer,int nBufLength,bool bEOF) +{ + bool MoreBodyNeeded = false; + + for (list<SignatureInfo>::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { + if (i->Status == DKIM_SUCCESS) { + if (i->BodyCanonicalization == DKIM_CANON_SIMPLE) { + if (nBufLength > 0) { + while (i->EmptyLineCount > 0) { + i->Hash("\r\n",2,true); + i->EmptyLineCount--; + } + i->Hash(szBuffer,nBufLength,true); + i->Hash("\r\n",2,true); + } else { + i->EmptyLineCount++; + if (bEOF) + i->Hash("\r\n",2,true); + } + } else if (i->BodyCanonicalization == DKIM_CANON_RELAXED) { + CompressSWSP(szBuffer, nBufLength); + if (nBufLength > 0) { + while (i->EmptyLineCount > 0) { + i->Hash("\r\n",2,true); + i->EmptyLineCount--; + } + i->Hash(szBuffer,nBufLength,true); + if (!bEOF) + i->Hash("\r\n",2,true); + } else i->EmptyLineCount++; + } else if (i->BodyCanonicalization == DKIM_CANON_NOWSP) { + RemoveSWSP(szBuffer,nBufLength); + i->Hash(szBuffer,nBufLength,true); + } + + if (i->UnverifiedBodyCount == 0) + MoreBodyNeeded = true; + } + } + + if (!MoreBodyNeeded) + return DKIM_FINISHED_BODY; + + return DKIM_SUCCESS; +} + +SelectorInfo::SelectorInfo(const string &sSelector,const string &sDomain) : Domain(sDomain),Selector(sSelector) +{ + AllowSHA1 = true; + AllowSHA256 = true; + PublicKey = NULL; + Testing = false; + SameDomain = false; + Status = DKIM_SUCCESS; +} + +SelectorInfo::~SelectorInfo() +{ + if (PublicKey != NULL) { + EVP_PKEY_free(PublicKey); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Parse - Parse a DKIM selector from DNS data +// +//////////////////////////////////////////////////////////////////////////////// +int SelectorInfo::Parse(char* Buffer) +{ + // for strtok_r() + char *saveptr; + char *PubKeyBase64; /*- public key Base64 encoded */ + char ed25519PubKey[61]; + + static const char *tags[] = {"v","g","h","k","p","s","t","n",NULL}; // 0, 1, 2, 3, 4 + char *values[sizeof(tags)/sizeof(tags[0])] = {NULL}; + + ParseTagValueList(Buffer,tags,values); + + // return DKIM_SELECTOR_INVALID; + if (values[0] != NULL) { + // make sure the version is "DKIM1" + if (strcmp(values[0],"DKIM1") != 0) + return DKIM_SELECTOR_INVALID; // todo: maybe create a new error code for unsupported selector version + + // make sure v= is the first tag in the response // todo: maybe don't enforce this, it seems unnecessary + for (unsigned j = 1; j < sizeof(values)/sizeof(values[0]); j++) { + if (values[j] != NULL && values[j] < values[0]) { + return DKIM_SELECTOR_INVALID; + } + } + } + + // selector MUST have p= tag + if (values[4] == NULL) + return DKIM_SELECTOR_INVALID; + + PubKeyBase64 = values[4]; // gotcha + + // granularity -- [g= ... ] + if (values[1] == NULL) + Granularity = "*"; + else + Granularity = values[1]; + + // hash algorithm -- [h=sha1|sha256] (not required) + if (values[2] == NULL) { + AllowSHA1 = true; + AllowSHA256 = true; + } else { + // MUST include "sha1" or "sha256" + char* s = strtok_r(values[2],":",&saveptr); + while (s != NULL) { + if (strcmp(s,"sha1") == 0) + { AllowSHA1 = true; AllowSHA256 = false; } + else if (strcmp(s,"sha256") == 0) + { AllowSHA256 = true; AllowSHA1 = false; } + s = strtok_r(NULL,":",&saveptr); + } + if (!(AllowSHA1 || AllowSHA256)) + return DKIM_SELECTOR_INVALID; // todo: maybe create a new error code for unsupported hash algorithm + } + + // key type -- [k=rsa|ed25519] (not required) + if (values[3] != NULL) { + // key type MUST be "rsa" or "ed25519" + if (strcmp(values[3],"rsa") != 0 && strcmp(values[3],"ed25519") != 0) // none of either + return DKIM_SELECTOR_INVALID; + if (strcmp(values[3],"ed25519") == 0) { + AllowSHA1 = false; + AllowSHA256 = true; + strcpy(ed25519PubKey,"MCowBQYDK2VwAyEA"); + /* + * rfc8463 + * since Ed25519 public keys are 256 bits long, + * the base64-encoded key is only 44 octets + */ + if (strlen(values[4]) > 44) + return DKIM_SELECTOR_PUBLIC_KEY_INVALID; + strcat(ed25519PubKey,values[4]); + PubKeyBase64 = ed25519PubKey; + } + } + + // service type -- [s= ...] (not required) + if (values[5] != NULL) { + // make sure "*" or "email" is in the list + bool ServiceTypeMatch = false; + char* s = strtok_r(values[5],":",&saveptr); + while (s != NULL) { + if (strcmp(s, "*") == 0 || strcmp(s,"email") == 0) { + ServiceTypeMatch = true; + break; + } + s = strtok_r(NULL,":",&saveptr); + } + if (!ServiceTypeMatch) + return DKIM_SELECTOR_INVALID; + } + + // flags -- [t= ...] (not required) + if (values[6] != NULL) { + char *s = strtok_r(values[6],":",&saveptr); + while (s != NULL) { + if (strcmp(s,"y") == 0) { + Testing = true; + } else if (strcmp(s,"s") == 0) { + SameDomain = true; + } + s = strtok_r(NULL,":",&saveptr); + } + } + + // public key data + unsigned PublicKeyLen = DecodeBase64(PubKeyBase64); + + if (PublicKeyLen == 0) { + return DKIM_SELECTOR_KEY_REVOKED; // this error causes the signature to fail + } else { + const unsigned char *PublicKeyData = (unsigned char* )PubKeyBase64; // 0-terminated + + EVP_PKEY *pkey = d2i_PUBKEY(NULL,&PublicKeyData,PublicKeyLen); /* retrieve and return PubKey from data */ + + if (pkey == NULL) + return DKIM_SELECTOR_PUBLIC_KEY_INVALID; + + // make sure public key is the correct type (we only support rsa & ed25519) +#if ((OPENSSL_VERSION_NUMBER < 0x10101000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { +#else + if ((EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) || + (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA2) || + (EVP_PKEY_base_id(pkey) == EVP_PKEY_ED25519)) { +#endif + PublicKey = pkey; + } else { + EVP_PKEY_free(pkey); + return DKIM_SELECTOR_PUBLIC_KEY_INVALID; + } + } + + return DKIM_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// GetSelector - Get a DKIM selector for a domain +// +//////////////////////////////////////////////////////////////////////////////// +SelectorInfo& CDKIMVerify::GetSelector(const string &sSelector,const string &sDomain) +{ + // see if we already have this selector + for (list<SelectorInfo>::iterator i = Selectors.begin(); i != Selectors.end(); ++i) { + if (_stricmp(i->Selector.c_str(),sSelector.c_str()) == 0 && _stricmp(i->Domain.c_str(),sDomain.c_str()) == 0) { + return *i; + } + } + + Selectors.push_back(SelectorInfo(sSelector,sDomain)); + SelectorInfo& sel = Selectors.back(); + + string sFQDN = sSelector; + sFQDN += "._domainkey."; + sFQDN += sDomain; + + int BufLen = 1024; + char Buffer[BufLen]; + + int DNSResult; + + if (m_pfnSelectorCallback) { + DNSResult = m_pfnSelectorCallback(sFQDN.c_str(),Buffer,BufLen); + } else + DNSResult = _DNSGetTXT(sFQDN.c_str(),Buffer,BufLen); + +// Buffer++; BufLen--; + + switch (DNSResult) { + case -1: case -2: case -3: case -5: sel.Status = DKIM_SELECTOR_DNS_TEMP_FAILURE; break; + case 0: case -6: sel.Status = DKIM_SELECTOR_DNS_PERM_FAILURE; break; + default: sel.Status = sel.Parse(Buffer); + } + + return sel; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// GetDetails - Get DKIM verification details (per signature) +// +//////////////////////////////////////////////////////////////////////////////// +int CDKIMVerify::GetDetails(int* nSigCount,DKIMVerifyDetails** pDetails) +{ + Details.clear(); + + for (list < SignatureInfo>::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { + DKIMVerifyDetails d; + d.szSignature = (char* )i->Header.c_str(); + d.szSignatureDomain = (char* )i->Domain.c_str(); + d.szIdentityDomain = (char* )i->IdentityDomain.c_str(); + d.szCanonicalizedData = (char* )i->CanonicalizedData.c_str(); + d.nResult = i->Status; + Details.push_back(d); + } + + *nSigCount = Details.size(); + *pDetails = (*nSigCount != 0) ? &Details[0] : NULL; + + return DKIM_SUCCESS; +} diff --git a/sqmail-4.3.07/src/dns.c b/sqmail-4.3.07/src/dns.c new file mode 100644 index 0000000..3f1154b --- /dev/null +++ b/sqmail-4.3.07/src/dns.c @@ -0,0 +1,201 @@ +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <sys/socket.h> +#include "ip.h" +#include "ipalloc.h" +#include "fmt.h" +#include "alloc.h" +#include "str.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "case.h" +#include "dns.h" +#include "buffer.h" +#include "exit.h" + +/** + @file dns.c + @brief DNS helpers: dns_ipplus, dns_ipalloc, dns_ip (IPv4+IPv6), dns_mxip + */ + +static stralloc glue = {0}; +static stralloc ip = {0}; + +static int dns_ipplus(ipalloc *ia,stralloc *sa,int pref) +{ + struct ip_mx ix; + int error = 0; + char ip4[4]; + char ip6[16]; + int i; + + /* Case 1: sa is just IPv4 */ + + if (ip4_scanbracket(sa->s,ip4)) { + if (!stralloc_copys(&glue,sa->s)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + if (glue.s[0]) { + ix.pref = 0; + ix.af = AF_INET; + byte_copy(&ix.addr,4,ip4); // = ip; //cp + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + return 0; + } + } + + /* Case 2: sa is just IPv6 */ + + if (ip6_scanbracket(sa->s,ip6)) { + if (!stralloc_copys(&glue,sa->s)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + if (glue.s[0]) { + ix.pref = 0; + ix.af = AF_INET6; + byte_copy(&ix.addr,16,ip6); // = ip; //cp + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + return 0; + } + } + + /* Case 3: sa is fqdn and looking for IPv6 */ + + if (dns_ip6(&ip,sa) > 0) { + for (i = 0; i + 16 <= ip.len; i += 16) { + if (ip6_isv4mapped(ip.s + i)) continue; + ix.af = AF_INET6; + ix.pref = pref; + byte_copy(&ix.addr,16,ip.s + i); // = ip; //cp + str_copy(ix.mxh,sa->s); // mx hostname + if (!ipalloc_append(ia,&ix)) { error = DNS_MEM; break; } + error = 0; + } + } else + error = 1; + + /* Case 4: sa is fqdn and looking for IPv4 */ + + if (dns_ip4(&ip,sa) > 0) { + for (i = 0; i + 4 <= ip.len; i += 4) { + ix.af = AF_INET; + ix.pref = pref; + byte_copy(&ix.addr,4,ip.s + i); // = ip; //cp + str_copy(ix.mxh,sa->s); // mx hostname + if (!ipalloc_append(ia,&ix)) { error = DNS_MEM; break; } + error = 0; + } + } else + error += 2; + + return error; +} + +int dns_ipalloc(ipalloc *ia,stralloc *sa) +{ + if (!ipalloc_readyplus(ia,0)) return DNS_MEM; + ia->len = 0; + + return dns_ipplus(ia,sa,0); +} + +/* dns_mxip */ + +int dns_mxip(ipalloc *ia,stralloc *sa,unsigned long random) { + struct mx { stralloc sa; unsigned short p; } *mx; + struct ip_mx ix; + int nummx; + int i; + int j = 0; + int len; + int flagsoft; + uint16 pref; + + /* Case 1: sa is just IPv4 or IPv6 */ + + if (!ipalloc_readyplus(ia,0)) return DNS_MEM; + ia->len = 0; + + if (!stralloc_copys(&glue,sa->s)) return DNS_MEM; + if (!stralloc_0(&glue)) return DNS_MEM; + if (glue.s[0]) { + ix.pref = 0; + if (!glue.s[ip4_scan(glue.s,(char *)&ix.addr.ip4)] || \ + !glue.s[ip4_scanbracket(glue.s,(char *)&ix.addr.ip4)]) { + ix.af = AF_INET; + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + return 0; + } + if (!glue.s[ip6_scan(glue.s,(char *)&ix.addr.ip6)] || \ + !glue.s[ip6_scanbracket(glue.s,(char *)&ix.addr.ip6)]) { + ix.af = AF_INET6; + if (!ipalloc_append(ia,&ix)) return DNS_MEM; + return 0; + } + } + + /* Case 2: sa is FQDN and do a mx lookup */ + + DNS_INIT + nummx = 0; + len = 0; + i = dns_mx(&ip,sa); + mx = (struct mx *) alloc(i * sizeof(struct mx)); + if (!mx) return DNS_MEM; + + if (i) { + do { + j = str_chr(ip.s + len + 2,'\0'); /* several answers */ + mx[nummx].sa.s = 0; + if (!stralloc_copys(&mx[nummx].sa,ip.s + len + 2)) { /* mxhost name */ + alloc_free(mx); return DNS_MEM; + } + ip.s[len + 3] = '\0'; + uint16_unpack_big(ip.s + len,&pref); + mx[nummx].p = pref; + len += j + 3; + ++nummx; + } while (len < ip.len); + } + + if (!nummx) return dns_ipalloc(ia,sa); /* e.g., CNAME -> A */ + flagsoft = 0; + + while (nummx > 0) { + unsigned long numsame; + i = 0; + numsame = 1; + for (j = 1; j < nummx; ++j) { + if (mx[j].p < mx[i].p) { + i = j; + numsame = 1; + } + else if (mx[j].p == mx[i].p) { + ++numsame; + random = random * 69069 + 1; + if ((random / 2) < (2147483647 / numsame)) i = j; + } + } + + switch (dns_ipplus(ia,&mx[i].sa,mx[i].p)) { + case -1: return DNS_MEM; + case -2: case -3: flagsoft = -5; break; + } + + alloc_free(mx[i].sa.s); + mx[i] = mx[--nummx]; + } + + alloc_free(mx); + return flagsoft; +} + +int dns_ip(ipalloc *ia,stralloc *sa) +{ + + if (!ipalloc_readyplus(ia,0)) return DNS_MEM; + ia->len = 0; + + return dns_ipplus(ia,sa,0); +} diff --git a/sqmail-4.3.07/src/dns_tlsa.c b/sqmail-4.3.07/src/dns_tlsa.c new file mode 100644 index 0000000..4b674c1 --- /dev/null +++ b/sqmail-4.3.07/src/dns_tlsa.c @@ -0,0 +1,53 @@ +#include "byte.h" +#include "stralloc.h" +#include "uint_t.h" +#include "dns.h" +#include "logmsg.h" + +static char *q = 0; + +int dns_tlsa_packet(stralloc *out,const char *buf,unsigned int len) +{ + unsigned int pos; + char header[12]; + uint16 datalen; + uint16 numanswers; + int ranswers = 0; + + if (!stralloc_copys(out,"")) return DNS_MEM; + + pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 6,&numanswers); + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos += 4; + + while (numanswers--) { + pos = dns_packet_skipname(buf,len,pos); if (!pos) return DNS_ERR; + pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return DNS_ERR; + uint16_unpack_big(header + 8,&datalen); + if (byte_equal(header,2,DNS_T_TLSA)) + if (byte_equal(header + 2,2,DNS_C_IN)) { + if (pos + datalen > len) return DNS_ERR; + if (!stralloc_catb(out,buf + pos,datalen)) return DNS_MEM; + } + pos += datalen; + ++ranswers; + } + if (!stralloc_0(out)) return DNS_MEM; + + return ranswers; +} + +int dns_tlsa(stralloc *out,const stralloc *fqdn) +{ + int rc = 0; + + if (dns_domain_fromdot(&q,fqdn->s,fqdn->len) <= 0) return DNS_ERR; + if (dns_resolve(q,DNS_T_TLSA) >= 0) { + if ((rc = dns_tlsa_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) < 0) return DNS_ERR; + dns_transmit_free(&dns_resolve_tx); + dns_domain_free(&q); + } + + return rc; +} diff --git a/sqmail-4.3.07/src/dnscname.c b/sqmail-4.3.07/src/dnscname.c new file mode 100644 index 0000000..546d273 --- /dev/null +++ b/sqmail-4.3.07/src/dnscname.c @@ -0,0 +1,32 @@ +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "exit.h" +#include "dns.h" +#include "logmsg.h" + +#define WHO "dnscname" + +stralloc sa = {0}; +stralloc out = {0}; + +int main(int argc,char **argv) +{ + int r; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"dnscname fqdn"); + + if (!stralloc_copys(&sa,argv[1])) + logmsg(WHO,111,FATAL,"out of memory"); + + DNS_INIT + if ((r = dns_cname(&out,&sa)) < 0) _exit(1); + if (r > 0) { + buffer_putflush(buffer_1,out.s,out.len); + buffer_putsflush(buffer_1,"\n"); + } + + _exit(0); +} diff --git a/sqmail-4.3.07/src/dnsdoe.c b/sqmail-4.3.07/src/dnsdoe.c new file mode 100644 index 0000000..ad6b253 --- /dev/null +++ b/sqmail-4.3.07/src/dnsdoe.c @@ -0,0 +1,14 @@ +#include <unistd.h> +#include "buffer.h" +#include "exit.h" +#include "dnsresolv.h" +#include "dns.h" + +void dnsdoe(int r) +{ + switch (r) { + case DNS_HARD: buffer_putsflush(buffer_2,"hard error\n"); _exit(100); + case DNS_SOFT: buffer_putsflush(buffer_2,"soft error\n"); _exit(111); + case DNS_MEM: buffer_putsflush(buffer_2,"out of memory\n"); _exit(111); + } +} diff --git a/sqmail-4.3.07/src/dnsfq.c b/sqmail-4.3.07/src/dnsfq.c new file mode 100644 index 0000000..a174541 --- /dev/null +++ b/sqmail-4.3.07/src/dnsfq.c @@ -0,0 +1,64 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "dns.h" +#include "ip.h" +#include "exit.h" +#include "logmsg.h" + +#define WHO "dnsfq" +#define MAXCNAME 10 + +stralloc ca = {0}; +stralloc sa = {0}; +stralloc ia = {0}; + +int main(int argc,char **argv) +{ + int i, r; + char ip4str[IP4_FMT]; + char ip6str[IP6_FMT]; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"dnsfq fqdn"); + + if (!stralloc_copys(&sa,argv[1])) + logmsg(WHO,111,FATAL,"out of memory"); + + DNS_INIT + buffer_put(buffer_1,sa.s,sa.len); + buffer_puts(buffer_1," "); + for (i = 0; i <= MAXCNAME; i++) { + if ((r = dns_cname(&ca,&sa)) < 0) _exit(1); + if (r > 0) { + if (!stralloc_copy(&sa,&ca)) + logmsg(WHO,111,FATAL,"out of memory"); + buffer_puts(buffer_1,"-> "); + buffer_put(buffer_1,sa.s,sa.len); + buffer_puts(buffer_1," "); + } + else break; + } + buffer_putsflush(buffer_1,"\n"); + + if ((i = dns_ip6(&ia,&sa)) > 0) { + for (i = 0; i + 16 <= ia.len; i += 16) { + if (ip6_isv4mapped(ia.s + i)) continue; + buffer_put(buffer_1,ip6str,ip6_fmt(ip6str,ia.s + i)); + buffer_puts(buffer_1,"\n"); + } + } + + if ((i = dns_ip4(&ia,&sa)) > 0) { + for (i = 0; i + 4 <= ia.len;i += 4) { + buffer_put(buffer_1,ip4str,ip4_fmt(ip4str,ia.s + i)); + buffer_puts(buffer_1,"\n"); + } + } + buffer_flush(buffer_1); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/dnsip.c b/sqmail-4.3.07/src/dnsip.c new file mode 100644 index 0000000..2c84d04 --- /dev/null +++ b/sqmail-4.3.07/src/dnsip.c @@ -0,0 +1,46 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "ip.h" +#include "exit.h" +#include "fmt.h" +#include "dns.h" +#include "logmsg.h" + +#define WHO "dnsip" + +stralloc sa = {0}; +stralloc out = {0}; + +int main(int argc, char **argv) +{ + int i; + char ip4str[IP4_FMT]; + char ip6str[IP6_FMT]; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"dnsip fqdn"); + + if (!stralloc_copys(&sa,argv[1])) + logmsg(WHO,111,FATAL,"out of memory"); + + DNS_INIT + if (dns_ip6(&out,&sa) > 0) /* IPv6 first */ + for (i = 0; i + 16 <= out.len; i += 16) { + if (ip6_isv4mapped(out.s + i)) continue; + buffer_put(buffer_1,ip6str,ip6_fmt(ip6str,out.s + i)); + buffer_puts(buffer_1,"\n"); + } + + if (dns_ip4(&out,&sa) > 0) + for (i = 0; i + 4 <= out.len;i += 4) { + buffer_put(buffer_1,ip4str,ip4_fmt(ip4str,out.s + i)); + buffer_puts(buffer_1,"\n"); + } + buffer_putsflush(buffer_1,""); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/dnsmxip.c b/sqmail-4.3.07/src/dnsmxip.c new file mode 100644 index 0000000..de3bb7c --- /dev/null +++ b/sqmail-4.3.07/src/dnsmxip.c @@ -0,0 +1,106 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "fmt.h" +#include "dnsresolv.h" +#include "ip.h" +#include "now.h" +#include "exit.h" +#include "dns.h" +#include "logmsg.h" +#include "str.h" + +#define WHO "dnsmxip" + +static stralloc sa = {0}; +static stralloc ia = {0}; +static stralloc out = {0}; +static stralloc ipaddr = {0}; + +int dns_ia(stralloc *ip,char *s) +{ + int i; + int j = 0; + int r = 0; + char ip4str[IP4_FMT]; + char ip6str[IP6_FMT]; + + if (!stralloc_copys(&sa,s)) return -1; + if (sa.s[sa.len-1] != '.') + if (!stralloc_append(&sa,".")) return -1; + if (!stralloc_copys(ip,"")) return -1; + + DNS_INIT + if (dns_ip6(&ia,&sa) > 0) { + for (i = 0; i + 16 <= ia.len; i += 16) { + if (ip6_isv4mapped(ia.s + i)) continue; + j = ip6_fmt(ip6str,ia.s + i); + r += j; + if (!stralloc_catb(ip,ip6str,j)) return -1; + if (!stralloc_cats(ip," ")) return -1; + r++; + } + } + + if (dns_ip4(&ia,&sa) > 0) { + for (i = 0; i + 4 <= ia.len; i += 4) { + j = ip4_fmt(ip4str,ia.s + i); + r += j; + if (!stralloc_catb(ip,ip4str,j)) return -1; + if (!stralloc_cats(ip," ")) return -1; + r++; + } + } + if (!stralloc_0(ip)) return -1; + + return r?r-1:0; +} + +int main(int argc,char **argv) +{ + int j, k, r; + uint16 u; + int len; + char num[FMT_ULONG]; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"dnsmx fqdn"); + if (!stralloc_copys(&sa,argv[1])) + logmsg(WHO,111,FATAL,"out of memory"); + + DNS_INIT + if ((r = dns_mx(&out,&sa)) < 0) _exit(1); + if (r > 0) { + j = len = 0; + do { + j = str_chr(out.s + len + 2,'\0'); + k = dns_ia(&ipaddr,out.s + len + 2); /* IP */ + if (k) { + buffer_put(buffer_1,out.s + len + 2,j); /* MX */ + buffer_puts(buffer_1,": "); + out.s[len + 3] = '\0'; + uint16_unpack_big(out.s + len,&u); + buffer_put(buffer_1,num,fmt_ulong(num,u)) ; + buffer_puts(buffer_1," ["); + buffer_put(buffer_1,ipaddr.s,k); + buffer_puts(buffer_1,"]"); + } + buffer_putsflush(buffer_1,"\n"); + len += j + 3; + } while (len < out.len); + } else { /* A/AAAA */ + k = dns_ia(&ipaddr,argv[1]); /* IP */ + if (k) { + buffer_puts(buffer_1,argv[1]); + buffer_puts(buffer_1,": -"); + buffer_puts(buffer_1," ["); + buffer_put(buffer_1,ipaddr.s,k); + buffer_puts(buffer_1,"]"); + buffer_putsflush(buffer_1,"\n"); + } + } + + _exit(0); +} diff --git a/sqmail-4.3.07/src/dnsptr.c b/sqmail-4.3.07/src/dnsptr.c new file mode 100644 index 0000000..25a4731 --- /dev/null +++ b/sqmail-4.3.07/src/dnsptr.c @@ -0,0 +1,37 @@ +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "str.h" +#include "dnsresolv.h" +#include "dns.h" +#include "ip.h" +#include "exit.h" +#include "logmsg.h" + +#define WHO "dnsptr" + +stralloc out = {0}; +char ip4[4]; +char ip6[16]; + +int main(int argc,char **argv) +{ + if (!argv[1]) + logmsg(WHO,100,USAGE,"dnsptr ipv4 || ipv6 (compactified)"); + + DNS_INIT + if (str_chr(argv[1],':') < str_len(argv[1])) { + if (!ip6_scan(argv[1],ip6)) + logmsg(WHO,111,FATAL,"wrong IPv6 format"); + if (dns_name6(&out,ip6) > 0) + buffer_put(buffer_1,out.s,out.len); + } else { + if (!ip4_scan(argv[1],ip4)) + logmsg(WHO,111,FATAL,"wrong IPv4 format"); + if (dns_name4(&out,ip4) > 0) + buffer_put(buffer_1,out.s,out.len); + } + buffer_putsflush(buffer_1,"\n"); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/dnstlsa.c b/sqmail-4.3.07/src/dnstlsa.c new file mode 100644 index 0000000..9871fff --- /dev/null +++ b/sqmail-4.3.07/src/dnstlsa.c @@ -0,0 +1,96 @@ +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "dns.h" +#include "exit.h" +#include "logmsg.h" +#include "getoptb.h" +#include "str.h" +#include "byte.h" + +#define WHO "dnstlsa" + +static stralloc cn = {0}; +static stralloc sa = {0}; +static stralloc out = {0}; + +int main(int argc,char **argv) +{ + int r; + uint16 usage; + uint16 selector; + uint16 type; + char *port = "25"; + char proto[7] = "._tcp."; + char *host; + unsigned char ch; + int opt; + int i, j, k; + int verbose = 0; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"dnstlsa [-v] [-p port] [-u(dp)|-t(cp)] host (tcp on port 25 is default)" ); + + while ((opt = getopt(argc,argv,"vutp:")) != opteof) { + switch (opt) { + case 'p': port = optarg; break; + case 't': break; + case 'u': str_copy(proto,"._udp."); break; + case 'v': verbose = 1; + } + } + if (optind < argc) + host = argv[optind++]; + + if (!stralloc_copyb(&sa, "_",1)) logmsg(WHO,111,FATAL,"out of memory"); + if (!stralloc_cats(&sa,port)) logmsg(WHO,111,FATAL,"out of memory"); + if (!stralloc_cats(&sa,proto)) logmsg(WHO,111,FATAL,"out of memory"); + if (!stralloc_cats(&sa,host)) logmsg(WHO,111,FATAL,"out of memory"); + + DNS_INIT + if (dns_cname(&cn,&sa) > 0) + { if ((r = dns_tlsa(&out,&cn)) < 0) _exit(1); } + else + if ((r = dns_tlsa(&out,&sa)) < 0) _exit(1); + if (!stralloc_0(&sa)) logmsg(WHO,111,FATAL,"out of memory"); + if (verbose) logmsg(WHO,0,INFO,B("checking for TLSA records: ",sa.s,"\n")); + + if (r > 0 && out.len > 4) { + for (i = 0; i <= out.len; i++) { + usage = (unsigned char) out.s[i]; + selector = (unsigned char) out.s[i + 1]; + type = (unsigned char) out.s[i + 2]; + + if (usage == 0) buffer_puts(buffer_1,"Usage: [0], "); + if (usage == 1) buffer_puts(buffer_1,"Usage: [1], "); + if (usage == 2) buffer_puts(buffer_1,"Usage: [2], "); + if (usage == 3) buffer_puts(buffer_1,"Usage: [3], "); + + if (selector == 0) buffer_puts(buffer_1,"Selector: [0], "); + if (selector == 1) buffer_puts(buffer_1,"Selector: [1], "); + + if (type == 0) buffer_puts(buffer_1,"Type: [0] "); // full cert + if (type == 1) buffer_puts(buffer_1,"Type: [1] "); // sha256 + if (type == 2) buffer_puts(buffer_1,"Type: [2] "); // sha512 + + /* Staff of Ra + "(is) six kadams high." However, the builder (h)as + to subtract one kadam out of respect for the Hebrew God. */ + + for (j = i + 3, k = 0; j <= out.len; ++j) { + ch = (unsigned char) out.s[j]; + if ((type == 1 && k == 32) || (type == 2 && k == 64)) { + buffer_putsflush(buffer_1,"\n"); + i = j - 1; break; + } else { + buffer_put(buffer_1,"0123456789abcdef" + (ch >> 4),1); + buffer_put(buffer_1,"0123456789abcdef" + (ch & 0x0f),1); + k++; + } + } + } + } + + _exit(0); +} diff --git a/sqmail-4.3.07/src/dnstxt.c b/sqmail-4.3.07/src/dnstxt.c new file mode 100644 index 0000000..385928e --- /dev/null +++ b/sqmail-4.3.07/src/dnstxt.c @@ -0,0 +1,32 @@ +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "dnsresolv.h" +#include "dns.h" +#include "exit.h" +#include "logmsg.h" + +#define WHO "dnstext" + +stralloc sa = {0}; +stralloc out = {0}; + +int main(int argc,char **argv) +{ + int r; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"dnstxt fqdn"); + + if (!stralloc_copys(&sa,argv[1])) + logmsg(WHO,111,FATAL,"out of memory"); + + DNS_INIT + if ((r = dns_txt(&out,&sa)) < 0) _exit(1); + if (r > 0) { + buffer_put(buffer_1,out.s,out.len); + buffer_putsflush(buffer_1,"\n"); + } + + _exit(0); +} diff --git a/sqmail-4.3.07/src/except.c b/sqmail-4.3.07/src/except.c new file mode 100644 index 0000000..edee976 --- /dev/null +++ b/sqmail-4.3.07/src/except.c @@ -0,0 +1,34 @@ +#include <unistd.h> +#include "wait.h" +#include "logmsg.h" +#include "exit.h" + +#define WHO "except" + +int main(int argc, char **argv) +{ + int pid; + int wstat; + + if (!argv[1]) + logmsg(WHO,100,USAGE,"except program [ arg ... ]"); + + pid = fork(); + if (pid == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + if (pid == 0) { + execvp(argv[1],argv + 1); + if (errno) _exit(111); + _exit(100); + } + + if (wait_pid(&wstat,pid) == -1) + logmsg(WHO,111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + logmsg(WHO,111,FATAL,"child crashed"); + switch (wait_exitcode(wstat)) { + case 0: _exit(100); + case 111: logmsg(WHO,111,FATAL,"temporary child error"); + default: _exit(0); + } +} diff --git a/sqmail-4.3.07/src/failures.sh b/sqmail-4.3.07/src/failures.sh new file mode 100644 index 0000000..c3fe532 --- /dev/null +++ b/sqmail-4.3.07/src/failures.sh @@ -0,0 +1,14 @@ + +awk ' + /^d d/ { + reason = $11 + fail[reason] += 1 + xdelay[reason] += $5 - $4 + } + END { + for (reason in fail) { + str = sprintf("%.2f",xdelay[reason]) + print fail[reason],str,reason + } + } +' diff --git a/sqmail-4.3.07/src/fastforward.c b/sqmail-4.3.07/src/fastforward.c new file mode 100644 index 0000000..f8a7d55 --- /dev/null +++ b/sqmail-4.3.07/src/fastforward.c @@ -0,0 +1,399 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "readclose.h" +#include "stralloc.h" +#include "buffer.h" +#include "strset.h" +#include "getoptb.h" +#include "exit.h" +#include "logmsg.h" +#include "env.h" +#include "sig.h" +#include "qmail.h" +#include "fmt.h" +#include "case.h" +#include "alloc.h" +#include "seek.h" +#include "wait.h" +#include "byte.h" +#include "str.h" +#include "open.h" +#include "cdbread.h" + +#define WHO "fastforward" + +static void usage() +{ + logmsg(WHO,100,USAGE,"fastforward [ -nNpP ] data.cdb"); +} +static void nomem() +{ + logmsg(WHO,111,FATAL,"out of memory"); +} + +static void print(char *s) +{ + char ch; + while ((ch = *s++)) { + buffer_put(buffer_2,&ch,1); + } +} + +static void printsafe(char *s) +{ + char ch; + while ((ch = *s++)) { + if (ch < 32) ch = '_'; + buffer_put(buffer_2,&ch,1); + } +} + +struct qmail qq; +char qp[FMT_ULONG]; +char qqbuf[1]; + +ssize_t qqwrite(int fd,char *buf,int len) +{ + qmail_put(&qq,buf,len); + return len; +} + +buffer bufq = BUFFER_INIT(qqwrite,-1,qqbuf,sizeof(qqbuf)); + +char messbuf[BUFSIZE_MESS]; +buffer mess = BUFFER_INIT(read,0,messbuf,sizeof(messbuf)); + +int flagdeliver = 1; +int flagpassthrough = 0; + +char *dtline; +stralloc sender = {0}; +stralloc programs = {0}; +stralloc forward = {0}; + +strset done; +stralloc todo = {0}; + +stralloc mailinglist = {0}; + +void dofile(char *fn) +{ + int fd; + struct stat st; + int i; + int j; + + if (!stralloc_copys(&mailinglist,"")) nomem(); + + fd = open_read(fn); + if (fd == -1) + logmsg(WHO,111,FATAL,B("unable to read: ",fn)); + if (fstat(fd,&st) == -1) + logmsg(WHO,111,FATAL,B("unable to read: ",fn)); + if ((st.st_mode & 0444) != 0444) + logmsg(WHO,111,FATAL,B(fn," is not world-readable")); + if (readclose_append(fd,&mailinglist,1024) == -1) + logmsg(WHO,111,FATAL,B("unable to read: ",fn)); + + i = 0; + for (j = 0; j < mailinglist.len; ++j) + if (!mailinglist.s[j]) { + if ((mailinglist.s[i] == '.') || (mailinglist.s[i] == '/')) { + if (!stralloc_cats(&todo,mailinglist.s + i)) nomem(); + if (!stralloc_0(&todo)) nomem(); + } + else if ((mailinglist.s[i] == '&') && (j - i < 900)) { + if (!stralloc_cats(&todo,mailinglist.s + i)) nomem(); + if (!stralloc_0(&todo)) nomem(); + } + i = j + 1; + } +} + +char *fncdb; +int fdcdb; +stralloc key = {0}; +uint32 dlen; +stralloc data = {0}; +struct cdb cdb; + +void cdbreaderror() +{ + logmsg(WHO,111,FATAL,B("unable to read: ",fncdb)); +} + +int findtarget(int flagwild,char *prepend,char *addr) +{ + int r; + int at; + + if (!stralloc_copys(&key,prepend)) nomem(); + if (!stralloc_cats(&key,addr)) nomem(); + case_lowerb(key.s,key.len); + + r = cdb_find(&cdb,key.s,key.len); + if (r == -1) cdbreaderror(); + if (r) return 1; + + if (!flagwild) return 0; + at = str_rchr(addr,'@'); + if (!addr[at]) return 0; + + if (!stralloc_copys(&key,prepend)) nomem(); + if (!stralloc_cats(&key,addr + at)) nomem(); + case_lowerb(key.s,key.len); + + r = cdb_find(&cdb,key.s,key.len); + if (r == -1) cdbreaderror(); + if (r) return 1; + + if (!stralloc_copys(&key,prepend)) nomem(); + if (!stralloc_catb(&key,addr,at + 1)) nomem(); + case_lowerb(key.s,key.len); + + r = cdb_find(&cdb,key.s,key.len); + if (r == -1) cdbreaderror(); + if (r) return 1; + + return 0; +} + +int gettarget(int flagwild,char *prepend,char *addr) +{ + if (!findtarget(flagwild,prepend,addr)) return 0; + dlen = cdb_datalen(&cdb); + if (!stralloc_ready(&data,(unsigned int) dlen)) nomem(); + data.len = dlen; + if (cdb_read(&cdb,data.s,data.len,cdb_datapos(&cdb)) == -1) + cdbreaderror(); + + return 1; +} + +void doprogram(char *arg) +{ + char *args[5]; + int child; + int wstat; + + if (!flagdeliver) { + print("run "); + printsafe(arg); + print("\n"); + buffer_flush(buffer_2); + return; + } + + if (*arg == '!') { + args[0] = "preline"; + args[1] = "sh"; + args[2] = "-c"; + args[3] = arg + 1; + args[4] = 0; + } + else { + args[0] = "sh"; + args[1] = "-c"; + args[2] = arg + 1; + args[3] = 0; + } + + switch (child = vfork()) { + case -1: + logmsg(WHO,111,FATAL,"unable to fork: "); + case 0: + sig_pipedefault(); + execvp(*args,args); + logmsg(WHO,111,FATAL,B("unable to run: ",arg)); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) + logmsg(WHO,111,FATAL,B("child crashed in: ",arg)); + + switch (wait_exitcode(wstat)) { + case 64: case 65: case 70: case 76: case 77: case 78: case 112: + case 100: _exit(100); + case 0: break; + default: _exit(111); + } + + if (seek_begin(0) == -1) + logmsg(WHO,111,FATAL,"unable to rewind input: "); +} + +void dodata() +{ + int i; + int j; + i = 0; + + for (j = 0; j < data.len; ++j) + if (!data.s[j]) { + if ((data.s[i] == '|') || (data.s[i] == '!')) + doprogram(data.s + i); + else if ((data.s[i] == '.') || (data.s[i] == '/')) { + if (!stralloc_cats(&todo,data.s + i)) nomem(); + if (!stralloc_0(&todo)) nomem(); + } + else if ((data.s[i] == '&') && (j - i < 900)) { + if (!stralloc_cats(&todo,data.s + i)) nomem(); + if (!stralloc_0(&todo)) nomem(); + } + i = j + 1; + } +} + +void dorecip(char *addr) +{ + + if (!findtarget(0,"?",addr)) + if (gettarget(0,":",addr)) { + dodata(); + return; + } + if (!stralloc_cats(&forward,addr)) nomem(); + if (!stralloc_0(&forward)) nomem(); +} + +void doorigrecip(char *addr) +{ + if (sender.len) + if ((sender.len != 4) || byte_diff(sender.s,4,"#@[]")) + if (gettarget(1,"?",addr)) + if (!stralloc_copy(&sender,&data)) nomem(); + if (!gettarget(1,":",addr)) + if (flagpassthrough) + _exit(0); + else + logmsg(WHO,100,ERROR,"Sorry, no mailbox here by that name. (#5.1.1)"); + dodata(); +} + +stralloc recipient = {0}; +int flagdefault = 0; + +int main(int argc,char **argv) +{ + int opt; + char *x; + int i; + + sig_pipeignore(); + + dtline = env_get("DTLINE"); + if (!dtline) dtline = ""; + + x = env_get("SENDER"); + if (!x) x = "original envelope sender"; + if (!stralloc_copys(&sender,x)) nomem(); + + if (!stralloc_copys(&forward,"")) nomem(); + if (!strset_init(&done)) nomem(); + + while ((opt = getopt(argc,argv,"nNpPdD")) != opteof) + switch (opt) { + case 'n': flagdeliver = 0; break; + case 'N': flagdeliver = 1; break; + case 'p': flagpassthrough = 1; break; + case 'P': flagpassthrough = 0; break; + case 'd': flagdefault = 1; break; + case 'D': flagdefault = 0; break; + default: usage(); + } + argv += optind; + + fncdb = *argv; + if (!fncdb) usage(); + fdcdb = open_read(fncdb); + if (fdcdb == -1) cdbreaderror(); + cdb_init(&cdb,fdcdb); + + if (flagdefault) { + x = env_get("DEFAULT"); + if (!x) x = env_get("EXT"); + if (!x) logmsg(WHO,100,FATAL,"$DEFAULT or $EXT must be set"); + if (!stralloc_copys(&recipient,x)) nomem(); + if (!stralloc_cats(&recipient,"@")) nomem(); + x = env_get("HOST"); + if (!x) logmsg(WHO,100,FATAL,"$HOST must be set"); + if (!stralloc_cats(&recipient,x)) nomem(); + if (!stralloc_0(&recipient)) nomem(); + x = recipient.s; + } + else { + x = env_get("RECIPIENT"); + if (!x) logmsg(WHO,100,FATAL,"$RECIPIENT must be set"); + } + if (!strset_add(&done,x)) nomem(); + doorigrecip(x); + + while (todo.len) { + i = todo.len - 1; + while ((i > 0) && todo.s[i - 1]) --i; + todo.len = i; + + if (strset_in(&done,todo.s + i)) continue; + + x = alloc(str_len(todo.s + i) + 1); + if (!x) nomem(); + str_copy(x,todo.s + i); + if (!strset_add(&done,x)) nomem(); + + x = todo.s + i; + if (*x == 0) + continue; + else if ((*x == '.') || (*x == '/')) + dofile(x); + else + dorecip(x + 1); + } + + if (!forward.len) { + if (!flagdeliver) { + print("no forwarding\n"); + buffer_flush(buffer_2); + } + _exit(flagpassthrough ? 99 : 0); + } + + if (!stralloc_0(&sender)) nomem(); + + if (!flagdeliver) { + print("from <"); + printsafe(sender.s); + print(">\n"); + while (forward.len) { + i = forward.len - 1; + while ((i > 0) && forward.s[i - 1]) --i; + forward.len = i; + print("to <"); + printsafe(forward.s + i); + print(">\n"); + } + buffer_flush(buffer_2); + _exit(flagpassthrough ? 99 : 0); + } + + if (qmail_open(&qq) == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + qmail_puts(&qq,dtline); + if (buffer_copy(&bufq,&mess) != 0) + logmsg(WHO,111,FATAL,"unable to read message: "); + buffer_flush(&bufq); + qp[fmt_ulong(qp,qmail_qp(&qq))] = 0; + + qmail_from(&qq,sender.s); + + while (forward.len) { + i = forward.len - 1; + while ((i > 0) && forward.s[i - 1]) --i; + forward.len = i; + qmail_to(&qq,forward.s + i); + } + + x = qmail_close(&qq); + if (*x) logmsg(WHO,*x == 'D' ? 100 : 111,FATAL,x + 1); + logmsg(WHO,flagpassthrough ? 99 : 0,LOG,B("qp ",qp)); +} diff --git a/sqmail-4.3.07/src/fifo.c b/sqmail-4.3.07/src/fifo.c new file mode 100644 index 0000000..5547294 --- /dev/null +++ b/sqmail-4.3.07/src/fifo.c @@ -0,0 +1,9 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include "fifo.h" + +#ifdef HASMKFIFO +int fifo_make(char *fn, int mode) { return mkfifo(fn,mode); } +#else +int fifo_make(char *fn, int mode) { return mknod(fn,S_IFIFO | mode,0); } +#endif diff --git a/sqmail-4.3.07/src/find-systype.sh b/sqmail-4.3.07/src/find-systype.sh new file mode 100755 index 0000000..16266d3 --- /dev/null +++ b/sqmail-4.3.07/src/find-systype.sh @@ -0,0 +1,144 @@ +# oper-:arch-:syst-:chip-:kern- +# oper = operating system type; e.g., sunos-4.1.4 +# arch = machine language; e.g., sparc +# syst = which binaries can run; e.g., sun4 +# chip = chip model; e.g., micro-2-80 +# kern = kernel version; e.g., sun4m +# dependence: arch --- chip +# \ \ +# oper --- syst --- kern +# so, for example, syst is interpreted in light of oper, but chip is not. +# anyway, no slashes, no extra colons, no uppercase letters. +# the point of the extra -'s is to ease parsing: can add hierarchies later. +# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium, +# and i386-486 (486s do have more instructions, you know) as well as i386. +# the idea here is to include ALL useful available information. + +exec 2>/dev/null +sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`" +if [ x"$sys" != x ] +then + unamer="`uname -r | tr /: ..`" + unamem="`uname -m | tr /: ..`" + unamev="`uname -v | tr /: ..`" + + case "$sys" in + bsd.os) + # in bsd 4.4, uname -v does not have useful info. + # in bsd 4.4, uname -m is arch, not chip. + oper="$sys-$unamer" + arch="$unamem" + syst="" + chip="`sysctl -n hw.model`" + kern="" + ;; + freebsd) + # see above about bsd 4.4 + oper="$sys-$unamer" + arch="$unamem" + syst="" + chip="`sysctl -n hw.model`" # hopefully + kern="" + ;; + netbsd) + # see above about bsd 4.4 + oper="$sys-$unamer" + arch="$unamem" + syst="" + chip="`sysctl -n hw.model`" # hopefully + kern="" + ;; + linux) + # as in bsd 4.4, uname -v does not have useful info. + oper="$sys-$unamer" + syst="" + chip="$unamem" + kern="" + case "$chip" in + i386|i486|i586|i686) + arch="i386" + ;; + alpha) + arch="alpha" + ;; + esac + ;; + aix) + # naturally IBM has to get uname -r and uname -v backwards. dorks. + oper="$sys-$unamev-$unamer" + arch="`arch | tr /: ..`" + syst="" + chip="$unamem" + kern="" + ;; + sunos) + oper="$sys-$unamer-$unamev" + arch="`(uname -p || mach) | tr /: ..`" + syst="`arch | tr /: ..`" + chip="$unamem" # this is wrong; is there any way to get the real info? + kern="`arch -k | tr /: ..`" + ;; + unix_sv) + oper="$sys-$unamer-$unamev" + arch="`uname -m`" + syst="" + chip="$unamem" + kern="" + ;; + *) + oper="$sys-$unamer-$unamev" + arch="`arch | tr /: ..`" + syst="" + chip="$unamem" + kern="" + ;; + esac +else + $CC -c trycpp.c + $LD -o trycpp trycpp.o + case `./trycpp` in + nextstep) + oper="nextstep-`hostinfo | sed -n 's/^[ ]*NeXT Mach \([^:]*\):.*$/\1/p'`" + arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`" + syst="" + chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`" + kern="" + ;; + *) + oper="unknown" + arch="" + syst="" + chip="" + kern="" + ;; + esac + rm -f trycpp.o trycpp +fi + +case "$chip" in +80486) + # let's try to be consistent here. (BSD/OS) + chip=i486 + ;; +i486DX) + # respect the hyphen hierarchy. (FreeBSD) + chip=i486-dx + ;; +i486.DX2) + # respect the hyphen hierarchy. (FreeBSD) + chip=i486-dx2 + ;; +Intel.586) + # no, you nitwits, there is no such chip. (NeXTStep) + chip=pentium + ;; +i586) + # no, you nitwits, there is no such chip. (Linux) + chip=pentium + ;; +i686) + # STOP SAYING THAT! (Linux) + chip=ppro +esac + +echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]' diff --git a/sqmail-4.3.07/src/fmtqfn.c b/sqmail-4.3.07/src/fmtqfn.c new file mode 100644 index 0000000..139cccf --- /dev/null +++ b/sqmail-4.3.07/src/fmtqfn.c @@ -0,0 +1,22 @@ +#include "fmtqfn.h" +#include "fmt.h" +#include "auto_split.h" + +unsigned int fmtqfn(char *s,char *dirslash,unsigned long id,int flagsplit) +{ + unsigned int len; + unsigned int i; + + len = 0; + i = fmt_str(s,dirslash); len += i; if (s) s += i; + + if (flagsplit) { + i = fmt_ulong(s,id % auto_split); len += i; if (s) s += i; + i = fmt_str(s,"/"); len += i; if (s) s += i; + } + + i = fmt_ulong(s,id); len += i; if (s) s += i; + if (s) *s++ = 0; ++len; + + return len; +} diff --git a/sqmail-4.3.07/src/fork.h1 b/sqmail-4.3.07/src/fork.h1 new file mode 100644 index 0000000..a1accc7 --- /dev/null +++ b/sqmail-4.3.07/src/fork.h1 @@ -0,0 +1,7 @@ +#ifndef FORK_H +#define FORK_H + +int fork(); +#define vfork fork + +#endif diff --git a/sqmail-4.3.07/src/fork.h2 b/sqmail-4.3.07/src/fork.h2 new file mode 100644 index 0000000..fa3dd5d --- /dev/null +++ b/sqmail-4.3.07/src/fork.h2 @@ -0,0 +1,7 @@ +#ifndef FORK_H +#define FORK_H + +int fork(); +int vfork(); + +#endif diff --git a/sqmail-4.3.07/src/forward.c b/sqmail-4.3.07/src/forward.c new file mode 100644 index 0000000..7421d8b --- /dev/null +++ b/sqmail-4.3.07/src/forward.c @@ -0,0 +1,58 @@ +#include <unistd.h> +#include "sig.h" +#include "exit.h" +#include "env.h" +#include "qmail.h" +#include "logmsg.h" +#include "buffer.h" +#include "fmt.h" + +#define WHO "forward" + +void die_nomem() { logmsg(WHO,111,FATAL,"out of memory"); } + +struct qmail qqt; + +ssize_t mywrite(int fd, char *buf, int len) +{ + qmail_put(&qqt,buf,len); + return len; +} + +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); +char outbuf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(mywrite,-1,outbuf,sizeof(outbuf)); + +char num[FMT_ULONG]; + +int main(int argc, char **argv) +{ + char *sender; + char *dtline; + char *qqx; + + sig_pipeignore(); + + sender = env_get("NEWSENDER"); + if (!sender) + logmsg(WHO,100,FATAL,"NEWSENDER not set"); + dtline = env_get("DTLINE"); + if (!dtline) + logmsg(WHO,100,FATAL,"DTLINE not set"); + + if (qmail_open(&qqt) == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + qmail_puts(&qqt,dtline); + if (buffer_copy(&bo,&bi) != 0) + logmsg(WHO,111,FATAL,"unable to read message: "); + buffer_flush(&bo); + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + qmail_from(&qqt,sender); + while (*++argv) qmail_to(&qqt,*argv); + qqx = qmail_close(&qqt); + if (*qqx) logmsg(WHO,*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + logmsg(WHO,0,LOG,B("qp ",num)); +} diff --git a/sqmail-4.3.07/src/gfrom.c b/sqmail-4.3.07/src/gfrom.c new file mode 100644 index 0000000..c04b65f --- /dev/null +++ b/sqmail-4.3.07/src/gfrom.c @@ -0,0 +1,8 @@ +#include "str.h" +#include "gfrom.h" + +int gfrom(char *s,int len) +{ + while ((len > 0) && (*s == '>')) { ++s; --len; } + return (len >= 5) && !str_diffn(s,"From ",5); +} diff --git a/sqmail-4.3.07/src/headerbody.c b/sqmail-4.3.07/src/headerbody.c new file mode 100644 index 0000000..82c5684 --- /dev/null +++ b/sqmail-4.3.07/src/headerbody.c @@ -0,0 +1,78 @@ +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "hfield.h" +#include "headerbody.h" + +static int getsa(buffer *b,stralloc *sa,int *match) +{ + if (!*match) return 0; + if (getln(b,sa,match,'\n') == -1) return -1; + if (*match) return 1; + if (!sa->len) return 0; + if (!stralloc_append(sa,"\n")) return -1; + + return 1; +} + +static stralloc line = {0}; +static stralloc nextline = {0}; + +int headerbody(b,dohf,hdone,dobl) +buffer *b; +void (*dohf)(); +void (*hdone)(); +void (*dobl)(); +{ + int match; + int flaglineok; + match = 1; + flaglineok = 0; + + for (;;) { + switch (getsa(b,&nextline,&match)) { + case -1: + return -1; + case 0: + if (flaglineok) dohf(&line); + hdone(); + /* no message body; could insert blank line here */ + return 0; + } + + if (flaglineok) { + if ((nextline.s[0] == ' ') || (nextline.s[0] == '\t')) { + if (!stralloc_cat(&line,&nextline)) return -1; + continue; + } + dohf(&line); + } + + if (nextline.len == 1) { + hdone(); + dobl(&nextline); + break; + } + + if (stralloc_starts(&nextline,"From ")) { + if (!stralloc_copys(&line,"MBOX-Line: ")) return -1; + if (!stralloc_cat(&line,&nextline)) return -1; + } else if (hfield_valid(nextline.s,nextline.len)) { + if (!stralloc_copy(&line,&nextline)) return -1; + } else { + hdone(); + if (!stralloc_copys(&line,"\n")) return -1; + dobl(&line); + dobl(&nextline); + break; + } + flaglineok = 1; + } + + for (;;) + switch (getsa(b,&nextline,&match)) { + case -1: return -1; + case 0: return 0; + case 1: dobl(&nextline); + } +} diff --git a/sqmail-4.3.07/src/hfield.c b/sqmail-4.3.07/src/hfield.c new file mode 100644 index 0000000..2376e1f --- /dev/null +++ b/sqmail-4.3.07/src/hfield.c @@ -0,0 +1,113 @@ +#include "hfield.h" + +static char *(hname[]) = { + "unknown-header" +, "sender" +, "from" +, "reply-to" +, "to" +, "cc" +, "bcc" +, "date" +, "message-id" +, "subject" +, "resent-sender" +, "resent-from" +, "resent-reply-to" +, "resent-to" +, "resent-cc" +, "resent-bcc" +, "resent-date" +, "resent-message-id" +, "return-receipt-to" +, "errors-to" +, "apparently-to" +, "received" +, "return-path" +, "delivered-to" +, "content-length" +, "content-type" +, "content-transfer-encoding" +, "notice-requested-upon-delivery-to" +, "mail-followup-to" +, 0 +}; + +static int hmatch( char *s,int len,char *t) +{ + int i; + char ch; + + for (i = 0; (ch = t[i]); ++i) { + if (i >= len) return 0; + if (ch != s[i]) { + if (ch == '-') return 0; + if (ch - 32 != s[i]) return 0; + } + } + for (;;) { + if (i >= len) return 0; + ch = s[i]; + if (ch == ':') return 1; + if ((ch != ' ') && (ch != '\t')) return 0; + ++i; + } +} + +int hfield_known(char *s,int len) +{ + int i; + char *t; + + for (i = 1; (t = hname[i]); ++i) + if (hmatch(s,len,t)) + return i; + + return 0; +} + +int hfield_valid(char *s,int len) +{ + int i; + int j; + char ch; + + for (j = 0; j < len; ++j) + if (s[j] == ':') break; + + if (j >= len) return 0; + + while (j) { + ch = s[j - 1]; + if ((ch != ' ') && (ch != '\t')) + break; + --j; + } + if (!j) return 0; + + for (i = 0; i < j; ++i) { + ch = s[i]; + if (ch <= 32) return 0; + if (ch >= 127) return 0; + } + return 1; +} + +unsigned int hfield_skipname(char *s,int len) +{ + int i; + char ch; + + for (i = 0; i < len; ++i) + if (s[i] == ':') break; + + if (i < len) ++i; + while (i < len) { + ch = s[i]; + if ((ch != '\t') && (ch != '\n') && (ch != '\r') && (ch != ' ')) + break; + ++i; + } + + return i; +} diff --git a/sqmail-4.3.07/src/hier.c b/sqmail-4.3.07/src/hier.c new file mode 100644 index 0000000..5169912 --- /dev/null +++ b/sqmail-4.3.07/src/hier.c @@ -0,0 +1,163 @@ +#include "auto_qmail.h" +#include "auto_split.h" +#include "auto_uids.h" +#include "fmt.h" +#include "fifo.h" +#include "ipalloc.h" +#include "tcpto.h" +#include "hier.h" + +char buf[100 + FMT_ULONG]; + +void dsplit(char *base,int uid,int mode) /* base must be under 100 bytes */ +{ + char *x; + unsigned long i; + + d(auto_qmail,base,uid,auto_gidq,mode); + + for (i = 0; i < auto_split; ++i) { + x = buf; + x += fmt_str(x,base); + x += fmt_str(x,"/"); + x += fmt_ulong(x,i); + *x = 0; + + d(auto_qmail,buf,uid,auto_gidq,mode); + } +} + +void hier() +{ + h(auto_qmail,auto_uido,auto_gidq,0755); + + d(auto_qmail,"control",auto_uido,auto_gidq,0755); + d(auto_qmail,"users",auto_uido,auto_gidq,0755); + d(auto_qmail,"bin",auto_uido,auto_gidq,0755); + d(auto_qmail,"alias",auto_uida,auto_gidq,02755); + + d(auto_qmail,"queue",auto_uidq,auto_gidq,0750); + d(auto_qmail,"queue/pid",auto_uidq,auto_gidq,0700); + d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700); + + dsplit("queue/dkim",auto_uidq,0750); + dsplit("queue/mess",auto_uidq,0750); + dsplit("queue/todo",auto_uidq,0750); + dsplit("queue/intd",auto_uidq,0700); + dsplit("queue/info",auto_uids,0700); + dsplit("queue/local",auto_uids,0700); + dsplit("queue/remote",auto_uids,0700); + + d(auto_qmail,"queue/lock",auto_uidq,auto_gidq,0750); + z(auto_qmail,"queue/lock/tcpto",TCPTO_BUFSIZ,auto_uidr,auto_gidq,0644); + z(auto_qmail,"queue/lock/sendmutex",0,auto_uids,auto_gidq,0600); + p(auto_qmail,"queue/lock/trigger",auto_uids,auto_gidq,0622); + + c(auto_qmail,"bin","qmail-queue",auto_uidq,auto_gidq,04711); + c(auto_qmail,"bin","qmail-qmaint",auto_uidq,auto_gidq,0711); + c(auto_qmail,"bin","qmail-lspawn",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-smtpam",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); + + c(auto_qmail,"bin","qmail-dkim",auto_uidq,auto_gidq,0711); + c(auto_qmail,"bin","qmail-dksign",auto_uidq,auto_gidq,04711); + c(auto_qmail,"bin","qmail-dkverify",auto_uidq,auto_gidq,04711); + + c(auto_qmail,"bin","qmail-authuser",auto_uido,auto_gidq,06711); + c(auto_qmail,"bin","qmail-vmailuser",auto_uido,auto_gidq,06711); + c(auto_qmail,"bin","qmail-postgrey",auto_uido,auto_gidq,06711); + c(auto_qmail,"bin","qmail-badloadertypes",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-badmimetypes",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-recipients",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-mfrules",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-mrtg",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-mrtg-queue",auto_uido,auto_gidq,0755); + + c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-showctl",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qread",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qstat",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-tcpto",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-tcpok",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-pop3d",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-popup",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-qmqpc",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755); + + c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","datemail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","mailsubj",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","except",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildirmake",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildir2mbox",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","maildirwatch",auto_uido,auto_gidq,0755); + + c(auto_qmail,"bin","fastforward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","printforward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","setforward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","newaliases",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","printmaillist",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","setmaillist",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","newinclude",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","srsforward",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","srsreverse",auto_uido,auto_gidq,0755); + + c(auto_qmail,"bin","ipmeprint",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","spfquery",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","dnscname",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","dnsfq",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","dnsip",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","dnsmxip",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","dnsptr",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","dnstlsa",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","dnstxt",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","hostname",auto_uido,auto_gidq,0755); + + c(auto_qmail,"bin","columnt",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","ddist",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","deferrals",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","failures",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","matchup",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","recipients",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","rhosts",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","rxdelay",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","senders",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","successes",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","suids",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","tai64nfrac",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","xqp",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","xrecipient",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","xsender",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zddist",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zdeferrals",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zfailures",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zfailures",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zoverall",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zrecipients",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zrhosts",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zrxdelay",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zsenders",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zsendmail",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zsuccesses",auto_uido,auto_gidq,0755); + c(auto_qmail,"bin","zsuids",auto_uido,auto_gidq,0755); +} diff --git a/sqmail-4.3.07/src/hmac_md5.c b/sqmail-4.3.07/src/hmac_md5.c new file mode 100644 index 0000000..310f0ef --- /dev/null +++ b/sqmail-4.3.07/src/hmac_md5.c @@ -0,0 +1,52 @@ +#include "global.h" +#include "md5.h" +#include "str.h" +#include "byte.h" + +/** +@file hmac_md5 +@brief caculates HMAC digest from challenge + password (DJB version) +@param input: unsigned char *text : pointer to challenge + int text_len : length of challenge + unsigned char *key : pointer to password + int key_len : length of password + output: unsigned char *digest: pointer to calculated digest +*/ + +void hmac_md5(unsigned char *text,int text_len,unsigned char * key,int key_len,unsigned char *digest) +{ + MD5_CTX context; + unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ + unsigned char k_opad[65]; /* outer padding - key XORd with opad */ + unsigned char tk[16]; + int i; + + if (key_len > 64) { + MD5_CTX tctx; + MD5Init(&tctx); + MD5Update(&tctx,key,key_len); + MD5Final(tk,&tctx); + key = tk; + key_len = 16; + } + + byte_zero(k_ipad,sizeof(k_ipad)); + byte_zero(k_opad,sizeof(k_opad)); + byte_copy(k_ipad,key_len,key); + byte_copy(k_opad,key_len,key); + + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + MD5Init(&context); /* init context for 1st pass */ + MD5Update(&context,k_ipad,64); /* start with inner pad */ + MD5Update(&context,text,text_len); /* then text of datagram */ + MD5Final(digest,&context); /* finish up 1st pass */ + + MD5Init(&context); /* init context for 2nd pass */ + MD5Update(&context,k_opad,64); /* start with outer pad */ + MD5Update(&context,digest,16); /* then results of 1st hash */ + MD5Final(digest,&context); /* finish up 2nd pass */ +} diff --git a/sqmail-4.3.07/src/hostname.c b/sqmail-4.3.07/src/hostname.c new file mode 100644 index 0000000..6a55309 --- /dev/null +++ b/sqmail-4.3.07/src/hostname.c @@ -0,0 +1,16 @@ +#include <unistd.h> +#include "buffer.h" +#include "exit.h" + +char host[256]; + +int main() +{ + host[0] = 0; /* sigh */ + gethostname(host,sizeof(host)); + host[sizeof(host) - 1] = 0; + buffer_puts(buffer_1small,host); + buffer_puts(buffer_1small,"\n"); + buffer_flush(buffer_1small); + _exit(0); +} diff --git a/sqmail-4.3.07/src/include/.dkimverify.h.swo b/sqmail-4.3.07/src/include/.dkimverify.h.swo Binary files differnew file mode 100644 index 0000000..c7116d9 --- /dev/null +++ b/sqmail-4.3.07/src/include/.dkimverify.h.swo diff --git a/sqmail-4.3.07/src/include/auto_break.h b/sqmail-4.3.07/src/include/auto_break.h new file mode 100644 index 0000000..b7f3a63 --- /dev/null +++ b/sqmail-4.3.07/src/include/auto_break.h @@ -0,0 +1,6 @@ +#ifndef AUTO_BREAK_H +#define AUTO_BREAK_H + +extern char auto_break[]; + +#endif diff --git a/sqmail-4.3.07/src/include/auto_patrn.h b/sqmail-4.3.07/src/include/auto_patrn.h new file mode 100644 index 0000000..77cdf1f --- /dev/null +++ b/sqmail-4.3.07/src/include/auto_patrn.h @@ -0,0 +1,6 @@ +#ifndef AUTO_PATRN_H +#define AUTO_PATRN_H + +extern int auto_patrn; + +#endif diff --git a/sqmail-4.3.07/src/include/auto_qmail.h b/sqmail-4.3.07/src/include/auto_qmail.h new file mode 100644 index 0000000..0c56001 --- /dev/null +++ b/sqmail-4.3.07/src/include/auto_qmail.h @@ -0,0 +1,6 @@ +#ifndef AUTO_QMAIL_H +#define AUTO_QMAIL_H + +extern char auto_qmail[]; + +#endif diff --git a/sqmail-4.3.07/src/include/auto_spawn.h b/sqmail-4.3.07/src/include/auto_spawn.h new file mode 100644 index 0000000..165d988 --- /dev/null +++ b/sqmail-4.3.07/src/include/auto_spawn.h @@ -0,0 +1,6 @@ +#ifndef AUTO_SPAWN_H +#define AUTO_SPAWN_H + +extern int auto_spawn; + +#endif diff --git a/sqmail-4.3.07/src/include/auto_split.h b/sqmail-4.3.07/src/include/auto_split.h new file mode 100644 index 0000000..3754129 --- /dev/null +++ b/sqmail-4.3.07/src/include/auto_split.h @@ -0,0 +1,6 @@ +#ifndef AUTO_SPLIT_H +#define AUTO_SPLIT_H + +extern int auto_split; + +#endif diff --git a/sqmail-4.3.07/src/include/auto_uids.h b/sqmail-4.3.07/src/include/auto_uids.h new file mode 100644 index 0000000..1252ecb --- /dev/null +++ b/sqmail-4.3.07/src/include/auto_uids.h @@ -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 diff --git a/sqmail-4.3.07/src/include/auto_usera.h b/sqmail-4.3.07/src/include/auto_usera.h new file mode 100644 index 0000000..49d7755 --- /dev/null +++ b/sqmail-4.3.07/src/include/auto_usera.h @@ -0,0 +1,6 @@ +#ifndef AUTO_USERA_H +#define AUTO_USERA_H + +extern char auto_usera[]; + +#endif diff --git a/sqmail-4.3.07/src/include/base64.h b/sqmail-4.3.07/src/include/base64.h new file mode 100644 index 0000000..9384411 --- /dev/null +++ b/sqmail-4.3.07/src/include/base64.h @@ -0,0 +1,9 @@ +#ifndef BASE64_H +#define BASE64_H + +#include "stralloc.h" + +extern int b64decode(const unsigned char *,int,stralloc *); +extern int b64encode(stralloc *,stralloc *); + +#endif diff --git a/sqmail-4.3.07/src/include/commands.h b/sqmail-4.3.07/src/include/commands.h new file mode 100644 index 0000000..3fd2cb8 --- /dev/null +++ b/sqmail-4.3.07/src/include/commands.h @@ -0,0 +1,12 @@ +#ifndef COMMANDS_H +#define COMMANDS_H + +struct commands { + char *text; + void (*fun)(); + void (*flush)(); +} ; + +int commands(); + +#endif diff --git a/sqmail-4.3.07/src/include/constmap.h b/sqmail-4.3.07/src/include/constmap.h new file mode 100644 index 0000000..750702e --- /dev/null +++ b/sqmail-4.3.07/src/include/constmap.h @@ -0,0 +1,21 @@ +#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; +} ; + +int constmap_init(struct constmap *,char *,int,int); +int constmap_init_char(struct constmap *,char *,int,int,char); +void constmap_free(); +char *constmap(); + +#endif diff --git a/sqmail-4.3.07/src/include/control.h b/sqmail-4.3.07/src/include/control.h new file mode 100644 index 0000000..732042c --- /dev/null +++ b/sqmail-4.3.07/src/include/control.h @@ -0,0 +1,12 @@ +#ifndef CONTROL_H +#define CONTROL_H + +#include "stralloc.h" + +int control_init(void); +int control_readline(stralloc *,char *); +int control_rldef(stralloc *,char *,int,char *); +int control_readint(int *,char *); +int control_readfile(stralloc *,char *,int); + +#endif diff --git a/sqmail-4.3.07/src/include/date822fmt.h b/sqmail-4.3.07/src/include/date822fmt.h new file mode 100644 index 0000000..a2f1432 --- /dev/null +++ b/sqmail-4.3.07/src/include/date822fmt.h @@ -0,0 +1,7 @@ +#ifndef DATE822FMT_H +#define DATE822FMT_H + +unsigned int date822fmt(char *,struct datetime *); +#define DATE822FMT 60 + +#endif diff --git a/sqmail-4.3.07/src/include/datetime.h b/sqmail-4.3.07/src/include/datetime.h new file mode 100644 index 0000000..68d1618 --- /dev/null +++ b/sqmail-4.3.07/src/include/datetime.h @@ -0,0 +1,20 @@ +#ifndef DATETIME_H +#define DATETIME_H + +struct datetime { + int hour; + int min; + int sec; + int wday; + int mday; + int yday; + int mon; + int year; +} ; + +typedef long datetime_sec; + +void datetime_tai(); +datetime_sec datetime_untai(); + +#endif diff --git a/sqmail-4.3.07/src/include/dkim.h b/sqmail-4.3.07/src/include/dkim.h new file mode 100644 index 0000000..508b2df --- /dev/null +++ b/sqmail-4.3.07/src/include/dkim.h @@ -0,0 +1,154 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ +#define DKIM_CALL +#define MAKELONG(a,b) ((long)(((unsigned)(a) & 0xffff) | (((unsigned)(b) & 0xffff) << 16))) + +#ifdef __cplusplus +extern "C" { +#endif + +// DKIM hash algorithms +#define DKIM_HASH_SHA1 1 +#define DKIM_HASH_SHA256 2 +#define DKIM_HASH_SHA1_AND_SHA256 3 +#define DKIM_HASH_ED25519 4 +#define DKIM_HASH_RSA256_AND_ED25519 5 + +// DKIM canonicalization methods +#define DKIM_CANON_SIMPLE 1 +#define DKIM_CANON_NOWSP 2 +#define DKIM_CANON_RELAXED 3 + +#define DKIM_SIGN_SIMPLE MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_SIMPLE) +#define DKIM_SIGN_SIMPLE_RELAXED MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_SIMPLE) +#define DKIM_SIGN_RELAXED MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_RELAXED) +#define DKIM_SIGN_RELAXED_SIMPLE MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_RELAXED) + +// DKIM Error codes +#define DKIM_SUCCESS 0 // operation successful +#define DKIM_FAIL -1 // verify error: message is suspicious +#define DKIM_BAD_SYNTAX -2 // signature error: DKIM-Signature could not parse or has bad tags/values +#define DKIM_SIGNATURE_BAD -3 // signature error: RSA/ED25519 verify failed +#define DKIM_SIGNATURE_BAD_BUT_TESTING -4 // signature error: RSA/ED25519 verify failed but testing +#define DKIM_SIGNATURE_EXPIRED -5 // signature error: x= is old +#define DKIM_SELECTOR_INVALID -6 // signature error: selector doesn't parse or contains invalid values +#define DKIM_SELECTOR_GRANULARITY_MISMATCH -7 // signature error: selector g= doesn't match i= +#define DKIM_SELECTOR_KEY_REVOKED -8 // signature error: selector p= empty +#define DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG -9 // signature error: selector domain name too long to request +#define DKIM_SELECTOR_DNS_TEMP_FAILURE -10 // signature error: temporary dns failure requesting selector +#define DKIM_SELECTOR_DNS_PERM_FAILURE -11 // signature error: permanent dns failure requesting selector +#define DKIM_SELECTOR_PUBLIC_KEY_INVALID -12 // signature error: selector p= value invalid or wrong format +#define DKIM_NO_SIGNATURES -13 // process error, no sigs +#define DKIM_NO_VALID_SIGNATURES -14 // process error, no valid sigs +#define DKIM_BODY_HASH_MISMATCH -15 // sigature verify error: message body does not hash to bh value +#define DKIM_SELECTOR_ALGORITHM_MISMATCH -16 // signature error: selector h= doesn't match signature a= +#define DKIM_STAT_INCOMPAT -17 // signature error: incompatible v= +#define DKIM_UNSIGNED_FROM -18 // signature error: not all message's From headers in signature +#define DKIM_OUT_OF_MEMORY -20 // memory allocation failed +#define DKIM_INVALID_CONTEXT -21 // DKIMContext structure invalid for this operation +#define DKIM_NO_SENDER -22 // signing error: Could not find From: or Sender: header in message +#define DKIM_BAD_PRIVATE_KEY -23 // signing error: Could not parse private key +#define DKIM_BUFFER_TOO_SMALL -24 // signing error: Buffer passed in is not large enough +#define DKIM_MAX_ERROR -25 // set this to 1 greater than the highest error code (but negative) + +// DKIM_SUCCESS // verify result: all signatures verified + // signature result: signature verified +#define DKIM_FINISHED_BODY 1 // process result: no more message body is needed +#define DKIM_PARTIAL_SUCCESS 2 // verify result: at least one but not all signatures verified +#define DKIM_NEUTRAL 3 // verify result: no signatures verified but message is not suspicous +#define DKIM_SUCCESS_BUT_EXTRA 4 // signature result: signature verified but it did not include all of the body + + + +// This function is called once for each header in the message +// return 1 to include this header in the signature and 0 to exclude. +typedef int (DKIM_CALL *DKIMHEADERCALLBACK)(const char* szHeader); + +// This function is called to retrieve a TXT record from DNS +typedef int (DKIM_CALL *DKIMDNSCALLBACK)(const char* szFQDN,char* szBuffer,int nBufLen); + +typedef struct DKIMContext_t +{ + unsigned int reserved1; + unsigned int reserved2; + void* reserved3; +} DKIMContext; + +typedef struct DKIMSignOptions_t +{ + int nCanon; // canonization + int nIncludeBodyLengthTag; // 0 = don't include l= tag, 1 = include l= tag + int nIncludeTimeStamp; // 0 = don't include t= tag, 1 = include t= tag + int nIncludeQueryMethod; // 0 = don't include q= tag, 1 = include q= tag + char szSelector[64]; // selector - required + char szSelectorE[64]; // 2nd selector - optional + char szDomain[256]; // domain - optional - if empty, domain is computed from sender + char szIdentity[256]; // for i= tag, if empty tag will not be included in sig + unsigned long expireTime; // for x= tag, if 0 tag will not be included in sig + DKIMHEADERCALLBACK pfnHeaderCallback; // header callback + char szRequiredHeaders[256]; // colon-separated list of headers that must be signed + int nHash; // use one of the DKIM_HASH_xx constants here + // even if not present in the message + int nIncludeCopiedHeaders; // 0 = don't include z= tag, 1 = include z= tag +} DKIMSignOptions; + +typedef struct DKIMVerifyOptions_t +{ + DKIMDNSCALLBACK pfnSelectorCallback; // selector record callback + DKIMDNSCALLBACK pfnPracticesCallback; // ADSP record callback + int nHonorBodyLengthTag; // 0 = ignore l= tag, 1 = use l= tag to limit the amount of body verified + int nCheckPractices; // 0 = use default (unknown) practices, 1 = request and use author domain signing practices + int nSubjectRequired; // 0 = subject is required to be signed, 1 = not required + int nSaveCanonicalizedData; // 0 = canonicalized data is not saved, 1 = canonicalized data is saved + int nAllowUnsignedFromHeaders; // 0 = From headers not included in the signature are not allowed, 1 = allowed +} DKIMVerifyOptions; + +typedef struct DKIMVerifyDetails_t +{ + char *szSignature; + char *szSignatureDomain; + char *szIdentityDomain; + char *szCanonicalizedData; + int nResult; +} DKIMVerifyDetails; + +int DKIM_CALL DKIMSignInit(DKIMContext* pSignContext,DKIMSignOptions* pOptions); +int DKIM_CALL DKIMSignProcess(DKIMContext* pSignContext,char* szBuffer,int nBufLength); +int DKIM_CALL DKIMSignGetSig2(DKIMContext* pSignContext,char* szRSAPrivKey,char *szECCPrivKey,char** pszSignature); +void DKIM_CALL DKIMSignFree(DKIMContext* pSignContext); + +int DKIM_CALL DKIMVerifyInit(DKIMContext* pVerifyContext,DKIMVerifyOptions* pOptions); +int DKIM_CALL DKIMVerifyProcess(DKIMContext* pVerifyContext,const char* szBuffer,int nBufLength); +int DKIM_CALL DKIMVerifyResults(DKIMContext* pVerifyContext); +int DKIM_CALL DKIMVerifyGetDetails(DKIMContext* pVerifyContext,int* nSigCount,DKIMVerifyDetails** pDetails,char* szPractices); +void DKIM_CALL DKIMVerifyFree(DKIMContext* pVerifyContext); + +// const char *DKIM_CALL DKIMVersion(); + +const char *DKIM_CALL DKIMGetErrorString(int ErrorCode); + +int _DKIM_ReportResult(char const *,char const *,char const *); +const char *DKIM_ErrorResult(const int); + +#ifdef __cplusplus +} +#endif diff --git a/sqmail-4.3.07/src/include/dkimbase.h b/sqmail-4.3.07/src/include/dkimbase.h new file mode 100644 index 0000000..25aac02 --- /dev/null +++ b/sqmail-4.3.07/src/include/dkimbase.h @@ -0,0 +1,79 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ +#ifndef DKIMBASE_H +#define DKIMBASE_H + +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/err.h> + +#define BUFFER_ALLOC_INCREMENT 256 + +#include <string> +#include <list> + +using namespace std; + +class CDKIMBase +{ +public: + + CDKIMBase(); + ~CDKIMBase(); + + int Init(void); + + int Process(const char* szBuffer,int nBufLength,bool bEOF); + int ProcessFinal(void); + + int Alloc(char*& szBuffer,int nRequiredSize); + int ReAlloc(char*& szBuffer,int& nBufferLength,int nRequiredSize); + void Free(char* szBuffer); + + static void RemoveSWSP(char* szBuffer); + static void RemoveSWSP(char* pBuffer,int& nBufLength); + static void RemoveSWSP(string& sBuffer); + + static void CompressSWSP(char* pBuffer,int& nBufLength); + static void CompressSWSP(string& sBuffer); + + static string RelaxHeader(const string& sHeader); + + virtual int ProcessHeaders(void); + virtual int ProcessBody(char* szBuffer,int nBufLength,bool bEOF); + +protected: + char* m_From; + char* m_Sender; + char* m_hTag; + int m_hTagSize; + int m_hTagPos; + char* m_Line; + int m_LineSize; + int m_LinePos; + bool m_InHeaders; + + list<string> HeaderList; +}; + + +#endif // DKIMBASE_H diff --git a/sqmail-4.3.07/src/include/dkimsign.h b/sqmail-4.3.07/src/include/dkimsign.h new file mode 100644 index 0000000..150a0b8 --- /dev/null +++ b/sqmail-4.3.07/src/include/dkimsign.h @@ -0,0 +1,113 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ +#ifndef DKIMSIGN_H +#define DKIMSIGN_H + +#include "dkimbase.h" + +class CDKIMSign : public CDKIMBase +{ +public: + CDKIMSign(); + ~CDKIMSign(); + + //int Init() = delete; + int Init(DKIMSignOptions* pOptions); + int GetSig2(char* szRSAPrivKey,char* szECCPrivKey,char** pszSignature); + + virtual int ProcessHeaders(void) override; + virtual int ProcessBody(char* szBuffer,int nBufLength,bool bEOF) override; + + enum CKDKIMConstants { OptimalHeaderLineLength = 65 }; + + void Hash(const char* szBuffer,int nBufLength,bool bHdr); + +protected: + + bool SignThisTag(const string& sTag); + void GetHeaderParams(const string& sHdr); + void ProcessHeader(const string& sHdr); + bool ParseFromAddress(void); + + void InitSig(void); + void AddTagToSig(const char* const Tag,const string &sValue,char cbrk,bool bFold); + void AddTagToSig(const char* const Tag,unsigned long nValue); + void AddInterTagSpace(int nSizeOfNextTag); + void AddFoldedValueToSig(const string &sValue,char cbrk); + + bool IsRequiredHeader(const string& sTag); + int ConstructSignature(char* szSignKey,int nSigAlg); + + int AssembleReturnedSig(char* szRSAPrivKey,char* szECCPrivKey); + +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_MD_CTX m_Hdr_sha1ctx; /* the RSA SHA1 signature */ + EVP_MD_CTX m_Hdr_sha256ctx; /* the RSA SHA256 signature */ + + EVP_MD_CTX m_Bdy_sha1ctx; /* the SHA1 digest */ + EVP_MD_CTX m_Bdy_sha256ctx; /* the SHA256 digest */ +#else + EVP_MD_CTX *m_Hdr_sha1ctx; /* the RSA SHA1 signature */ + EVP_MD_CTX *m_Hdr_sha256ctx; /* the RSA SHA256 signature */ + EVP_MD_CTX *m_Hdr_ed25519ctx; /* the PureEd25519 signature */ + + EVP_MD_CTX *m_Bdy_sha1ctx; /* the SHA1 digest */ + EVP_MD_CTX *m_Bdy_sha256ctx; /* the SHA256 digest for RSA */ + EVP_MD_CTX *m_Edy_sha256ctx; /* the SHA256 digest for Ed25519 */ +#endif + + int m_Canon; /* canonization method */ + + int m_EmptyLineCount; + + string hParam; + string sFrom; + string sSender; + string sSelector; + string eSelector; /* Used for Ed25519 signatures */ + string sDomain; + string sIdentity; /* for i= tag, if empty tag will not be included in sig */ + string sRequiredHeaders; + + bool m_IncludeBodyLengthTag; + int m_nBodyLength; + time_t m_ExpireTime; + int m_nIncludeTimeStamp; // 0 = don't include t= tag, 1 = include t= tag + int m_nIncludeQueryMethod; // 0 = don't include q= tag, 1 = include q= tag + int m_nHash; // use one of the DKIM_HASH_xx constants here + int m_nIncludeCopiedHeaders; // 0 = don't include z= tag, 1 = include z= tag + + DKIMHEADERCALLBACK m_pfnHdrCallback; + + string m_sSig; // DKIM-Signature .... + int m_nSigPos; + + string m_sReturnedSig; + bool m_bReturnedSigAssembled; + + string m_sCopiedHeaders; + + string SigHdrs; + int m_SigHdrs; +}; + +#endif // DKIMSIGN_H diff --git a/sqmail-4.3.07/src/include/dkimverify.h b/sqmail-4.3.07/src/include/dkimverify.h new file mode 100644 index 0000000..64de2a1 --- /dev/null +++ b/sqmail-4.3.07/src/include/dkimverify.h @@ -0,0 +1,152 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +*****************************************************************************/ +#ifndef DKIMVERIFY_H +#define DKIMVERIFY_H + +#include "dkimbase.h" +#include <vector> + +/* not used anymore +#define DKIM_ADSP_UNKNOWN 1 +#define DKIM_ADSP_ALL 2 +#define DKIM_ADSP_DISCARDABLE 3 +*/ + +#define DKIM_POLICY_DOMAIN_NAME_TOO_LONG -50 // internal error +#define DKIM_POLICY_DNS_TEMP_FAILURE -51 // internal error +#define DKIM_POLICY_DNS_PERM_FAILURE -52 // internal error +#define DKIM_POLICY_INVALID -53 // internal error + +/* dito +#define DKIM_SIG_VERSION_PRE_02 0 +#define DKIM_SIG_VERSION_02_PLUS 1 +*/ + +class SelectorInfo +{ +public: + SelectorInfo(const string &sSelector,const string &sDomain); + ~SelectorInfo(); + + string Domain; + string Selector; + string Granularity; + bool AllowSHA1; + bool AllowSHA256; + EVP_PKEY *PublicKey; /* the public key */ + bool Testing; + bool SameDomain; + + int Status; + + int Parse(char* Buffer); +}; + +class SignatureInfo +{ +public: + SignatureInfo(bool SaveCanonicalizedData); + ~SignatureInfo(); + + void Hash(const char* szBuffer,unsigned nBufLength,bool IsBody=false); + + string Header; + unsigned Version; + string Domain; + string Selector; + string SignatureData; + string BodyHashData; + string IdentityLocalPart; + string IdentityDomain; + string CanonicalizedData; + vector<string> SignedHeaders; + unsigned BodyLength; + unsigned HeaderCanonicalization; + unsigned BodyCanonicalization; + unsigned ExpireTime; + + unsigned VerifiedBodyCount; + unsigned UnverifiedBodyCount; + +#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_MD_CTX m_Hdr_ctx; + EVP_MD_CTX m_Bdy_ctx; +#else + EVP_MD_CTX *m_Hdr_ctx; + EVP_MD_CTX *m_Bdy_ctx; +#endif +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + EVP_MD_CTX *m_Msg_ctx; +#endif + + SelectorInfo *m_pSelector; + + int Status; + int m_nHash; // use one of the DKIM_HASH_xxx constants here + unsigned EmptyLineCount; + bool m_SaveCanonicalizedData; +}; + +class CDKIMVerify : public CDKIMBase +{ +public: + + CDKIMVerify(); + ~CDKIMVerify(); +// virtual ~CDKIMVerify() = 0; + + int Init(DKIMVerifyOptions* pOptions); + + int GetResults(void); + int GetDetails(int* nSigCount,DKIMVerifyDetails** pDetails); +// int _DNSGetTXT(const char* szFQDN,char* Buffer,int nBufLen); + + virtual int ProcessHeaders(void); + virtual int ProcessBody(char* szBuffer,int nBufLength,bool bEOF); + + const char* GetPractices() { return Practices.c_str(); } + +protected: + + int ParseDKIMSignature(const string& sHeader,SignatureInfo &sig); + + SelectorInfo& GetSelector(const string &sSelector,const string &sDomain); + +// int GetADSP(const string &sDomain,int &iADSP); + + list<SignatureInfo> Signatures; + list<SelectorInfo> Selectors; + + DKIMDNSCALLBACK m_pfnSelectorCallback; // selector record callback +// DKIMDNSCALLBACK m_pfnPracticesCallback; // ADSP record callback + + bool m_HonorBodyLengthTag; + bool m_CheckPractices; + bool m_SubjectIsRequired; + bool m_SaveCanonicalizedData; + bool m_AllowUnsignedFromHeaders; + + vector<DKIMVerifyDetails> Details; + string Practices; +}; + +#endif //DKIMVERIFY_H diff --git a/sqmail-4.3.07/src/include/dns.h b/sqmail-4.3.07/src/include/dns.h new file mode 100644 index 0000000..6293478 --- /dev/null +++ b/sqmail-4.3.07/src/include/dns.h @@ -0,0 +1,27 @@ +#ifndef DNS_H +#define DNS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "dnsresolv.h" +#include "ipalloc.h" +#include "stralloc.h" + +#define DNS_INIT static char seed[128]; dns_random_init(seed); +#define DNS_NXD 0 +#define DNS_SOFT -5 +#define DNS_HARD -6 + +void dns_init(int); +int dns_ip(ipalloc *,stralloc *); +int dns_mxip(ipalloc *,stralloc *,unsigned long); +int dns_tlsa(stralloc *,const stralloc *); +int dns_mxhost(stralloc *,const stralloc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sqmail-4.3.07/src/include/dnsdoe.h b/sqmail-4.3.07/src/include/dnsdoe.h new file mode 100644 index 0000000..eacd7fc --- /dev/null +++ b/sqmail-4.3.07/src/include/dnsdoe.h @@ -0,0 +1,6 @@ +#ifndef DNSDOE_H +#define DNSDOE_H + +void dnsdoe(int); + +#endif diff --git a/sqmail-4.3.07/src/include/dnsgettxt.h b/sqmail-4.3.07/src/include/dnsgettxt.h new file mode 100644 index 0000000..6d6b8ea --- /dev/null +++ b/sqmail-4.3.07/src/include/dnsgettxt.h @@ -0,0 +1,7 @@ +#ifdef cplusplus +extern "C" { + +int DNSGetTXT(const char* szFQDN,char* Buffer,int nBufLen); +extern void dns_random_init(const char [12]); + +#endif diff --git a/sqmail-4.3.07/src/include/exit.h b/sqmail-4.3.07/src/include/exit.h new file mode 100644 index 0000000..d7351ba --- /dev/null +++ b/sqmail-4.3.07/src/include/exit.h @@ -0,0 +1,16 @@ +#ifndef EXIT_H +#define EXIT_H + +/* Return code conventions: + + 110: Unable to access dir + 111: General (memory) error + 112: Unable to access file +*/ + +void _exit(); + +int rename(const char *, const char *); + + +#endif diff --git a/sqmail-4.3.07/src/include/extra.h b/sqmail-4.3.07/src/include/extra.h new file mode 100644 index 0000000..c598175 --- /dev/null +++ b/sqmail-4.3.07/src/include/extra.h @@ -0,0 +1,7 @@ +#ifndef EXTRA_H +#define EXTRA_H + +#define QUEUE_EXTRA "" +#define QUEUE_EXTRALEN 0 + +#endif diff --git a/sqmail-4.3.07/src/include/fifo.h b/sqmail-4.3.07/src/include/fifo.h new file mode 100644 index 0000000..f48c863 --- /dev/null +++ b/sqmail-4.3.07/src/include/fifo.h @@ -0,0 +1,6 @@ +#ifndef FIFO_H +#define FIFO_H + +int fifo_make(char *,int); + +#endif diff --git a/sqmail-4.3.07/src/include/fmtqfn.h b/sqmail-4.3.07/src/include/fmtqfn.h new file mode 100644 index 0000000..e11e51e --- /dev/null +++ b/sqmail-4.3.07/src/include/fmtqfn.h @@ -0,0 +1,8 @@ +#ifndef FMTQFN_H +#define FMTQFN_H + +unsigned int fmtqfn(char *,char *,unsigned long,int); + +#define FMTQFN 40 /* maximum space needed, if len(dirslash) <= 10 */ + +#endif diff --git a/sqmail-4.3.07/src/include/gfrom.h b/sqmail-4.3.07/src/include/gfrom.h new file mode 100644 index 0000000..121ff6d --- /dev/null +++ b/sqmail-4.3.07/src/include/gfrom.h @@ -0,0 +1,6 @@ +#ifndef GFROM_H +#define GFROM_H + +int gfrom(char *,int); + +#endif diff --git a/sqmail-4.3.07/src/include/global.h b/sqmail-4.3.07/src/include/global.h new file mode 100644 index 0000000..2d8ccf4 --- /dev/null +++ b/sqmail-4.3.07/src/include/global.h @@ -0,0 +1,53 @@ +/* GLOBAL.H - RSAREF types and constants */ + +#include <string.h> +#include "uint_t.h" + +/* Copyright (C) RSA Laboratories, a division of RSA Data Security, + Inc., created 1991. All rights reserved. + */ + +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ 1 + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 1 if it has not already been + defined as 0 with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 1 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +/* UINT4 defines a four byte word */ +#ifdef UINT32_H +#define UINT4 uint32 +#else +typedef unsigned long int UINT4; +#endif + +#ifndef NULL_PTR +#define NULL_PTR ((POINTER)0) +#endif + +#ifndef UNUSED_ARG +#define UNUSED_ARG(x) x = *(&x); +#endif + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +#endif /* end _GLOBAL_H_ */ diff --git a/sqmail-4.3.07/src/include/headerbody.h b/sqmail-4.3.07/src/include/headerbody.h new file mode 100644 index 0000000..a074981 --- /dev/null +++ b/sqmail-4.3.07/src/include/headerbody.h @@ -0,0 +1,6 @@ +#ifndef HEADERBODY_H +#define HEADERBODY_H + +int headerbody(); + +#endif diff --git a/sqmail-4.3.07/src/include/hfield.h b/sqmail-4.3.07/src/include/hfield.h new file mode 100644 index 0000000..ab367f4 --- /dev/null +++ b/sqmail-4.3.07/src/include/hfield.h @@ -0,0 +1,38 @@ +#ifndef HFIELD_H +#define HFIELD_H + +unsigned int hfield_skipname(); +int hfield_known(); +int hfield_valid(); + +#define H_SENDER 1 +#define H_FROM 2 +#define H_REPLYTO 3 +#define H_TO 4 +#define H_CC 5 +#define H_BCC 6 +#define H_DATE 7 +#define H_MESSAGEID 8 +#define H_SUBJECT 9 +#define H_R_SENDER 10 +#define H_R_FROM 11 +#define H_R_REPLYTO 12 +#define H_R_TO 13 +#define H_R_CC 14 +#define H_R_BCC 15 +#define H_R_DATE 16 +#define H_R_MESSAGEID 17 +#define H_RETURNRECEIPTTO 18 +#define H_ERRORSTO 19 +#define H_APPARENTLYTO 20 +#define H_RECEIVED 21 +#define H_RETURNPATH 22 +#define H_DELIVEREDTO 23 +#define H_CONTENTLENGTH 24 +#define H_CONTENTTYPE 25 +#define H_CONTENTTRANSFERENCODING 26 +#define H_NOTICEREQUESTEDUPONDELIVERYTO 27 +#define H_MAILFOLLOWUPTO 28 +#define H_NUM 29 + +#endif diff --git a/sqmail-4.3.07/src/include/hier.h b/sqmail-4.3.07/src/include/hier.h new file mode 100644 index 0000000..f040e79 --- /dev/null +++ b/sqmail-4.3.07/src/include/hier.h @@ -0,0 +1,10 @@ +#ifndef HIER_H +#define HIER_H + +void c(char *,char *,char *,int,int,int); +void h(char *,int,int,int); +void d(char *,char *,int,int,int); +void p(char *,char *,int,int,int); +void z(char *,char *,int,int,int,int); + +#endif diff --git a/sqmail-4.3.07/src/include/hmac_md5.h b/sqmail-4.3.07/src/include/hmac_md5.h new file mode 100644 index 0000000..87a2c37 --- /dev/null +++ b/sqmail-4.3.07/src/include/hmac_md5.h @@ -0,0 +1,7 @@ +#ifndef HMAC_MD5_H +#define HMAC_MD5_H + +void hmac_md5(unsigned char *,int,unsigned char *,int,unsigned char *); + +#endif + diff --git a/sqmail-4.3.07/src/include/ipalloc.h b/sqmail-4.3.07/src/include/ipalloc.h new file mode 100644 index 0000000..0f58e92 --- /dev/null +++ b/sqmail-4.3.07/src/include/ipalloc.h @@ -0,0 +1,22 @@ +#ifndef IPALLOC_H +#define IPALLOC_H + +#include "ip.h" +#include "genalloc.h" +#define NAME_LEN 256 + +struct ip_mx { + unsigned short af; + union { + struct ip4_address ip4; + struct ip6_address ip6; + } addr; + int pref; + char mxh[NAME_LEN]; +}; + +GEN_ALLOC_typedef(ipalloc,struct ip_mx,ix,len,a) +int ipalloc_readyplus(); +int ipalloc_append(); + +#endif diff --git a/sqmail-4.3.07/src/include/ipme.h b/sqmail-4.3.07/src/include/ipme.h new file mode 100644 index 0000000..9705f45 --- /dev/null +++ b/sqmail-4.3.07/src/include/ipme.h @@ -0,0 +1,14 @@ +#ifndef IPME_H +#define IPME_H + +#include "ip.h" +#include "ipalloc.h" + +extern ipalloc ipme; + +int ipme_init(); +int ipme_is4(); +int ipme_is6(); +int ipme_is(); + +#endif diff --git a/sqmail-4.3.07/src/include/maildir.h b/sqmail-4.3.07/src/include/maildir.h new file mode 100644 index 0000000..5e48822 --- /dev/null +++ b/sqmail-4.3.07/src/include/maildir.h @@ -0,0 +1,13 @@ +#ifndef MAILDIR_H +#define MAILDIR_H + +#include "logmsg.h" +#include "prioq.h" + +extern struct strerr maildir_chdir_err; +extern struct strerr maildir_scan_err; + +int maildir_chdir(void); +void maildir_clean(stralloc *); +int maildir_scan(prioq *,stralloc *,int,int); +#endif diff --git a/sqmail-4.3.07/src/include/md5.h b/sqmail-4.3.07/src/include/md5.h new file mode 100644 index 0000000..94774ba --- /dev/null +++ b/sqmail-4.3.07/src/include/md5.h @@ -0,0 +1,49 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#ifndef _MD5_H_ +#define _MD5_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sqmail-4.3.07/src/include/mfrules.h b/sqmail-4.3.07/src/include/mfrules.h new file mode 100644 index 0000000..b79f338 --- /dev/null +++ b/sqmail-4.3.07/src/include/mfrules.h @@ -0,0 +1,9 @@ +#ifndef MFRULES_H +#define MFRULES_H + +#include "stralloc.h" + +extern stralloc key; +int mfrules(int,char *,char *,char *,char *); + +#endif diff --git a/sqmail-4.3.07/src/include/myctime.h b/sqmail-4.3.07/src/include/myctime.h new file mode 100644 index 0000000..97a30ca --- /dev/null +++ b/sqmail-4.3.07/src/include/myctime.h @@ -0,0 +1,8 @@ +#ifndef MYCTIME_H +#define MYCTIME_H + +#include "datetime.h" + +char *myctime(datetime_sec); + +#endif diff --git a/sqmail-4.3.07/src/include/newfield.h b/sqmail-4.3.07/src/include/newfield.h new file mode 100644 index 0000000..049fb5e --- /dev/null +++ b/sqmail-4.3.07/src/include/newfield.h @@ -0,0 +1,12 @@ +#ifndef NEWFIELD_H +#define NEWFIELD_H + +#include "stralloc.h" + +extern stralloc newfield_date; +int newfield_datemake(); + +extern stralloc newfield_msgid; +int newfield_msgidmake(); + +#endif diff --git a/sqmail-4.3.07/src/include/now.h b/sqmail-4.3.07/src/include/now.h new file mode 100644 index 0000000..1379d76 --- /dev/null +++ b/sqmail-4.3.07/src/include/now.h @@ -0,0 +1,8 @@ +#ifndef NOW_H +#define NOW_H + +#include "datetime.h" + +datetime_sec now(); + +#endif diff --git a/sqmail-4.3.07/src/include/prioq.h b/sqmail-4.3.07/src/include/prioq.h new file mode 100644 index 0000000..3547b1c --- /dev/null +++ b/sqmail-4.3.07/src/include/prioq.h @@ -0,0 +1,15 @@ +#ifndef PRIOQ_H +#define PRIOQ_H + +#include "datetime.h" +#include "genalloc.h" + +struct prioq_elt { datetime_sec dt; unsigned long id; } ; + +GEN_ALLOC_typedef(prioq,struct prioq_elt,p,len,a) + +int prioq_insert(); +int prioq_min(); +void prioq_delmin(); + +#endif diff --git a/sqmail-4.3.07/src/include/prot.h b/sqmail-4.3.07/src/include/prot.h new file mode 100644 index 0000000..08203da --- /dev/null +++ b/sqmail-4.3.07/src/include/prot.h @@ -0,0 +1,7 @@ +#ifndef PROT_H +#define PROT_H + +int prot_gid(); +int prot_uid(); + +#endif diff --git a/sqmail-4.3.07/src/include/qlx.h b/sqmail-4.3.07/src/include/qlx.h new file mode 100644 index 0000000..713946d --- /dev/null +++ b/sqmail-4.3.07/src/include/qlx.h @@ -0,0 +1,18 @@ +#ifndef QLX_H +#define QLX_H + +/* 0, 111, 100 are qmail-local success, soft, hard */ + +#define QLX_USAGE 112 +#define QLX_BUG 101 +#define QLX_ROOT 113 +#define QLX_NFS 115 +#define QLX_NOALIAS 116 +#define QLX_CDB 117 +#define QLX_SYS 118 +#define QLX_NOMEM 119 +#define QLX_EXECSOFT 120 +#define QLX_EXECPW 121 +#define QLX_EXECHARD 126 + +#endif diff --git a/sqmail-4.3.07/src/include/qmail.h b/sqmail-4.3.07/src/include/qmail.h new file mode 100644 index 0000000..dd44816 --- /dev/null +++ b/sqmail-4.3.07/src/include/qmail.h @@ -0,0 +1,30 @@ +#ifndef QMAIL_H +#define QMAIL_H + +#include "buffer.h" // BUFFER_INSIZE = BUFFER_OUTSIZE = 8192 + +#define BUFSIZE_QMAIL 8192 // Memory page size +#define BUFSIZE_MESS 4096 +#define BUFSIZE_LINE 1024 // RFC 5322: 998 chars - why? - SMTP Line +#define BUFSIZE_AUTH 512 +#define BUFSIZE_LOG 256 + +struct qmail { + int flagerr; + unsigned long pid; + int fdm; + int fde; + buffer ss; + char buf[BUFSIZE_QMAIL]; +} ; + +extern int qmail_open(struct qmail *); +extern void qmail_put(struct qmail *,char *, int); +extern void qmail_puts(struct qmail *,char *); +extern void qmail_from(struct qmail *,char *); +extern void qmail_to(struct qmail *,char *); +extern void qmail_fail(struct qmail *); +extern char *qmail_close(struct qmail *); +extern unsigned long qmail_qp(struct qmail *); + +#endif diff --git a/sqmail-4.3.07/src/include/qsutil.h b/sqmail-4.3.07/src/include/qsutil.h new file mode 100644 index 0000000..a77a3f8 --- /dev/null +++ b/sqmail-4.3.07/src/include/qsutil.h @@ -0,0 +1,17 @@ +#ifndef QSUTIL_H +#define QSUTIL_H + +#include "stralloc.h" + +void log1s(char *); +void log2s(char *,char *); +void log3s(char *,char *,char *); +void log4s(char *,char *,char *,char *); +void log5s(char *,char *,char *,char *,char *); +void logsa(stralloc *); +void nomem(); +void pausedir(char *); +void logsafe(char *); +int issafe(char); + +#endif diff --git a/sqmail-4.3.07/src/include/quote.h b/sqmail-4.3.07/src/include/quote.h new file mode 100644 index 0000000..4afbc94 --- /dev/null +++ b/sqmail-4.3.07/src/include/quote.h @@ -0,0 +1,10 @@ +#ifndef QUOTE_H +#define QUOTE_H + +#include "stralloc.h" + +int quote_need(char *,unsigned int); +int quote(stralloc *, stralloc *); +int quote2(stralloc *,char *); + +#endif diff --git a/sqmail-4.3.07/src/include/rcpthosts.h b/sqmail-4.3.07/src/include/rcpthosts.h new file mode 100644 index 0000000..0c58797 --- /dev/null +++ b/sqmail-4.3.07/src/include/rcpthosts.h @@ -0,0 +1,7 @@ +#ifndef RCPTHOSTS_H +#define RCPTHOSTS_H + +int rcpthosts_init(); +int rcpthosts(); + +#endif diff --git a/sqmail-4.3.07/src/include/readsubdir.h b/sqmail-4.3.07/src/include/readsubdir.h new file mode 100644 index 0000000..e612fac --- /dev/null +++ b/sqmail-4.3.07/src/include/readsubdir.h @@ -0,0 +1,20 @@ +#ifndef READSUBDIR_H +#define READSUBDIR_H + +#include "direntry.h" + +typedef struct readsubdir +{ + DIR *dir; + int pos; + char *name; + void (*pause)(); +} +readsubdir; + +void readsubdir_init(); +int readsubdir_next(); + +#define READSUBDIR_NAMELEN 10 + +#endif diff --git a/sqmail-4.3.07/src/include/readwrite.h b/sqmail-4.3.07/src/include/readwrite.h new file mode 100644 index 0000000..4fdb771 --- /dev/null +++ b/sqmail-4.3.07/src/include/readwrite.h @@ -0,0 +1,11 @@ +#ifndef READWRITE_H +#define READWRITE_H + +#include <unistd.h> +/* Already in unistd.h */ +/* +int read(); +int write(); +*/ + +#endif diff --git a/sqmail-4.3.07/src/include/received.h b/sqmail-4.3.07/src/include/received.h new file mode 100644 index 0000000..2b2ce75 --- /dev/null +++ b/sqmail-4.3.07/src/include/received.h @@ -0,0 +1,9 @@ +#ifndef RECEIVED_H +#define RECEIVED_H + +#include "qmail.h" + +void received(struct qmail *,char *,char *,char *,char *,char *,char *,char *,char *); +void spfheader(struct qmail *,char *,char *,char *,char *,char *); + +#endif diff --git a/sqmail-4.3.07/src/include/recipients.h b/sqmail-4.3.07/src/include/recipients.h new file mode 100644 index 0000000..b73f37e --- /dev/null +++ b/sqmail-4.3.07/src/include/recipients.h @@ -0,0 +1,8 @@ +#ifndef RECIPIENTS_H +#define RECIPIENTS_H + +int recipients_init(void); +int recipients(char *,int); +ssize_t safewrite(); + +#endif diff --git a/sqmail-4.3.07/src/include/sendtodo.h b/sqmail-4.3.07/src/include/sendtodo.h new file mode 100644 index 0000000..86b4434 --- /dev/null +++ b/sqmail-4.3.07/src/include/sendtodo.h @@ -0,0 +1,14 @@ +#ifndef SENDTODO_H +#define SENDTODO_H + +/* critical timing feature #1: if not triggered, do not busy-loop */ +/* critical timing feature #2: if triggered, respond within fixed time */ +/* important timing feature: when triggered, respond instantly */ +#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */ +#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */ +#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */ +#define SLEEP_CLEANUP 76431 /* time between cleanups */ +#define SLEEP_SYSFAIL 123 +#define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */ + +#endif diff --git a/sqmail-4.3.07/src/include/sha1.h b/sqmail-4.3.07/src/include/sha1.h new file mode 100644 index 0000000..43d141d --- /dev/null +++ b/sqmail-4.3.07/src/include/sha1.h @@ -0,0 +1,31 @@ +#ifndef SHA1_H +#define SHA1_H + +/* + SHA-1 in C + By Steve Reid <steve@edmweb.com> + 100% Public Domain + + adopted for s/qmail (feh) + */ + +#include <stdint.h> +/* SHA1 implementation */ + +#define SHA1_BLOCKSIZE 64 +#define SHA1_DIGESTSIZE 20 + +typedef struct +{ + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[SHA1_BLOCKSIZE]; +} sha1_ctx; + +void sha1_init(sha1_ctx *context); +void sha1_update(sha1_ctx *context, const uint8_t *data, uint32_t len); +void sha1_final(uint8_t hash[SHA1_DIGESTSIZE], sha1_ctx *context); +void sha1_transform(uint32_t state[5], const uint8_t buffer[SHA1_BLOCKSIZE]); +void sha1_hash(char *hash, const char *data, uint32_t len); + +#endif /* SHA1_H */ diff --git a/sqmail-4.3.07/src/include/sha256.h b/sqmail-4.3.07/src/include/sha256.h new file mode 100644 index 0000000..e8979c5 --- /dev/null +++ b/sqmail-4.3.07/src/include/sha256.h @@ -0,0 +1,18 @@ +#ifndef SHA256_H +#define SHA256_H + +typedef struct +{ + uint8_t data[64]; + uint32_t datalen; + uint32_t bitlen[2]; + uint32_t state[8]; +} sha256_ctx; + +static void sha256_init(sha256_ctx *ctx); +static void sha256_transform(sha256_ctx *ctx, uint8_t *data); +static void sha256_update(sha256_ctx *ctx, uint8_t *data, uint32_t len); +static void sha256_final(uint8_t *hash, sha256_ctx *ctx); +extern void sha256_hash(char *hash,const char *data, size_t len); + +#endif diff --git a/sqmail-4.3.07/src/include/smtpdlog.h b/sqmail-4.3.07/src/include/smtpdlog.h new file mode 100644 index 0000000..0feb126 --- /dev/null +++ b/sqmail-4.3.07/src/include/smtpdlog.h @@ -0,0 +1,73 @@ +#ifndef SMTPDLOG_H +#define SMTPDLOG_H +#define FDLOG 2 + +void flush(); +void out(); + +void smtpdlog_init(void); +void smtp_loga(char *,char *,char *,char *,char *,char *,char *,char *,char *); +void smtp_logb(char *,char *,char *,char *,char *,char *,char *); +void smtp_logg(char *,char *,char *,char *,char *,char *,char *); +void smtp_logh(char *,char *,char *,char *,char *); +void smtp_logi(char *,char *,char *,char *,char *,char *,char *,char *); +void smtp_logr(char *,char *,char *,char *,char *,char *,char *,char *); + +void die_read(void); +void die_alarm(void); +void die_nomem(void); +void die_control(void); +void die_ipme(void); +void die_starttls(void); +void die_recipients(void); +void straynewline(void); + +void err_unimpl(void); +void err_syntax(void); +void err_noop(void); +void err_vrfy(void); +void err_wantrcpt(void); +void err_qqt(void); + +int err_child(void); +int err_fork(void); +int err_pipe(void); +int err_write(void); +int err_starttls(void); +void err_tlsreq(char *,char *,char *,char *,char *); + +void err_helo(char *,char *,char *,char *,char *,char *,char *,char *); +void err_spf(char *,char *,char *,char *,char *,char *,char *,char *); + +void err_authsetup(char *,char *,char *,char *,char *); +void err_authd(void); +void err_authmail(void); +void err_authfail(char *,char *,char *,char *,char *,char *,char *); +void err_authinvalid(char *,char *,char *,char *,char *); +void err_authabrt(void); +void err_authreq(char *,char *,char *,char *,char *); +void err_submission(char *,char *,char *,char *,char *); +int err_authabort(void); +int err_authinput(void); +int err_noauth(void); + +void err_wantmail(void); +void err_mav(char *,char *,char *,char *,char *,char *,char *); +void err_bmf(char *,char *,char *,char *,char *,char *,char *,char *); +void err_mfdns(char *,char *,char *,char *,char *,char *,char *); + +void err_nogateway(char *,char *,char *,char *,char *,char *,char *); +void err_brt(char *,char *,char *,char *,char *,char *,char *); +void err_rcpts(char *,char *,char *,char *,char *,char *,char *); +void err_recipient(char *,char *,char *,char *,char *,char *,char *); + +void straynewline(void); +void err_notorious(void); +void err_size(char *,char *,char *,char *,char *,char *,char *); +void err_data(char *,char *,char *,char *,char *,char *,char *,char *); + +int err_postgl(void); +int err_forkgl(void); +void postgrey(char *,char *,char *,char *,char *,char *,char *); + +#endif diff --git a/sqmail-4.3.07/src/include/spf.h b/sqmail-4.3.07/src/include/spf.h new file mode 100644 index 0000000..ca20418 --- /dev/null +++ b/sqmail-4.3.07/src/include/spf.h @@ -0,0 +1,111 @@ +#ifndef SPF_H +#define SPF_H + +#include "stralloc.h" +#include "ipalloc.h" + +/* (Internal) Processing codes */ + +#define SPF_INIT -1 +#define SPF_EXT -2 /* x */ +#define SPF_ME -3 +#define SPF_EXHAUST -4 +#define SPF_LOOP -5 +#define SPF_MULTIRR -6 +#define SPF_LOCAL -7 +#define SPF_ERROR -8 +#define SPF_NOMEM -9 +#define SPF_SYNTAX -10 /* Setup problem */ + +/* (External) Resulting codes */ + +#define SPF_OK 0 /* + Pass */ +#define SPF_NONE 1 /* o None */ +#define SPF_UNKNOWN 2 /* u Unknown method */ +#define SPF_NEUTRAL 3 /* ? Neutral */ +#define SPF_SOFTFAIL 4 /* ~ Softfail */ +#define SPF_FAIL 5 /* - Not Permitted */ +#define SPF_DNSSOFT 6 /* d From DNS; not used */ + +#define LOOKUP_LIMIT 10 + +/* spfinfo: S=remoteip|O=mailfrom|C=identity/domain|H=helo|M(echanism)=query|D=redirect|I=domain|P=problem|R:result */ + +#define SPF_DEFEXP "See http://%{d}/why.html?sender=%{s}&ip=%{i}&receiver=%{r}" + +extern int flagip6; +extern stralloc spfmf; +extern stralloc spfhelo; +extern stralloc spfinfo; +extern stralloc spfdomain; +extern stralloc dnsname; +extern stralloc spflocalrules; +extern stralloc spfrecord; +extern stralloc expdomain; +extern stralloc spfexplain; +extern stralloc spfexpmsg; + +/* this table and macro came from wget more or less */ +/* and was in turn stolen by me++ from libspf as is :) */ + +const static unsigned char urlchr_table[256] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, /* NUL SOH STX ETX EOT ENQ ACK BEL */ + 1, 1, 1, 1, 1, 1, 1, 1, /* BS HT LF VT FF CR SO SI */ + 1, 1, 1, 1, 1, 1, 1, 1, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 1, 1, 1, 1, 1, 1, 1, 1, /* CAN EM SUB ESC FS GS RS US */ + 1, 0, 1, 1, 0, 1, 1, 0, /* SP ! " # $ % & ' */ + 0, 0, 0, 1, 0, 0, 0, 1, /* ( ) * + , - . / */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 1 2 3 4 5 6 7 */ + 0, 0, 1, 1, 1, 1, 1, 1, /* 8 9 : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, /* @ A B C D E F G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H I J K L M N O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P Q R S T U V W */ + 0, 0, 0, 1, 1, 1, 1, 0, /* X Y Z [ \ ] ^ _ */ + 1, 0, 0, 0, 0, 0, 0, 0, /* ` a b c d e f g */ + 0, 0, 0, 0, 0, 0, 0, 0, /* h i j k l m n o */ + 0, 0, 0, 0, 0, 0, 0, 0, /* p q r s t u v w */ + 0, 0, 0, 1, 1, 1, 1, 1, /* x y z { | } ~ DEL */ + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n') +#define NXTOK(b, p, a) do { (b) = (p); \ + while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \ + while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \ + } while(0) + +/* spfdnsip.c */ + +int match_ip4(unsigned char [4],int,char [4]); +int match_ip6(unsigned char [16],int,char [16]); +int get_prefix(char *); +int spf_records(stralloc *,stralloc *); +int spf_include(char *,char *); +int spf_a(char *,char *); +int spf_mx(char *,char *); +int spf_ptr(char *,char *); +int spf_ip4(char *,char *); +int spf_ip6(char *,char *); +int spf_exists(char *,char *); + +/* spf.c */ + +int spf_query(const char *,const char *,const char *,const char *,const int); +int spf_lookup(stralloc *); +int spf_mechanism(char *,char *,char *,char *); +int spf_parse(stralloc *,char *,char *); +int spf_macros(stralloc *,char *,char *); +int spf_info(char *,const char *); + +#endif + diff --git a/sqmail-4.3.07/src/include/srs2.h b/sqmail-4.3.07/src/include/srs2.h new file mode 100644 index 0000000..e993928 --- /dev/null +++ b/sqmail-4.3.07/src/include/srs2.h @@ -0,0 +1,126 @@ +#ifndef SRS2_H +#define SRS2_H +#include <stdint.h> +#include <time.h> + +/* Adjusted to s/qmail (feh) */ + +/* Copyright (c) 2004 Shevek (srs@anarres.org) + * All rights reserved. + * + * This file is a part of libsrs2 from http://www.libsrs2.org/ + * + * Redistribution and use in source and binary forms, with or without + * modification, under the terms of either the GNU General Public + * License version 2 or the BSD license, at the discretion of the + * user. Copies of these licenses have been included in the libsrs2 + * distribution. See the the file called LICENSE for more + * information. + */ + +/* This is ugly, but reasonably safe. */ +#undef TRUE +#define TRUE 1 +#undef FALSE +#define FALSE 0 + +#define SRSSEP '=' +#define SRS0TAG "SRS0" +#define SRS1TAG "SRS1" + +/* Error codes */ + +#define SRS_ERRTYPE_MASK 0xf000 +#define SRS_ERRTYPE_NONE 0x0000 +#define SRS_ERRTYPE_CONFIG 0x1000 +#define SRS_ERRTYPE_INPUT 0x2000 +#define SRS_ERRTYPE_SYNTAX 0x4000 +#define SRS_ERRTYPE_SRS 0x8000 + +#define SRS_SUCCESS (0) +#define SRS_ENOTSRSADDRESS (1) +#define SRS_ENOTREWRITTEN (2) + +#define SRS_ENOSECRETS (SRS_ERRTYPE_CONFIG | 1) +#define SRS_ESEPARATORINVALID (SRS_ERRTYPE_CONFIG | 2) + +#define SRS_ENOSENDERATSIGN (SRS_ERRTYPE_INPUT | 1) +#define SRS_EBUFTOOSMALL (SRS_ERRTYPE_INPUT | 2) + +#define SRS_ENOSRS0HOST (SRS_ERRTYPE_SYNTAX | 1) +#define SRS_ENOSRS0USER (SRS_ERRTYPE_SYNTAX | 2) +#define SRS_ENOSRS0HASH (SRS_ERRTYPE_SYNTAX | 3) +#define SRS_ENOSRS0STAMP (SRS_ERRTYPE_SYNTAX | 4) +#define SRS_ENOSRS1HOST (SRS_ERRTYPE_SYNTAX | 5) +#define SRS_ENOSRS1USER (SRS_ERRTYPE_SYNTAX | 6) +#define SRS_ENOSRS1HASH (SRS_ERRTYPE_SYNTAX | 7) +#define SRS_EBADTIMESTAMPCHAR (SRS_ERRTYPE_SYNTAX | 8) +#define SRS_EHASHTOOSHORT (SRS_ERRTYPE_SYNTAX | 9) + +#define SRS_ETIMESTAMPOUTOFDATE (SRS_ERRTYPE_SRS | 1) +#define SRS_EHASHINVALID (SRS_ERRTYPE_SRS | 2) + +#define SRS_ERROR_TYPE(x) ((x) & SRS_ERRTYPE_MASK) + +/* SRS implementation */ + +#define SRS_IS_SRS_ADDRESS(x) ( \ + (strncasecmp((x),"SRS",3) == 0) && \ + (strchr("01", (x)[3]) != NULL) && \ + (strchr("-+=", (x)[4]) != NULL) \ +) + +typedef void *(*srs_malloc_t)(size_t); +typedef void *(*srs_realloc_t)(void *,size_t); +typedef void (*srs_free_t)(void *); + +typedef int srs_bool; + +typedef struct _srs_t { + /* Rewriting parameters */ +// stralloc cookies; + char **secrets; + int numsecrets; + char separator; + + /* Security parameters */ + int maxage; /* Maximum allowed age in seconds */ + int hashlen; + int hashmin; + + /* Behaviour parameters */ + srs_bool alwaysrewrite; /* Rewrite even into same domain? */ + srs_bool noforward; /* Never perform forwards rewriting */ + srs_bool noreverse; /* Never perform reverse rewriting */ + char **neverrewrite; /* A list of non-rewritten domains */ +} srs_t; + +/* Interface */ +int srs_set_malloc(srs_malloc_t m,srs_realloc_t r,srs_free_t f); +srs_t *srs_new(); +void srs_init(srs_t *); +void srs_free(srs_t *); +int srs_forward(srs_t *,char *,int,const char *,const char *); +int srs_forward_alloc(srs_t *,char **,const char *,const char *); +int srs_reverse(srs_t *,char *,int, const char *); +int srs_reverse_alloc(srs_t *,char **,const char *); +const char *srs_strerror(int); +int srs_add_secret(srs_t *,const char *); +const char * srs_get_secret(srs_t *,int); + /* You probably shouldn't call these. */ +int srs_timestamp_create(srs_t *,char *,time_t); +int srs_timestamp_check(srs_t *,const char *); + +#define SRS_PARAM_DECLARE(n, t) \ + int srs_set_ ## n (srs_t *srs, t value); \ + t srs_get_ ## n (srs_t *srs); + +SRS_PARAM_DECLARE(alwaysrewrite,srs_bool) +SRS_PARAM_DECLARE(separator,char) +SRS_PARAM_DECLARE(maxage,int) +SRS_PARAM_DECLARE(hashlen,int) +SRS_PARAM_DECLARE(hashmin,int) +SRS_PARAM_DECLARE(noforward,srs_bool) +SRS_PARAM_DECLARE(noreverse,srs_bool) + +#endif /* SRS2_H */ diff --git a/sqmail-4.3.07/src/include/strset.h b/sqmail-4.3.07/src/include/strset.h new file mode 100644 index 0000000..4a5703e --- /dev/null +++ b/sqmail-4.3.07/src/include/strset.h @@ -0,0 +1,29 @@ +#ifndef STRSET_H +#define STRSET_H + +#include "uint_t.h" + +typedef struct strset_list +{ + uint32 h; + int next; +} +strset_list; + +typedef struct +{ + int mask; /* mask + 1 is power of 2, size of hash table */ + int n; /* number of entries used in list and x */ + int a; /* number of entries allocated in list and x */ + int *first; /* first[h] is front of hash list h */ + strset_list *p; /* p[i].next is next; p[i].h is hash of x[i] */ + char **x; /* x[i] is entry i */ +} +strset; + +extern uint32 strset_hash(char *); +extern int strset_init(strset *); +extern char *strset_in(strset *,char *); +extern int strset_add(strset *,char *); + +#endif diff --git a/sqmail-4.3.07/src/include/tcpto.h b/sqmail-4.3.07/src/include/tcpto.h new file mode 100644 index 0000000..ca4f97a --- /dev/null +++ b/sqmail-4.3.07/src/include/tcpto.h @@ -0,0 +1,25 @@ +#ifndef TCPTO_H +#define TCPTO_H + +#define TCPTO_BUFSIZ 1024 + +/* persistency structure: record +struct tcpto { + unsigned char af; -- 1 byte -- IPv4: x'2' / IPv6: x'a' (10) + unsigned char nul[3]; -- 3 byte + unsigned char errorcount -- 1 byte -- if err_timeout || err_conrefused || err_proto (TLS) + unsigned char nul[3]; -- 3 byte + unsigned long when; -- 8 byte + union { + struct ip_address ip; + struct ip6_address ip6; + unsigned char nul[16]; -- 16 byte -- IPv4: filled up with '.' = x'2e' + } addr; +}; total: 32 byte +*/ + +int tcpto(); +void tcpto_err(); +void tcpto_clean(); + +#endif diff --git a/sqmail-4.3.07/src/include/tls_errors.h b/sqmail-4.3.07/src/include/tls_errors.h new file mode 100644 index 0000000..a61e8fd --- /dev/null +++ b/sqmail-4.3.07/src/include/tls_errors.h @@ -0,0 +1,42 @@ +#ifndef TLS_CLIENTS_H +#define TLS_CLIENTS_H + +#include "stralloc.h" + +extern void temp_tlscon(); +extern void temp_tlspeercert(); +extern void temp_tlspeervalid(); +extern void temp_tlspeerverify(); + +extern stralloc host; +extern stralloc remotehost; +extern stralloc cafile; +extern stralloc cadir; +extern stralloc ciphers; +extern stralloc certfile; +extern stralloc keyfile; +extern stralloc keypwd; + +void temp_nomem(void); +void temp_tlsctx(void); +void temp_tlsca(void); +void temp_tlscipher(void); +void temp_tlscert(void); +void temp_tlscertfp(void); +void temp_tlsdigest(void); +void temp_tlshost(void); +void temp_tlskey(void); +void temp_tlschk(void); +void temp_tlsctx(void); +void temp_tlserr(void); +void temp_tlsepeercert(void); +void temp_tlsepeerverify(void); +void temp_invaliddigest(void); +void temp_tlsainvalid(void); +void temp_tlsamissing(void); + +void zerodie(void); +void out(char *); +void outsafe(stralloc *); + +#endif diff --git a/sqmail-4.3.07/src/include/tls_remote.h b/sqmail-4.3.07/src/include/tls_remote.h new file mode 100644 index 0000000..c3c7933 --- /dev/null +++ b/sqmail-4.3.07/src/include/tls_remote.h @@ -0,0 +1,32 @@ +#ifndef TLS_REMOTE_H +#define TLS_REMOTE_H + +#include <openssl/ssl.h> + +/* the version is like this: 0xMNNFFPPS: major minor fix patch status */ +#if OPENSSL_VERSION_NUMBER < 0x00908000L +# error "Need OpenSSL version at least 0.9.8" +#endif + +extern char *tlsdestinfo; +extern struct constmap maptlsdestinations; +extern char *tlsdomaininfo; +extern struct constmap mapdomaincerts; +extern stralloc ciphers; + +int tls_domaincerts(const stralloc); +int tls_destination(const stralloc); +int tlsa_check(const STACK_OF(X509) *,const stralloc,const unsigned long); +int tls_fingerprint(X509 *,const char *,const int); +int tls_chainfile(SSL_CTX *,const char *); +int tls_certkey(SSL_CTX *,const char *,const char *,char *); +int tls_conn(SSL *,int); +int tls_setup(int,char *,char *); +int tls_checkpeer(SSL *,X509 *,const stralloc,const int,const int); +int tls_checkcrl(SSL *); +int tls_error(void); +int tls_exit(SSL *); + +int utf8string(unsigned char *,int); + +#endif diff --git a/sqmail-4.3.07/src/include/tls_start.h b/sqmail-4.3.07/src/include/tls_start.h new file mode 100644 index 0000000..d0417f9 --- /dev/null +++ b/sqmail-4.3.07/src/include/tls_start.h @@ -0,0 +1,7 @@ +#ifndef TLS_START_H +#define TLS_START_H + +int starttls_init(void); +int starttls_info(void); + +#endif diff --git a/sqmail-4.3.07/src/include/tls_timeoutio.h b/sqmail-4.3.07/src/include/tls_timeoutio.h new file mode 100644 index 0000000..175757e --- /dev/null +++ b/sqmail-4.3.07/src/include/tls_timeoutio.h @@ -0,0 +1,15 @@ +#ifndef TLS_TIMEOUTIO_H +#define TLS_TIMEOUTIO_H + +#include <openssl/ssl.h> + +int tls_timeoutconn(int t, int rfd, int wfd, SSL *tls); +int tls_timeoutaccept(int t, int rfd, int wfd, SSL *tls); +int tsl_timeoutrehandshake(int t, int rfd, int wfd, SSL *tls); + +int tls_timeoutread(int t, int rfd, int wfd, SSL *tls, char *buf, int len); +int tls_timeoutwrite(int t, int rfd, int wfd, SSL *tls, char *buf, int len); + +int tls_timeoutio(int (*fun)(), int t, int rfd, int wfd, SSL *tls, char *buf, int len); + +#endif diff --git a/sqmail-4.3.07/src/include/token822.h b/sqmail-4.3.07/src/include/token822.h new file mode 100644 index 0000000..42c909f --- /dev/null +++ b/sqmail-4.3.07/src/include/token822.h @@ -0,0 +1,36 @@ +#ifndef TOKEN822_H +#define TOKEN822_H + +#include "genalloc.h" + +struct token822 { + int type; + char *s; + int slen; +}; + +GEN_ALLOC_typedef(token822_alloc,struct token822,t,len,a) + +int token822_parse(); +int token822_addrlist(); +int token822_unquote(); +int token822_unparse(); +void token822_free(); +void token822_reverse(); +int token822_ready(); +int token822_readyplus(); +int token822_append(); + +#define TOKEN822_ATOM 1 +#define TOKEN822_QUOTE 2 +#define TOKEN822_LITERAL 3 +#define TOKEN822_COMMENT 4 +#define TOKEN822_LEFT 5 +#define TOKEN822_RIGHT 6 +#define TOKEN822_AT 7 +#define TOKEN822_COMMA 8 +#define TOKEN822_SEMI 9 +#define TOKEN822_COLON 10 +#define TOKEN822_DOT 11 + +#endif diff --git a/sqmail-4.3.07/src/include/trigger.h b/sqmail-4.3.07/src/include/trigger.h new file mode 100644 index 0000000..d0f4a3e --- /dev/null +++ b/sqmail-4.3.07/src/include/trigger.h @@ -0,0 +1,9 @@ +#ifndef TRIGGER_H +#define TRIGGER_H + +extern void trigger_set(); +extern void trigger_selprep(); +extern int trigger_pulled(); +extern void triggerpull(); + +#endif diff --git a/sqmail-4.3.07/src/include/triggerpull.h b/sqmail-4.3.07/src/include/triggerpull.h new file mode 100644 index 0000000..188f4f2 --- /dev/null +++ b/sqmail-4.3.07/src/include/triggerpull.h @@ -0,0 +1,6 @@ +#ifndef TRIGGERPULL_H +#define TRIGGERPULL_H + +void triggerpull(void); + +#endif diff --git a/sqmail-4.3.07/src/include/ucspitls.h b/sqmail-4.3.07/src/include/ucspitls.h new file mode 100644 index 0000000..40f8a81 --- /dev/null +++ b/sqmail-4.3.07/src/include/ucspitls.h @@ -0,0 +1,45 @@ +#ifndef UCSPITLS_H +#define UCSPITLS_H + +#include <openssl/ssl.h> +#include <openssl/opensslv.h> +#include <openssl/ec.h> +#include "stralloc.h" + +#define SSL_NAME_LEN 256 +#define OPENSSL_VERSION_NUMBER 0x101010100L + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) +#define ssl_client() (ssl_context(SSLv23_client_method())) +#define ssl_server() (ssl_context(SSLv23_server_method())) +#else +#define ssl_client() (ssl_context(TLS_client_method())) +#define ssl_server() (ssl_context(TLS_server_method())) +#endif + +extern int ssl_errno; +int ssl_io(SSL *,int,int,unsigned int); +SSL_CTX *ssl_context(SSL_METHOD *); +int ssl_timeoutconn(SSL *,unsigned int); +int ssl_timeoutaccept(SSL *,unsigned int); +SSL *ssl_new(SSL_CTX *,int); +int ssl_certkey(SSL_CTX *,const char *,const char *,pem_password_cb *); +int ssl_ca(SSL_CTX *,const char *,const char *,int); +int ssl_cca(SSL_CTX *,const char *); +int ssl_ciphers(SSL_CTX *,const char *); +int ssl_verify(SSL *,const char *); +int ssl_params(SSL_CTX *,const char *,int); +int ssl_server_env(SSL *,stralloc *); +int ssl_client_env(SSL *,stralloc *); +char *ssl_error_str(int); + +#define ssl_errstr() (SSL_load_error_strings()) +#define ssl_free(ssl) (SSL_free((ssl))) +#define ssl_close(ssl) (close(SSL_get_fd((ssl)))) + +#define ssl_pending(ssl) (SSL_pending((ssl))) +#define ssl_shutdown(ssl) (SSL_shutdown((ssl))) +#define ssl_shutdown_pending(ssl) (SSL_get_shutdown((ssl)) & SSL_RECEIVED_SHUTDOWN) +#define ssl_shutdown_sent(ssl) (SSL_get_shutdown((ssl)) & SSL_SENT_SHUTDOWN) + +#endif diff --git a/sqmail-4.3.07/src/include/wildmat.h b/sqmail-4.3.07/src/include/wildmat.h new file mode 100644 index 0000000..501b7e4 --- /dev/null +++ b/sqmail-4.3.07/src/include/wildmat.h @@ -0,0 +1,6 @@ +#ifndef WILDMAT_H +#define WILDMAT_H + +extern int wildmat(char *,char *); + +#endif diff --git a/sqmail-4.3.07/src/install.c b/sqmail-4.3.07/src/install.c new file mode 100644 index 0000000..c738fb7 --- /dev/null +++ b/sqmail-4.3.07/src/install.c @@ -0,0 +1,139 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "buffer.h" +#include "logmsg.h" +#include "open.h" +#include "exit.h" +#include "fifo.h" + +extern void hier(); + +#define WHO "install" + +int fdsourcedir = -1; + +void h(char *home,int uid,int gid,int mode) +{ + if (mkdir(home,0700) == -1) + if (errno != EEXIST) + logmsg(WHO,111,FATAL,B("unable to mkdir: ",home)); + if (chown(home,uid,gid) == -1) + logmsg(WHO,111,FATAL,B("unable to chown: ",home)); + if (chmod(home,mode) == -1) + logmsg(WHO,111,FATAL,B("unable to chmod: ",home)); +} + +void d(char *home,char *subdir,int uid,int gid,int mode) +{ + if (chdir(home) == -1) + logmsg(WHO,110,FATAL,B("unable to switch to: ",home)); + if (mkdir(subdir,0700) == -1) + if (errno != EEXIST) + logmsg(WHO,111,FATAL,B("unable to mkdir: ",home,"/",subdir)); + if (chown(subdir,uid,gid) == -1) + logmsg(WHO,111,FATAL,B("unable to chown: ",home,"/",subdir)); + if (chmod(subdir,mode) == -1) + logmsg(WHO,111,FATAL,B("unable to chmod: ",home,"/",subdir)); +} + +void p(char *home,char *fifo,int uid,int gid,int mode) +{ + if (chdir(home) == -1) + logmsg(WHO,110,FATAL,B("unable to switch to: ",home)); + if (fifo_make(fifo,0700) == -1) + if (errno != EEXIST) + logmsg(WHO,111,FATAL,B("unable to mkfifo: ",home,"/",fifo)); + if (chown(fifo,uid,gid) == -1) + logmsg(WHO,111,FATAL,B("unable to chown: ",home,"/",fifo)); + if (chmod(fifo,mode) == -1) + logmsg(WHO,111,FATAL,B("unable to chmod: ",home,"/",fifo)); +} + +char inbuf[BUFFER_INSIZE]; +buffer bi; +char outbuf[BUFFER_OUTSIZE]; +buffer bo; + +void c(char *home,char *subdir,char *file,int uid,int gid,int mode) +{ + int fdin; + int fdout; + + if (fchdir(fdsourcedir) == -1) + logmsg(WHO,110,FATAL,"unable to switch back to source directory: "); + + fdin = open_read(file); + if (fdin == -1) + logmsg(WHO,111,FATAL,B("unable to read: ",file)); + buffer_init(&bi,read,fdin,inbuf,sizeof(inbuf)); + + if (chdir(home) == -1) + logmsg(WHO,110,FATAL,B("unable to switch to: ",home)); + if (chdir(subdir) == -1) + logmsg(WHO,110,FATAL,B("unable to switch to: ",home,"/",subdir)); + + fdout = open_trunc(file); + if (fdout == -1) + logmsg(WHO,111,FATAL,B("unable to write .../",subdir,"/",file)); + buffer_init(&bo,write,fdout,outbuf,sizeof(outbuf)); + + switch (buffer_copy(&bo,&bi)) { + case -2: + logmsg(WHO,111,FATAL,B("unable to read: ",file)); + case -3: + logmsg(WHO,111,FATAL,B("unable to write .../",subdir,"/",file)); + } + + close(fdin); + if (buffer_flush(&bo) == -1) + logmsg(WHO,111,FATAL,B("unable to write .../",subdir,"/",file)); + if (fsync(fdout) == -1) + logmsg(WHO,111,FATAL,B("unable to write .../",subdir,"/",file)); + if (close(fdout) == -1) /* NFS silliness */ + logmsg(WHO,111,FATAL,B("unable to write .../",subdir,"/",file)); + + if (chown(file,uid,gid) == -1) + logmsg(WHO,111,FATAL,B("unable to chown .../",subdir,"/",file)); + if (chmod(file,mode) == -1) + logmsg(WHO,111,FATAL,B("unable to chmod .../",subdir,"/",file)); +} + +void z(char *home,char *file,int len,int uid,int gid,int mode) +{ + int fdout; + + if (chdir(home) == -1) + logmsg(WHO,110,FATAL,B("unable to switch to: ",home)); + + fdout = open_trunc(file); + if (fdout == -1) + logmsg(WHO,111,FATAL,B("unable to write: ",home,"/",file)); + buffer_init(&bo,write,fdout,outbuf,sizeof(outbuf)); + + while (len-- > 0) + if (buffer_put(&bo,"",1) == -1) + logmsg(WHO,111,FATAL,B("unable to write: ",home,"/",file)); + + if (buffer_flush(&bo) == -1) + logmsg(WHO,111,FATAL,B("unable to write: ",home,"/",file)); + if (fsync(fdout) == -1) + logmsg(WHO,111,FATAL,B("unable to write: ",home,"/",file)); + if (close(fdout) == -1) /* NFS silliness */ + logmsg(WHO,111,FATAL,B("unable to write: ",home,"/",file)); + + if (chown(file,uid,gid) == -1) + logmsg(WHO,111,FATAL,B("unable to chown: ",home,"/",file)); + if (chmod(file,mode) == -1) + logmsg(WHO,111,FATAL,B("unable to chmod: ",home,"/",file)); +} + +int main() +{ + fdsourcedir = open_read("."); + if (fdsourcedir == -1) + logmsg(WHO,110,FATAL,"unable to open current directory: "); + + umask(077); + hier(); + _exit(0); +} diff --git a/sqmail-4.3.07/src/instcheck.c b/sqmail-4.3.07/src/instcheck.c new file mode 100644 index 0000000..e47da87 --- /dev/null +++ b/sqmail-4.3.07/src/instcheck.c @@ -0,0 +1,73 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "logmsg.h" +#include "exit.h" +#include "hier.h" + +extern void hier(); + +#define WHO "instcheck" + +void perm(char *prefix1,char *prefix2,char *prefix3,char *file,int type,int uid,int gid,int mode) +{ + struct stat st; + + if (stat(file,&st) == -1) { + if (errno == ENOENT) + logmsg(WHO,0,WARN,B("file does nost exist:",prefix1,prefix2,prefix3,file)); + else + logmsg(WHO,errno,WARN,B("unable to stat: ../",file)); + return; + } + + if ((uid != -1) && (st.st_uid != uid)) + logmsg(WHO,0,WARN,B("file has wrong owner: ",prefix1,prefix2,prefix3,file)); + if ((gid != -1) && (st.st_gid != gid)) + logmsg(WHO,0,WARN,B("file has wrong group: ",prefix1,prefix2,prefix3,file)); + if ((st.st_mode & 07777) != mode) + logmsg(WHO,0,WARN,B("file has wrong permissions: ",prefix1,prefix2,prefix3,file)); + if ((st.st_mode & S_IFMT) != type) + logmsg(WHO,0,WARN,B("file has wrong type: ",prefix1,prefix2,prefix3,file)); +} + +void h(char *home,int uid,int gid,int mode) +{ + perm("","","",home,S_IFDIR,uid,gid,mode); +} + +void d(char *home,char *subdir,int uid,int gid,int mode) +{ + if (chdir(home) == -1) + logmsg(WHO,111,FATAL,B("unable to switch to: ",home)); + perm("",home,"/",subdir,S_IFDIR,uid,gid,mode); +} + +void p(char *home,char *fifo,int uid,int gid,int mode) +{ + if (chdir(home) == -1) + logmsg(WHO,111,FATAL,B("unable to switch to: ",home)); + perm("",home,"/",fifo,S_IFIFO,uid,gid,mode); +} + +void c(char *home,char *subdir,char *file,int uid,int gid,int mode) +{ + if (chdir(home) == -1) + logmsg(WHO,111,FATAL,B("unable to switch to: ",home)); + if (chdir(subdir) == -1) + logmsg(WHO,111,FATAL,B("unable to switch to: ",home,"/",subdir)); + perm(".../",subdir,"/",file,S_IFREG,uid,gid,mode); +} + +void z(char *home,char *file,int len,int uid,int gid,int mode) +{ + if (chdir(home) == -1) + logmsg(WHO,111,FATAL,B("unable to switch to: ",home)); + perm("",home,"/",file,S_IFREG,uid,gid,mode); +} + +int main() +{ + hier(); + _exit(0); +} diff --git a/sqmail-4.3.07/src/ipalloc.c b/sqmail-4.3.07/src/ipalloc.c new file mode 100644 index 0000000..390bd7b --- /dev/null +++ b/sqmail-4.3.07/src/ipalloc.c @@ -0,0 +1,7 @@ +#include "alloc.h" +#include "ip.h" +#include "ipalloc.h" +#include "genalloc.h" + +GEN_ALLOC_readyplus(ipalloc,struct ip_mx,ix,len,a,i,n,x,22,ipalloc_readyplus) +GEN_ALLOC_append(ipalloc,struct ip_mx,ix,len,a,i,n,x,22,ipalloc_readyplus,ipalloc_append) diff --git a/sqmail-4.3.07/src/ipme.c b/sqmail-4.3.07/src/ipme.c new file mode 100644 index 0000000..ba19722 --- /dev/null +++ b/sqmail-4.3.07/src/ipme.c @@ -0,0 +1,95 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <ifaddrs.h> +#ifndef SIOCGIFCONF /* whatever works */ +#include <sys/sockio.h> +#endif +#include "hassalen.h" +#include "byte.h" +#include "ip.h" +#include "ipalloc.h" +#include "stralloc.h" +#include "ipme.h" + +/** @file ipme.c + @brief ipme_is4, ipme_is6, ipme_is46, ipme_init + */ + +static int ipmeok = 0; +ipalloc ipme = {0}; + +int ipme_is4(struct ip4_address *ip) +{ + int i; + + if (ipme_init() != 1) return -1; + + for (i = 0; i < ipme.len; ++i) + if (ipme.ix[i].af == AF_INET && byte_equal(&ipme.ix[i].addr,4,ip)) + return 1; + return 0; +} + +int ipme_is6(struct ip6_address *ip) +{ + int i; + + if (ipme_init() != 1) return -1; + + for (i = 0; i < ipme.len; ++i) + if (ipme.ix[i].af == AF_INET6 && byte_equal(&ipme.ix[i].addr,16,ip)) + return 1; + return 0; +} + +int ipme_is(struct ip_mx *mxip) +{ + switch (mxip->af) { + case AF_INET: return ipme_is4(&mxip->addr.ip4); + case AF_INET6: return ipme_is6(&mxip->addr.ip6); + } + return 0; +} + +/* @brief ipme_init uses now getifaddrs() instead of ioctl calls */ + +int ipme_init() +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct ip_mx ix; + + if (ipmeok) return 1; + if (!ipalloc_readyplus(&ipme,0)) return 0; + ipme.len = 0; + ix.pref = 0; + + if (getifaddrs(&ifap)) return 0; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr) { + if (ifa->ifa_addr->sa_family == AF_INET) { + sin = (struct sockaddr_in *) ifa->ifa_addr; + byte_copy(&ix.addr.ip4,4,&sin->sin_addr); + ix.af = AF_INET; + if (!ipalloc_append(&ipme,&ix)) return 0; + } + if (ifa->ifa_addr->sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *) ifa->ifa_addr; + byte_copy(&ix.addr.ip6,16,&sin6->sin6_addr); + ix.af = AF_INET6; + if (!ipalloc_append(&ipme,&ix)) return 0; + } + } + + freeifaddrs(ifap); + ipmeok = 1; + + return 1; +} diff --git a/sqmail-4.3.07/src/ipmeprint.c b/sqmail-4.3.07/src/ipmeprint.c new file mode 100644 index 0000000..6379219 --- /dev/null +++ b/sqmail-4.3.07/src/ipmeprint.c @@ -0,0 +1,39 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> +#include "buffer.h" +#include "ip.h" +#include "ipme.h" +#include "exit.h" +#include "fmt.h" + +char ipaddr[IPFMT]; + +int main() +{ + int j; + + switch (ipme_init()) { + case 0: buffer_putsflush(buffer_2,"out of memory\n"); _exit(111); + case -1: buffer_putsflush(buffer_2,"hard error\n"); _exit(100); + } + + for (j = 0;j < ipme.len;++j) { + switch (ipme.ix[j].af) { + case AF_INET: + buffer_put(buffer_1,ipaddr,ip4_fmt(ipaddr,&ipme.ix[j].addr.ip4.d)); + break; + case AF_INET6: + buffer_put(buffer_1,ipaddr,ip6_fmt(ipaddr,&ipme.ix[j].addr.ip6.d)); + break; + default: + buffer_puts(buffer_1,"Unknown address family = "); + buffer_put(buffer_1,ipaddr,fmt_ulong(ipaddr,ipme.ix[j].af)); + } + buffer_puts(buffer_1,"\n"); + } + + buffer_flush(buffer_1); + _exit(0); +} diff --git a/sqmail-4.3.07/src/it-analog=d b/sqmail-4.3.07/src/it-analog=d new file mode 100644 index 0000000..cbc4c74 --- /dev/null +++ b/sqmail-4.3.07/src/it-analog=d @@ -0,0 +1,25 @@ +columnt +ddist +deferrals +failures +matchup +recipients +rhosts +rxdelay +senders +successes +suids +xqp +xrecipient +xsender +zddist +zdeferrals +zfailures +zoverall +zrecipients +zrhosts +zrxdelay +zsenders +zsendmail +zsuccesses +zsuids diff --git a/sqmail-4.3.07/src/it-base=d b/sqmail-4.3.07/src/it-base=d new file mode 100644 index 0000000..ee4adf5 --- /dev/null +++ b/sqmail-4.3.07/src/it-base=d @@ -0,0 +1,10 @@ +qmail-clean +qmail-inject +qmail-local +qmail-lspawn +qmail-send +qmail-queue +qmail-rspawn +qmail-send +qmail-start +qmail-todo diff --git a/sqmail-4.3.07/src/it-clients=d b/sqmail-4.3.07/src/it-clients=d new file mode 100644 index 0000000..eb741bd --- /dev/null +++ b/sqmail-4.3.07/src/it-clients=d @@ -0,0 +1,4 @@ +mailsubj +qmail-remote +qmail-qmqpc +sendmail diff --git a/sqmail-4.3.07/src/it-control=d b/sqmail-4.3.07/src/it-control=d new file mode 100644 index 0000000..a88caf6 --- /dev/null +++ b/sqmail-4.3.07/src/it-control=d @@ -0,0 +1,5 @@ +qmail-mfrules +qmail-showctl +qmail-badloadertypes +qmail-badmimetypes +qmail-recipients diff --git a/sqmail-4.3.07/src/it-dkim=d b/sqmail-4.3.07/src/it-dkim=d new file mode 100644 index 0000000..1ad1bf5 --- /dev/null +++ b/sqmail-4.3.07/src/it-dkim=d @@ -0,0 +1,2 @@ +qmail-dkim +qmail-dksign diff --git a/sqmail-4.3.07/src/it-dns=d b/sqmail-4.3.07/src/it-dns=d new file mode 100644 index 0000000..c1d692c --- /dev/null +++ b/sqmail-4.3.07/src/it-dns=d @@ -0,0 +1,10 @@ +dnscname +dnsfq +dnsip +dnsmxip +dnsptr +dnstlsa +dnstxt +hostname +ipmeprint +spfquery diff --git a/sqmail-4.3.07/src/it-forward=d b/sqmail-4.3.07/src/it-forward=d new file mode 100644 index 0000000..135e855 --- /dev/null +++ b/sqmail-4.3.07/src/it-forward=d @@ -0,0 +1,8 @@ +fastforward +forward +setforward +newaliases +newinclude +printforward +printmaillist +setmaillist diff --git a/sqmail-4.3.07/src/it-log=d b/sqmail-4.3.07/src/it-log=d new file mode 100644 index 0000000..3fe9f6f --- /dev/null +++ b/sqmail-4.3.07/src/it-log=d @@ -0,0 +1,4 @@ +qmail-mrtg +qmail-mrtg-queue +splogger +tai64nfrac diff --git a/sqmail-4.3.07/src/it-mbox=d b/sqmail-4.3.07/src/it-mbox=d new file mode 100644 index 0000000..11e36d6 --- /dev/null +++ b/sqmail-4.3.07/src/it-mbox=d @@ -0,0 +1,9 @@ +condredirect +bouncesaying +except +maildirmake +maildir2mbox +maildirwatch +preline +qbiff +qreceipt diff --git a/sqmail-4.3.07/src/it-pam=d b/sqmail-4.3.07/src/it-pam=d new file mode 100644 index 0000000..27a6a69 --- /dev/null +++ b/sqmail-4.3.07/src/it-pam=d @@ -0,0 +1,4 @@ +qmail-authuser +qmail-smtpam +qmail-vmailuser +qmail-postgrey diff --git a/sqmail-4.3.07/src/it-pop=d b/sqmail-4.3.07/src/it-pop=d new file mode 100644 index 0000000..2b2f59c --- /dev/null +++ b/sqmail-4.3.07/src/it-pop=d @@ -0,0 +1,2 @@ +qmail-popup +qmail-pop3d diff --git a/sqmail-4.3.07/src/it-queue=d b/sqmail-4.3.07/src/it-queue=d new file mode 100644 index 0000000..d439b37 --- /dev/null +++ b/sqmail-4.3.07/src/it-queue=d @@ -0,0 +1,5 @@ +qmail-qread +qmail-qstat +qmail-tcpok +qmail-tcpto +qmail-qmaint diff --git a/sqmail-4.3.07/src/it-server=d b/sqmail-4.3.07/src/it-server=d new file mode 100644 index 0000000..9ecb56d --- /dev/null +++ b/sqmail-4.3.07/src/it-server=d @@ -0,0 +1,3 @@ +qmail-qmtpd +qmail-qmqpd +qmail-smtpd diff --git a/sqmail-4.3.07/src/it-setup=d b/sqmail-4.3.07/src/it-setup=d new file mode 100644 index 0000000..bcc9a56 --- /dev/null +++ b/sqmail-4.3.07/src/it-setup=d @@ -0,0 +1,4 @@ +config +config-fast +install +instcheck diff --git a/sqmail-4.3.07/src/it-srs=d b/sqmail-4.3.07/src/it-srs=d new file mode 100644 index 0000000..02db41a --- /dev/null +++ b/sqmail-4.3.07/src/it-srs=d @@ -0,0 +1,2 @@ +srsforward +srsreverse diff --git a/sqmail-4.3.07/src/it-user=d b/sqmail-4.3.07/src/it-user=d new file mode 100644 index 0000000..55d4d04 --- /dev/null +++ b/sqmail-4.3.07/src/it-user=d @@ -0,0 +1,4 @@ +qmail-getpw +qmail-newu +qmail-newmrh +qmail-pw2u diff --git a/sqmail-4.3.07/src/it=d b/sqmail-4.3.07/src/it=d new file mode 100644 index 0000000..cedc560 --- /dev/null +++ b/sqmail-4.3.07/src/it=d @@ -0,0 +1,16 @@ +it-analog +it-base +it-clients +it-control +it-dkim +it-dns +it-forward +it-log +it-mbox +it-pam +it-pop +it-queue +it-server +it-srs +it-setup +it-user diff --git a/sqmail-4.3.07/src/maildir.c b/sqmail-4.3.07/src/maildir.c new file mode 100644 index 0000000..8832d75 --- /dev/null +++ b/sqmail-4.3.07/src/maildir.c @@ -0,0 +1,97 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "prioq.h" +#include "env.h" +#include "stralloc.h" +#include "direntry.h" +#include "datetime.h" +#include "now.h" +#include "str.h" +#include "maildir.h" +#include "logmsg.h" + +#define WHO "maildir" + +int maildir_chdir() +{ + char *maildir; + maildir = env_get("MAILDIR"); + if (!maildir) + logmsg(WHO,111,ERROR,"MAILDIR not set"); + if (chdir(maildir) == -1) + logmsg(WHO,110,FATAL,B("unable to chdir to: ",maildir)); + return 0; +} + +void maildir_clean(stralloc *tmpname) +{ + DIR *dir; + direntry *d; + datetime_sec time; + struct stat st; + + time = now(); + + dir = opendir("tmp"); + if (!dir) return; + + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') continue; + if (!stralloc_copys(tmpname,"tmp/")) break; + if (!stralloc_cats(tmpname,d->d_name)) break; + if (!stralloc_0(tmpname)) break; + if (stat(tmpname->s,&st) == 0) + if (time > st.st_atime + 129600) + unlink(tmpname->s); + } + closedir(dir); +} + +static int append(prioq *pq, stralloc *filenames, char *subdir, datetime_sec time) +{ + DIR *dir; + direntry *d; + struct prioq_elt pe; + unsigned int pos; + struct stat st; + + dir = opendir(subdir); + if (!dir) + logmsg(WHO,112,FATAL,B("unable to scan $MAILDIR/:",subdir)); + + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') continue; + pos = filenames->len; + if (!stralloc_cats(filenames,subdir)) break; + if (!stralloc_cats(filenames,"/")) break; + if (!stralloc_cats(filenames,d->d_name)) break; + if (!stralloc_0(filenames)) break; + if (stat(filenames->s + pos,&st) == 0) + if (st.st_mtime < time) { /* don't want to mix up the order */ + pe.dt = st.st_mtime; + pe.id = pos; + if (!prioq_insert(pq,&pe)) break; + } + } + + closedir(dir); + if (d) logmsg(WHO,112,FATAL,B("unable to read $MAILDIR/:",subdir)); + return 0; +} + +int maildir_scan(prioq *pq, stralloc *filenames, int flagnew, int flagcur) +{ + struct prioq_elt pe; + datetime_sec time; + + if (!stralloc_copys(filenames,"")) return 0; + while (prioq_min(pq,&pe)) + prioq_delmin(pq); + + time = now(); + + if (flagnew) if (append(pq,filenames,"new",time) == -1) return -1; + if (flagcur) if (append(pq,filenames,"cur",time) == -1) return -1; + return 0; +} diff --git a/sqmail-4.3.07/src/maildir2mbox.c b/sqmail-4.3.07/src/maildir2mbox.c new file mode 100644 index 0000000..ba187e7 --- /dev/null +++ b/sqmail-4.3.07/src/maildir2mbox.c @@ -0,0 +1,156 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "env.h" +#include "genalloc.h" +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "logmsg.h" +#include "open.h" +#include "lock.h" +#include "gfrom.h" +#include "str.h" +#include "exit.h" +#include "myctime.h" +#include "maildir.h" +#include "prioq.h" + +char *mbox; +char *mboxtmp; + +int rename(const char *,const char *); // stdio.h + +stralloc filenames = {0}; +prioq pq = {0}; +prioq pq2 = {0}; + +stralloc line = {0}; + +stralloc ufline = {0}; + +char inbuf[BUFFER_INSIZE]; +char outbuf[BUFFER_OUTSIZE]; + +#define WHO "maildir2mbox" + +void die_nomem() { logmsg(WHO,111,FATAL,"out of memory"); } + +int main() +{ + buffer bi; + buffer bo; + struct prioq_elt pe; + int fdoldmbox; + int fdnewmbox; + int fd; + int match; + int fdlock; + + umask(077); + + mbox = env_get("MAIL"); + if (!mbox) logmsg(WHO,111,FATAL,"MAIL not set"); + mboxtmp = env_get("MAILTMP"); + if (!mboxtmp) logmsg(WHO,111,FATAL,"MAILTMP not set"); + + if (maildir_chdir() == -1) + logmsg(WHO,110,FATAL,"Can't changet maildir"); + maildir_clean(&filenames); + if (maildir_scan(&pq,&filenames,1,1) == -1) + logmsg(WHO,112,FATAL,"Can't read maidir"); + + if (!prioq_min(&pq,&pe)) _exit(0); /* nothing new */ + + fdlock = open_append(mbox); + if (fdlock == -1) + logmsg(WHO,111,FATAL,B("unable to lock: ",mbox)); + if (lock_ex(fdlock) == -1) + logmsg(WHO,111,FATAL,B("unable to lock: ",mbox)); + + fdoldmbox = open_read(mbox); + if (fdoldmbox == -1) + logmsg(WHO,112,FATAL,B("unable to read: ",mbox)); + + fdnewmbox = open_trunc(mboxtmp); + if (fdnewmbox == -1) + logmsg(WHO,112,FATAL,B("unable to create: ",mboxtmp)); + + buffer_init(&bi,read,fdoldmbox,inbuf,sizeof(inbuf)); + buffer_init(&bo,write,fdnewmbox,outbuf,sizeof(outbuf)); + + switch (buffer_copy(&bo,&bi)) { + case -2: logmsg(WHO,112,FATAL,B("unable to read: ",mbox)); + case -3: logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + } + + while (prioq_min(&pq,&pe)) { + prioq_delmin(&pq); + if (!prioq_insert(&pq2,&pe)) die_nomem(); + + fd = open_read(filenames.s + pe.id); + if (fd == -1) + logmsg(WHO,112,FATAL,B("unable to read: $MAILDIR/",filenames.s + pe.id)); + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + if (getln(&bi,&line,&match,'\n') != 0) + logmsg(WHO,112,FATAL,B("unable to read: $MAILDIR/",filenames.s + pe.id)); + + if (!stralloc_copys(&ufline,"From XXX ")) die_nomem(); + if (match) + if (stralloc_starts(&line,"Return-Path: <")) { + if (line.s[14] == '>') { + if (!stralloc_copys(&ufline,"From MAILER-DAEMON ")) die_nomem(); + } else { + int i; + if (!stralloc_ready(&ufline,line.len)) die_nomem(); + if (!stralloc_copys(&ufline,"From ")) die_nomem(); + + for (i = 14;i < line.len - 2;++i) + if ((line.s[i] == ' ') || (line.s[i] == '\t')) + ufline.s[ufline.len++] = '-'; + else + ufline.s[ufline.len++] = line.s[i]; + if (!stralloc_cats(&ufline," ")) die_nomem(); + } + } + if (!stralloc_cats(&ufline,myctime(pe.dt))) die_nomem(); + if (buffer_put(&bo,ufline.s,ufline.len) == -1) + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + + while (match && line.len) { + if (gfrom(line.s,line.len)) + if (buffer_puts(&bo,">") == -1) + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + if (buffer_put(&bo,line.s,line.len) == -1) + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + if (!match) { + if (buffer_puts(&bo,"\n") == -1) + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + break; + } + if (getln(&bi,&line,&match,'\n') != 0) + logmsg(WHO,112,FATAL,B("unable to read: $MAILDIR/",filenames.s + pe.id)); + } + if (buffer_puts(&bo,"\n")) + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + + close(fd); + } + + if (buffer_flush(&bo) == -1) + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + if (fsync(fdnewmbox) == -1) + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + if (close(fdnewmbox) == -1) /* NFS dorks */ + logmsg(WHO,112,FATAL,B("unable to write to: ",mboxtmp)); + if (rename(mboxtmp,mbox) == -1) + logmsg(WHO,112,FATAL,B("unable to move ",mboxtmp," to: ",mbox)); + + while (prioq_min(&pq2,&pe)) { + prioq_delmin(&pq2); + if (unlink(filenames.s + pe.id) == -1) + logmsg(WHO,0,WARN,B("$MAILDIR/",filenames.s + pe.id," will be delivered twice; unable to unlink")); + } + + _exit(0); +} diff --git a/sqmail-4.3.07/src/maildirmake.c b/sqmail-4.3.07/src/maildirmake.c new file mode 100644 index 0000000..47edc44 --- /dev/null +++ b/sqmail-4.3.07/src/maildirmake.c @@ -0,0 +1,24 @@ +#include <sys/stat.h> +#include <unistd.h> +#include "logmsg.h" +#include "exit.h" + +#define WHO "maildirmake" + +int main(int argc, char **argv) +{ + umask(077); + if (!argv[1]) + logmsg(WHO,100,USAGE,"maildirmake name"); + if (mkdir(argv[1],0700) == -1) + logmsg(WHO,111,FATAL,B("unable to mkdir: ",argv[1])); + if (chdir(argv[1]) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",argv[1])); + if (mkdir("tmp",0700) == -1) + logmsg(WHO,111,FATAL,B("unable to mkdir: ",argv[1],"/tmp")); + if (mkdir("new",0700) == -1) + logmsg(WHO,111,FATAL,B("unable to mkdir: ",argv[1],"/new")); + if (mkdir("cur",0700) == -1) + logmsg(WHO,111,FATAL,B("unable to mkdir: ",argv[1],"/cur")); + _exit(0); +} diff --git a/sqmail-4.3.07/src/maildirwatch.c b/sqmail-4.3.07/src/maildirwatch.c new file mode 100644 index 0000000..aea5cbb --- /dev/null +++ b/sqmail-4.3.07/src/maildirwatch.c @@ -0,0 +1,123 @@ +#include <unistd.h> +#include "getln.h" +#include "buffer.h" +#include "prioq.h" +#include "stralloc.h" +#include "str.h" +#include "exit.h" +#include "hfield.h" +#include "logmsg.h" +#include "open.h" +#include "headerbody.h" +#include "maildir.h" + +#define WHO "maildirwatch" + +void die_nomem() { logmsg(WHO,111,FATAL,"out of memory"); } + +stralloc recipient = {0}; +stralloc sender = {0}; +stralloc fromline = {0}; +stralloc text = {0}; + +void addtext(char *s, int n) +{ + if (!stralloc_catb(&text,s,n)) die_nomem(); + if (text.len > 158) text.len = 158; +} + +void dobody(stralloc *h) { addtext(h->s,h->len); } + +void doheader(stralloc *h) +{ + int i; + switch (hfield_known(h->s,h->len)) { + case H_SUBJECT: + i = hfield_skipname(h->s,h->len); + addtext(h->s + i,h->len - i); + break; + case H_DELIVEREDTO: + i = hfield_skipname(h->s,h->len); + if (i < h->len) + if (!stralloc_copyb(&recipient,h->s + i,h->len - i - 1)) die_nomem(); + break; + case H_RETURNPATH: + i = hfield_skipname(h->s,h->len); + if (i < h->len) + if (!stralloc_copyb(&sender,h->s + i,h->len - i - 1)) die_nomem(); + break; + case H_FROM: + if (!stralloc_copyb(&fromline,h->s,h->len - 1)) die_nomem(); + break; + } +} +void finishheader() { ; } + +stralloc filenames = {0}; +prioq pq = {0}; + +char inbuf[BUFFER_INSIZE]; +buffer bi; + +int main() +{ + struct prioq_elt pe; + int fd; + int i; + + if (maildir_chdir() == -1) + logmsg(WHO,111,FATAL,"Can't change to maildir"); + + for (;;) { + maildir_clean(&filenames); + if (maildir_scan(&pq,&filenames,1,0) == -1) + logmsg(WHO,111,FATAL,"Can't read maildir"); + + buffer_putsflush(buffer_1,"\033[;H\033[;J"); + + while (prioq_min(&pq,&pe)) { + prioq_delmin(&pq); + + fd = open_read(filenames.s + pe.id); + if (fd == -1) continue; + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + if (!stralloc_copys(&sender,"?")) die_nomem(); + if (!stralloc_copys(&recipient,"?")) die_nomem(); + if (!stralloc_copys(&fromline,"")) die_nomem(); + if (!stralloc_copys(&text,"")) die_nomem(); + if (headerbody(&bi,doheader,finishheader,dobody) == -1) + logmsg(WHO,111,FATAL,"trouble reading new message"); + + for (i = 0; i < fromline.len; ++i) + if ((fromline.s[i] < 32) || (fromline.s[i] > 126)) + fromline.s[i] = '/'; + for (i = 0; i < sender.len; ++i) + if ((sender.s[i] < 32) || (sender.s[i] > 126)) + sender.s[i] = '?'; + for (i = 0; i < recipient.len; ++i) + if ((recipient.s[i] < 32) || (recipient.s[i] > 126)) + recipient.s[i] = '?'; + for (i = 0; i < text.len; ++i) + if ((text.s[i] < 32) || (text.s[i] > 126)) + text.s[i] = '/'; + buffer_puts(buffer_1,"FROM "); + buffer_put(buffer_1,sender.s,sender.len); + buffer_puts(buffer_1," TO <"); + buffer_put(buffer_1,recipient.s,recipient.len); + buffer_puts(buffer_1,">\n"); + if (fromline.len) { + buffer_puts(buffer_1,"\033[1m"); + buffer_put(buffer_1,fromline.s,fromline.len); + buffer_puts(buffer_1,"\033[0m\n"); + } + buffer_put(buffer_1,text.s,text.len); + buffer_puts(buffer_1,"\n\n"); + + close(fd); + } + + buffer_flush(buffer_1); + sleep(30); + } +} diff --git a/sqmail-4.3.07/src/mailsubj.sh b/sqmail-4.3.07/src/mailsubj.sh new file mode 100755 index 0000000..f7a40ce --- /dev/null +++ b/sqmail-4.3.07/src/mailsubj.sh @@ -0,0 +1,7 @@ +subject="$1" +shift +( echo Subject: "$subject" + echo To: ${1+"$@"} + echo '' + cat +) | HOME/bin/qmail-inject diff --git a/sqmail-4.3.07/src/make-compile.sh b/sqmail-4.3.07/src/make-compile.sh new file mode 100755 index 0000000..a1eb501 --- /dev/null +++ b/sqmail-4.3.07/src/make-compile.sh @@ -0,0 +1 @@ +echo exec "$CC" -c '${1+"$@"}' diff --git a/sqmail-4.3.07/src/make-load.sh b/sqmail-4.3.07/src/make-load.sh new file mode 100755 index 0000000..de07d2e --- /dev/null +++ b/sqmail-4.3.07/src/make-load.sh @@ -0,0 +1,2 @@ +echo 'main="$1"; shift' +echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}' diff --git a/sqmail-4.3.07/src/make-makelib.sh b/sqmail-4.3.07/src/make-makelib.sh new file mode 100755 index 0000000..d6b7c8c --- /dev/null +++ b/sqmail-4.3.07/src/make-makelib.sh @@ -0,0 +1,16 @@ +echo 'main="$1"; shift' +echo 'rm -f "$main"' +echo 'ar cr "$main" ${1+"$@"}' + +case "$1" in +sunos-5.*) ;; +unix_sv*) ;; +irix64-*) ;; +irix-*) ;; +dgux-*) ;; +hp-ux-*) ;; +sco*) ;; +*) + echo 'ranlib "$main"' + ;; +esac diff --git a/sqmail-4.3.07/src/matchup.c b/sqmail-4.3.07/src/matchup.c new file mode 100644 index 0000000..047ea65 --- /dev/null +++ b/sqmail-4.3.07/src/matchup.c @@ -0,0 +1,489 @@ +#include <unistd.h> +#include "genalloc.h" +#include "alloc.h" +#include "stralloc.h" +#include "logmsg.h" +#include "getln.h" +#include "buffer.h" +#include "readwrite.h" +#include "exit.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "case.h" + +#define WHO "matchup" + +void nomem() { logmsg(WHO,111,FATAL,"out of memory"); } +void die_read() { logmsg(WHO,110,ERROR,"unable to read input: "); } +void die_write() { logmsg(WHO,110,ERROR,"unable to write output: "); } +void die_write5() { logmsg(WHO,111,FATAL,"unable to write fd 5: "); } + +void out(char *buf,int len) +{ + if (buffer_put(buffer_1,buf,len) == -1) die_write(); +} +void outs(char *buf) +{ + if (buffer_puts(buffer_1,buf) == -1) die_write(); +} + +char buf5[512]; +buffer bo5 = BUFFER_INIT(write,5,buf5,sizeof(buf5)); + +void out5(char *buf,int len) +{ + if (buffer_put(&bo5,buf,len) == -1) + die_write5(); +} +void outs5(char *buf) +{ + if (buffer_puts(&bo5,buf) == -1) + die_write5(); +} + +GEN_ALLOC_typedef(ulongalloc,unsigned long,u,len,a) +GEN_ALLOC_ready(ulongalloc,unsigned long,u,len,a,i,n,x,30,ulongalloc_ready) +GEN_ALLOC_readyplus(ulongalloc,unsigned long,u,len,a,i,n,x,30,ulongalloc_readyplus) + +char strnum[FMT_ULONG]; + +stralloc pool = {0}; +unsigned int poolbytes = 0; + +int nummsg = 0; +ulongalloc msg = {0}; +ulongalloc bytes = {0}; +ulongalloc qp = {0}; +ulongalloc uid = {0}; +ulongalloc numk = {0}; +ulongalloc numd = {0}; +ulongalloc numz = {0}; +ulongalloc sender = {0}; +ulongalloc birth = {0}; + +int msg_find(unsigned long m) +{ + int i; + for (i = 0; i < nummsg; ++i) + if (msg.u[i] == m) return i; + return -1; +} + +int msg_add(unsigned long m) +{ + int i; + for (i = 0; i < nummsg; ++i) + if (msg.u[i] == m) return i; + i = nummsg++; + if (!ulongalloc_ready(&msg,nummsg)) nomem(); + if (!ulongalloc_ready(&bytes,nummsg)) nomem(); + if (!ulongalloc_ready(&qp,nummsg)) nomem(); + if (!ulongalloc_ready(&uid,nummsg)) nomem(); + if (!ulongalloc_ready(&numk,nummsg)) nomem(); + if (!ulongalloc_ready(&numd,nummsg)) nomem(); + if (!ulongalloc_ready(&numz,nummsg)) nomem(); + if (!ulongalloc_ready(&sender,nummsg)) nomem(); + if (!ulongalloc_ready(&birth,nummsg)) nomem(); + msg.u[i] = m; + return i; +} + +void msg_kill(int i) +{ + poolbytes -= str_len(pool.s + sender.u[i]) + 1; + poolbytes -= str_len(pool.s + birth.u[i]) + 1; + + --nummsg; + msg.u[i] = msg.u[nummsg]; + bytes.u[i] = bytes.u[nummsg]; + qp.u[i] = qp.u[nummsg]; + uid.u[i] = uid.u[nummsg]; + numk.u[i] = numk.u[nummsg]; + numd.u[i] = numd.u[nummsg]; + numz.u[i] = numz.u[nummsg]; + sender.u[i] = sender.u[nummsg]; + birth.u[i] = birth.u[nummsg]; +} + +int numdel = 0; +ulongalloc del = {0}; +ulongalloc dmsg = {0}; +ulongalloc dchan = {0}; +ulongalloc drecip = {0}; +ulongalloc dstart = {0}; + +int del_find(unsigned long d) +{ + int i; + for (i = 0; i < numdel; ++i) + if (del.u[i] == d) return i; + return -1; +} + +int del_add(unsigned long d) +{ + int i; + for (i = 0; i < numdel; ++i) + if (del.u[i] == d) return i; + i = numdel++; + if (!ulongalloc_ready(&del,numdel)) nomem(); + if (!ulongalloc_ready(&dmsg,numdel)) nomem(); + if (!ulongalloc_ready(&dchan,numdel)) nomem(); + if (!ulongalloc_ready(&drecip,numdel)) nomem(); + if (!ulongalloc_ready(&dstart,numdel)) nomem(); + del.u[i] = d; + return i; +} + +void del_kill(int i) +{ + poolbytes -= str_len(pool.s + dchan.u[i]) + 1; + poolbytes -= str_len(pool.s + drecip.u[i]) + 1; + poolbytes -= str_len(pool.s + dstart.u[i]) + 1; + --numdel; + del.u[i] = del.u[numdel]; + dmsg.u[i] = dmsg.u[numdel]; + dchan.u[i] = dchan.u[numdel]; + drecip.u[i] = drecip.u[numdel]; + dstart.u[i] = dstart.u[numdel]; +} + +stralloc pool2 = {0}; + +void garbage() +{ + int i; + char *x; + + if (pool.len - poolbytes < poolbytes + 4096) return; + + if (!stralloc_copys(&pool2,"")) nomem(); + + for (i = 0; i < nummsg; ++i) { + x = pool.s + birth.u[i]; + birth.u[i] = pool2.len; + if (!stralloc_cats(&pool2,x)) nomem(); + if (!stralloc_0(&pool2)) nomem(); + x = pool.s + sender.u[i]; + sender.u[i] = pool2.len; + if (!stralloc_cats(&pool2,x)) nomem(); + if (!stralloc_0(&pool2)) nomem(); + } + + for (i = 0; i < numdel; ++i) { + x = pool.s + dstart.u[i]; + dstart.u[i] = pool2.len; + if (!stralloc_cats(&pool2,x)) nomem(); + if (!stralloc_0(&pool2)) nomem(); + x = pool.s + dchan.u[i]; + dchan.u[i] = pool2.len; + if (!stralloc_cats(&pool2,x)) nomem(); + if (!stralloc_0(&pool2)) nomem(); + x = pool.s + drecip.u[i]; + drecip.u[i] = pool2.len; + if (!stralloc_cats(&pool2,x)) nomem(); + if (!stralloc_0(&pool2)) nomem(); + } + + if (!stralloc_copy(&pool,&pool2)) nomem(); + + poolbytes = pool.len; /* redundant, but doesn't hurt */ +} + +stralloc line = {0}; +int match; + +#define FIELDS 20 +int field[FIELDS]; + +void clear() +{ + while (numdel > 0) + del_kill(0); + garbage(); +} + +void starting() +{ + unsigned long d; + unsigned long m; + int dpos; + + scan_ulong(line.s + field[3],&d); + scan_ulong(line.s + field[5],&m); + + dpos = del_add(d); + + dmsg.u[dpos] = m; + + dstart.u[dpos] = pool.len; + if (!stralloc_cats(&pool,line.s + field[0])) nomem(); + if (!stralloc_0(&pool)) nomem(); + + dchan.u[dpos] = pool.len; + if (!stralloc_cats(&pool,line.s + field[7])) nomem(); + if (!stralloc_0(&pool)) nomem(); + + drecip.u[dpos] = pool.len; + if (!stralloc_cats(&pool,line.s + field[8])) nomem(); + if (!stralloc_0(&pool)) nomem(); + case_lowers(pool.s + drecip.u[dpos]); + + poolbytes += pool.len - dstart.u[dpos]; +} + +void delivery() +{ + unsigned long d; + unsigned long m; + int dpos; + int mpos; + char *result = "?"; + char *reason = ""; + + scan_ulong(line.s + field[2],&d); + + dpos = del_find(d); + if (dpos == -1) return; + + m = dmsg.u[dpos]; + mpos = msg_find(m); + + if (str_start(line.s + field[3],"succ")) { + if (mpos != -1) ++numk.u[mpos]; + result = "d k "; + reason = line.s + field[4]; + } + else if (str_start(line.s + field[3],"fail")) { + if (mpos != -1) ++numd.u[mpos]; + result = "d d "; + reason = line.s + field[4]; + } + else if (str_start(line.s + field[3],"defer")) { + if (mpos != -1) ++numz.u[mpos]; + result = "d z "; + reason = line.s + field[4]; + } + else if (str_start(line.s + field[3],"report")) { + if (mpos != -1) ++numz.u[mpos]; + result = "d z "; + reason = "report_mangled"; + } + + outs(result); + + if (mpos != -1) { + outs(pool.s + birth.u[mpos]); + outs(" "); outs(pool.s + dstart.u[dpos]); + outs(" "); outs(line.s + field[0]); + outs(" "); out(strnum,fmt_ulong(strnum,bytes.u[mpos])); + outs(" "); outs(pool.s + sender.u[mpos]); + outs(" "); outs(pool.s + dchan.u[dpos]); + outs("."); outs(pool.s + drecip.u[dpos]); + outs(" "); out(strnum,fmt_ulong(strnum,qp.u[mpos])); + outs(" "); out(strnum,fmt_ulong(strnum,uid.u[mpos])); + outs(" "); outs(reason); + } else { + outs(pool.s + dstart.u[dpos]); + outs(" "); outs(pool.s + dstart.u[dpos]); + outs(" "); outs(line.s + field[0]); + outs(" 0 ? "); outs(pool.s + dchan.u[dpos]); + outs("."); outs(pool.s + drecip.u[dpos]); + outs(" ? ? "); outs(reason); + } + + outs("\n"); + + del_kill(dpos); + garbage(); +} + +void newmsg() +{ + unsigned long m; + int mpos; + + scan_ulong(line.s + field[3],&m); + mpos = msg_find(m); + if (mpos == -1) return; + msg_kill(mpos); + garbage(); +} + +void endmsg() +{ + unsigned long m; + int mpos; + + scan_ulong(line.s + field[3],&m); + mpos = msg_find(m); + if (mpos == -1) return; + + outs("m "); outs(pool.s + birth.u[mpos]); + outs(" "); outs(line.s + field[0]); + outs(" "); out(strnum,fmt_ulong(strnum,bytes.u[mpos])); + outs(" "); out(strnum,fmt_ulong(strnum,numk.u[mpos])); + outs(" "); out(strnum,fmt_ulong(strnum,numd.u[mpos])); + outs(" "); out(strnum,fmt_ulong(strnum,numz.u[mpos])); + outs(" "); outs(pool.s + sender.u[mpos]); + outs(" "); out(strnum,fmt_ulong(strnum,qp.u[mpos])); + outs(" "); out(strnum,fmt_ulong(strnum,uid.u[mpos])); + outs("\n"); + + msg_kill(mpos); + garbage(); +} + +void info() +{ + unsigned long m; + int mpos; + + scan_ulong(line.s + field[3],&m); + mpos = msg_add(m); + + scan_ulong(line.s + field[5],&bytes.u[mpos]); + scan_ulong(line.s + field[9],&qp.u[mpos]); + scan_ulong(line.s + field[11],&uid.u[mpos]); + + numk.u[mpos] = 0; + numd.u[mpos] = 0; + numz.u[mpos] = 0; + + birth.u[mpos] = pool.len; + if (!stralloc_cats(&pool,line.s + field[0])) nomem(); + if (!stralloc_0(&pool)) nomem(); + + sender.u[mpos] = pool.len; + if (!stralloc_cats(&pool,line.s + field[7])) nomem(); + if (!stralloc_0(&pool)) nomem(); + case_lowers(pool.s + sender.u[mpos]); + + poolbytes += pool.len - birth.u[mpos]; +} + +void extra() +{ + unsigned long m; + int mpos; + + scan_ulong(line.s + field[2],&m); + mpos = msg_find(m); + if (mpos == -1) return; + + scan_ulong(line.s + field[3],&numk.u[mpos]); + scan_ulong(line.s + field[4],&numz.u[mpos]); + scan_ulong(line.s + field[5],&numd.u[mpos]); +} + +void pending() +{ + int i; + + for (i = 0; i < nummsg; ++i) { + outs5(pool.s + birth.u[i]); + outs5(" info msg "); + out5(strnum,fmt_ulong(strnum,msg.u[i])); + outs5(": bytes "); + out5(strnum,fmt_ulong(strnum,bytes.u[i])); + outs5(" from "); + outs5(pool.s + sender.u[i]); + outs5(" qp "); + out5(strnum,fmt_ulong(strnum,qp.u[i])); + outs5(" uid "); + out5(strnum,fmt_ulong(strnum,uid.u[i])); + outs5("\n"); + outs5(pool.s + birth.u[i]); + outs5(" extra "); + out5(strnum,fmt_ulong(strnum,msg.u[i])); + outs5(" "); + out5(strnum,fmt_ulong(strnum,numk.u[i])); + outs5(" "); + out5(strnum,fmt_ulong(strnum,numz.u[i])); + outs5(" "); + out5(strnum,fmt_ulong(strnum,numd.u[i])); + outs5("\n"); + } + + for (i = 0; i < numdel; ++i) { + outs5(pool.s + dstart.u[i]); + outs5(" starting delivery "); + out5(strnum,fmt_ulong(strnum,del.u[i])); + outs5(": msg "); + out5(strnum,fmt_ulong(strnum,dmsg.u[i])); + outs5(" to "); + outs5(pool.s + dchan.u[i]); + outs5(" "); + outs5(pool.s + drecip.u[i]); + outs5("\n"); + } + + out5(line.s,line.len); + if (buffer_flush(&bo5) == -1) die_write5(); +} + +stralloc outline = {0}; + +int main() +{ + int i; + int j; + char ch; + + if (!stralloc_copys(&pool,"")) nomem(); + + if (!ulongalloc_ready(&msg,1)) nomem(); + if (!ulongalloc_ready(&bytes,1)) nomem(); + if (!ulongalloc_ready(&qp,1)) nomem(); + if (!ulongalloc_ready(&uid,1)) nomem(); + if (!ulongalloc_ready(&numk,1)) nomem(); + if (!ulongalloc_ready(&numd,1)) nomem(); + if (!ulongalloc_ready(&numz,1)) nomem(); + if (!ulongalloc_ready(&del,1)) nomem(); + if (!ulongalloc_ready(&dmsg,1)) nomem(); + + for (;;) { + if (getln(buffer_0,&line,&match,'\n') == -1) die_read(); + if (!match) break; + + if (!stralloc_copy(&outline,&line)) nomem(); + + for (i = 0; i < line.len; ++i) { + ch = line.s[i]; + if ((ch == '\n') || (ch == ' ') || (ch == '\t')) line.s[i] = 0; + } + j = 0; + for (i = 0; i < FIELDS; ++i) { + while (j < line.len) if (line.s[j]) break; else ++j; + field[i] = j; + while (j < line.len) if (!line.s[j]) break; else ++j; + } + if (!stralloc_0(&line)) nomem(); + + if (str_equal(line.s + field[1],"status:")) ; + else if (str_equal(line.s + field[1],"starting")) starting(); + else if (str_equal(line.s + field[1],"delivery")) delivery(); + else if (str_equal(line.s + field[1],"new")) newmsg(); + else if (str_equal(line.s + field[1],"end")) endmsg(); + else if (str_equal(line.s + field[1],"info")) info(); + else if (str_equal(line.s + field[1],"extra")) extra(); + else if (str_equal(line.s + field[1],"running")) clear(); + else if (str_equal(line.s + field[1],"exiting")) clear(); + else if (str_equal(line.s + field[1],"number")) ; + else if (str_equal(line.s + field[1],"local")) ; + else if (str_equal(line.s + field[1],"remote")) ; + else if (str_equal(line.s + field[1],"warning:")) out(outline.s,outline.len); + else if (str_equal(line.s + field[1],"alert:")) out(outline.s,outline.len); + else { + outs("? "); + out(outline.s,outline.len); + } + } + + if (buffer_flush(buffer_1) == -1) die_write(); + + pending(); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/md5c.c b/sqmail-4.3.07/src/md5c.c new file mode 100644 index 0000000..3143159 --- /dev/null +++ b/sqmail-4.3.07/src/md5c.c @@ -0,0 +1,327 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. + */ + +#include "global.h" +#include "md5.h" +#include <stdint.h> + +/* Constants for MD5Transform routine. */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. */ + +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. */ + +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. */ + +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ + +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). + Assumes len is a multiple of 4. */ + +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). + Assumes len is a multiple of 4. */ + +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. */ + +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} diff --git a/sqmail-4.3.07/src/mfrules.c b/sqmail-4.3.07/src/mfrules.c new file mode 100644 index 0000000..9c6240f --- /dev/null +++ b/sqmail-4.3.07/src/mfrules.c @@ -0,0 +1,146 @@ +#include "alloc.h" +#include "stralloc.h" +#include "open.h" +#include "cdbread.h" +#include "case.h" +#include "mfrules.h" +#include "str.h" +#include "byte.h" +#include "close.h" + +/* return -9: problems reading cdb */ +/* return -1: key matches; data not */ +/* return 0: no key */ +/* return 1: key matches without data */ +/* return 2: key and data match */ + +stralloc key = {0}; + +static struct cdb cdb; + +static int mffind(char *mf) +{ + char *x; + char *data; + unsigned int datalen; + int plus = 0; + int dlen; + int len; + int mflen; + int delta; + + switch (cdb_find(&cdb,key.s,key.len)) { + case -1: return -9; + case 0: return 0; + } + + datalen = cdb_datalen(&cdb); + data = alloc(datalen); + if (!data) return -9; + if (!datalen) return 1; + mflen = str_len(mf); + + if (cdb_read(&cdb,data,datalen,cdb_datapos(&cdb)) == -1) { + alloc_free(data); + return -9; + } + + x = data; dlen = datalen - 1; /* trailing separator */ + + while (dlen > 0) { + plus = byte_rchr(data,dlen,'+'); + x = data + plus + 1; + len = dlen - plus; + delta = (mflen > len) ? mflen - len : 0; + if (!byte_diff(x,len,mf + delta)) { alloc_free(data); return 2; } + dlen = plus - 1; + } + + alloc_free(data); + return -1; +} + +int mfsearch(char *ip,char *host,char *info,char *mf) +{ + int r; + + if (info) { + if (!stralloc_copys(&key,info)) return -9; + r = mffind(mf); + if (r < -1 || r > 0) return r; + + if (!stralloc_cats(&key,"@")) return -9; + if (!stralloc_cats(&key,ip)) return -9; + r = mffind(mf); + if (r < -1 || r > 0) return r; + + if (host) { + if (!stralloc_copys(&key,info)) return -9; + if (!stralloc_cats(&key,"@=")) return -9; + if (!stralloc_cats(&key,host)) return -9; + r = mffind(mf); + if (r < -1 || r > 0) return r; + } + } + + if (!stralloc_copys(&key,ip)) return -9; + r = mffind(mf); + if (r < -1 || r > 0) return r; + + if (host) { + if (!stralloc_copys(&key,"=")) return -9; + if (!stralloc_cats(&key,host)) return -9; + r = mffind(mf); + if (r < -1 || r > 0) return r; + } + + if (!stralloc_copys(&key,ip)) return -9; /* IPv6 */ + while (key.len > 0) { + if (ip[key.len - 1] == ':') { + r = mffind(mf); + if (r < -1 || r > 0) return r; + } + --key.len; + } + + if (!stralloc_copys(&key,ip)) return -9; /* IPv4 */ + while (key.len > 0) { + if (ip[key.len - 1] == '.') { + r = mffind(mf); + if (r < -1 || r > 0) return r; + } + --key.len; + } + + if (host) { + while (*host) { + if (*host == '.') { + if (!stralloc_copys(&key,"=")) return -9; + if (!stralloc_cats(&key,host)) return -9; + r = mffind(mf); + if (r < -1 || r > 0) return r; + } + ++host; + } + if (!stralloc_copys(&key,"=")) return -9; + r = mffind(mf); + if (r < -1 || r > 0) return r; + } + + key.len = 0; +/* return mffind(mf); */ + return -1; +} + +int mfrules(int fd,char *ip,char *host,char *info,char *mf) +{ + int r; + + cdb_init(&cdb,fd); + case_lowers(mf); + r = mfsearch(ip,host,info,mf); + cdb_free(&cdb); + close(fd); + + return r; +} diff --git a/sqmail-4.3.07/src/migrate.sh b/sqmail-4.3.07/src/migrate.sh new file mode 100755 index 0000000..c08f7e6 --- /dev/null +++ b/sqmail-4.3.07/src/migrate.sh @@ -0,0 +1,6 @@ +#/bin/sh + +for FILE in *.c +do + cat $FILE | sed -f sedfile > $FILE.new && mv $FILE.new $FILE +done diff --git a/sqmail-4.3.07/src/myctime.c b/sqmail-4.3.07/src/myctime.c new file mode 100644 index 0000000..6f554c9 --- /dev/null +++ b/sqmail-4.3.07/src/myctime.c @@ -0,0 +1,36 @@ +#include "datetime.h" +#include "fmt.h" +#include "myctime.h" + +static char *daytab[7] = { +"Sun","Mon","Tue","Wed","Thu","Fri","Sat" +}; +static char *montab[12] = { +"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +static char result[30]; + +char *myctime(datetime_sec t) +{ + struct datetime dt; + unsigned int len; + datetime_tai(&dt,t); + len = 0; + len += fmt_str(result + len,daytab[dt.wday]); + result[len++] = ' '; + len += fmt_str(result + len,montab[dt.mon]); + result[len++] = ' '; + len += fmt_uint0(result + len,dt.mday,2); + result[len++] = ' '; + len += fmt_uint0(result + len,dt.hour,2); + result[len++] = ':'; + len += fmt_uint0(result + len,dt.min,2); + result[len++] = ':'; + len += fmt_uint0(result + len,dt.sec,2); + result[len++] = ' '; + len += fmt_uint(result + len,1900 + dt.year); + result[len++] = '\n'; + result[len++] = 0; + return result; +} diff --git a/sqmail-4.3.07/src/newaliases.c b/sqmail-4.3.07/src/newaliases.c new file mode 100644 index 0000000..98aa095 --- /dev/null +++ b/sqmail-4.3.07/src/newaliases.c @@ -0,0 +1,326 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "buffer.h" +#include "logmsg.h" +#include "genalloc.h" +#include "stralloc.h" +#include "getln.h" +#include "open.h" +#include "token822.h" +#include "control.h" +#include "auto_qmail.h" +#include "case.h" +#include "cdbmake.h" +#include "byte.h" + +#define WHO "newaliases" + +int rename(const char *,const char *); // stdio.h + +void nomem() +{ + logmsg(WHO,111,FATAL,"out of memory"); +} +void nulbyte() +{ + logmsg(WHO,100,FATAL,"NUL bytes are not permitted"); +} +void longaddress() +{ + logmsg(WHO,100,FATAL,"addresses over 800 bytes are not permitted"); +} +void writeerr() +{ + logmsg(WHO,111,FATAL,"unable to write to /etc/aliases.tmp"); +} +void readerr() +{ + logmsg(WHO,111,FATAL,"unable to read /etc/aliases"); +} +void die_control() +{ + logmsg(WHO,111,FATAL,"unable to read controls"); +} + +stralloc me = {0}; +stralloc defaulthost = {0}; +stralloc defaultdomain = {0}; +stralloc plusdomain = {0}; + +void readcontrols() +{ + int r; + int fddir; + + fddir = open_read("."); + if (fddir == -1) + logmsg(WHO,111,FATAL,"unable to open current directory"); + + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",auto_qmail)); + + r = control_readline(&me,"control/me"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copys(&me,"me")) nomem(); + + r = control_readline(&defaultdomain,"control/defaultdomain"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copy(&defaultdomain,&me)) nomem(); + + r = control_readline(&defaulthost,"control/defaulthost"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copy(&defaulthost,&me)) nomem(); + + r = control_readline(&plusdomain,"control/plusdomain"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copy(&plusdomain,&me)) nomem(); + + if (fchdir(fddir) == -1) + logmsg(WHO,111,FATAL,"unable to set current directory"); +} + +stralloc target = {0}; +stralloc fulltarget = {0}; +stralloc instr = {0}; + +stralloc cbuf = {0}; +token822_alloc toks = {0}; +token822_alloc tokaddr = {0}; +stralloc address = {0}; + +void gotincl() +{ + token822_reverse(&tokaddr); + if (token822_unquote(&address,&tokaddr) != 1) nomem(); + tokaddr.len = 0; + + if (!address.len) + logmsg(WHO,111,FATAL,"empty :include: filenames not permitted"); + if (byte_chr(address.s,address.len,'\0') < address.len) + logmsg(WHO,111,FATAL,"NUL not permitted in :include: filenames"); + + if ((address.s[0] != '.') && (address.s[0] != '/')) + if (!stralloc_cats(&instr,"./")) nomem(); + if (!stralloc_cat(&instr,&address)) nomem(); + if (!stralloc_cats(&instr,".bin")) nomem(); + if (!stralloc_0(&instr)) nomem(); +} + +void gotaddr() +{ + int i; + int j; + int flaghasat; + + token822_reverse(&tokaddr); + if (token822_unquote(&address,&tokaddr) != 1) nomem(); + + if (!address.len) + logmsg(WHO,111,FATAL,"empty recipient addresses not permitted"); + + flaghasat = 0; + for (i = 0; i < tokaddr.len; ++i) + if (tokaddr.t[i].type == TOKEN822_AT) + flaghasat = 1; + + tokaddr.len = 0; + + if (!address.len) return; + + if (!flaghasat) + if (address.s[0] == '/') { + if (!stralloc_0(&address)) nomem(); + logmsg(WHO,111,FATAL,B("file delivery for ",address.s," not supported")); + } + if (!flaghasat) + if (address.s[0] == '|') { + if (byte_chr(address.s,address.len,'\0') < address.len) + logmsg(WHO,111,FATAL,"NUL not permitted in program names"); + if (!stralloc_cats(&instr,"!")) nomem(); + if (!stralloc_catb(&instr,address.s + 1,address.len - 1)) nomem(); + if (!stralloc_0(&instr)) nomem(); + return; + } + + + if (target.len) { + if (!stralloc_cats(&instr,"&")) nomem(); + if (!stralloc_cat(&instr,&fulltarget)) nomem(); + if (!stralloc_0(&instr)) nomem(); + } + + if (!flaghasat) + if (!stralloc_cats(&address,"@")) nomem(); + + if (!stralloc_copy(&target,&address)) nomem(); + if (!stralloc_copy(&fulltarget,&address)) nomem(); + + if (fulltarget.s[fulltarget.len - 1] == '@') + if (!stralloc_cat(&fulltarget,&defaulthost)) nomem(); + if (fulltarget.s[fulltarget.len - 1] == '+') { + fulltarget.s[fulltarget.len - 1] = '.'; + if (!stralloc_cat(&fulltarget,&plusdomain)) nomem(); + } + + j = 0; + for (i = 0;i < fulltarget.len;++i) if (fulltarget.s[i] == '@') j = i; + for (i = j;i < fulltarget.len;++i) if (fulltarget.s[i] == '.') break; + if (i == fulltarget.len) { + if (!stralloc_cats(&fulltarget,".")) nomem(); + if (!stralloc_cat(&fulltarget,&defaultdomain)) nomem(); + } + + if (fulltarget.len > 800) longaddress(); + if (byte_chr(fulltarget.s,fulltarget.len,'\0') < fulltarget.len) + logmsg(WHO,111,FATAL,"NUL not permitted in recipient addresses"); +} + +stralloc line = {0}; +stralloc newline = {0}; +int match; + +void parseerr() +{ + if (!stralloc_0(&line)) nomem(); + logmsg(WHO,111,FATAL,B("unable to parse this line: ",line.s)); +} + +void parseline() +{ + int wordok; + struct token822 *t; + struct token822 *beginning; + + switch (token822_parse(&toks,&line,&cbuf)) { + case -1: nomem(); + case 0: parseerr(); + } + + beginning = toks.t; + t = toks.t + toks.len; + wordok = 1; + + if (!token822_readyplus(&tokaddr,1)) nomem(); + tokaddr.len = 0; + + while (t > beginning) + switch ((--t)->type) { + case TOKEN822_SEMI: + break; /*XXX*/ + case TOKEN822_COLON: + if (t >= beginning + 2) + if (t[-2].type == TOKEN822_COLON) + if (t[-1].type == TOKEN822_ATOM) + if (t[-1].slen == 7) + if (!byte_diff(t[-1].s,7,"include")) { + gotincl(); + t -= 2; + } + break; /*XXX*/ + case TOKEN822_RIGHT: + if (tokaddr.len) gotaddr(); + while ((t > beginning) && (t[-1].type != TOKEN822_LEFT)) + if (!token822_append(&tokaddr,--t)) nomem(); + gotaddr(); + if (t <= beginning) parseerr(); + --t; + while ((t > beginning) && ((t[-1].type == TOKEN822_COMMENT) || (t[-1].type == TOKEN822_ATOM) || (t[-1].type == TOKEN822_QUOTE) || (t[-1].type == TOKEN822_AT) || (t[-1].type == TOKEN822_DOT))) + --t; + wordok = 0; + continue; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: + if (!wordok) if (tokaddr.len) gotaddr(); + wordok = 0; + if (!token822_append(&tokaddr,t)) nomem(); + continue; + case TOKEN822_COMMENT: + /* comment is lexically a space; shouldn't affect wordok */ + break; + case TOKEN822_COMMA: + if (tokaddr.len) gotaddr(); + wordok = 1; + break; + default: + wordok = 1; + if (!token822_append(&tokaddr,t)) nomem(); + continue; + } + if (tokaddr.len) gotaddr(); +} + +char inbuf[1024]; +buffer bi; +struct cdb_make cdb; +stralloc key = {0}; + +void doit() +{ + if (!instr.len) { + if (target.len) parseerr(); + return; + } + + if (!target.len) parseerr(); + + if (stralloc_starts(&target,"owner-")) { + if (!stralloc_copys(&key,"?")) nomem(); + if (!stralloc_catb(&key,target.s + 6,target.len - 6)) nomem(); + case_lowerb(key.s,key.len); + if (cdb_make_add(&cdb,key.s,key.len,fulltarget.s,fulltarget.len) == -1) writeerr(); + } + + if (!stralloc_copys(&key,":")) nomem(); + if (!stralloc_cat(&key,&target)) nomem(); + case_lowerb(key.s,key.len); + if (cdb_make_add(&cdb,key.s,key.len,instr.s,instr.len) == -1) writeerr(); +} + +int main() +{ + int fd; + + umask(033); + readcontrols(); + + fd = open_read("/etc/aliases"); + if (fd == -1) readerr(); + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + fd = open_trunc("/etc/aliases.tmp"); + if (fd == -1) logmsg(WHO,111,FATAL,"unable to create /etc/aliases.tmp"); + if (cdb_make_start(&cdb,fd) == -1) writeerr(); + + if (!stralloc_copys(&line,"")) nomem(); + + for (;;) { + if (getln(&bi,&newline,&match,'\n') != 0) readerr(); + + if (match && (newline.s[0] == '\n')) continue; + + if (match && ((newline.s[0] == ' ') || (newline.s[0] == '\t'))) { + if (!stralloc_cat(&line,&newline)) nomem(); + continue; + } + + if (line.len) + if (line.s[0] != '#') { + if (!stralloc_copys(&target,"")) nomem(); + if (!stralloc_copys(&fulltarget,"")) nomem(); + if (!stralloc_copys(&instr,"")) nomem(); + parseline(); + doit(); + } + + if (!match) break; + if (!stralloc_copy(&line,&newline)) nomem(); + } + + if (cdb_make_finish(&cdb) == -1) writeerr(); + if (fsync(fd) == -1) writeerr(); + if (close(fd) == -1) writeerr(); /* NFS stupidity */ + + if (rename("/etc/aliases.tmp","/etc/aliases.cdb") == -1) + logmsg(WHO,111,FATAL,"unable to move /etc/aliases.tmp to /etc/aliases.cdb"); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/newfield.c b/sqmail-4.3.07/src/newfield.c new file mode 100644 index 0000000..6d69ec6 --- /dev/null +++ b/sqmail-4.3.07/src/newfield.c @@ -0,0 +1,59 @@ +#include <unistd.h> +#include "fmt.h" +#include "datetime.h" +#include "stralloc.h" +#include "date822fmt.h" +#include "newfield.h" + +/* "Date: 26 Sep 1995 04:46:53 -0000\n" */ +stralloc newfield_date = {0}; +/* "Message-ID: <19950926044653.12345.qmail@silverton.berkeley.edu>\n" */ +stralloc newfield_msgid = {0}; + +static unsigned int datefmt(char *s, datetime_sec when) +{ + unsigned int i; + unsigned int len; + struct datetime dt; + datetime_tai(&dt,when); + len = 0; + i = fmt_str(s,"Date: "); len += i; if (s) s += i; + i = date822fmt(s,&dt); len += i; if (s) s += i; + return len; +} + +static unsigned int msgidfmt(char *s, char *idhost, int idhostlen, datetime_sec when) +{ + unsigned int i; + unsigned int len; + struct datetime dt; + datetime_tai(&dt,when); + len = 0; + i = fmt_str(s,"Message-ID: <"); len += i; if (s) s += i; + i = fmt_uint(s,dt.year + 1900); len += i; if (s) s += i; + i = fmt_uint0(s,dt.mon + 1,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.mday,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.hour,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.min,2); len += i; if (s) s += i; + i = fmt_uint0(s,dt.sec,2); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_uint(s,getpid()); len += i; if (s) s += i; + i = fmt_str(s,".qmail@"); len += i; if (s) s += i; + i = fmt_strn(s,idhost,idhostlen); len += i; if (s) s += i; + i = fmt_str(s,">\n"); len += i; if (s) s += i; + return len; +} + +int newfield_datemake(datetime_sec when) +{ + if (!stralloc_ready(&newfield_date,datefmt(FMT_LEN,when))) return 0; + newfield_date.len = datefmt(newfield_date.s,when); + return 1; +} + +int newfield_msgidmake(char *idhost, int idhostlen, datetime_sec when) +{ + if (!stralloc_ready(&newfield_msgid,msgidfmt(FMT_LEN,idhost,idhostlen,when))) return 0; + newfield_msgid.len = msgidfmt(newfield_msgid.s,idhost,idhostlen,when); + return 1; +} diff --git a/sqmail-4.3.07/src/newinclude.c b/sqmail-4.3.07/src/newinclude.c new file mode 100644 index 0000000..746b6a4 --- /dev/null +++ b/sqmail-4.3.07/src/newinclude.c @@ -0,0 +1,317 @@ +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> // rename +#include "buffer.h" +#include "logmsg.h" +#include "genalloc.h" +#include "stralloc.h" +#include "getln.h" +#include "open.h" +#include "token822.h" +#include "control.h" +#include "auto_qmail.h" +#include "byte.h" +#include "env.h" + +#define WHO "newinclude" + +int rename(const char *,const char *); // stdio.h + +void nomem() +{ + logmsg(WHO,111,FATAL,"out of memory"); +} +void usage() +{ + logmsg(WHO,100,USAGE,"newinclude list"); +} + +char *fnlist; +char listbuf[1024]; +buffer bi; + +stralloc bin = {0}; +#define fnbin bin.s +stralloc tmp = {0}; +#define fntmp tmp.s +char tmpbuf[1024]; +buffer bt; + + +void readerr() +{ + logmsg(WHO,111,FATAL,B("unable to read: ",fnlist)); +} +void writeerr() +{ + logmsg(WHO,111,FATAL,B("unable to write to ",fntmp)); +} + +static void out(char *s,int len) +{ + if (buffer_put(&bt,s,len) == -1) writeerr(); +} + +void doincl(buf,len) +char *buf; +int len; +{ + if (!len) + logmsg(WHO,111,FATAL,"empty :include: filenames not permitted"); + if (byte_chr(buf,len,'\n') != len) + logmsg(WHO,111,FATAL,"newlines not permitted in :include: filenames"); + if (byte_chr(buf,len,'\0') != len) + logmsg(WHO,111,FATAL,"NUL not permitted in :include: filenames"); + if ((buf[0] != '.') && (buf[0] != '/')) + out("./",2); + out(buf,len); + out("",1); +} + +void dorecip(buf,len) +char *buf; +int len; +{ + if (!len) + logmsg(WHO,111,FATAL,"empty recipient addresses not permitted"); + if (byte_chr(buf,len,'\n') != len) + logmsg(WHO,111,FATAL,"newlines not permitted in recipient addresses"); + if (byte_chr(buf,len,'\0') != len) + logmsg(WHO,111,FATAL,"NUL not permitted in recipient addresses"); + if (len > 800) + logmsg(WHO,111,FATAL,"addresses must be under 800 bytes"); + if ((buf[len - 1] == ' ') || (buf[len - 1] == '\t')) + logmsg(WHO,111,FATAL,"spaces and tabs not permitted at ends of addresses"); + out("&",1); + out(buf,len); + out("",1); +} + + +void die_control() +{ + logmsg(WHO,111,FATAL,"unable to read controls"); +} + +stralloc me = {0}; +stralloc defaulthost = {0}; +stralloc defaultdomain = {0}; +stralloc plusdomain = {0}; + +void readcontrols() +{ + int r; + int fddir; + char *x; + + fddir = open_read("."); + if (fddir == -1) + logmsg(WHO,111,FATAL,"unable to open current directory"); + + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",auto_qmail)); + + r = control_readline(&me,"control/me"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copys(&me,"me")) nomem(); + + r = control_readline(&defaultdomain,"control/defaultdomain"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copy(&defaultdomain,&me)) nomem(); + x = env_get("QMAILDEFAULTDOMAIN"); + if (x) if (!stralloc_copys(&defaultdomain,x)) nomem(); + + r = control_readline(&defaulthost,"control/defaulthost"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copy(&defaulthost,&me)) nomem(); + x = env_get("QMAILDEFAULTHOST"); + if (x) if (!stralloc_copys(&defaulthost,x)) nomem(); + + r = control_readline(&plusdomain,"control/plusdomain"); + if (r == -1) die_control(); + if (!r) if (!stralloc_copy(&plusdomain,&me)) nomem(); + x = env_get("QMAILPLUSDOMAIN"); + if (x) if (!stralloc_copys(&plusdomain,x)) nomem(); + + if (fchdir(fddir) == -1) + logmsg(WHO,111,FATAL,"unable to set current directory"); +} + +stralloc cbuf = {0}; +token822_alloc toks = {0}; +token822_alloc tokaddr = {0}; +stralloc address = {0}; + +void gotincl() +{ + token822_reverse(&tokaddr); + if (token822_unquote(&address,&tokaddr) != 1) nomem(); + tokaddr.len = 0; + doincl(address.s,address.len); +} + +void gotaddr() +{ + int i; + int j; + int flaghasat; + + token822_reverse(&tokaddr); + if (token822_unquote(&address,&tokaddr) != 1) nomem(); + + flaghasat = 0; + for (i = 0;i < tokaddr.len;++i) + if (tokaddr.t[i].type == TOKEN822_AT) + flaghasat = 1; + + tokaddr.len = 0; + + if (!address.len) return; + + if (!flaghasat) + if (address.s[0] == '/') { + if (!stralloc_0(&address)) nomem(); + logmsg(WHO,111,FATAL,B("file delivery for ",address.s," not supported")); + } + if (!flaghasat) + if (address.s[0] == '|') { + if (!stralloc_0(&address)) nomem(); + logmsg(WHO,111,FATAL,B("program delivery for ",address.s," not supported")); + } + + if (!flaghasat) { + if (!stralloc_cats(&address,"@")) nomem(); + if (!stralloc_cat(&address,&defaulthost)) nomem(); + } + if (address.s[address.len - 1] == '+') { + address.s[address.len - 1] = '.'; + if (!stralloc_cat(&address,&plusdomain)) nomem(); + } + j = 0; + for (i = 0;i < address.len;++i) if (address.s[i] == '@') j = i; + for (i = j;i < address.len;++i) if (address.s[i] == '.') break; + if (i == address.len) { + if (!stralloc_cats(&address,".")) nomem(); + if (!stralloc_cat(&address,&defaultdomain)) nomem(); + } + + dorecip(address.s,address.len); +} + + +stralloc line = {0}; +int match; + +void parseerr() +{ + if (!stralloc_0(&line)) nomem(); + logmsg(WHO,111,FATAL,B("unable to parse this line: ",line.s)); +} + +void parseline() +{ + int wordok; + struct token822 *t; + struct token822 *beginning; + + switch (token822_parse(&toks,&line,&cbuf)) { + case -1: nomem(); + case 0: parseerr(); + } + + beginning = toks.t; + t = toks.t + toks.len; + wordok = 1; + + if (!token822_readyplus(&tokaddr,1)) nomem(); + tokaddr.len = 0; + + while (t > beginning) + switch ((--t)->type) { + case TOKEN822_SEMI: + break; /*XXX*/ + case TOKEN822_COLON: + if (t >= beginning + 2) + if (t[-2].type == TOKEN822_COLON) + if (t[-1].type == TOKEN822_ATOM) + if (t[-1].slen == 7) + if (!byte_diff(t[-1].s,7,"include")) { + gotincl(); + t -= 2; + } + break; /*XXX*/ + case TOKEN822_RIGHT: + if (tokaddr.len) gotaddr(); + while ((t > beginning) && (t[-1].type != TOKEN822_LEFT)) + if (!token822_append(&tokaddr,--t)) nomem(); + gotaddr(); + if (t <= beginning) parseerr(); + --t; + while ((t > beginning) && ((t[-1].type == TOKEN822_COMMENT) || (t[-1].type == TOKEN822_ATOM) || (t[-1].type == TOKEN822_QUOTE) || (t[-1].type == TOKEN822_AT) || (t[-1].type == TOKEN822_DOT))) + --t; + wordok = 0; + continue; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: + if (!wordok) if (tokaddr.len) gotaddr(); + wordok = 0; + if (!token822_append(&tokaddr,t)) nomem(); + continue; + case TOKEN822_COMMENT: + /* comment is lexically a space; shouldn't affect wordok */ + break; + case TOKEN822_COMMA: + if (tokaddr.len) gotaddr(); + wordok = 1; + break; + default: + wordok = 1; + if (!token822_append(&tokaddr,t)) nomem(); + continue; + } + if (tokaddr.len) gotaddr(); +} + + +int main(argc,argv) +int argc; +char **argv; +{ + int fd; + + umask(033); + readcontrols(); + + fnlist = argv[1]; if (!fnlist) usage(); + + if (!stralloc_copys(&bin,fnlist)) nomem(); + if (!stralloc_cats(&bin,".bin")) nomem(); + if (!stralloc_0(&bin)) nomem(); + + if (!stralloc_copys(&tmp,fnlist)) nomem(); + if (!stralloc_cats(&tmp,".tmp")) nomem(); + if (!stralloc_0(&tmp)) nomem(); + + fd = open_read(fnlist); + if (fd == -1) readerr(); + buffer_init(&bi,read,fd,listbuf,sizeof(listbuf)); + + fd = open_trunc(fntmp); + if (fd == -1) writeerr(); + buffer_init(&bt,write,fd,tmpbuf,sizeof(tmpbuf)); + + for (;;) { + if (getln(&bi,&line,&match,'\n') == -1) readerr(); + if (!line.len) break; + if (line.s[0] != '#') parseline(); + if (!match) break; + } + + if (buffer_flush(&bt) == -1) writeerr(); + if (fsync(fd) == -1) writeerr(); + if (close(fd) == -1) writeerr(); /* NFS stupidity */ + + if (rename(fntmp,fnbin) == -1) + logmsg(WHO,111,FATAL,B("unable to move ",fntmp," to: ",fnbin)); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/now.c b/sqmail-4.3.07/src/now.c new file mode 100644 index 0000000..5ce4d90 --- /dev/null +++ b/sqmail-4.3.07/src/now.c @@ -0,0 +1,8 @@ +#include <time.h> +#include "datetime.h" +#include "now.h" + +datetime_sec now() +{ + return time((long *) 0); +} diff --git a/sqmail-4.3.07/src/predate.c b/sqmail-4.3.07/src/predate.c new file mode 100644 index 0000000..f6007b3 --- /dev/null +++ b/sqmail-4.3.07/src/predate.c @@ -0,0 +1,113 @@ +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include "datetime.h" +#include "wait.h" +#include "fd.h" +#include "fmt.h" +#include "logmsg.h" +#include "buffer.h" +#include "exit.h" +#include "sig.h" + +#define WHO "predate" + +static char *montab[12] = { +"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +char num[FMT_ULONG]; +char outbuf[1024]; + +int main(int argc, char **argv) +{ + time_t now; + struct tm *tm; + struct datetime dt; + datetime_sec utc; + datetime_sec local; + int minutes; + int pi[2]; + buffer bo; + int wstat; + int pid; + + sig_pipeignore(); + + if (!argv[1]) + logmsg(WHO,100,USAGE,"predate child"); + + if (pipe(pi) == -1) + logmsg(WHO,111,FATAL,"unable to create pipe"); + + switch (pid = fork()) { + case -1: + logmsg(WHO,111,FATAL,"unable to fork"); + case 0: + close(pi[1]); + if (fd_move(0,pi[0]) == -1) + logmsg(WHO,111,FATAL,"unable to set up fds"); + sig_pipedefault(); + execvp(argv[1],argv + 1); + logmsg(WHO,111,FATAL,B("unable to run: ",argv[1])); + } + close(pi[0]); + buffer_init(&bo,write,pi[1],outbuf,sizeof(outbuf)); + + time(&now); + + tm = gmtime(&now); + dt.year = tm->tm_year; + dt.mon = tm->tm_mon; + dt.mday = tm->tm_mday; + dt.hour = tm->tm_hour; + dt.min = tm->tm_min; + dt.sec = tm->tm_sec; + utc = datetime_untai(&dt); /* utc == now, if gmtime ignores leap seconds */ + + tm = localtime(&now); + dt.year = tm->tm_year; + dt.mon = tm->tm_mon; + dt.mday = tm->tm_mday; + dt.hour = tm->tm_hour; + dt.min = tm->tm_min; + dt.sec = tm->tm_sec; + local = datetime_untai(&dt); + + buffer_puts(&bo,"Date: "); + buffer_put(&bo,num,fmt_uint(num,dt.mday)); + buffer_puts(&bo," "); + buffer_puts(&bo,montab[dt.mon]); + buffer_puts(&bo," "); + buffer_put(&bo,num,fmt_uint(num,dt.year + 1900)); + buffer_puts(&bo," "); + buffer_put(&bo,num,fmt_uint0(num,dt.hour,2)); + buffer_puts(&bo,":"); + buffer_put(&bo,num,fmt_uint0(num,dt.min,2)); + buffer_puts(&bo,":"); + buffer_put(&bo,num,fmt_uint0(num,dt.sec,2)); + + if (local < utc) { + minutes = (utc - local + 30) / 60; + buffer_puts(&bo," -"); + buffer_put(&bo,num,fmt_uint0(num,minutes / 60,2)); + buffer_put(&bo,num,fmt_uint0(num,minutes % 60,2)); + } + else { + minutes = (local - utc + 30) / 60; + buffer_puts(&bo," +"); + buffer_put(&bo,num,fmt_uint0(num,minutes / 60,2)); + buffer_put(&bo,num,fmt_uint0(num,minutes % 60,2)); + } + + buffer_puts(&bo,"\n"); + buffer_copy(&bo,buffer_0); + buffer_flush(&bo); + close(pi[1]); + + if (wait_pid(&wstat,pid) == -1) + logmsg(WHO,111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + logmsg(WHO,111,FATAL,"child crashed"); + _exit(wait_exitcode(wstat)); +} diff --git a/sqmail-4.3.07/src/preline.c b/sqmail-4.3.07/src/preline.c new file mode 100644 index 0000000..c2af3bf --- /dev/null +++ b/sqmail-4.3.07/src/preline.c @@ -0,0 +1,87 @@ +#include <unistd.h> +#include "fd.h" +#include "buffer.h" +#include "exit.h" +#include "wait.h" +#include "env.h" +#include "sig.h" +#include "getoptb.h" +#include "logmsg.h" +#include "qmail.h" + +#define WHO "preline" + +void die_usage() +{ + logmsg(WHO,100,USAGE,"preline cmd [ arg ... ]"); +} + +int flagufline = 1; char *ufline; +int flagrpline = 1; char *rpline; +int flagdtline = 1; char *dtline; + +char outbuf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(write,1,outbuf,sizeof(outbuf)); +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); + +int main(int argc, char **argv) +{ + int opt; + int pi[2]; + int pid; + int wstat; + + sig_pipeignore(); + + if (!(ufline = env_get("UFLINE"))) die_usage(); + if (!(rpline = env_get("RPLINE"))) die_usage(); + if (!(dtline = env_get("DTLINE"))) die_usage(); + + while ((opt = getopt(argc,argv,"frdFRD")) != opteof) + switch (opt) { + case 'f': flagufline = 0; break; + case 'r': flagrpline = 0; break; + case 'd': flagdtline = 0; break; + case 'F': flagufline = 1; break; + case 'R': flagrpline = 1; break; + case 'D': flagdtline = 1; break; + default: die_usage(); + } + argc -= optind; + argv += optind; + if (!*argv) die_usage(); + + if (pipe(pi) == -1) + logmsg(WHO,111,FATAL,"unable to create pipe"); + + pid = fork(); + if (pid == -1) + logmsg(WHO,111,FATAL,"unable to fork"); + + if (pid == 0) { + close(pi[1]); + if (fd_move(0,pi[0]) == -1) + logmsg(WHO,111,FATAL,"unable to set up fds"); + sig_pipedefault(); + execvp(*argv,argv); + logmsg(WHO,errno,FATAL,B("unable to run: ",*argv)); + } + close(pi[0]); + if (fd_move(1,pi[1]) == -1) + logmsg(WHO,111,FATAL,"unable to set up fds"); + + if (flagufline) buffer_puts(&bo,ufline); + if (flagrpline) buffer_puts(&bo,rpline); + if (flagdtline) buffer_puts(&bo,dtline); + if (buffer_copy(&bo,&bi) != 0) + logmsg(WHO,111,FATAL,"unable to copy input"); + buffer_flush(&bo); + close(1); + + if (wait_pid(&wstat,pid) == -1) + logmsg(WHO,111,FATAL,"wait failed"); + if (wait_crashed(wstat)) + logmsg(WHO,111,FATAL,"child crashed"); + _exit(wait_exitcode(wstat)); +} diff --git a/sqmail-4.3.07/src/printforward.c b/sqmail-4.3.07/src/printforward.c new file mode 100644 index 0000000..0ab7ad9 --- /dev/null +++ b/sqmail-4.3.07/src/printforward.c @@ -0,0 +1,142 @@ +#include <unistd.h> +#include "buffer.h" +#include "logmsg.h" +#include "stralloc.h" +#include "cdbread.h" + +#define WHO "printmaillist" + +void badformat() +{ + logmsg(WHO,100,FATAL,"bad database format"); +} +void nomem() +{ + logmsg(WHO,111,FATAL,"out of memory"); +} + +void getch(char *ch) +{ + int r; + r = buffer_get(buffer_0small,ch,1); + if (r == -1) + logmsg(WHO,111,FATAL,"unable to read input"); + if (r == 0) + badformat(); +} + +void out(char *ch) +{ + if (buffer_put(buffer_1small,ch,1) == -1) + logmsg(WHO,111,FATAL,"unable to write output"); +} + +void printbuf(char *buf) +{ + while (*buf) + out(buf++); +} + +void printsafe(char *buf,int len) +{ + char ch; + + while (len) { + ch = *buf; + if ((ch <= 32) || (ch == ',') || (ch == ':') || (ch == ';') || (ch == '\\') || (ch == '#')) + out("\\"); + out(&ch); + ++buf; + --len; + } +} + +stralloc key = {0}; +stralloc data = {0}; + +int main() +{ + uint32 eod; + uint32 pos; + uint32 klen; + uint32 dlen; + char buf[8]; + char ch; + int i; + int j; + + for (i = 0; i < 4; ++i) + getch(buf + i); + eod = cdb_unpack(buf); + + for (i = 4; i < 2048; ++i) + getch(&ch); + + pos = 2048; + while (pos < eod) { + if (eod - pos < 8) badformat(); + pos += 8; + for (i = 0; i < 8; ++i) getch(buf + i); + klen = cdb_unpack(buf); + dlen = cdb_unpack(buf + 4); + + if (!stralloc_copys(&key,"")) nomem(); + if (eod - pos < klen) badformat(); + pos += klen; + while (klen) { + --klen; + getch(&ch); + if (!stralloc_append(&key,&ch)) nomem(); + } + + if (eod - pos < dlen) badformat(); + pos += dlen; + if (!stralloc_copys(&data,"")) nomem(); + while (dlen) { + --dlen; + getch(&ch); + if (!stralloc_append(&data,&ch)) nomem(); + } + + if (!key.len) badformat(); + if (key.s[0] == '?') { + printsafe(key.s + 1,key.len - 1); + printbuf(": ?"); + printsafe(data.s,data.len); + printbuf(";\n"); + } + else if (key.s[0] == ':') { + printsafe(key.s + 1,key.len - 1); + printbuf(":\n"); + + i = 0; + for (j = 0; j < data.len; ++j) + if (!data.s[j]) { + if ((data.s[i] == '.') || (data.s[i] == '/')) { + printbuf(", "); + printsafe(data.s + i,j - i); + printbuf("\n"); + } + else if ((data.s[i] == '|') || (data.s[i] == '!')) { + printbuf(", "); + printsafe(data.s + i,j - i); + printbuf("\n"); + } + else if ((data.s[i] == '&') && (j - i < 900)) { + printbuf(", "); + printsafe(data.s + i,j - i); + printbuf("\n"); + } + else badformat(); + i = j + 1; + } + if (i != j) badformat(); + printbuf(";\n"); + } + else badformat(); + } + + if (buffer_flush(buffer_1small) == -1) + logmsg(WHO,111,FATAL,"unable to write output"); + _exit(0); +} diff --git a/sqmail-4.3.07/src/printmaillist.c b/sqmail-4.3.07/src/printmaillist.c new file mode 100644 index 0000000..6edb3b3 --- /dev/null +++ b/sqmail-4.3.07/src/printmaillist.c @@ -0,0 +1,53 @@ +#include <unistd.h> +#include "buffer.h" +#include "logmsg.h" +#include "stralloc.h" +#include "getln.h" +#include "str.h" + +#define WHO "printmaillist" + +void badformat() +{ + logmsg(WHO,100,FATAL,"bad mailing list format"); +} + +stralloc line = {0}; +int match; + +int main() +{ + for (;;) { + if (getln(buffer_1small,&line,&match,'\0') == -1) + logmsg(WHO,111,FATAL,"unable to read input: "); + if (!match) { + if (line.len) + badformat(); + if (buffer_flush(buffer_1small) == -1) + logmsg(WHO,111,FATAL,"unable to write output: "); + _exit(0); + } + + if (line.s[str_chr(line.s,'\n')]) badformat(); + if (line.s[line.len - 1] == ' ') badformat(); + if (line.s[line.len - 1] == '\t') badformat(); + + if ((line.s[0] == '.') || (line.s[0] == '/')) { + if (buffer_puts(buffer_1small,line.s) == -1) + logmsg(WHO,111,FATAL,"unable to write output: "); + if (buffer_puts(buffer_1small,"\n") == -1) + logmsg(WHO,111,FATAL,"unable to write output: "); + continue; + } + if (line.s[0] == '&') { + if (line.len > 900) badformat(); + if (buffer_puts(buffer_1small,line.s) == -1) + logmsg(WHO,111,FATAL,"unable to write output: "); + if (buffer_puts(buffer_1small,"\n") == -1) + logmsg(WHO,111,FATAL,"unable to write output: "); + continue; + } + + badformat(); + } +} diff --git a/sqmail-4.3.07/src/prioq.c b/sqmail-4.3.07/src/prioq.c new file mode 100644 index 0000000..9559d31 --- /dev/null +++ b/sqmail-4.3.07/src/prioq.c @@ -0,0 +1,54 @@ +#include "alloc.h" +#include "genalloc.h" +#include "prioq.h" + +GEN_ALLOC_readyplus(prioq,struct prioq_elt,p,len,a,i,n,x,100,prioq_readyplus) + +int prioq_insert(prioq *pq, struct prioq_elt *pe) +{ + int i; + int j; + + if (!prioq_readyplus(pq,1)) return 0; + j = pq->len++; + while (j) { + i = (j - 1)/2; + if (pq->p[i].dt <= pe->dt) break; + pq->p[j] = pq->p[i]; + j = i; + } + pq->p[j] = *pe; + return 1; +} + +int prioq_min(prioq *pq, struct prioq_elt *pe) +{ + if (!pq->p) return 0; + if (!pq->len) return 0; + *pe = pq->p[0]; + return 1; +} + +void prioq_delmin(prioq *pq) +{ + int i; + int j; + int n; + + if (!pq->p) return; + n = pq->len; + if (!n) return; + i = 0; + --n; + + for (;;) { + j = i + i + 2; + if (j > n) break; + if (pq->p[j - 1].dt <= pq->p[j].dt) --j; + if (pq->p[n].dt <= pq->p[j].dt) break; + pq->p[i] = pq->p[j]; + i = j; + } + pq->p[i] = pq->p[n]; + pq->len = n; +} diff --git a/sqmail-4.3.07/src/prot.c b/sqmail-4.3.07/src/prot.c new file mode 100644 index 0000000..5bcddd0 --- /dev/null +++ b/sqmail-4.3.07/src/prot.c @@ -0,0 +1,21 @@ +#include "hasshsgr.h" +#include "prot.h" + +/* XXX: there are more portability problems here waiting to leap out at me */ + +int prot_gid(int gid) +{ +#ifdef HASSHORTSETGROUPS + short x[2]; + x[0] = gid; x[1] = 73; /* catch errors */ + if (setgroups(1,x) == -1) return -1; +#else + if (setgroups(1,&gid) == -1) return -1; +#endif + return setgid(gid); /* _should_ be redundant, but on some systems it isn't */ +} + +int prot_uid(int uid) +{ + return setuid(uid); +} diff --git a/sqmail-4.3.07/src/qbiff.c b/sqmail-4.3.07/src/qbiff.c new file mode 100644 index 0000000..b9b55bf --- /dev/null +++ b/sqmail-4.3.07/src/qbiff.c @@ -0,0 +1,141 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "hasutmp.h" +#ifdef HASUTMP +#include <utmp.h> +#ifndef UTMP_FILE +#ifdef _PATH_UTMP +#define UTMP_FILE _PATH_UTMP +#else +#define UTMP_FILE "/etc/utmp" +#endif +#endif +#else +#include <utmpx.h> +#endif +#include "stralloc.h" +#include "buffer.h" +#include "open.h" +#include "byte.h" +#include "str.h" +#include "headerbody.h" +#include "hfield.h" +#include "env.h" +#include "exit.h" + +buffer b; +#ifdef HASUTMP +char bufutmp[sizeof(struct utmp) * 16]; +int fdutmp; +#endif +char buftty[1024]; +int fdtty; + +#ifdef HASUTMP +struct utmp ut; +char line[sizeof(ut.ut_line) + 1]; +#else +struct utmpx *ut; +char line[sizeof(ut->ut_line) + 1]; +#endif + +stralloc woof = {0}; +stralloc tofrom = {0}; +stralloc text = {0}; + +void doit(char *s, int n) +{ + if (!stralloc_catb(&text,s,n)) _exit(0); + if (text.len > 78) text.len = 78; +} + +void dobody(stralloc *h) { doit(h->s,h->len); } + +void doheader(stralloc *h) +{ + int i; + + if (hfield_known(h->s,h->len) == H_SUBJECT) { + i = hfield_skipname(h->s,h->len); + doit(h->s + i,h->len - i); + } +} + +void finishheader() { ; } + +int main() +{ + char *user; + char *sender; + char *userext; + struct stat st; + int i; + + if (chdir("/dev") == -1) _exit(0); + + if (!(user = env_get("USER"))) _exit(0); + if (!(sender = env_get("SENDER"))) _exit(0); + if (!(userext = env_get("LOCAL"))) _exit(0); +#ifdef HASUTMP + if (str_len(user) > sizeof(ut.ut_name)) _exit(0); +#else + if (str_len(user) > sizeof(ut->ut_user)) _exit(0); +#endif + + if (!stralloc_copys(&tofrom,"*** TO <")) _exit(0); + if (!stralloc_cats(&tofrom,userext)) _exit(0); + if (!stralloc_cats(&tofrom,"> FROM <")) _exit(0); + if (!stralloc_cats(&tofrom,sender)) _exit(0); + if (!stralloc_cats(&tofrom,">")) _exit(0); + + for (i = 0; i < tofrom.len; ++i) + if ((tofrom.s[i] < 32) || (tofrom.s[i] > 126)) + tofrom.s[i] = '_'; + + if (!stralloc_copys(&text," ")) _exit(0); + if (headerbody(buffer_0,doheader,finishheader,dobody) == -1) _exit(0); + + for (i = 0; i < text.len; ++i) + if ((text.s[i] < 32) || (text.s[i] > 126)) + text.s[i] = '/'; + + if (!stralloc_copys(&woof,"\015\n\007")) _exit(0); + if (!stralloc_cat(&woof,&tofrom)) _exit(0); + if (!stralloc_cats(&woof,"\015\n")) _exit(0); + if (!stralloc_cat(&woof,&text)) _exit(0); + if (!stralloc_cats(&woof,"\015\n")) _exit(0); + +#ifdef HASUTMP + fdutmp = open_read(UTMP_FILE); + if (fdutmp == -1) _exit(0); + buffer_init(&b,read,fdutmp,bufutmp,sizeof(bufutmp)); + + while (buffer_get(&b,&ut,sizeof(ut)) == sizeof(ut)) + if (!str_diffn(ut.ut_name,user,sizeof(ut.ut_name))) { +#else + while ((ut = getutxent()) != 0) + if (ut->ut_type == USER_PROCESS && !str_diffn(ut->ut_user,user,sizeof(ut->ut_user))) { +#endif +#ifdef HASUTMP + byte_copy(line,sizeof(ut.ut_line),ut.ut_line); + line[sizeof(ut.ut_line)] = 0; +#else + byte_copy(line,sizeof(ut->ut_line),ut->ut_line); + line[sizeof(ut->ut_line)] = 0; +#endif + if (line[0] == '/') continue; + if (!line[0]) continue; + if (line[str_chr(line,'.')]) continue; + + fdtty = open_append(line); + if (fdtty == -1) continue; + if (fstat(fdtty,&st) == -1) { close(fdtty); continue; } + if (!(st.st_mode & 0100)) { close(fdtty); continue; } + if (st.st_uid != getuid()) { close(fdtty); continue; } + buffer_init(&b,write,fdtty,buftty,sizeof(buftty)); + buffer_putflush(&b,woof.s,woof.len); + close(fdtty); + } + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-authuser.c b/sqmail-4.3.07/src/qmail-authuser.c new file mode 100755 index 0000000..ff0891b --- /dev/null +++ b/sqmail-4.3.07/src/qmail-authuser.c @@ -0,0 +1,446 @@ +#include <stdio.h> +#include <unistd.h> +#include "global.h" +#include "stralloc.h" +#include "buffer.h" +#include "auto_qmail.h" +#include "case.h" +#include "control.h" +#include "constmap.h" +#include "str.h" +#include "fmt.h" +#include "fd.h" +#include "open.h" +#include "byte.h" +#include "scan.h" +#include "md5.h" +#include "hmac_md5.h" +#include "sha1.h" +#include "sha256.h" +#include "pathexec.h" +#include "prot.h" +#include "wait.h" +#include "sig.h" +#include "error.h" +#include "env.h" +#include "qmail.h" +#define FDAUTH 3 +#define FDGOSSIP 1 +#define SOCKET_CALL "-s" +#define DOVECOT_SERVICE "-x" + +extern char *crypt(); +#include <pwd.h> +static struct passwd *pw; + +#include "hasspnam.h" +#ifdef HASGETSPNAM +#include <shadow.h> +static struct spwd *spw; +#endif + +#include "hasuserpw.h" +#ifdef HASUSERPW +#include <userpw.h> +static struct userpw *upw; +#endif + +/** + @file qmail-authuser.c + @brief user authentication for qmail-smtpd/qmail-pop3d,bincimapd + @return 0: ok + 1: credentials failure + 2: qmail-authuser is misused + 110: can't read controls + 111: temporary problem checking the password +*/ + +char authbuf[BUFSIZE_AUTH]; +buffer ba = BUFFER_INIT(write,FDAUTH,authbuf,sizeof(authbuf)); + +struct constmap mapauthuser; +stralloc authfile = {0}; +stralloc disabled = {0}; +stralloc user = {0}; // user w/o domain appended +stralloc homedir = {0}; +stralloc shell = {0}; + +/** + @brief Supported storage methods: + (1) authuser:[=]plainpasswd, + (2) authuser:%hashpasswd, + (3) authuser:?, authuser:!, *:?, *:! (! -> +environment) + (4) x:+ -> checkvpw; x = { user@domain, @domain, @ } vmailmgr + (5) x:& -> vchkpw; x = { user@domain, @domain, @ } vpopmail + (6) x:= -> qmail-client; x = { user@domain, @domain, @ } dovecot + Supported auth methods: + user/login/plain: (1,2,3,4,5,6), + cram-md5/apop: (1,5) +**/ + +void exit(int fail) +{ + int i; + + for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0; + _exit(fail); +} + +int dig_ascii(char *digascii,const char *digest,const int len) +{ + static const char hextab[] = "0123456789abcdef"; + int j; + + for (j = 0; j < len; j++) { + digascii[2 * j] = hextab[digest[j] >> 4]; + digascii[2 * j + 1] = hextab[digest[j] & 0x0f]; + } + digascii[2 * len] = '\0'; + + return (2*j); // 2*len +} + +int auth_sha1(char *pwdhash,char *response) +{ + unsigned char digest[20]; + unsigned char digascii[41]; + + sha1_hash(digest,response,str_len(response)); + dig_ascii(digascii,digest,20); + + return str_diffn(digascii,pwdhash,40); +} + +int auth_sha256(char *pwdhash,char *response) +{ + unsigned char digest[32]; + unsigned char digascii[65]; + + sha256_hash(digest,response,str_len(response)); + dig_ascii(digascii,digest,32); + + return str_diffn(digascii,pwdhash,64); +} + +int auth_md5(char *pwdhash,char *response) +{ + MD5_CTX ctx; + unsigned char digest[16]; + unsigned char digascii[33]; + + MD5Init(&ctx); + MD5Update(&ctx,response,str_len(response)); + MD5Final(digest,&ctx); + dig_ascii(digascii,digest,16); + + return str_diffn(digascii,pwdhash,32); +} + +int auth_hash(char *password,char *response) +{ + switch (str_len(password)) { + case 32: return auth_md5(password,response); + case 40: return auth_sha1(password,response); + case 64: return auth_sha256(password,response); + default: return -1; + } +} + +int auth_unix(char *user,char* response) +{ + char *encrypted = 0; + char *stored = 0; + int r = -1; + + pw = getpwnam(user); + if (pw) { + stored = pw->pw_passwd; + if (!stralloc_copys(&homedir,pw->pw_dir)) exit(111); + if (!stralloc_copys(&shell,pw->pw_shell)) exit(111); + } else { + if (errno == ETXTBSY) exit(111); + exit(1); + } + + if (response) { +#ifdef HASUSERPW + upw = getuserpw(user); + if (upw) + stored = upw->upw_passwd; + else + if (errno == ETXTBSY) exit(111); +#elif HASGETSPNAM + spw = getspnam(user); + if (spw) + stored = spw->sp_pwdp; + else + if (errno == ETXTBSY) exit(111); +#endif + if (!stored || !*stored) exit(111); + encrypted = crypt(response,stored); + if (!encrypted) exit(111); // no password given (tx. M.B.) + r = str_diff(encrypted,stored); + } + + if (r == 0 || !response) { + if (prot_gid((int) pw->pw_gid) == -1) exit(1); + if (prot_uid((int) pw->pw_uid) == -1) exit(1); + if (chdir(pw->pw_dir) == -1) exit(111); + } + + return r; +} + +int auth_apop(unsigned char *password,unsigned char *response,unsigned char *challenge) +{ + MD5_CTX context; + unsigned char digest[16]; + unsigned char digascii[33]; + + MD5Init(&context); + MD5Update(&context,challenge,str_len(challenge)); + MD5Update(&context,password,str_len(password)); + MD5Final(digest,&context); + dig_ascii(digascii,digest,16); + + return (str_diff(digascii,response)); +} + +int auth_cram(unsigned char *password,unsigned char *response,unsigned char *challenge) +{ + unsigned char digest[16]; + unsigned char digascii[33]; + + hmac_md5(challenge,str_len(challenge),password,str_len(password),digest); + dig_ascii(digascii,digest,16); + + return (str_diff(digascii,response) && str_diff(password,response)); // cram or plain +} + +int auth_dovecot(char *user,char *response,char *socket,char *service) +{ + int wstat; + int child; + char *wrapper[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i = 0; + + close(FDGOSSIP); /* gossiping doveadm */ + + switch (child = fork()) { + case -1: + exit(111); + case 0: + wrapper[i] = "doveadm"; + wrapper[++i] = "auth"; + wrapper[++i] = "test"; + if (socket) { + wrapper[++i] = "-a"; + wrapper[++i] = socket; + } + if (service) { + wrapper[++i] = "-x"; + wrapper[++i] = service; + } + wrapper[++i] = user; + wrapper[++i] = response; + wrapper[++i] = 0; + + execvp(wrapper[0],wrapper); + exit(111); + } + + if (wait_pid(&wstat,child) == -1) exit(111); + if (wait_crashed(wstat)) exit(111); + return wait_exitcode(wstat); +} + +int auth_wrapper(char *pam,char *arg1,char *arg2,char *auth,int len) +{ + int wstat; + int child; + int pi[2]; + char *wrapper[4] = {0, 0, 0, 0}; + + if (pipe(pi) == -1) exit(111); + if (pi[0] != FDAUTH) exit(111); + + switch (child = fork()) { + case -1: + exit(111); + case 0: + close(pi[1]); + if (fd_copy(FDAUTH,pi[0]) == -1) exit(111); + wrapper[0] = pam; + wrapper[1] = arg1; + wrapper[2] = arg2; + wrapper[3] = 0; + sig_pipedefault(); + + execvp(wrapper[0],wrapper); + exit(111); + } + close(pi[0]); + + buffer_init(&ba,write,pi[1],authbuf,sizeof(authbuf)); + if (buffer_put(&ba,auth,len) == -1) exit(111); + if (buffer_flush(&ba) == -1) exit(111); + close(pi[1]); + + if (wait_pid(&wstat,child) == -1) exit(111); + if (wait_crashed(wstat)) exit(111); + return wait_exitcode(wstat); +} + +int main(int argc,char **argv) +{ + char *authuser; + char *authpass; + char *response = 0; + char *challenge = 0; + char *domain = 0; + char *authsocket = 0; + char *service = 0; + char *program = 0; + char *maildirname = 0; + int rc = -1; /* initialise: -1; ok: 0; !ok: > 0 */ + int authlen = 0; + int buflen = 0; + int domlen = 0; + int i = 0; + int r; + + if (!argv[1]) exit(2); + + if (!case_diffs(argv[1],SOCKET_CALL)) { // dovecot socket + if (!argv[3]) exit(2); + authsocket = argv[2]; + if (!case_diffs(argv[3],DOVECOT_SERVICE)) { // ++ dovecot service + service = argv[4]; + if (!argv[5]) exit(2); + } + } else if (!case_diffs(argv[1],DOVECOT_SERVICE)) { // dovecot service + if (!argv[3]) exit(2); + service = argv[2]; + if (!case_diffs(argv[3],SOCKET_CALL)) { // ++ dovecot socket + if (!argv[5]) exit(2); + authsocket = argv[4]; + } + } else if (argv[2]) { // pop or imap user with mailbox + if (case_starts(argv[2],"mail") || case_starts(argv[2],"mbox")) { + program = argv[1]; + maildirname = argv[2]; + } + } + env_unset("USER"); + + /* Read input on FDAUTH */ + + for (;;) { + do + r = read(FDAUTH,authbuf + buflen,sizeof(authbuf) - buflen); + while ((r == -1) && (errno == EINTR)); + if (r == -1) exit(111); + if (r == 0) break; + buflen += r; + if (buflen >= sizeof(authbuf)) exit(2); + } + close(FDAUTH); + + authuser = authbuf + i; /* username */ + if (i == buflen) exit(2); + while (authbuf[i++]) /* response */ + if (i == buflen) exit(2); + response = authbuf + i; + if (i == buflen) exit(2); + while (authbuf[i++]) /* challenge */ + if (i == buflen) exit(2); + challenge = authbuf + i; + + authlen = str_len(authuser); + if (!stralloc_copyb(&user,authuser,authlen)) exit(111); + + if ((i = byte_rchr(authuser,authlen,'@'))) /* @domain */ + if (i < authlen && authuser[i] == '@') { + domain = authuser + i; + domlen = str_len(domain); + case_lowerb(domain,domlen); + user.len = 0; + if (!stralloc_copyb(&user,authuser,i)) exit(111); + } + if (!stralloc_0(&user)) exit(111); + if (!env_put("USER",authuser)) exit(111); + + /* Read control file users/authuser and go for checks */ + + if (chdir(auto_qmail) == -1) exit(110); + + switch (control_readfile(&authfile,"users/authuser",0)) { + case -1: exit(110); + case 0: if (!constmap_init(&mapauthuser,"",0,1)) exit(111); + case 1: if (!constmap_init(&mapauthuser,authfile.s,authfile.len,1)) exit(111); + } + + /* Check for disabled authuser/domains */ + + if (!stralloc_copys(&disabled,"!")) exit(111); + if (!stralloc_catb(&disabled,authuser,authlen)) exit(111); + if (constmap(&mapauthuser,disabled.s,disabled.len)) exit(1); + + if (domlen) { + disabled.len = 0; + if (!stralloc_copys(&disabled,"!")) exit(111); + if (!stralloc_catb(&disabled,domain,domlen)) exit(111); + if (constmap(&mapauthuser,disabled.s,disabled.len)) exit(1); + } + + /* Virtual and system user accounts */ + + authpass = constmap(&mapauthuser,authuser,authlen); + + if (!authpass && domlen) + authpass = constmap(&mapauthuser,domain,domlen); // 1. authuser accounts + if (!authpass) + authpass = constmap(&mapauthuser,"*",1); // 2. system accounts + if (!authpass) + authpass = constmap(&mapauthuser,"@",1); // 3. virtual user accounts + + if (!authpass) exit(1); + + if (str_len(authpass) == 1) { // external IdP + switch (authpass[0]) { + case '?': rc = auth_unix(user.s,response); break; + case '+': if (maildirname) + rc = auth_wrapper("checkvpw",program,maildirname,authbuf,buflen); + else + rc = auth_wrapper("checkvpw","true","Maildir",authbuf,buflen); // Pseudo arg + break; + case '&': rc = auth_wrapper("vchkpw",program,maildirname,authbuf,buflen); + break; + case '=': rc = auth_dovecot(authuser,response,authsocket,service); + break; + default: rc = 2; + break; + } + } else { // authuser file + switch (authpass[0]) { + case '%': rc = auth_hash(authpass + 1,response); + break; + default: if (maildirname) { + if ((rc = auth_cram(authpass,response,challenge) == 0)) break; // IMAP C/R + if ((rc = auth_apop(authpass,response,challenge)) == 0) { + auth_unix(user.s,0); // Unix environment only + } + } else rc = auth_cram(authpass,response,challenge); + break; + } + } + + if (rc) exit(rc); + + for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0; + + if (authsocket && service) pathexec(argv + 5); + else if (authsocket || service) pathexec(argv + 3); + else pathexec(argv + 1); + exit(111); +} diff --git a/sqmail-4.3.07/src/qmail-badloadertypes.c b/sqmail-4.3.07/src/qmail-badloadertypes.c new file mode 100644 index 0000000..3472fd5 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-badloadertypes.c @@ -0,0 +1,68 @@ +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include "logmsg.h" +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "exit.h" +#include "open.h" +#include "auto_qmail.h" +#include "cdbmake.h" + +#define WHO "qmail-badloadertypes" +#define LOADER_LEN 5 + +int rename(const char *,const char *); // stdio.h + +void die_read() +{ + logmsg(WHO,111,FATAL,"unable to read control/badloadertypes"); +} +void die_write() +{ + logmsg(WHO,111,FATAL,"unable to write to control/badloadertypes.tmp"); +} + +char inbuf[1024]; +buffer b; + +int fd; +int fdtemp; + +struct cdb_make cdb; +stralloc line = {0}; +int match; + +int main() +{ + umask(033); + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",auto_qmail)); + + fd = open_read("control/badloadertypes"); + if (fd == -1) die_read(); + + buffer_init(&b,read,fd,inbuf,sizeof(inbuf)); + + fdtemp = open_trunc("control/badloadertypes.tmp"); + if (fdtemp == -1) die_write(); + + if (cdb_make_start(&cdb,fdtemp) == -1) die_write(); + + for (;;) { + if (getln(&b,&line,&match,'\n') != 0) die_read(); + if (line.s[0] != '#' && line.len > LOADER_LEN) + if (cdb_make_add(&cdb,line.s,LOADER_LEN,"",0) == -1) + die_write(); + if (!match) break; + } + + if (cdb_make_finish(&cdb) == -1) die_write(); + if (fsync(fdtemp) == -1) die_write(); + if (close(fdtemp) == -1) die_write(); /* NFS stupidity */ + if (rename("control/badloadertypes.tmp","control/badloadertypes.cdb") == -1) + logmsg(WHO,111,FATAL,"unable to move control/badloadertypes.tmp to control/badloadertypes.cdb"); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-badmimetypes.c b/sqmail-4.3.07/src/qmail-badmimetypes.c new file mode 100644 index 0000000..a75ad4c --- /dev/null +++ b/sqmail-4.3.07/src/qmail-badmimetypes.c @@ -0,0 +1,67 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "logmsg.h" +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "exit.h" +#include "open.h" +#include "auto_qmail.h" +#include "cdbmake.h" + +#define WHO "qmail-badmimetypes" +#define MIMETYPE_LEN 9 + +int rename(const char *,const char *); // stdio.h + +void die_read() +{ + logmsg(WHO,111,FATAL,"unable to read control/badmimetypes"); +} +void die_write() +{ + logmsg(WHO,111,FATAL,"unable to write to control/badmimetypes.tmp"); +} + +char inbuf[1024]; +buffer b; + +int fd; +int fdtemp; + +struct cdb_make cdb; +stralloc line = {0}; +int match; + +int main() +{ + umask(033); + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",auto_qmail)); + + fd = open_read("control/badmimetypes"); + if (fd == -1) die_read(); + + buffer_init(&b,read,fd,inbuf,sizeof(inbuf)); + + fdtemp = open_trunc("control/badmimetypes.tmp"); + if (fdtemp == -1) die_write(); + + if (cdb_make_start(&cdb,fdtemp) == -1) die_write(); + + for (;;) { + if (getln(&b,&line,&match,'\n') != 0) die_read(); + if (line.s[0] != '#' && line.len > MIMETYPE_LEN) + if (cdb_make_add(&cdb,line.s,MIMETYPE_LEN,"",0) == -1) + die_write(); + if (!match) break; + } + + if (cdb_make_finish(&cdb) == -1) die_write(); + if (fsync(fdtemp) == -1) die_write(); + if (close(fdtemp) == -1) die_write(); /* NFS stupidity */ + if (rename("control/badmimetypes.tmp","control/badmimetypes.cdb") == -1) + logmsg(WHO,111,FATAL,"unable to move control/badmimetypes.tmp to control/badmimetypes.cdb"); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-clean.c b/sqmail-4.3.07/src/qmail-clean.c new file mode 100644 index 0000000..df149a5 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-clean.c @@ -0,0 +1,100 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "sig.h" +#include "now.h" +#include "str.h" +#include "direntry.h" +#include "getln.h" +#include "stralloc.h" +#include "buffer.h" +#include "byte.h" +#include "scan.h" +#include "fmt.h" +#include "exit.h" +#include "error.h" +#include "fmtqfn.h" +#include "auto_qmail.h" + +#define OSSIFIED 129600 /* see qmail-send.c */ + +stralloc line = {0}; + +void cleanuppid() +{ + DIR *dir; + direntry *d; + struct stat st; + datetime_sec time; + + time = now(); + dir = opendir("pid"); + if (!dir) return; + + while ((d = readdir(dir))) { + if (str_equal(d->d_name,".")) continue; + if (str_equal(d->d_name,"..")) continue; + if (!stralloc_copys(&line,"pid/")) continue; + if (!stralloc_cats(&line,d->d_name)) continue; + if (!stralloc_0(&line)) continue; + if (stat(line.s,&st) == -1) continue; + if (time < st.st_atime + OSSIFIED) continue; + unlink(line.s); + } + closedir(dir); +} + +char fnbuf[FMTQFN]; + +void respond(char *s) +{ + if (buffer_putflush(buffer_1small,s,1) == -1) _exit(100); +} + +int main() +{ + int i; + int match; + int cleanuploop; + unsigned long id; + + if (chdir(auto_qmail) == -1) _exit(110); + if (chdir("queue") == -1) _exit(110); + + sig_pipeignore(); + + if (!stralloc_ready(&line,200)) _exit(111); + + cleanuploop = 0; + + for (;;) { + if (cleanuploop) --cleanuploop; else { cleanuppid(); cleanuploop = 30; } + if (getln(buffer_0small,&line,&match,'\0') == -1) break; + if (!match) break; + if (line.len < 7) { respond("x"); continue; } + if (line.len > 100) { respond("x"); continue; } + if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */ + + for (i = line.len - 2; i > 4; --i) { + if (line.s[i] == '/') break; + if ((unsigned char) (line.s[i] - '0') > 9) + { respond("x"); continue; } + } + if (line.s[i] == '/') + if (!scan_ulong(line.s + i + 1,&id)) { respond("x"); continue; } + if (byte_equal(line.s,5,"foop/")) { +#define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \ + if (unlink(fnbuf) == -1) if (errno != ENOENT) { respond("!"); continue; } + U("intd/",1) + U("mess/",1) + respond("+"); + } else if (byte_equal(line.s,4,"todo/")) { + U("intd/",1) + U("todo/",1) + respond("+"); + } + else + respond("x"); + } + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-dkim.cpp b/sqmail-4.3.07/src/qmail-dkim.cpp new file mode 100644 index 0000000..fba94fe --- /dev/null +++ b/sqmail-4.3.07/src/qmail-dkim.cpp @@ -0,0 +1,343 @@ +/***************************************************************************** +* Copyright 2005 Alt-N Technologies, Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* This code incorporates intellectual property owned by Yahoo! and licensed +* pursuant to the Yahoo! DomainKeys Patent License Agreement. +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Changes done by ¢feh@fehcom.de obeying the above license +* +* Comment: Awful mixture of C and C++ making use of the worst parts of it. +* Style: Partial Hungarian notation (see Torvalds comments) +* C++: Obsolete classes, allocators, virtual constructors w/o destructors +* C: Stdio interface routines +* OpenSSL: Brain demaged EVP_Digest calls with memory leaks. +* Network: Sigh, exchanged internal DNS routines by fehQlibs resolver +* +*****************************************************************************/ +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <unistd.h> +#include "dkim.h" +extern "C" { +#include "dns.h" +} + +// change these to your selector name, domain name, etc +#define MYRSASELECTOR "default" +#define MYECCSELECTOR "eddy" +#define MYDOMAIN "" //"bardenhagen.com" +#define MYIDENTITY "" //"dkimtest@bardenhagen.com" + +#define strnicmp strncasecmp +#define FDLOG stderr /* writing to another FD requires a method */ + +int DKIM_CALL SignThisHeader(const char* szHeader) +{ + if (strnicmp(szHeader,"X-",2) == 0 ) { return 0; } + return 1; +} + +int DKIM_CALL SelectorCallback(const char* szFQDN,char* szBuffer,int nBufLen) +{ + return 0; +} + +void usage() +{ + char version[] = "1.4.0"; + fprintf(FDLOG,"qmail-dkim %s \n",version); + fprintf(FDLOG,"Usage: qmail-dkim [-h|-v|-s] [tags] <msgfile> [<RSAkeyfile> <outfile> <Ed25519keyfile>]\n\n"); + fprintf(FDLOG, "Options:\n\t-h show this help\n"); + fprintf(FDLOG, "\t-s sign the message \n"); + fprintf(FDLOG, "\t-v verify the message\n"); + fprintf(FDLOG, "\t-V verify the message and write result to output file (Pass/Fail)\n\n"); + fprintf(FDLOG, "These tags are available:\n"); + fprintf(FDLOG, "\t-c<canonicalization> - r=relaxed [DEFAULT], s=simple, t=relaxed/simple, u=simple/relaxed\n"); + fprintf(FDLOG, "\t-d<sdid> - Signing Domain Identifier (if not provided it will be determined from the sender/from header)\n"); + fprintf(FDLOG, "\t-i<auid> - Agent User Identifier, usually the sender's email address (optional)\n"); + fprintf(FDLOG, "\t-l - include body length tag (optional)\n"); + fprintf(FDLOG, "\t-q - include query method tag\n"); + fprintf(FDLOG, "\t-t - include a timestamp tag (optional)\n"); + fprintf(FDLOG, "\t-x<expire_time> - the expire time in seconds since epoch (optional, DEFAULT = current time + 604800)\n"); + fprintf(FDLOG, "\t-y<selector> - set RSA selector (DEFAULT: default)\n"); + fprintf(FDLOG, "\t-Y<selector> - set Ed25519 selector (DEFAULT: default)\n"); + fprintf(FDLOG, "\t-z<hash> - set signature algorithm type (1=rsa-sha1, 2=rsa-sha256, 3=both, 4=ed25519, 5=hybrid)\n"); +} + +int main(int argc, char* argv[]) +{ + int n; + const char* RSAKeyFile = "rsa.pem"; + const char* ECCKeyFile = "ed25519.pem"; + const char* MsgFile = "test.msg"; + const char* OutFile = "signed.msg"; + int nKeyLen; + char RSAPrivKey[4196]; // storge for private key FILE including header and DER envelope + char ECCPrivKey[128]; + char Buffer[1000]; + int BufLen; + char szSignature[8192]; + time_t t; + DKIMContext ctxt; + DKIMSignOptions opts = {0}; + + opts.nHash = DKIM_HASH_SHA256; // default + + time(&t); + + opts.nCanon = DKIM_SIGN_RELAXED; + opts.nIncludeBodyLengthTag = 0; + opts.nIncludeQueryMethod = 0; + opts.nIncludeTimeStamp = 0; + opts.expireTime = t + 604800; // expires in 1 week + strcpy(opts.szSelector,MYRSASELECTOR); + strcpy(opts.szSelectorE,MYECCSELECTOR); + strcpy(opts.szDomain,MYDOMAIN); + strcpy(opts.szIdentity,MYIDENTITY); + opts.pfnHeaderCallback = SignThisHeader; + strcpy(opts.szRequiredHeaders,"NonExistant"); + opts.nIncludeCopiedHeaders = 0; + + int nArgParseState = 0; + bool bSign = true; + bool bRes = false; + + if (argc < 2){ + usage(); + exit(1); + } + + for (n = 1; n < argc; n++) { + if (argv[n][0] == '-' && strlen(argv[n]) > 1) { + switch (argv[n][1]) { + case 'c': // canonicalization + if (argv[n][2] == 'r') { opts.nCanon = DKIM_SIGN_RELAXED; } + else if (argv[n][2] == 's') { opts.nCanon = DKIM_SIGN_SIMPLE; } + else if (argv[n][2] == 't') { opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE; } + else if (argv[n][2] == 'u') { opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED; } + break; + case 'd': + strncpy(opts.szDomain,(const char*)(argv[n] + 2),sizeof(opts.szDomain) - 1); + break; + case 'l': // body length tag + opts.nIncludeBodyLengthTag = 1; + break; + case 'h': + usage(); + return 0; + case 'i': // identity + if (argv[n][2] == '-') { opts.szIdentity[0] = '\0'; } + else { strncpy(opts.szIdentity, argv[n] + 2,sizeof(opts.szIdentity) - 1); } + break; + case 'q': // query method tag + opts.nIncludeQueryMethod = 1; + break; + case 's': // sign with and use potentially Ed25519 private key + bSign = true; + break; + case 't': // timestamp tag + opts.nIncludeTimeStamp = 1; + break; + case 'v': // verify + bSign = false; + break; + case 'V': // verify and write result to OutFile + bSign = false; + bRes = true; + break; + case 'x': // expire time + if (argv[n][2] == '-') { opts.expireTime = 0; } + else { opts.expireTime = t + atoi(argv[n] + 2); } + break; + case 'y': + strncpy(opts.szSelector,argv[n] + 2,sizeof(opts.szSelector) - 1); + break; + case 'Y': + strncpy(opts.szSelectorE,argv[n] + 2,sizeof(opts.szSelectorE) - 1); + break; + case 'z': // sign w/ sha1, sha256, both, ed25519, hybrid + opts.nHash = atoi(&argv[n][2]); + } + } + else { + switch (nArgParseState) { + case 0: + MsgFile = argv[n]; + break; + case 1: + RSAKeyFile = argv[n]; + break; + case 2: + OutFile = argv[n]; + break; + case 3: + ECCKeyFile = argv[n]; + break; + } + nArgParseState++; + } + } + +/** Go for DKIM signing ... **/ + + if (bSign) { + if (opts.nHash != 4) { + FILE* RSAPrivKeyFP = fopen(RSAKeyFile,"r"); + if (RSAPrivKeyFP == NULL) { +#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: can't open private key file (%s) \n",RSAKeyFile); +#endif + exit(1); + } + nKeyLen = fread(RSAPrivKey,1,sizeof(RSAPrivKey),RSAPrivKeyFP); // we read sizeof(RSAPrivKey) members with size of 1 byte each; sigh +#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: private key file (%s) - length %i \n",RSAKeyFile,nKeyLen); +#endif + if (nKeyLen >= sizeof(RSAPrivKey)) { /* (TC9) on return, we get the number of members read! */ +#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: private key buffer isn't big enough for private key length %i \n",nKeyLen); +#endif + exit(1); + } + RSAPrivKey[nKeyLen] = '\0'; + fclose(RSAPrivKeyFP); + } + +/** Ed25519 signing **/ + + if (opts.nHash == 4 || opts.nHash == 5) { + FILE* ECCPrivKeyFP = fopen(ECCKeyFile,"r"); + if (ECCPrivKeyFP == NULL) { +#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: can't open Ed25519 private key file (%s) \n",ECCKeyFile); +#endif + exit(1); + } + nKeyLen = fread(ECCPrivKey,1,sizeof(ECCPrivKey),ECCPrivKeyFP); +#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: Ed25519 private key file (%s) - length %i \n",ECCKeyFile,nKeyLen); +#endif + if (nKeyLen >= sizeof(ECCPrivKey)) { +#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: ECC private key buffer isn't big enough for ECC private key length %i \n",nKeyLen); +#endif + exit(1); + } + ECCPrivKey[nKeyLen] = '\0'; + fclose(ECCPrivKeyFP); + } + +/** Input message for signing **/ + + FILE* MsgFP = fopen(MsgFile,"rb"); + if (MsgFP == NULL) { +#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: can't open msg file (%s) \n",MsgFile); +#endif + exit(1); + } + + n = DKIMSignInit(&ctxt,&opts); + + while (1) { + BufLen = fread(Buffer,1,sizeof(Buffer),MsgFP); + if (BufLen > 0) { DKIMSignProcess(&ctxt,Buffer,BufLen); } + else { break; } + } + fclose(MsgFP); + + char* pSig = NULL; + +/** Do the actual signing **/ + + n = DKIMSignGetSig2(&ctxt,RSAPrivKey,ECCPrivKey,&pSig); + + strcpy(szSignature,pSig); + + DKIMSignFree(&ctxt); + + FILE* in = fopen(MsgFile,"rb"); + FILE* out = fopen(OutFile,"wb+"); + +#ifdef SHOWLOG + fprintf(FDLOG," outfile written %s \n",OutFile); +#endif + + fwrite(szSignature,1,strlen(szSignature),out); + fwrite("\r\n",1,2,out); + + while (1) { + BufLen = fread(Buffer,1,sizeof(Buffer),in); + if (BufLen > 0) { fwrite(Buffer,1,BufLen,out); } + else { break; } + } + fclose(in); + + } + +/** Now go for verification **/ + + else { + FILE* in = fopen(MsgFile,"rb"); + if (in == NULL) { +//#ifdef SHOWLOG + fprintf(FDLOG," qmail-dkim: can't open input file\n"); +//#endif + return 0; // bad option -- no CTX set up yet + } + + DKIMVerifyOptions vopts = {0}; + vopts.pfnSelectorCallback = NULL; //SelectorCallback; + + n = DKIMVerifyInit(&ctxt,&vopts); + + while (1) { + BufLen = fread(Buffer,1,sizeof(Buffer),in); + if (BufLen > 0) { DKIMVerifyProcess(&ctxt,Buffer,BufLen); } + else { break; } + } + + n = DKIMVerifyResults(&ctxt); + + int nSigCount = 0; + DKIMVerifyDetails* pDetails; + char szPolicy[512]; + + n = DKIMVerifyGetDetails(&ctxt,&nSigCount,&pDetails,szPolicy); + + for (int i = 0; i < nSigCount; i++) { + const char s[] = "pass"; + const char f[] = "fail"; + const char* error = DKIM_ErrorResult(pDetails[i].nResult); + if (!bRes) + fprintf(FDLOG," Signature #%d: ",i + 1); + if (pDetails[i].nResult >= 0 ) { + if (bRes) { + _DKIM_ReportResult(OutFile,s,0); + } else + printf(" Pass\n"); + } else { // fail + if (bRes) { + _DKIM_ReportResult(OutFile,f,error); + } else + printf(" Fail %s \n",error); + } + } + DKIMVerifyFree(&ctxt); + } + return 0; +} diff --git a/sqmail-4.3.07/src/qmail-dksign.c b/sqmail-4.3.07/src/qmail-dksign.c new file mode 100755 index 0000000..406afc1 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-dksign.c @@ -0,0 +1,512 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include "sig.h" +#include "stralloc.h" +#include "buffer.h" +#include "error.h" +#include "auto_qmail.h" +#include "control.h" +#include "str.h" +#include "exit.h" +#include "case.h" +#include "constmap.h" +#include "uint_t.h" +#include "fd.h" +#include "logmsg.h" +#include "open.h" +#include "fmt.h" +#include "fmtqfn.h" +#include "readwrite.h" +#include "qmail.h" +#include "wait.h" +#include "pathexec.h" +#include "rcpthosts.h" + +#define WHO "qmail-dksign" + +#define DOMAINKEYS "ssl/domainkeys/" + +/** @file qmail-dksign.c -- generate signature and attach in DKIM header to outgoing message + + Steps: + ------ + a) DKIM controls: get private key for sending domain + b) Prepare two staging files at queue/dkim (before and after signing) + c) Read input at fd0 and insert CR for every line and store at dkim/x/pre + d) DKIM sign the message with provided private key and store at dkim/y/post + e) Copy signed file from fd to 0 + f) Invoke qmail-remote (respecting the \r\n) + g) Remove staging files (pre/post) + + Hack for hybrid signatures: + --------------------------- + + a) selector is a link to RSA private key + b) selector2 is a link to Ed25519 private key + c) Both are provided in the 'selector' field of dkimdomains separated by colon + d) The coupled selector information is provided to qmail-dkim as: -yselector ,-Yselector2 + e) The RSA privat key is given unaltered + f) The Ed25519 private is supplied as additional argument + */ + +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); +char outbuf[BUFSIZE_MESS]; +buffer bo = BUFFER_INIT(write,1,outbuf,sizeof(outbuf)); + +void die(int e) { _exit(e); } +void die_write(char *fn) { unlink(fn); die(53); }; +void die_read() { die(54); }; +void out(char *s) { if (buffer_puts(&bo,s) == -1) _exit(111); } +void zero() { if (buffer_put(&bo,"\0",1) == -1) _exit(111); } +void zerodie() { zero(); buffer_flush(&bo); _exit(111); } + +stralloc fndkin = {0}; +stralloc fndkout = {0}; + +stralloc sender = {0}; // will be re-written +stralloc senddomain = {0}; +stralloc originator = {0}; +stralloc dkimdomains = {0}; +struct constmap mapdkimdomains; + +stralloc ecckey = {0}; +stralloc rsakey = {0}; +char *dkimparams = 0; + +void temp_nomem() +{ + out("ZOut of memory. (#4.3.0)\n"); + zerodie(); +} +void temp_chdir() +{ + out("ZUnable to switch to target directory. (#4.3.0)\n"); + zerodie(); +} +void temp_create() +{ + out("ZUnable to create DKIM stage file: "); + out(error_str(errno)); + out(fndkin.s); out(". (#4.3.0)\n"); + zerodie(); +} +void temp_unlink() +{ + out("ZUnable to unlink DKIM stage file. (#4.3.0)\n"); + zerodie(); +} +void temp_control() +{ + out("ZUnable to read DKIM control files. (#4.3.0)\n"); + zerodie(); +} +void perm_usage() +{ + out("Zqmail-dksign was invoked improperly. (#5.3.5)\n"); + zerodie(); +} +void temp_read() +{ + out("DUnable to read message for DKIM signing. (#4.3.0)\n"); + zerodie(); +} +void temp_nosignkey() +{ + out("DCan't read sign key: "); + out(rsakey.s); + out(" or "); + out(ecckey.s); + out(". (#4.3.0)\n"); + zerodie(); +} + +int get_controls() +{ + int i; + stralloc domname = {0}; + + if (control_init() == -1) temp_control(); + + switch (control_readfile(&dkimdomains,"control/dkimdomains",0)) { + case -1: return 0; + case 0: if (!constmap_init(&mapdkimdomains,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&mapdkimdomains,dkimdomains.s,dkimdomains.len,1)) temp_nomem(); break; + } + +/* Check for disabled DKIM send domains */ + + if (!stralloc_copys(&domname,"!")) temp_nomem(); + if (!stralloc_cats(&domname,senddomain.s)) temp_nomem(); + if (constmap(&mapdkimdomains,domname.s,domname.len)) return 0; + +/* Parenting domains; senddomain 0-terminated; lowercase */ + + for (i = 0; i <= senddomain.len; ++i) { + if ((i == 0) || (senddomain.s[i] == '.')) + if ((dkimparams = constmap(&mapdkimdomains,senddomain.s + i,senddomain.len - i - 1))) { + if (!stralloc_copys(&sender,senddomain.s + i)) temp_nomem(); + if (!stralloc_0(&sender)) temp_nomem(); + return 3; + } + } + +/* We sign only senddomains we take responsibility for: rcpthosts */ + + if ((dkimparams = constmap(&mapdkimdomains,"=",1))) { + if (rcpthosts_init() == -1) temp_control(); + if (rcpthosts(originator.s,originator.len)) { + if ((control_readline(&sender,"control/defaultdomain") != 1)) + if (control_readline(&sender,"control/me") == -1) temp_control(); + if (!stralloc_0(&sender)) temp_nomem(); + return 2; + } + } + +/* Default settings for MTA: 'defaultdomain' or even 'me' */ + + if ((dkimparams = constmap(&mapdkimdomains,"*",1))) { + if ((control_readline(&sender,"control/defaultdomain") != 1)) + if (control_readline(&sender,"control/me") == -1) temp_control(); + if (!stralloc_0(&sender)) temp_nomem(); + return 1; + } + + return 0; +} + +void fnmake_dkim(unsigned long id) +{ + fndkin.len = fmtqfn(fndkin.s,"queue/dkim/",id,1); + id += id; + fndkout.len = fmtqfn(fndkout.s,"queue/dkim/",id,1); +} + +void dkim_unlink() +{ + if (unlink(fndkin.s) == -1) + if (errno != ENOENT) temp_unlink(); + if (unlink(fndkout.s) == -1) + if (errno != ENOENT) temp_unlink(); +} + +void dkim_stage() +{ + int r; + int fd; + int in, out; + struct stat st; + unsigned char tmpbuf[BUFSIZE_MESS + 2]; // intermediate write buffer + + if (!stralloc_ready(&fndkin,FMTQFN)) temp_nomem(); + if (!stralloc_ready(&fndkout,FMTQFN)) temp_nomem(); + + fnmake_dkim(getpid()); // pre-staging + dkim_unlink(); // duplicate, left over file + fd = open_excl(fndkin.s); + if (fd == -1) die_write(fndkin.s); + + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + buffer_init(&bo,write,fd,outbuf,sizeof(outbuf)); + + while((r = buffer_get(&bi,inbuf,sizeof(inbuf)))) { // read into buffer + if (r == -1) temp_read(); + + for (in = out = 0; in < r; in++) { + if (inbuf[in] == '\r') continue; // ignore CR + if (inbuf[in] != '\n') { + tmpbuf[out++] = inbuf[in]; + } else { // add CR for every LF + tmpbuf[out++] = '\r'; + tmpbuf[out++] = '\n'; + } + } + if (out) buffer_put(&bo,tmpbuf,out); // ok + } + + if (buffer_flush(&bo) == -1) die(51); + if (fstat(fd,&st) == -1) die_read(); + if (fsync(fd) == -1) die_write(fndkin.s); + if (close(fd) == -1) die_write(fndkin.s); +} + +/* to construct DKIM information */ + +stralloc selector = {0}; +stralloc selectore = {0}; +stralloc sdid = {0}; +stralloc auid = {0}; +stralloc expire = {0}; +stralloc canon = {0}; // -c r = relax, s = simple, t = relaxed/simple, u = simple/realxed +stralloc hash = {0}; // -z 1/2/3/4/5 sha1/sha2/both/ed25519/ed25519+rsa-sha256 +stralloc length = {0}; // -l + +/** + + qmail-dkim [-h|-v|-s] [tags] <msgfile> [<RSAkeyfile> <outfile> <Ed25519keyfile>] + -------------------------------------------------------------------------------- + tags: + ---- + -c<canonicalization> - r=relaxed [DEFAULT], s=simple, t=relaxed/simple, u=simple/relaxed + -d<sdid> - Signing Domain Identifier,if not provided it will be determined from the envelope originator/from header + -i<auid> - Agent User Identifier, usually the sender's email address (optional) + -l - include body length tag (optional) + -q - include query method tag + -t - include a timestamp tag (optional) + -x<expire_time> - the expire time in seconds since epoch (optional, DEFAULT = current time + 604800) + -y<selector> - set RSA selector (DEFAULT: default) + -Y<selector> - set Ed25519 selector (DEFAULT: default) + -z<hash> - set signature type (1=sha1, 2=sha256, 3=both, 4=ed25519, 5=hybrid) +*/ + +int dkim_sign(const char *rsakeyfile,const char *ecckeyfile,const char *fnin,const char *fnout) +{ + int child; + int wstat; + char *(args[17]); + int i = 0; + + args[i] = "qmail-dkim"; ++i; + args[i] = "-s"; ++i; + args[i] = "-q"; ++i; + if (sdid.len > 3) { args[i] = sdid.s; ++i; } + if (selector.len > 3) { args[i] = selector.s; ++i; } + if (selectore.len > 3) { args[i] = selectore.s; ++i; } + if (auid.len > 3) { args[i] = auid.s; ++i; } + if (expire.len > 3) { args[i] = expire.s; ++i; } + if (canon.len > 2) { args[i] = canon.s; ++i; } + if (hash.len > 2) { args[i] = hash.s; ++i; } + if (length.len > 2) { args[i] = length.s; ++i; } + args[i] = fnin; ++i; + args[i] = rsakeyfile; ++i; + args[i] = fnout; ++i; + if (str_len(ecckeyfile) > 3) { args[i] = ecckeyfile; ++i; } + args[i] = 0; + + if (!(child = vfork())) { + pathexec(args); + if (errno) _exit(111); + _exit(100); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) return 1; + + switch (wait_exitcode(wstat)) { + case 1: return 1; + default: return 0; + } +} + +int qmail_remote(char **qargs,int fd) +{ + int child; + int wstat; + char *(args[5]); + + args[0] = "qmail-remote"; + args[1] = qargs[1]; + args[2] = qargs[2]; + args[3] = qargs[3]; + args[4] = 0; + + if (!(child = vfork())) { + if (fd) { + if (fd_move(0,fd) == -1) _exit(111); + if (fd_copy(2,1) == -1) _exit(111); + } + pathexec(args); + if (errno) _exit(111); + _exit(100); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) return 1; + + switch (wait_exitcode(wstat)) { + case 111: return 1; + default: return 0; + } +} + +void dkim_setup() +{ + int c, i, j, k, l; + char *opt, *pos; + + /* defaults: selector=default, IETF format, q=dns/txt, z=2, c=r */ + + if (!stralloc_copys(&sdid,"-d")) temp_nomem(); + if (!stralloc_cat(&sdid,&sender)) temp_nomem(); + if (!stralloc_0(&sdid)) temp_nomem(); + if (!stralloc_copys(&selector,"-ydefault")) temp_nomem(); + if (!stralloc_0(&selector)) temp_nomem(); + if (!stralloc_copys(&selectore,"-Yeddy")) temp_nomem(); + if (!stralloc_0(&selectore)) temp_nomem(); + if (!stralloc_copys(&canon,"-cr")) temp_nomem(); + if (!stralloc_0(&canon)) temp_nomem(); + if (!stralloc_copys(&hash,"-z2")) temp_nomem(); + if (!stralloc_0(&hash)) temp_nomem(); + + /* domain:selector,selectore|sdid|[auid|~]|expire|c:z:l; c=[r|s|t|u], z=[1,2,3,4,5], l=l */ + + if (dkimparams && *dkimparams) { + i = str_chr(dkimparams,'|'); + pos = dkimparams + i; + if (*pos == '|' || *pos == '\0') { // selector + dkimparams[i] = '\0'; + c = str_chr(dkimparams,','); // selectore=eddy + if (dkimparams[c] == ',') { + dkimparams[c] = '\0'; + if (str_len(dkimparams + c + 1)) { + if (!stralloc_copys(&selectore,"-Y")) temp_nomem(); + if (!stralloc_cats(&selectore,dkimparams + c + 1)) temp_nomem(); + if (!stralloc_0(&selectore)) temp_nomem(); + } + } else if (str_len(dkimparams)) { // selector=default + if (!stralloc_copys(&selector,"-y")) temp_nomem(); + if (!stralloc_cats(&selector,dkimparams)) temp_nomem(); + if (!stralloc_0(&selector)) temp_nomem(); + } + + j = str_chr(dkimparams + i + 1,'|'); + pos = dkimparams + i + j + 1; + if (*pos == '|' || *pos == '\0') { // sdid; domain in DKIM header + dkimparams[i + j + 1] = '\0'; + if (!stralloc_copys(&sdid,"-d")) temp_nomem(); + if (!stralloc_cats(&sdid,dkimparams + i + 1)) temp_nomem(); + if (!stralloc_0(&sdid)) temp_nomem(); + + k = str_chr(dkimparams + i + j + 2,'|'); + pos = dkimparams + i + j + k + 2; + if (*pos == '|' || *pos == '\0') { // auid = identifier + dkimparams[i + j + k + 2] = '\0'; + if (!stralloc_copys(&auid,"-i")) temp_nomem(); + if (dkimparams[i + j + 2] == '~') { + if (!stralloc_cat(&auid,&originator)) temp_nomem(); + } else + if (!stralloc_cats(&auid,dkimparams + i + j + 2)) temp_nomem(); + + if (!stralloc_0(&auid)) temp_nomem(); + + l = str_chr(dkimparams + i + j + k + 3,'|'); + pos = dkimparams + i + j + k + l + 3; + if (*pos == '|' || *pos == '\0') { // expire after n secs + dkimparams[i + j + k + l + 3] = '\0'; + if (!stralloc_copys(&expire,"-x")) temp_nomem(); + if (!stralloc_cats(&expire,dkimparams + i + j + k + 3)) temp_nomem(); + if (!stralloc_0(&expire)) temp_nomem(); + + /* Options to follow */ + + opt = dkimparams + i + j + k + l + 4; + if (*opt == '\0') return; + if (*opt != ':') { + if (!stralloc_copys(&canon,"-c")) temp_nomem(); // canonicalization + if (!stralloc_catb(&canon,opt,1)) temp_nomem(); + if (!stralloc_0(&canon)) temp_nomem(); + ++opt; if (*opt == '\0') return; // next colon + } + if (*opt != ':' || *opt == '\0') return; + if (*opt == ':') ++opt; + if (*opt != ':') { + if (!stralloc_copys(&hash,"-z")) temp_nomem(); // hash + if (!stralloc_catb(&hash,opt,1)) temp_nomem(); + if (!stralloc_0(&hash)) temp_nomem(); + ++opt; if (*opt == '\0') return; // next colon + } + if (*opt != ':' || *opt == '\0') return; + if (*opt == ':') ++opt; + if (*opt != ':' && *opt == 'l') { + if (!stralloc_copys(&length,"-l")) temp_nomem(); // length + if (!stralloc_0(&length)) temp_nomem(); + } + } + } + } + } + } + + return; +} + +int main(int argc,char **args) +{ + int i; + int fdin = 0; // initial read from FD 0 + int nkey = 0; + char *(qargs[4]); + struct stat st; + + qargs[0] = args[0]; + qargs[1] = args[1]; // host + qargs[2] = args[2]; // originator + qargs[3] = args[3]; // recipient + + umask(033); + sig_pipeignore(); + if (argc < 4) perm_usage(); + if (chdir(auto_qmail) == -1) temp_chdir(); + + if (str_len(args[2]) > 2) { + i = str_chr(args[2],'@'); + if (*(args[2] + i) == '@') + if (!stralloc_copys(&senddomain,args[2] + i + 1)) temp_nomem(); + } + if (!stralloc_0(&senddomain)) temp_nomem(); + if (!stralloc_copys(&originator,args[2])) temp_nomem(); + + if (!get_controls()) { + qmail_remote(qargs,fdin); + _exit(0); + } + + dkim_setup(); // sender is evaluated from originator (senddomain) + + /* Setup keys: they are composed from selector */ + + case_lowerb(sender.s,sender.len); // needs to be lowercase + if (!stralloc_copys(&rsakey,DOMAINKEYS)) temp_nomem(); + if (!stralloc_cats(&rsakey,sender.s)) temp_nomem(); + if (!stralloc_cats(&rsakey,"/")) temp_nomem(); + + if (!stralloc_copys(&ecckey,DOMAINKEYS)) temp_nomem(); + if (!stralloc_cats(&ecckey,sender.s)) temp_nomem(); + if (!stralloc_cats(&ecckey,"/")) temp_nomem(); + + /* RSA key common for SHA1 and SHA256: rsakeyfile -> selector */ + + if (!stralloc_cats(&rsakey,selector.s + 2)) temp_nomem(); // -y prepended + if (!stralloc_0(&rsakey)) temp_nomem(); + if (stat(rsakey.s,&st) != -1) + if (open_read(rsakey.s) > 0) ++nkey; + + /* ECC key follows: ecckeyfile -> (,)selector2 */ + + if (!stralloc_cats(&ecckey,selectore.s + 2)) temp_nomem(); // -Y prepended + if (!stralloc_0(&ecckey)) temp_nomem(); + if (stat(ecckey.s,&st) != -1) + if (open_read(ecckey.s) > 0) ++nkey; + + /* We got keys - go for staging */ + + if (nkey) { // otherwise no key exists; why bother + dkim_stage(); + if (!dkim_sign(rsakey.s,ecckey.s,fndkin.s,fndkout.s)) { + fdin = open_read(fndkout.s); + if (fdin == -1) die_read(); + } else { + fdin = fndkin.s; // DKIM key failed to sign + } + } else + temp_nosignkey(); + + qmail_remote(qargs,fdin); // closes fdin + if (nkey) dkim_unlink(); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-dkverify.c b/sqmail-4.3.07/src/qmail-dkverify.c new file mode 100644 index 0000000..2cfe00a --- /dev/null +++ b/sqmail-4.3.07/src/qmail-dkverify.c @@ -0,0 +1,368 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include "sig.h" +#include "stralloc.h" +#include "buffer.h" +#include "error.h" +#include "auto_qmail.h" +#include "str.h" +#include "exit.h" +#include "uint_t.h" +#include "fd.h" +#include "open.h" +#include "fmt.h" +#include "fmtqfn.h" +#include "readwrite.h" +#include "getln.h" +#include "qmail.h" +#include "wait.h" +#include "byte.h" +#include "case.h" +#include "control.h" +#include "pathexec.h" +#include "env.h" +#include "logmsg.h" + +#define WHO "qmail-dkverify" + +/** @file qmail-dkverify.c + @brief stub routine for DKIM signature verification and indication in received message + + Steps: + ------ + a) Store message with CRLF + b) Get DKIM signature from message - if given: + c) Call qmail-dkim for verification + d) Include results as appended header + e) Queue the message for processing + + */ + +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); // read buffer +char outbuf[BUFSIZE_MESS]; +buffer bo = BUFFER_INIT(write,1,outbuf,sizeof(outbuf)); // output message + +void die(int e) { _exit(e); } +void die_pipe(char *fn) { unlink(fn); die(53); }; +void die_write(char *fn) { unlink(fn); die(53); }; +void die_read() { die(54); }; +void out(char *s) { if (buffer_puts(&bo,s) == -1) _exit(111); } +void zero() { if (buffer_put(&bo,"\0",1) == -1) _exit(111); } +void zerodie() { zero(); buffer_flush(&bo); _exit(111); } + +void temp_nomem() +{ + out("ZOut of memory. (#4.3.0)\n"); + zerodie(); +} +void temp_chdir() +{ + out("ZUnable to switch to target directory. (#4.3.0)\n"); + zerodie(); +} +void temp_create() +{ + out("ZUnable to create DKIM stage file. (#4.3.0)\n"); + zerodie(); +} +void temp_unlink() +{ + out("ZUnable to unlink DKIM stage file. (#4.3.0)\n"); + zerodie(); +} +void temp_read() +{ + out("ZUnable to read message. (#4.3.0)\n"); + zerodie(); +} +void temp_socket() +{ + out("ZUnable to crate socket pair. (#4.3.0)\n"); + zerodie(); +} +void temp_control() +{ + out("ZUnable to read control files. (#4.3.0)\n"); + zerodie(); +} + +static stralloc me = {0}; +static stralloc senddomain = {0}; +static stralloc dkheader = {0}; +static stralloc fndkin = {0}; +static stralloc fndkout = {0}; +static stralloc result = {0}; + +void fnmake_dkim(unsigned long id) +{ + fndkin.len = fmtqfn(fndkin.s,"queue/dkim/",id,1); + id += id; + fndkout.len = fmtqfn(fndkout.s,"queue/dkim/",id,1); +} + +void dkim_stage() +{ + int r; + int fd; + int in, out; + struct stat st; + char tmpbuf[BUFSIZE_MESS + 2]; + + if (!stralloc_ready(&fndkin,FMTQFN)) temp_nomem(); + if (!stralloc_ready(&fndkout,FMTQFN)) temp_nomem(); + + fnmake_dkim(getpid()); // pre-staging + fd = open_excl(fndkin.s); + if (fd == -1) die_write(fndkin.s); + + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + buffer_init(&bo,write,fd,outbuf,sizeof(outbuf)); + + while((r = buffer_get(&bi,inbuf,sizeof(inbuf)))) { // read into buffer + if (r == -1) temp_read(); + + for (in = out = 0; in < r; in++) { // reconstruct CRLF (ok) + if (inbuf[in] != '\n') { + tmpbuf[out++] = inbuf[in]; + } else { + tmpbuf[out++] = '\r'; + tmpbuf[out++] = '\n'; + } + } + if (out) buffer_put(&bo,tmpbuf,out); + } + + if (buffer_flush(&bo) == -1) die(51); + if (fstat(fd,&st) == -1) die_write(fndkin.s); + if (fsync(fd) == -1) die_write(fndkin.s); + if (close(fd) == -1) die_write(fndkin.s); +} + +int mess_dkim() +{ + stralloc line = {0}; + int match; + int fd; + int at = 0; + int ket = 0; + int end = 0; + int len = 0; + int r = 0; + int i; + + fd = open_read(fndkin.s); + if (fd == -1) die_read(); + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + if (!stralloc_copys(&senddomain,"")) temp_nomem(); + + for (;;) { + if (getln(&bi,&line,&match,'\n') == -1) temp_read(); + if (case_starts(line.s,"DKIM-Signature: ")) r = 1; + if (r == 1) { + if (case_starts(line.s,"From: ")) { // fallback: From + at = str_chr(line.s,'@'); + if (at < line.len) { + end = str_chr(line.s,'\n'); // From: user@senddomain\n + ket = str_chr(line.s,'>'); // From: User <user@senddomain> + len = (ket < end) ? ket : end; + if (!stralloc_copyb(&senddomain,line.s + at + 1,len - at - 1)) temp_nomem(); + r = 2; + } + } + for (i = 0; i < line.len; ++i) { // d=domain.tld + if (*(line.s + i) == '=' && *(line.s + i - 1) == 'd') { + ++i; // gotcha + while (*(line.s + i) != ';') { + if (!stralloc_catb(&senddomain,line.s + i,1)) temp_nomem(); + i++; + r = 3; + } + } + } + } + if (r >= 2 || !match) break; + } + if (senddomain.len < 2) + if (!stralloc_copys(&senddomain,"unknown")) temp_nomem(); + if (!stralloc_0(&senddomain)) temp_nomem(); + + return r; +} + +int dkim_verify() +{ + int child; + int wstat; + char *(args[6]); + int r = -1; + + args[0] = "qmail-dkim"; + args[1] = "-V"; + args[2] = fndkin.s; + args[3] = "none"; + args[4] = fndkout.s; + args[5] = 0; + + if (!(child = fork())) { + pathexec(args); + if (errno) _exit(111); + _exit(100); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) return 1; + + switch (r = wait_exitcode(wstat)) { + case 10: return 1; + default: return 0; + } +} + +int dkim_result(const char *me) +{ + int max = 64; + int fd; + int j; + char ch; + int r = 0; + + if (!stralloc_copys(&result,"")) temp_nomem(); + + if ((fd = open_read(fndkout.s)) == -1) return 0; // nothing to read + while ((r = read(fd,inbuf,sizeof(inbuf))) > 0) + if (!stralloc_catb(&result,inbuf,r)) temp_nomem(); + + if (!stralloc_0(&result)) temp_nomem(); + + if (result.len > 2) { + if (case_starts(result.s,"pass")) r = 0; + if (case_starts(result.s,"fail")) r = 35; + } else + if (!stralloc_copys(&result,"unknown")) temp_nomem(); + + if (!stralloc_copys(&dkheader,"X-Authentication-Results: ")) temp_nomem(); + if (!stralloc_cats(&dkheader,senddomain.s)) temp_nomem(); + if (!stralloc_cats(&dkheader,"; dkim=")) temp_nomem(); + + for (j = 0; j < result.len; j++) { + ch = result.s[j]; + if (ch == '\r' || ch == '\n' || ch == '\0') continue; + if (j <= max) if (!stralloc_catb(&dkheader,&ch,1)) temp_nomem(); + if (ch == ' ' && (j > max)) { + if (!stralloc_cats(&dkheader,"\n ")) temp_nomem(); + max += max; + } + } + + if (!stralloc_cats(&dkheader,"; ")) temp_nomem(); + if (!stralloc_cats(&dkheader,me)) temp_nomem(); + if (!stralloc_0(&dkheader)) temp_nomem(); + + return r; +} + +int qmail_queue() +{ + int fd; + int r; + int child; + int wstat; + int pi[2]; + char *(args[2]); + char tmpbuf[BUFSIZE_MESS]; + int in, out; + + if (pipe(pi) == -1) die_pipe(fndkin.s); + + args[0] = "qmail-queue"; + args[1] = 0; + + switch (child = vfork()) { + case -1: + close(pi[0]); close(pi[1]); + die_write(fndkin.s); + case 0: + close(pi[1]); + if (fd_move(0,pi[0]) == -1) die_pipe(fndkin.s); + sig_pipedefault(); + pathexec(args); + if (errno) _exit(111); + _exit(100); + } + close(pi[0]); + + buffer_init(&bo,write,pi[1],outbuf,sizeof(outbuf)); + + if (dkheader.len > 2) { // write DKIM header + if (buffer_put(&bo,dkheader.s,dkheader.len - 1) == -1) die_write(fndkout.s); + if (buffer_put(&bo,"\n",1) == -1) die_write(fndkout.s); + if (buffer_flush(&bo) == -1) die_write(fndkout.s); + } + + /* read/write message; we need to remove the CR (ok) */ + + if ((fd = open_read(fndkin.s)) == -1) die_read(); + while ((r = read(fd,tmpbuf,sizeof(tmpbuf))) > 0) { + for (in = 0, out = 0; in < r; ++in) { + if (tmpbuf[in] == '\r') { + buffer_put(&bo,&tmpbuf[out],in - out); + out = in + 1; // \n to follow + } + } + } + + if (buffer_flush(&bo) == -1) die_write(fndkin.s); + close(pi[1]); + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) return 1; + + switch (r = wait_exitcode(wstat)) { + case 10: return 1; + default: return 0; + } + + return 0; +} + +void dkim_unlink() +{ + if (unlink(fndkin.s) == -1) + if (errno != ENOENT) temp_unlink(); + if (unlink(fndkout.s) == -1) + if (errno != ENOENT) temp_unlink(); +} + +int main() +{ + int r = 0; + char *mode = 0; + + umask(033); + if (chdir(auto_qmail) == -1) temp_chdir(); + if (control_init() == -1) temp_control(); + if (control_readline(&me,"control/me") == -1) temp_control(); + if (!stralloc_0(&me)) temp_nomem(); + + dkim_stage(); + + if (mess_dkim()) { + dkim_verify(); + r = dkim_result(me.s); + } + + /* we are done: call qmail-queue */ + + mode = env_get("DKIM"); + if (!mode || *mode != '+') r = 0; + + qmail_queue(); + dkim_unlink(); + + _exit(r); +} diff --git a/sqmail-4.3.07/src/qmail-getpw.c b/sqmail-4.3.07/src/qmail-getpw.c new file mode 100644 index 0000000..f801c3c --- /dev/null +++ b/sqmail-4.3.07/src/qmail-getpw.c @@ -0,0 +1,85 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#include <unistd.h> +#include "error.h" +#include "buffer.h" +#include "exit.h" +#include "byte.h" +#include "str.h" +#include "case.h" +#include "fmt.h" +#include "auto_usera.h" +#include "auto_break.h" +#include "qlx.h" + +#define GETPW_USERLEN 32 + +char *local; +struct passwd *pw; +char *dash; +char *extension; + +int userext() +{ + char username[GETPW_USERLEN]; + struct stat st; + + extension = local + str_len(local); + for (;;) { + if (extension - local < sizeof(username)) + if (!*extension || (*extension == *auto_break)) { + byte_copy(username,extension - local,local); + username[extension - local] = 0; + case_lowers(username); + errno = 0; + pw = getpwnam(username); + if (errno == ETXTBSY) _exit(QLX_SYS); + if (pw) + if (pw->pw_uid) + if (stat(pw->pw_dir,&st) == 0) { + if (st.st_uid == pw->pw_uid) { + dash = ""; + if (*extension) { ++extension; dash = "-"; } + return 1; + } + } else { + if (errno) _exit(QLX_NFS); + } + } + if (extension == local) return 0; + --extension; + } +} + +char num[FMT_ULONG]; + +int main(int argc,char **argv) +{ + local = argv[1]; + if (!local) _exit(100); + + if (!userext()) { + extension = local; + dash = "-"; + pw = getpwnam(auto_usera); + } + + if (!pw) _exit(QLX_NOALIAS); + + buffer_puts(buffer_1small,pw->pw_name); + buffer_put(buffer_1small,"",1); + buffer_put(buffer_1small,num,fmt_ulong(num,(long) pw->pw_uid)); + buffer_put(buffer_1small,"",1); + buffer_put(buffer_1small,num,fmt_ulong(num,(long) pw->pw_gid)); + buffer_put(buffer_1small,"",1); + buffer_puts(buffer_1small,pw->pw_dir); + buffer_put(buffer_1small,"",1); + buffer_puts(buffer_1small,dash); + buffer_put(buffer_1small,"",1); + buffer_puts(buffer_1small,extension); + buffer_put(buffer_1small,"",1); + buffer_flush(buffer_1small); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-inject.c b/sqmail-4.3.07/src/qmail-inject.c new file mode 100644 index 0000000..0071316 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-inject.c @@ -0,0 +1,793 @@ +#include <unistd.h> +#include "sig.h" +#include "buffer.h" +#include "genalloc.h" +#include "stralloc.h" +#include "getoptb.h" +#include "getln.h" +#include "alloc.h" +#include "str.h" +#include "fmt.h" +#include "hfield.h" +#include "token822.h" +#include "control.h" +#include "env.h" +#include "qmail.h" +#include "now.h" +#include "exit.h" +#include "error.h" +#include "quote.h" +#include "headerbody.h" +#include "auto_qmail.h" +#include "newfield.h" +#include "constmap.h" + +#define LINELEN 80 + +datetime_sec starttime; + +char *qmopts; +int flagdeletesender = 0; +int flagdeletefrom = 0; +int flagdeletemessid = 0; +int flagnamecomment = 0; +int flaghackmess = 0; +int flaghackrecip = 0; +char *mailhost; +char *mailuser; +int mailusertokentype; +char *mailrhost; +char *mailruser; + +stralloc control_idhost = {0}; +stralloc control_defaultdomain = {0}; +stralloc control_defaulthost = {0}; +stralloc control_plusdomain = {0}; + +stralloc sender = {0}; +stralloc envsbuf = {0}; +token822_alloc envs = {0}; +int flagrh; + +int flagqueue; +struct qmail qqt; + +void out(char *s,int len) +{ + if (flagqueue) qmail_put(&qqt,s,len); + else buffer_putflush(buffer_1,s,len); +} + +void outs(char *s) { out(s,str_len(s)); } + +void perm() { _exit(100); } +void temp() { _exit(111); } + +void die_nomem() +{ + buffer_putsflush(buffer_2,"qmail-inject: fatal: out of memory\n"); + temp(); +} +void die_invalid(stralloc *sa) +{ + buffer_putsflush(buffer_2,"qmail-inject: fatal: invalid header field: "); + buffer_putflush(buffer_2,sa->s,sa->len); + perm(); +} +void die_qqt() +{ + buffer_putsflush(buffer_2,"qmail-inject: fatal: unable to run qmail-queue\n"); + temp(); +} +void die_chdir() +{ + buffer_putsflush(buffer_2,"qmail-inject: fatal: internal bug\n"); + temp(); +} +void die_read() +{ + if (errno == ENOMEM) die_nomem(); + buffer_putsflush(buffer_2,"qmail-inject: fatal: read error\n"); + temp(); +} +void doordie(stralloc *sa,int r) +{ + if (r == 1) return; + if (r == -1) die_nomem(); + buffer_putsflush(buffer_2,"qmail-inject: fatal: unable to parse this line:\n"); + buffer_putflush(buffer_2,sa->s,sa->len); + perm(); +} + +GEN_ALLOC_typedef(saa,stralloc,sa,len,a) +GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) + +static stralloc sauninit = {0}; + +saa savedh = {0}; +saa hrlist = {0}; +saa tocclist = {0}; +saa hrrlist = {0}; +saa reciplist = {0}; +int flagresent; + +void exitnicely() +{ + char *qqx; + + if (!flagqueue) buffer_flush(buffer_1); + + if (flagqueue) { + int i; + + if (!stralloc_0(&sender)) die_nomem(); + qmail_from(&qqt,sender.s); + + for (i = 0; i < reciplist.len; ++i) { + if (!stralloc_0(&reciplist.sa[i])) die_nomem(); + qmail_to(&qqt,reciplist.sa[i].s); + } + if (flagrh) { + if (flagresent) { + for (i = 0; i < hrrlist.len; ++i) { + if (!stralloc_0(&hrrlist.sa[i])) die_nomem(); + qmail_to(&qqt,hrrlist.sa[i].s); + } + } else { + for (i = 0; i < hrlist.len; ++i) { + if (!stralloc_0(&hrlist.sa[i])) die_nomem(); + qmail_to(&qqt,hrlist.sa[i].s); + } + } + } + + qqx = qmail_close(&qqt); + if (*qqx) { + if (*qqx == 'D') { + buffer_puts(buffer_2,"qmail-inject: fatal: "); + buffer_puts(buffer_2,qqx + 1); + buffer_puts(buffer_2,"\n"); + buffer_flush(buffer_2); + perm(); + } else { + buffer_puts(buffer_2,"qmail-inject: fatal: "); + buffer_puts(buffer_2,qqx + 1); + buffer_puts(buffer_2,"\n"); + buffer_flush(buffer_2); + temp(); + } + } + } + + _exit(0); +} + +void savedh_append(stralloc *h) +{ + if (!saa_readyplus(&savedh,1)) die_nomem(); + savedh.sa[savedh.len] = sauninit; + if (!stralloc_copy(savedh.sa + savedh.len,h)) die_nomem(); + ++savedh.len; +} + +void savedh_print() +{ + int i; + + for (i = 0; i < savedh.len; ++i) + out(savedh.sa[i].s,savedh.sa[i].len); +} + +stralloc defaultdomainbuf = {0}; +token822_alloc defaultdomain = {0}; +stralloc defaulthostbuf = {0}; +token822_alloc defaulthost = {0}; +stralloc plusdomainbuf = {0}; +token822_alloc plusdomain = {0}; + +void rwroute(token822_alloc *addr) +{ + if (addr->t[addr->len - 1].type == TOKEN822_AT) + while (addr->len) + if (addr->t[--addr->len].type == TOKEN822_COLON) + return; +} + +void rwextraat(token822_alloc *addr) +{ + int i; + + if (addr->t[0].type == TOKEN822_AT) { + --addr->len; + for (i = 0; i < addr->len; ++i) + addr->t[i] = addr->t[i + 1]; + } +} + +void rwextradot(token822_alloc *addr) +{ + int i; + + if (addr->t[0].type == TOKEN822_DOT) { + --addr->len; + for (i = 0; i < addr->len; ++i) + addr->t[i] = addr->t[i + 1]; + } +} + +void rwnoat(token822_alloc *addr) +{ + int i; + int shift; + + for (i = 0; i < addr->len; ++i) + if (addr->t[i].type == TOKEN822_AT) + return; + shift = defaulthost.len; + if (!token822_readyplus(addr,shift)) die_nomem(); + for (i = addr->len - 1; i >= 0; --i) + addr->t[i + shift] = addr->t[i]; + + addr->len += shift; + + for (i = 0; i < shift; ++i) + addr->t[i] = defaulthost.t[shift - 1 - i]; +} + +void rwnodot(token822_alloc *addr) +{ + int i; + int shift; + + for (i = 0; i < addr->len; ++i) { + if (addr->t[i].type == TOKEN822_DOT) + return; + if (addr->t[i].type == TOKEN822_AT) + break; + } + for (i = 0; i < addr->len; ++i) { + if (addr->t[i].type == TOKEN822_LITERAL) + return; + if (addr->t[i].type == TOKEN822_AT) + break; + } + shift = defaultdomain.len; + if (!token822_readyplus(addr,shift)) die_nomem(); + + for (i = addr->len - 1; i >= 0; --i) + addr->t[i + shift] = addr->t[i]; + + addr->len += shift; + + for (i = 0; i < shift; ++i) + addr->t[i] = defaultdomain.t[shift - 1 - i]; +} + +void rwplus(token822_alloc *addr) +{ + int i; + int shift; + + if (addr->t[0].type != TOKEN822_ATOM) return; + if (!addr->t[0].slen) return; + if (addr->t[0].s[addr->t[0].slen - 1] != '+') return; + + --addr->t[0].slen; /* remove + */ + + shift = plusdomain.len; + if (!token822_readyplus(addr,shift)) die_nomem(); + + for (i = addr->len - 1; i >= 0; --i) + addr->t[i + shift] = addr->t[i]; + + addr->len += shift; + + for (i = 0; i < shift; ++i) + addr->t[i] = plusdomain.t[shift - 1 - i]; +} + +void rwgeneric(token822_alloc *addr) +{ + if (!addr->len) return; /* don't rewrite <> */ + if (addr->len >= 2) + if (addr->t[1].type == TOKEN822_AT) + if (addr->t[0].type == TOKEN822_LITERAL) + if (!addr->t[0].slen) /* don't rewrite <foo@[]> */ + return; + + rwroute(addr); + if (!addr->len) return; /* <@foo:> -> <> */ + rwextradot(addr); + if (!addr->len) return; /* <.> -> <> */ + rwextraat(addr); + if (!addr->len) return; /* <@> -> <> */ + rwnoat(addr); + rwplus(addr); + rwnodot(addr); +} + +int setreturn(token822_alloc *addr) +{ + if (!sender.s) { + token822_reverse(addr); + if (token822_unquote(&sender,addr) != 1) die_nomem(); + if (flaghackrecip) + if (!stralloc_cats(&sender,"-@[]")) die_nomem(); + token822_reverse(addr); + } + return 1; +} + +int rwreturn(token822_alloc *addr) +{ + rwgeneric(addr); + setreturn(addr); + return 1; +} + +int rwsender(token822_alloc *addr) +{ + rwgeneric(addr); + return 1; +} + +void rwappend(token822_alloc *addr,saa *xl) +{ + token822_reverse(addr); + if (!saa_readyplus(xl,1)) die_nomem(); + xl->sa[xl->len] = sauninit; + if (token822_unquote(&xl->sa[xl->len],addr) != 1) die_nomem(); + ++xl->len; + token822_reverse(addr); +} + +int rwhrr(token822_alloc *addr) +{ + rwgeneric(addr); + rwappend(addr,&hrrlist); + return 1; +} + +int rwhr(token822_alloc *addr) +{ + rwgeneric(addr); + rwappend(addr,&hrlist); + return 1; +} + +int rwtocc(token822_alloc *addr) +{ + rwgeneric(addr); + rwappend(addr,&hrlist); + rwappend(addr,&tocclist); + return 1; +} + +int htypeseen[H_NUM]; +stralloc hfbuf = {0}; +token822_alloc hfin = {0}; +token822_alloc hfrewrite = {0}; +token822_alloc hfaddr = {0}; + +void doheaderfield(stralloc *h) +{ + int htype; + int (*rw)() = 0; + + htype = hfield_known(h->s,h->len); + if (flagdeletefrom) if (htype == H_FROM) return; + if (flagdeletemessid) if (htype == H_MESSAGEID) return; + if (flagdeletesender) if (htype == H_RETURNPATH) return; + + if (htype) + htypeseen[htype] = 1; + else + if (!hfield_valid(h->s,h->len)) + die_invalid(h); + + switch (htype) { + case H_TO: case H_CC: + rw = rwtocc; break; + case H_BCC: case H_APPARENTLYTO: + rw = rwhr; break; + case H_R_TO: case H_R_CC: case H_R_BCC: + rw = rwhrr; break; + case H_RETURNPATH: + rw = rwreturn; break; + case H_SENDER: case H_FROM: case H_REPLYTO: + case H_RETURNRECEIPTTO: case H_ERRORSTO: + case H_R_SENDER: case H_R_FROM: case H_R_REPLYTO: + rw = rwsender; break; + } + + if (rw) { + doordie(h,token822_parse(&hfin,h,&hfbuf)); + doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rw)); + if (token822_unparse(h,&hfrewrite,LINELEN) != 1) + die_nomem(); + } + + if (htype == H_BCC) return; + if (htype == H_R_BCC) return; + if (htype == H_RETURNPATH) return; + if (htype == H_CONTENTLENGTH) return; /* some things are just too stupid */ + savedh_append(h); +} + +void dobody(stralloc *h) +{ + out(h->s,h->len); +} + +stralloc torecip = {0}; +token822_alloc tr = {0}; + +void dorecip(char *s) +{ + if (!quote2(&torecip,s)) die_nomem(); + + switch (token822_parse(&tr,&torecip,&hfbuf)) { + case -1: die_nomem(); + case 0: + buffer_puts(buffer_2,"qmail-inject: fatal: unable to parse address: "); + buffer_puts(buffer_2,s); + buffer_putsflush(buffer_2,"\n"); + perm(); + } + token822_reverse(&tr); + rwgeneric(&tr); + rwappend(&tr,&reciplist); +} + +stralloc defaultfrom = {0}; +token822_alloc df = {0}; + +void defaultfrommake() +{ + char *fullname; + fullname = env_get("QMAILNAME"); + if (!fullname) fullname = env_get("MAILNAME"); + if (!fullname) fullname = env_get("NAME"); + if (!token822_ready(&df,20)) die_nomem(); + + df.len = 0; + df.t[df.len].type = TOKEN822_ATOM; + df.t[df.len].s = "From"; + df.t[df.len].slen = 4; + ++df.len; + df.t[df.len].type = TOKEN822_COLON; + ++df.len; + + if (fullname && !flagnamecomment) { + df.t[df.len].type = TOKEN822_QUOTE; + df.t[df.len].s = fullname; + df.t[df.len].slen = str_len(fullname); + ++df.len; + df.t[df.len].type = TOKEN822_LEFT; + ++df.len; + } + + df.t[df.len].type = mailusertokentype; + df.t[df.len].s = mailuser; + df.t[df.len].slen = str_len(mailuser); + ++df.len; + + if (mailhost) { + df.t[df.len].type = TOKEN822_AT; + ++df.len; + df.t[df.len].type = TOKEN822_ATOM; + df.t[df.len].s = mailhost; + df.t[df.len].slen = str_len(mailhost); + ++df.len; + } + + if (fullname && !flagnamecomment) { + df.t[df.len].type = TOKEN822_RIGHT; + ++df.len; + } + + if (fullname && flagnamecomment) { + df.t[df.len].type = TOKEN822_COMMENT; + df.t[df.len].s = fullname; + df.t[df.len].slen = str_len(fullname); + ++df.len; + } + + if (token822_unparse(&defaultfrom,&df,LINELEN) != 1) die_nomem(); + doordie(&defaultfrom,token822_parse(&df,&defaultfrom,&hfbuf)); + doordie(&defaultfrom,token822_addrlist(&hfrewrite,&hfaddr,&df,rwsender)); + if (token822_unparse(&defaultfrom,&hfrewrite,LINELEN) != 1) die_nomem(); +} + +stralloc defaultreturnpath = {0}; +token822_alloc drp = {0}; +stralloc hackedruser = {0}; +char strnum[FMT_ULONG]; + +void dodefaultreturnpath() +{ + if (!stralloc_copys(&hackedruser,mailruser)) die_nomem(); + + if (flaghackmess) { + if (!stralloc_cats(&hackedruser,"-")) die_nomem(); + if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) starttime))) die_nomem(); + if (!stralloc_cats(&hackedruser,".")) die_nomem(); + if (!stralloc_catb(&hackedruser,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); + } + if (flaghackrecip) + if (!stralloc_cats(&hackedruser,"-")) die_nomem(); + + if (!token822_ready(&drp,10)) die_nomem(); + + drp.len = 0; + drp.t[drp.len].type = TOKEN822_ATOM; + drp.t[drp.len].s = "Return-Path"; + drp.t[drp.len].slen = 11; + ++drp.len; + drp.t[drp.len].type = TOKEN822_COLON; + ++drp.len; + drp.t[drp.len].type = TOKEN822_QUOTE; + drp.t[drp.len].s = hackedruser.s; + drp.t[drp.len].slen = hackedruser.len; + ++drp.len; + + if (mailrhost) { + drp.t[drp.len].type = TOKEN822_AT; + ++drp.len; + drp.t[drp.len].type = TOKEN822_ATOM; + drp.t[drp.len].s = mailrhost; + drp.t[drp.len].slen = str_len(mailrhost); + ++drp.len; + } + + if (token822_unparse(&defaultreturnpath,&drp,LINELEN) != 1) die_nomem(); + doordie(&defaultreturnpath,token822_parse(&drp,&defaultreturnpath,&hfbuf)); + doordie(&defaultreturnpath,token822_addrlist(&hfrewrite,&hfaddr,&drp,rwreturn)); + if (token822_unparse(&defaultreturnpath,&hfrewrite,LINELEN) != 1) die_nomem(); +} + +int flagmft = 0; +stralloc mft = {0}; +struct constmap mapmft; + +void mft_init() +{ + char *x; + int r; + + x = env_get("QMAILMFTFILE"); + if (!x) return; + + r = control_readfile(&mft,x,0); + if (r == -1) die_read(); /*XXX*/ + if (!r) return; + + if (!constmap_init(&mapmft,mft.s,mft.len,0)) die_nomem(); + flagmft = 1; +} + +void finishmft() +{ + int i; + static stralloc sa = {0}; + static stralloc sa2 = {0}; + + if (!flagmft) return; + if (htypeseen[H_MAILFOLLOWUPTO]) return; + + for (i = 0; i < tocclist.len; ++i) + if (constmap(&mapmft,tocclist.sa[i].s,tocclist.sa[i].len)) + break; + + if (i == tocclist.len) return; + + outs("Mail-Followup-To: "); + i = tocclist.len; + while (i--) { + if (!stralloc_copy(&sa,&tocclist.sa[i])) die_nomem(); + if (!stralloc_0(&sa)) die_nomem(); + if (!quote2(&sa2,sa.s)) die_nomem(); + out(sa2.s,sa2.len); + if (i) outs(",\n "); + } + outs("\n"); +} + +void finishheader() +{ + flagresent = + htypeseen[H_R_SENDER] || htypeseen[H_R_FROM] || htypeseen[H_R_REPLYTO] + || htypeseen[H_R_TO] || htypeseen[H_R_CC] || htypeseen[H_R_BCC] + || htypeseen[H_R_DATE] || htypeseen[H_R_MESSAGEID]; + + if (!sender.s) + dodefaultreturnpath(); + + if (!flagqueue) { + static stralloc sa = {0}; + static stralloc sa2 = {0}; + + if (!stralloc_copy(&sa,&sender)) die_nomem(); + if (!stralloc_0(&sa)) die_nomem(); + if (!quote2(&sa2,sa.s)) die_nomem(); + + outs("Return-Path: <"); + out(sa2.s,sa2.len); + outs(">\n"); + } + + /* could check at this point whether there are any recipients */ + if (flagqueue) + if (qmail_open(&qqt) == -1) die_qqt(); + + if (flagresent) { + if (!htypeseen[H_R_DATE]) { + if (!newfield_datemake(starttime)) die_nomem(); + outs("Resent-"); + out(newfield_date.s,newfield_date.len); + } + if (!htypeseen[H_R_MESSAGEID]) { + if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem(); + outs("Resent-"); + out(newfield_msgid.s,newfield_msgid.len); + } + if (!htypeseen[H_R_FROM]) { + defaultfrommake(); + outs("Resent-"); + out(defaultfrom.s,defaultfrom.len); + } + if (!htypeseen[H_R_TO] && !htypeseen[H_R_CC]) + outs("Resent-Cc: recipient list not shown: ;\n"); + } else { + if (!htypeseen[H_DATE]) { + if (!newfield_datemake(starttime)) die_nomem(); + out(newfield_date.s,newfield_date.len); + } + if (!htypeseen[H_MESSAGEID]) { + if (!newfield_msgidmake(control_idhost.s,control_idhost.len,starttime)) die_nomem(); + out(newfield_msgid.s,newfield_msgid.len); + } + if (!htypeseen[H_FROM]) { + defaultfrommake(); + out(defaultfrom.s,defaultfrom.len); + } + if (!htypeseen[H_TO] && !htypeseen[H_CC]) + outs("Cc: recipient list not shown: ;\n"); + finishmft(); + } + + savedh_print(); +} + +void getcontrols() +{ + static stralloc sa = {0}; + char *x; + + mft_init(); + + if (chdir(auto_qmail) == -1) die_chdir(); + if (control_init() == -1) die_read(); + + if (control_rldef(&control_defaultdomain,"control/defaultdomain",1,"defaultdomain") != 1) + die_read(); + x = env_get("QMAILDEFAULTDOMAIN"); + if (x) if (!stralloc_copys(&control_defaultdomain,x)) die_nomem(); + if (!stralloc_copys(&sa,".")) die_nomem(); + if (!stralloc_cat(&sa,&control_defaultdomain)) die_nomem(); + doordie(&sa,token822_parse(&defaultdomain,&sa,&defaultdomainbuf)); + + if (control_rldef(&control_defaulthost,"control/defaulthost",1,"defaulthost") != 1) + die_read(); + x = env_get("QMAILDEFAULTHOST"); + if (x) if (!stralloc_copys(&control_defaulthost,x)) die_nomem(); + if (!stralloc_copys(&sa,"@")) die_nomem(); + if (!stralloc_cat(&sa,&control_defaulthost)) die_nomem(); + doordie(&sa,token822_parse(&defaulthost,&sa,&defaulthostbuf)); + + if (control_rldef(&control_plusdomain,"control/plusdomain",1,"plusdomain") != 1) + die_read(); + x = env_get("QMAILPLUSDOMAIN"); + if (x) if (!stralloc_copys(&control_plusdomain,x)) die_nomem(); + if (!stralloc_copys(&sa,".")) die_nomem(); + if (!stralloc_cat(&sa,&control_plusdomain)) die_nomem(); + doordie(&sa,token822_parse(&plusdomain,&sa,&plusdomainbuf)); + + if (control_rldef(&control_idhost,"control/idhost",1,"idhost") != 1) + die_read(); + x = env_get("QMAILIDHOST"); + if (x) if (!stralloc_copys(&control_idhost,x)) die_nomem(); +} + +#define RECIP_DEFAULT 1 +#define RECIP_ARGS 2 +#define RECIP_HEADER 3 +#define RECIP_AH 4 + +int main(int argc,char **argv) +{ + int i; + int opt; + int recipstrategy; + + sig_pipeignore(); + + starttime = now(); + + qmopts = env_get("QMAILINJECT"); + if (qmopts) + while (*qmopts) + switch (*qmopts++) { + case 'c': flagnamecomment = 1; break; + case 's': flagdeletesender = 1; break; + case 'f': flagdeletefrom = 1; break; + case 'i': flagdeletemessid = 1; break; + case 'r': flaghackrecip = 1; break; + case 'm': flaghackmess = 1; break; + } + + mailhost = env_get("QMAILHOST"); + if (!mailhost) mailhost = env_get("MAILHOST"); + mailrhost = env_get("QMAILSHOST"); + if (!mailrhost) mailrhost = mailhost; + + mailuser = env_get("QMAILUSER"); + if (!mailuser) mailuser = env_get("MAILUSER"); + if (!mailuser) mailuser = env_get("USER"); + if (!mailuser) mailuser = env_get("LOGNAME"); + if (!mailuser) mailuser = "anonymous"; + mailusertokentype = TOKEN822_ATOM; + if (quote_need(mailuser,str_len(mailuser))) mailusertokentype = TOKEN822_QUOTE; + mailruser = env_get("QMAILSUSER"); + if (!mailruser) mailruser = mailuser; + + for (i = 0; i < H_NUM; ++i) htypeseen[i] = 0; + + recipstrategy = RECIP_DEFAULT; + flagqueue = 1; + + getcontrols(); + + if (!saa_readyplus(&hrlist,1)) die_nomem(); + if (!saa_readyplus(&tocclist,1)) die_nomem(); + if (!saa_readyplus(&hrrlist,1)) die_nomem(); + if (!saa_readyplus(&reciplist,1)) die_nomem(); + + while ((opt = getopt(argc,argv,"aAhHnNf:")) != opteof) + switch (opt) { + case 'a': recipstrategy = RECIP_ARGS; break; + case 'A': recipstrategy = RECIP_DEFAULT; break; + case 'h': recipstrategy = RECIP_HEADER; break; + case 'H': recipstrategy = RECIP_AH; break; + case 'n': flagqueue = 0; break; + case 'N': flagqueue = 1; break; + case 'f': + if (!quote2(&sender,optarg)) die_nomem(); + doordie(&sender,token822_parse(&envs,&sender,&envsbuf)); + token822_reverse(&envs); + rwgeneric(&envs); + token822_reverse(&envs); + if (token822_unquote(&sender,&envs) != 1) die_nomem(); + break; + case '?': + default: + perm(); + } + + argc -= optind; + argv += optind; + + if (recipstrategy == RECIP_DEFAULT) + recipstrategy = (*argv ? RECIP_ARGS : RECIP_HEADER); + + if (recipstrategy != RECIP_HEADER) + while (*argv) + dorecip(*argv++); + + flagrh = (recipstrategy != RECIP_ARGS); + + if (headerbody(buffer_0,doheaderfield,finishheader,dobody) == -1) + die_read(); + + exitnicely(); +} diff --git a/sqmail-4.3.07/src/qmail-ldapam.c b/sqmail-4.3.07/src/qmail-ldapam.c new file mode 100644 index 0000000..2d5b78f --- /dev/null +++ b/sqmail-4.3.07/src/qmail-ldapam.c @@ -0,0 +1,369 @@ +#include <sys/types.h> +#include <unistd.h> +#include <grp.h> +#include <pwd.h> +#include <ldap.h> +#include "auto_qmail.h" +#include "qmail.h" +#include "case.h" +#include "control.h" +#include "constmap.h" +#include "readwrite.h" +#include "buffer.h" +#include "fd.h" +#include "byte.h" +#include "case.h" +#include "str.h" +#include "stralloc.h" +#include "exit.h" +#include "logmsg.h" +#include "pathexec.h" +#include "getln.h" +#include "scan.h" + +#define WHO "qmail-ldapam" + +#define LDAP_SCOPE LDAP_SCOPE_SUBTREE + +#define MAIL_ACCOUNT_NAME "mail" +#define MAIL_ACCOUNT_UID 8 +#define MAIL_ACCOUNT_GID 12 + +#define FDAUTH 3 +#define FDPWD 5 +#define FLAG_DIR "-d" +#define FLAG_MAIL "-m" + +#define PORT_LDAP 389 +#define PORT_LDAPS 636 + +char authbuf[BUFSIZE_AUTH]; +buffer ba = BUFFER_INIT(write,FDAUTH,authbuf,sizeof(authbuf)); +char bspace[512]; +buffer bp; + +struct constmap mapldapauth; +stralloc ldapcntl = {0}; +stralloc disabled = {0}; + +/* LDAP binding params */ + +stralloc binddn = {0}; +stralloc bindpw = {0}; +stralloc bindpwds = {0}; +stralloc bindbase = {0}; +stralloc bindhost = {0}; +stralloc bindmbox = {0}; +stralloc filter = {0}; + +stralloc user = {0}; // user w/o domain appended +stralloc homeparam = {0}; + +unsigned long port = PORT_LDAP; + +void temp_nomem() +{ + logmsg(WHO,110,FATAL,"out of memory"); +} + +void exit(int fail) +{ + int i; + for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0; + _exit(fail); +} + +int match = 0; + +void read_passwd(void) +{ + if (!bindpwds.len) { + buffer_init(&bp,buffer_unixread,FDPWD,bspace,sizeof(bspace)); + if (getln(&bp,&bindpwds,&match,'\0') == -1) + logmsg(WHO,111,ERROR,"unable to read password"); + close(5); + if (match) --bindpwds.len; + } +} + +static int ldap_lookup(char *host,int port,char *user,char *pwd) +{ + char *attrs[] = { NULL }; + char *dn = 0; + LDAP *ld; + LDAPMessage *res, *entry; + int r; + + if ((ld = ldap_init(host,port)) == 0) + logmsg(WHO,110,ERROR,"Unable to initialise LDAP connection"); + +// if (starttls) +// ldap_start_tls_s(LDAP *ld, LDAPControl **serverctrls, LDAPControl **clientctrls); + + + r = ldap_simple_bind_s(ld,binddn.s,bindpw.s); + if (r) logmsg(WHO,110,ERROR,"can't bind with LDAP server"); + + r = ldap_search_s(ld,bindbase.s,LDAP_SCOPE,filter.s,attrs,0,&res); + if (r) logmsg(WHO,1,ERROR,B("search failed:",ldap_err2string(r))); + + entry = ldap_first_entry(ld,res); + if (!entry) return 1; + + dn = ldap_get_dn(ld,res); + ldap_msgfree(res); + r = ldap_simple_bind_s(ld,dn,pwd); + if (r) return 1; + + ldap_memfree(dn); + ldap_unbind(ld); + + return 0; +} + +static int ldap_userhome(char *host,int port,char *user,char *pwd,char *mbox) +{ + char *attrs[] = { NULL }; + char **values; + LDAP *ld; + LDAPMessage *res, *entry; + int r; + + if ((ld = ldap_init(host,port) == 0)) + logmsg(WHO,110,ERROR,"Unable to setup connection"); + + r = ldap_simple_bind_s(ld,binddn.s,bindpw.s); + if (r) logmsg(WHO,110,ERROR,"can't bind to LDAP server"); + + r = ldap_search_s(ld,bindbase.s,LDAP_SCOPE,filter.s,attrs,0,&res); + if (r) logmsg(WHO,1,ERROR,B("search failed: ",ldap_err2string(r))); + + entry = ldap_first_entry(ld,res); + if (!entry) return 1; + + values = ldap_get_values(ld,entry,mbox); + if (values && values[0]) { + if (!stralloc_copys(&homeparam,values[0])) temp_nomem(); + if (!stralloc_cats(&homeparam,"../../")) temp_nomem(); + if (!stralloc_0(&homeparam)) temp_nomem(); + } + + ldap_msgfree(res); + ldap_unbind(ld); + + return 0; +} + +static stralloc cafile = {0}; +static stralloc cadir = {0}; +static stralloc certfile = {0}; +static stralloc keyfile = {0}; +static stralloc certpwd = {0}; + +int main (int argc, char **argv) +{ + char *authuser = 0; + char *ldaparam = 0; + char *domain = 0; + char *password = 0; + char *host = 0; + int authlen = 0; + int buflen = 0; + int domlen = 0; + int flaghome = 0; + int flagmail = 0; + int i = 0; + int f, h, j, k, p, c, r, t, w; + int rc; + + if (!argv[1]) logmsg(WHO,100,USAGE,"qmail-ldapam [-d|-m] prog"); + if (!case_diffs(argv[1],FLAG_DIR)) { + if (!argv[2]) logmsg(WHO,100,USAGE,"qmail-ldapam [-d|-m] prog"); + flaghome = 1; + } else { + if (!case_diffs(argv[1],FLAG_MAIL)) { + if (!argv[2]) logmsg(WHO,100,USAGE,"qmail-ldapam [-d|-m] prog"); + flagmail = 1; + } + } + + for (;;) { + do + rc = read(FDAUTH,authbuf + buflen,sizeof(authbuf) - buflen); + while ((rc == -1) && (errno == EINTR)); + if (r == -1) exit(111); + if (rc == 0) break; + buflen += rc; + if (buflen >= sizeof(authbuf)) exit(2); + } + close(FDAUTH); + + authuser = authbuf + i; /* username */ + if (i == buflen) exit(2); + while (authbuf[i++]) /* password */ + if (i == buflen) exit(2); + password= authbuf + i; + if (i == buflen) exit(2); + + authlen = str_len(authuser); + if (!stralloc_copyb(&user,authuser,authlen)) temp_nomem(); + + if ((i = byte_rchr(authuser,authlen,'@'))) /* @domain */ + if (i < authlen && authuser[i] == '@') { + domain = authuser + i; + domlen = str_len(domain); + case_lowerb(domain,domlen); + user.len = 0; + if (!stralloc_copyb(&user,authuser,i)) temp_nomem(); + } + if (!stralloc_0(&user)) exit(111); + + /* Read control file users/ldapauth and go for checks */ + + if (chdir(auto_qmail) == -1) exit(110); + + switch (control_readfile(&ldapcntl,"control/ldapauth",0)) { + case -1: exit(110); + case 0: if (!constmap_init(&mapldapauth,"",0,1)) temp_nomem(); + case 1: if (!constmap_init(&mapldapauth,ldapcntl.s,ldapcntl.len,1)) temp_nomem(); + } + + /* Check for disabled authuser/domains */ + + if (!stralloc_copys(&disabled,"!")) temp_nomem(); + if (!stralloc_catb(&disabled,authuser,authlen)) temp_nomem(); + if (constmap(&mapldapauth,disabled.s,disabled.len)) temp_nomem(); + + if (domlen) { + disabled.len = 0; + if (!stralloc_copys(&disabled,"!")) temp_nomem(); + if (!stralloc_catb(&disabled,domain,domlen)) temp_nomem(); + if (constmap(&mapldapauth,disabled.s,disabled.len)) temp_nomem(); + } + + if (!ldaparam && domlen) + ldaparam = constmap(&mapldapauth,domain,domlen); // 1. ldap server by domain + if (!ldaparam) + ldaparam = constmap(&mapldapauth,"*",1); // 2. one ldap for all + + if (!ldaparam) exit(1); + + /* Evaluate LDAP lookup params: i j h p t c w f h + Host:Bind_DN|Bind_PW|Base|Host;Port|CA|Cert:Pwd|Filter:Homedir */ + + if (!stralloc_copys(&bindhost,"localhost")) temp_nomem(); /* Default LDAP host */ + if (!stralloc_copys(&bindmbox,"homeDirectory")) temp_nomem(); /* Default POSIX name*/ + + i = str_chr(ldaparam,'|'); /* Bind DN */ + if (ldaparam[i] == '|') { + ldaparam[i] = 0; + + j = str_chr(ldaparam + i,'|'); /* Bind PWD */ + if (ldaparam[i + j + 1] == '|') { + ldaparam[i + j + 1] = 0; + + k = str_chr(ldaparam + i + j + 2,'|'); /* Base */ + if (ldaparam[i + j + k + 2] == '|') { + ldaparam[i + j + k + 2] = 0; + if (!stralloc_copys(&bindbase,ldaparam + i + j + 2)) temp_nomem(); + + p = str_chr(ldaparam + i + j + k + 3,';'); /* Host;Port */ + if (ldaparam[i + j + k + p + 3] == ';') { + ldaparam[i + j + k + p + 2] = 0; + if (p > 0) scan_ulong(ldaparam + i + j + k + p + 4,&port); + if (!stralloc_copys(&bindhost,ldaparam + i + j + k + 3)) temp_nomem(); + + t = str_chr(ldaparam + i + j + k + 3,'|'); /* Trust Cert */ + if (ldaparam[i + j + k + t + 3] == '|') { + ldaparam[i + j + k + t + 3] = 0; + if (ldaparam[i + j + k + t + 2] == '/') { + if (!stralloc_copys(&cadir,ldaparam + i + j + k + t + 3)) temp_nomem(); + if (!stralloc_0(&cadir)) temp_nomem(); + } else { + if (!stralloc_copys(&cafile,ldaparam + i + j + k + t + 3)) temp_nomem(); + if (!stralloc_0(&cafile)) temp_nomem(); + } + + w = str_chr(ldaparam + i + j + k + t + 4,':'); /* Cert:Pwd */ + if (ldaparam[i + j + k + t + w + 4] == ':') { + ldaparam[i + j + k + t + w + 4] = 0; + if (!stralloc_copys(&certfile,ldaparam + i + j + k + t + 4)) temp_nomem(); + + f = str_chr(ldaparam + i + j + k + t + w + 5,':'); /* Filter */ + if (ldaparam[i + j + k + t + w + 4] == '|') { + ldaparam[i + j + k + t + w + 4] = 0; + if (!stralloc_copys(&certpwd,ldaparam + i + j + k + f + 4)) temp_nomem(); + + h = str_chr(ldaparam + i + j + k + 3,'|'); /* Homedir */ + if (ldaparam[i + j + k + h + 3] == '|') { + ldaparam[i + j + k + t + 3] = 0; + if (!stralloc_copys(&bindmbox,ldaparam + i + j + k + 3)) temp_nomem(); + if (!stralloc_0(&bindmbox)) temp_nomem(); + } + + } // f + } // c + } // t + } // k + } // j + + if (!stralloc_copys(&bindpw,ldaparam + i + 1)) temp_nomem(); + if (!stralloc_0(&bindpw)) temp_nomem(); + } // i + if (!stralloc_copys(&binddn,ldaparam)) temp_nomem(); + if (!stralloc_0(&binddn)) temp_nomem(); + } + + if (flagmail) { /* LDAP filter */ + if (!stralloc_copys(&filter,"(&(mail=")) temp_nomem(); + if (!stralloc_cats(&filter,authuser)) temp_nomem(); + if (!stralloc_cats(&filter,"))")) temp_nomem(); + if (!stralloc_0(&filter)) temp_nomem(); + } else { + if (!stralloc_copys(&filter,"(&uid=")) temp_nomem(); + if (!stralloc_cat(&filter,&user)) temp_nomem(); + if (!stralloc_cats(&filter,")(dc=")) temp_nomem(); + if (!stralloc_cats(&filter,host)) temp_nomem(); + if (!stralloc_cats(&filter,"))")) temp_nomem(); + if (!stralloc_0(&filter)) temp_nomem(); + } + + if (!str_diff(bindpw.s,"*")) { + read_passwd(); + + for (i = 0; i < bindpwds.len; i++) { + if (bindpwds.s[i] == ' ') { + if (!stralloc_copyb(&bindpw,bindpwds.s,i - 1)) temp_nomem(); + if (!stralloc_0(&bindpw)) temp_nomem(); + host = bindhost.s; + if (!ldap_lookup(host,port,authuser,password)) break; + bindpwds.s = bindpwds.s + i; + ++i; + } + } + logmsg(WHO,110,ERROR,B("can't bind to LDAP host: ",host)); + } else + if (ldap_lookup(host,port,authuser,password)) + logmsg(WHO,110,ERROR,B("can't bind to LDAP host: ",host)); + + /* Now we check the user's mailbox for POP3 and IMAP4 capabilities */ + + if (flaghome) { + if (ldap_userhome(host,port,authuser,password,bindmbox.s)) exit(1); + + if (initgroups(MAIL_ACCOUNT_NAME, MAIL_ACCOUNT_GID)) + logmsg(WHO,107,ERROR,B("Unable to set supplementary groups: ",strerror(errno))); + if (setgid(MAIL_ACCOUNT_GID)) + logmsg(WHO,106,ERROR,B("Unable to set gid: ",strerror(errno))); + if (setuid(MAIL_ACCOUNT_UID)) + logmsg(WHO,105,ERROR,B("Unable to set uid: ",strerror(errno))); + if (chdir(homeparam.s)) + logmsg(WHO,108,ERROR,B("Unable to change to home dir: ",homeparam.s,strerror(errno))); + } + + for (i = 0; i < sizeof(authbuf); ++i) authbuf[i] = 0; + + if (flaghome || flagmail) pathexec(argv + 2); + else pathexec(argv + 1); + exit(111); +} diff --git a/sqmail-4.3.07/src/qmail-local.c b/sqmail-4.3.07/src/qmail-local.c new file mode 100644 index 0000000..990eb8f --- /dev/null +++ b/sqmail-4.3.07/src/qmail-local.c @@ -0,0 +1,725 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include "sig.h" +#include "env.h" +#include "byte.h" +#include "exit.h" +#include "open.h" +#include "wait.h" +#include "lock.h" +#include "seek.h" +#include "buffer.h" +#include "getln.h" +#include "getoptb.h" +#include "alloc.h" +#include "logmsg.h" +#include "stralloc.h" +#include "fmt.h" +#include "str.h" +#include "now.h" +#include "case.h" +#include "quote.h" +#include "qmail.h" +#include "readclose.h" +#include "myctime.h" +#include "gfrom.h" +#include "auto_break.h" +#include "auto_patrn.h" + +/** + @file qmail-local + local delivery agent to Mailbox and Maildir + includes patches from Matthias Andree and Toby Betts + @return 0 ok; > 0 failure, 111 temp failure + */ + +#define WHO "qmail-local" + +void usage() { logmsg(WHO,100,USAGE,"qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); } + +void temp_nomem() { logmsg(WHO,111,FATAL,"Out of memory. (#4.3.0)"); } +void temp_rewind() { logmsg(WHO,111,FATAL,"Unable to rewind message. (#4.3.0)"); } +void temp_childcrashed() { logmsg(WHO,111,FATAL,"Aack, child crashed. (#4.3.0)"); } +void temp_fork() { logmsg(WHO,111,FATAL,B("Unable to fork: ",error_str(errno),". (#4.3.0)")); } +void temp_read() { logmsg(WHO,111,ERROR,B("Unable to read message: ",error_str(errno),". (#4.3.0)")); } +void temp_slowlock() +{ logmsg(WHO,111,ERROR,"File has been locked for 30 seconds straight. (#4.3.0)"); } +void temp_qmail(fn) char *fn; +{ logmsg(WHO,111,FATAL,B("Unable to open: ",fn," ",error_str(errno),". (#4.3.0)")); } + +int flagdoit; +int flag99; + +char *user; +char *homedir; +char *local; +char *dash; +char *ext; +char *host; +char *sender; +char *aliasempty; + +stralloc safeext = {0}; +stralloc ufline = {0}; +stralloc rpline = {0}; +stralloc envrecip = {0}; +stralloc dtline = {0}; +stralloc qme = {0}; +stralloc ueo = {0}; +stralloc cmds = {0}; +stralloc messline = {0}; +stralloc foo = {0}; +stralloc hostname = {0}; + +char inbuf[BUFSIZE_LINE]; +char outbuf[BUFSIZE_LINE]; + +/* child process */ + +char fntmptph[80 + FMT_ULONG * 2]; +char fnnewtph[80 + FMT_ULONG * 2]; +void tryunlinktmp() { unlink(fntmptph); } +void sigalrm() { tryunlinktmp(); _exit(3); } + +void maildir_child(char *dir) +{ + unsigned long pid; + struct timeval time; + char host[64]; + char *s; + int loop; + struct stat st; + int fd; + buffer bi; + buffer bo; + + sig_alarmcatch(sigalrm); + if (chdir(dir) == -1) { if (errno != ENOENT) _exit(1); _exit(2); } + pid = getpid(); + host[0] = 0; + gethostname(host,sizeof(host)); + + s = host; + for (loop = 0; loop < str_len(host); ++loop) { + if (host[loop] == '/') { + if (!stralloc_cats(&hostname,"\\057")) temp_nomem(); + continue; + } + if (host[loop] == ':') { + if (!stralloc_cats(&hostname,"\\072")) temp_nomem(); + continue; + } + if (!stralloc_append(&hostname,s+loop)) temp_nomem(); + } + + for (loop = 0 ;; ++loop) { + gettimeofday(&time,0); + s = fntmptph; + s += fmt_str(s,"tmp/"); + s += fmt_ulong(s,time.tv_sec); *s++ = '.'; + *s++ = 'M'; s += fmt_ulong(s,time.tv_usec); + *s++ = 'P'; s += fmt_ulong(s,pid); *s++ = '.'; + s += fmt_strn(s,hostname.s,hostname.len); *s++ = 0; + + if (stat(fntmptph,&st) == -1) if (errno == ENOENT) break; + /* really should never get to this point */ + if (loop == 2) _exit(1); + sleep(2); + } + + alarm(86400); + fd = open_excl(fntmptph); + if (fd == -1) _exit(1); + + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + buffer_init(&bo,write,fd,outbuf,sizeof(outbuf)); + if (buffer_put(&bo,rpline.s,rpline.len) == -1) goto FAIL; + if (buffer_put(&bo,dtline.s,dtline.len) == -1) goto FAIL; + + switch (buffer_copy(&bo,&bi)) { + case -2: tryunlinktmp(); _exit(4); + case -3: goto FAIL; + } + + if (buffer_flush(&bo) == -1) goto FAIL; + if (fstat(fd,&st) == -1) goto FAIL; + if (fsync(fd) == -1) goto FAIL; + if (close(fd) == -1) goto FAIL; /* NFS dorks */ + + s = fnnewtph; + s += fmt_str(s,"new/"); + s += fmt_ulong(s,time.tv_sec); *s++ = '.'; + + /* in hexadecimal */ + *s++ = 'I'; s += fmt_xlong(s,st.st_ino); + *s++ = 'V'; s += fmt_xlong(s,st.st_dev); + + /* in decimal */ + *s++ = 'M'; s += fmt_ulong(s,time.tv_usec); + *s++ = 'P'; s += fmt_ulong(s,pid); *s++ = '.'; + + s += fmt_strn(s,hostname.s,hostname.len); *s++ = 0; + + if (link(fntmptph,fnnewtph) == -1) goto FAIL; + if ((fd = open(fnnewtph,O_RDONLY)) < 0 || fsync(fd) < 0 || close(fd)) goto FAIL; + /* DJB: if it was error_exist, almost certainly successful; i hate NFS -- FEH: Reiser patch */ + tryunlinktmp(); _exit(0); + + FAIL: tryunlinktmp(); _exit(1); +} + +/* end child process */ + +void maildir(char *fn) +{ + int child; + int wstat; + + if (seek_begin(0) == -1) temp_rewind(); + + switch (child = fork()) { + case -1: + temp_fork(); + case 0: + maildir_child(fn); + _exit(111); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) + temp_childcrashed(); + + switch (wait_exitcode(wstat)) { + case 0: break; + case 2: logmsg(WHO,111,ERROR,"Unable to chdir to maildir. (#4.2.1)"); + case 3: logmsg(WHO,111,ERROR,"Timeout on maildir delivery. (#4.3.0)"); + case 4: logmsg(WHO,111,ERROR,"Unable to read message. (#4.3.0)"); + default: logmsg(WHO,111,ERROR,"Temporary error on maildir delivery. (#4.3.0)"); + } +} + +void mailfile(char *fn) +{ + int fd; + buffer bi; + buffer bo; + int match; + seek_pos pos; + int flaglocked; + + if (seek_begin(0) == -1) temp_rewind(); + + fd = open_append(fn); + if (fd == -1) + logmsg(WHO,111,ERROR,B("Unable to open:",fn," ",error_str(errno),". (#4.2.1)")); + + sig_alarmcatch(temp_slowlock); + alarm(30); + flaglocked = (lock_ex(fd) != -1); + alarm(0); + sig_alarmdefault(); + + seek_end(fd); + pos = seek_cur(fd); + + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + buffer_init(&bo,write,fd,outbuf,sizeof(outbuf)); + if (buffer_put(&bo,ufline.s,ufline.len)) goto WRITERRS; + if (buffer_put(&bo,rpline.s,rpline.len)) goto WRITERRS; + if (buffer_put(&bo,dtline.s,dtline.len)) goto WRITERRS; + + for (;;) { + if (getln(&bi,&messline,&match,'\n') != 0) { + logmsg(WHO,0,WARN,B("Unable to read message: ",error_str(errno),". (#4.3.0)")); + if (flaglocked) seek_trunc(fd,pos); + close(fd); + _exit(111); + } + if (!match && !messline.len) break; + if (gfrom(messline.s,messline.len)) + if (buffer_puts(&bo,">")) goto WRITERRS; + if (buffer_put(&bo,messline.s,messline.len)) goto WRITERRS; + if (!match) { + if (buffer_puts(&bo,"\n")) goto WRITERRS; + break; + } + } + + if (buffer_puts(&bo,"\n")) goto WRITERRS; + if (buffer_flush(&bo)) goto WRITERRS; + if (fsync(fd) == -1) goto WRITERRS; + close(fd); + return; + + WRITERRS: + logmsg(WHO,0,WARN,B("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)")); + if (flaglocked) seek_trunc(fd,pos); + close(fd); + _exit(111); +} + +void mailprogram(char *prog) +{ + int child; + char *(args[4]); + int wstat; + + if (seek_begin(0) == -1) temp_rewind(); + + switch (child = fork()) { + case -1: + temp_fork(); + case 0: + args[0] = "/bin/sh"; + args[1] = "-c"; + args[2] = prog; + args[3] = 0; + sig_pipedefault(); + execv(*args,args); + logmsg(WHO,0,ERROR,B("Unable to run /bin/sh: ",error_str(errno),". (#4.3.0)")); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) + temp_childcrashed(); + + switch (wait_exitcode(wstat)) { + case 100: + case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100); + case 0: break; + case 99: flag99 = 1; break; + default: _exit(111); + } +} + +unsigned long mailforward_qp = 0; + +void mailforward(char **recips) +{ + struct qmail qqt; + char *qqx; + buffer bi; + int match; + + if (seek_begin(0) == -1) temp_rewind(); + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + + if (qmail_open(&qqt) == -1) temp_fork(); + mailforward_qp = qmail_qp(&qqt); + qmail_put(&qqt,dtline.s,dtline.len); + + do { + if (getln(&bi,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; } + qmail_put(&qqt,messline.s,messline.len); + } while (match); + + qmail_from(&qqt,ueo.s); + while (*recips) qmail_to(&qqt,*recips++); + qqx = qmail_close(&qqt); + if (!*qqx) return; + logmsg(WHO,*qqx == 'D' ? 100 : 111,ERROR,B("Unable to forward message: ",qqx + 1,".")); +} + +void bouncexf() +{ + int match; + buffer bi; + + if (seek_begin(0) == -1) temp_rewind(); + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + + for (;;) { + if (getln(&bi,&messline,&match,'\n') != 0) temp_read(); + if (!match) break; + if (messline.len <= 1) + break; + if (messline.len == dtline.len) + if (!str_diffn(messline.s,dtline.s,dtline.len)) + logmsg(WHO,100,ERROR,"This message is looping: it already has my Delivered-To line. (#5.4.6)"); + } +} + +void checkhome() +{ + struct stat st; + + if (stat(".",&st) == -1) + logmsg(WHO,111,ERROR,B("Unable to stat home directory: ",error_str(errno),". (#4.3.0)")); + if (st.st_mode & auto_patrn) + logmsg(WHO,111,ERROR,"Uh-oh: home directory is writable. (#4.7.0)"); + if (st.st_mode & 01000) + if (flagdoit) + logmsg(WHO,111,ERROR,"Home directory is sticky: user is editing his .qmail file. (#4.2.1)"); + else + logmsg(WHO,0,WARN,"Warning: home directory is sticky."); +} + +int qmeox(char *dashowner) +{ + struct stat st; + + if (!stralloc_copys(&qme,".qmail")) temp_nomem(); + if (!stralloc_cats(&qme,dash)) temp_nomem(); + if (!stralloc_cat(&qme,&safeext)) temp_nomem(); + if (!stralloc_cats(&qme,dashowner)) temp_nomem(); + if (!stralloc_0(&qme)) temp_nomem(); + + if (stat(qme.s,&st) == -1) { + if (errno != ENOENT) temp_qmail(qme.s); + return -1; + } + return 0; +} + +int qmeexists(int *fd,int *cutable) +{ + struct stat st; + + if (!stralloc_0(&qme)) temp_nomem(); + + *fd = open_read(qme.s); + if (*fd == -1) { + if (errno != ENOENT) temp_qmail(qme.s); + if (errno == EPERM) temp_qmail(qme.s); + if (errno == EACCES) temp_qmail(qme.s); + return 0; + } + + if (fstat(*fd,&st) == -1) temp_qmail(qme.s); + if ((st.st_mode & S_IFMT) == S_IFREG) { + if (st.st_mode & auto_patrn) + logmsg(WHO,111,ERROR,"Uh-oh: .qmail file is writable. (#4.7.0)"); + *cutable = !!(st.st_mode & 0100); + return 1; + } + close(*fd); + return 0; +} + +/* "" "": "" */ +/* "-/" "": "-/" "-/default" */ +/* "-/" "a": "-/a" "-/default" */ +/* "-/" "a-": "-/a-" "-/a-default" "-/default" */ +/* "-/" "a-b": "-/a-b" "-/a-default" "-/default" */ +/* "-/" "a-b-": "-/a-b-" "-/a-b-default" "-/a-default" "-/default" */ +/* "-/" "a-b-c": "-/a-b-c" "-/a-b-default" "-/a-default" "-/default" */ + +void qmesearch(int *fd,int *cutable) +{ + int i; + + if (!stralloc_copys(&qme,".qmail")) temp_nomem(); + if (!stralloc_cats(&qme,dash)) temp_nomem(); + if (!stralloc_cat(&qme,&safeext)) temp_nomem(); + if (qmeexists(fd,cutable)) { + if (safeext.len >= 7) { + i = safeext.len - 7; + if (!byte_diff("default",7,safeext.s + i)) + if (i <= str_len(ext)) /* paranoia */ + if (!env_put("DEFAULT",ext + i)) temp_nomem(); + } + return; + } + + for (i = safeext.len; i >= 0 ;--i) + if (!i || (safeext.s[i - 1] == '-')) { + if (!stralloc_copys(&qme,".qmail")) temp_nomem(); + if (!stralloc_cats(&qme,dash)) temp_nomem(); + if (!stralloc_catb(&qme,safeext.s,i)) temp_nomem(); + if (!stralloc_cats(&qme,"default")) temp_nomem(); + if (qmeexists(fd,cutable)) { + if (i <= str_len(ext)) /* paranoia */ + if (!env_put("DEFAULT",ext + i)) temp_nomem(); + return; + } + } + + *fd = -1; +} + +unsigned long count_file = 0; +unsigned long count_forward = 0; +unsigned long count_program = 0; +char count_buf[FMT_ULONG]; +char buflog[BUFSIZE_LOG]; +buffer bl = BUFFER_INIT(write,1,buflog,sizeof(buflog)); + +void count_print() +{ + buffer_puts(&bl,"did "); + buffer_put(&bl,count_buf,fmt_ulong(count_buf,count_file)); + buffer_puts(&bl,"+"); + buffer_put(&bl,count_buf,fmt_ulong(count_buf,count_forward)); + buffer_puts(&bl,"+"); + buffer_put(&bl,count_buf,fmt_ulong(count_buf,count_program)); + buffer_puts(&bl,"\n"); + + if (mailforward_qp) { + buffer_puts(&bl,"qp "); + buffer_put(&bl,count_buf,fmt_ulong(count_buf,mailforward_qp)); + buffer_puts(&bl,"\n"); + } + buffer_flush(&bl); +} + +void sayit(char *type,char *cmd,int len) +{ + buffer_puts(&bl,type); + buffer_put(&bl,cmd,len); + buffer_putsflush(&bl,"\n"); +} + +int main(int argc,char **argv) +{ + int opt; + int i, j, k; + int fd; + int numforward; + char **recips; + datetime_sec starttime; + int flagforwardonly; + char *x; + + umask(077); + sig_pipeignore(); + + if (!env_init()) temp_nomem(); + + flagdoit = 1; + while ((opt = getopt(argc,argv,"nN")) != opteof) + switch (opt) { + case 'n': flagdoit = 0; break; + case 'N': flagdoit = 1; break; + default: usage(); + } + argc -= optind; + argv += optind; + + if (!(user = *argv++)) usage(); + if (!(homedir = *argv++)) usage(); + if (!(local = *argv++)) usage(); + if (!(dash = *argv++)) usage(); + if (!(ext = *argv++)) usage(); + if (!(host = *argv++)) usage(); + if (!(sender = *argv++)) usage(); + if (!(aliasempty = *argv++)) usage(); + if (*argv) usage(); + + if (homedir[0] != '/') usage(); + if (chdir(homedir) == -1) + logmsg(WHO,111,ERROR,B("Unable to switch to: ",homedir," ",error_str(errno),". (#4.3.0)")); + checkhome(); + + if (!env_put("HOST",host)) temp_nomem(); + if (!env_put("HOME",homedir)) temp_nomem(); + if (!env_put("USER",user)) temp_nomem(); + if (!env_put("LOCAL",local)) temp_nomem(); + +#ifdef HIDEVIRTUALUSER + if (str_len(ext) > 1) { + i = str_chr(local,*auto_break); + if (!stralloc_copys(&envrecip,local + i + 1)) temp_nomem(); + } else +#endif + if (!stralloc_copys(&envrecip,local)) temp_nomem(); + if (!stralloc_cats(&envrecip,"@")) temp_nomem(); + if (!stralloc_cats(&envrecip,host)) temp_nomem(); + + if (!stralloc_copy(&foo,&envrecip)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put("RECIPIENT",foo.s)) temp_nomem(); + + if (!stralloc_copys(&dtline,"Delivered-To: ")) temp_nomem(); + if (!stralloc_cat(&dtline,&envrecip)) temp_nomem(); + + for (i = 0; i < dtline.len; ++i) + if (dtline.s[i] == '\n') dtline.s[i] = '_'; + if (!stralloc_cats(&dtline,"\n")) temp_nomem(); + + if (!stralloc_copy(&foo,&dtline)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put("DTLINE",foo.s)) temp_nomem(); + + if (flagdoit) bouncexf(); + + if (!env_put("SENDER",sender)) temp_nomem(); + + if (!quote2(&foo,sender)) temp_nomem(); + if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem(); + if (!stralloc_cat(&rpline,&foo)) temp_nomem(); + for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_'; + if (!stralloc_cats(&rpline,">\n")) temp_nomem(); + + if (!stralloc_copy(&foo,&rpline)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put("RPLINE",foo.s)) temp_nomem(); + + if (!stralloc_copys(&ufline,"From ")) temp_nomem(); + + if (*sender) { + int len; int i; char ch; + + len = str_len(sender); + if (!stralloc_readyplus(&ufline,len)) temp_nomem(); + + for (i = 0;i < len;++i) { + ch = sender[i]; + if ((ch == ' ') || (ch == '\t') || (ch == '\n')) ch = '-'; + ufline.s[ufline.len + i] = ch; + } + ufline.len += len; + } else + if (!stralloc_cats(&ufline,"MAILER-DAEMON")) temp_nomem(); + + if (!stralloc_cats(&ufline," ")) temp_nomem(); + starttime = now(); + if (!stralloc_cats(&ufline,myctime(starttime))) temp_nomem(); + + if (!stralloc_copy(&foo,&ufline)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put("UFLINE",foo.s)) temp_nomem(); + + x = ext; + if (!env_put("EXT",x)) temp_nomem(); + x += str_chr(x,'-'); if (*x) ++x; + if (!env_put("EXT2",x)) temp_nomem(); + x += str_chr(x,'-'); if (*x) ++x; + if (!env_put("EXT3",x)) temp_nomem(); + x += str_chr(x,'-'); if (*x) ++x; + if (!env_put("EXT4",x)) temp_nomem(); + + if (!stralloc_copys(&safeext,ext)) temp_nomem(); + case_lowerb(safeext.s,safeext.len); + + for (i = 0; i < safeext.len; ++i) + if (safeext.s[i] == '.') + safeext.s[i] = ':'; + + i = str_len(host); + i = byte_rchr(host,i,'.'); + if (!stralloc_copyb(&foo,host,i)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put("HOST2",foo.s)) temp_nomem(); + i = byte_rchr(host,i,'.'); + if (!stralloc_copyb(&foo,host,i)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put("HOST3",foo.s)) temp_nomem(); + i = byte_rchr(host,i,'.'); + if (!stralloc_copyb(&foo,host,i)) temp_nomem(); + if (!stralloc_0(&foo)) temp_nomem(); + if (!env_put("HOST4",foo.s)) temp_nomem(); + + flagforwardonly = 0; + qmesearch(&fd,&flagforwardonly); + + if (fd == -1) + if (*dash) + logmsg(WHO,100,ERROR,"Sorry, no mailbox here by that name. (#5.1.1)"); + + if (!stralloc_copys(&ueo,sender)) temp_nomem(); + if (str_diff(sender,"")) + if (str_diff(sender,"#@[]")) + if (qmeox("-owner") == 0) { + if (qmeox("-owner-default") == 0) { + if (!stralloc_copys(&ueo,local)) temp_nomem(); + if (!stralloc_cats(&ueo,"-owner-@")) temp_nomem(); + if (!stralloc_cats(&ueo,host)) temp_nomem(); + if (!stralloc_cats(&ueo,"-@[]")) temp_nomem(); + } else { + if (!stralloc_copys(&ueo,local)) temp_nomem(); + if (!stralloc_cats(&ueo,"-owner@")) temp_nomem(); + if (!stralloc_cats(&ueo,host)) temp_nomem(); + } + } + if (!stralloc_0(&ueo)) temp_nomem(); + if (!env_put("NEWSENDER",ueo.s)) temp_nomem(); + + if (!stralloc_ready(&cmds,0)) temp_nomem(); + cmds.len = 0; + + if (fd != -1) + if (readclose_append(fd,&cmds,256) == -1) temp_nomem(); + + if (!cmds.len) { + if (!stralloc_copys(&cmds,aliasempty)) temp_nomem(); + flagforwardonly = 0; + } + if (!cmds.len || (cmds.s[cmds.len - 1] != '\n')) + if (!stralloc_cats(&cmds,"\n")) temp_nomem(); + + numforward = 0; + i = 0; + + for (j = 0; j < cmds.len; ++j) + if (cmds.s[j] == '\n') { + switch (cmds.s[i]) { + case '#': case '.': case '/': case '|': break; + default: ++numforward; + } + i = j + 1; + } + + recips = (char **) alloc((numforward + 1) * sizeof(char *)); + if (!recips) temp_nomem(); + numforward = 0; + + flag99 = 0; + + i = 0; + for (j = 0; j < cmds.len; ++j) + if (cmds.s[j] == '\n') { + cmds.s[j] = 0; + k = j; + /* Patch contributed by Erik Sjolund <erik.sjolund@gmail.com>. */ + while ((k > i) && ((cmds.s[k - 1] == ' ') || (cmds.s[k - 1] == '\t'))) + cmds.s[--k] = 0; + switch (cmds.s[i]) { + case 0: /* k == i */ + if (i) break; + logmsg(WHO,111,ERROR,"Uh-oh: first line of .qmail file is blank. (#4.2.1)"); + case '#': + break; + case '.': + case '/': + ++count_file; + if (flagforwardonly) logmsg(WHO,111,ERROR,"Uh-oh: .qmail has file delivery but has x bit set. (#4.7.0)"); + if (cmds.s[k - 1] == '/') + if (flagdoit) maildir(cmds.s + i); + else sayit("maildir ",cmds.s + i,k - i); + else + if (flagdoit) mailfile(cmds.s + i); + else sayit("mbox ",cmds.s + i,k - i); + break; + case '|': + ++count_program; + if (flagforwardonly) logmsg(WHO,111,ERROR,"Uh-oh: .qmail has prog delivery but has x bit set. (#4.7.0)"); + if (flagdoit) mailprogram(cmds.s + i + 1); + else sayit("program ",cmds.s + i + 1,k - i - 1); + break; + case '+': + if (str_equal(cmds.s + i + 1,"list")) + flagforwardonly = 1; + break; + case '&': + ++i; + default: + ++count_forward; + if (flagdoit) recips[numforward++] = cmds.s + i; + else sayit("forward ",cmds.s + i,k - i); + break; + } + i = j + 1; + if (flag99) break; + } + + if (numforward) if (flagdoit) { + recips[numforward] = 0; + mailforward(recips); + } + + count_print(); + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-lspawn.c b/sqmail-4.3.07/src/qmail-lspawn.c new file mode 100644 index 0000000..fed5c4c --- /dev/null +++ b/sqmail-4.3.07/src/qmail-lspawn.c @@ -0,0 +1,241 @@ +#include <unistd.h> +#include "fd.h" +#include "wait.h" +#include "prot.h" +#include "buffer.h" +#include "stralloc.h" +#include "scan.h" +#include "exit.h" +#include "cdbread.h" +#include "case.h" +#include "readclose.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "qlx.h" +#include "error.h" +#include "open.h" +#include "byte.h" + +char *aliasempty; + +void initialize(int argc,char **argv) +{ + aliasempty = argv[1]; + if (!aliasempty) _exit(100); +} + +int truncreport = 3000; + +void report(buffer *log,int wstat,char *s,int len) +{ + int i; + if (wait_crashed(wstat)) { buffer_putsflush(log,"Zqmail-lspawn: qmail-local crashed.\n"); return; } + + switch (wait_exitcode(wstat)) { + case QLX_CDB: + buffer_putsflush(log,"Zqmail-lspawn: Trouble reading users/assign.cdb.\n"); return; + case QLX_NOMEM: + buffer_putsflush(log,"Zqmail-lspawn: Out of memory.\n"); return; + case QLX_SYS: + buffer_putsflush(log,"Zqmail-lspawn: Temporary failure.\n"); return; + case QLX_NOALIAS: + buffer_putsflush(log,"Zqmail-lspawn: Unable to find alias user!\n"); return; + case QLX_ROOT: + buffer_putsflush(log,"Zqmail-spawn: Not allowed to perform deliveries as root.\n"); return; + case QLX_USAGE: + buffer_putsflush(log,"Zqmail-spawn: Internal bug.\n"); return; + case QLX_NFS: + buffer_putsflush(log,"Zqmail-spawn: NFS failure in qmail-local.\n"); return; + case QLX_EXECHARD: + buffer_putsflush(log,"Dqmail-spawn: Unable to run qmail-local.\n"); return; + case QLX_EXECSOFT: + buffer_putsflush(log,"Zqmail-spawn: Unable to run qmail-local.\n"); return; + case QLX_EXECPW: + buffer_putsflush(log,"Zqmail-spawn: Unable to run qmail-getpw.\n"); return; + case 111: case 71: case 74: case 75: + buffer_put(log,"Z",1); break; + case 0: + buffer_put(log,"K",1); break; + case 100: + default: + buffer_put(log,"D",1); break; + } + + for (i = 0; i < len; ++i) + if (!s[i]) break; + + buffer_put(log,s,i); +} + +stralloc lower = {0}; +stralloc nughde = {0}; +stralloc wildchars = {0}; + +static struct cdb c; + +void nughde_get(char *local) +{ + char *(args[3]); + int pi[2]; + int gpwpid; + int gpwstat; + int r; + int fd; + int flagwild; + + if (!stralloc_copys(&lower,"!")) _exit(QLX_NOMEM); + if (!stralloc_cats(&lower,local)) _exit(QLX_NOMEM); + if (!stralloc_0(&lower)) _exit(QLX_NOMEM); + case_lowerb(lower.s,lower.len); + + if (!stralloc_copys(&nughde,"")) _exit(QLX_NOMEM); + + fd = open_read("users/assign.cdb"); + if (fd == -1) + if (errno != ENOENT) + _exit(QLX_CDB); + + if (fd != -1) { + unsigned int i; + + cdb_init(&c,fd); + r = cdb_find(&c,"",0); + if (r != 1) _exit(QLX_CDB); + if (!stralloc_ready(&wildchars,cdb_datalen(&c))) _exit(QLX_NOMEM); + wildchars.len = cdb_datalen(&c); + if (cdb_read(&c,wildchars.s,wildchars.len,cdb_datapos(&c)) == -1) _exit(QLX_CDB); + + i = lower.len; + flagwild = 0; + + do { + /* i > 0 */ + if (!flagwild || (i == 1) || (byte_chr(wildchars.s,wildchars.len,lower.s[i - 1]) < wildchars.len)) { + r = cdb_find(&c,lower.s,i); + if (r == -1) _exit(QLX_CDB); + if (r == 1) { + if (!stralloc_ready(&nughde,cdb_datalen(&c))) _exit(QLX_NOMEM); + nughde.len = cdb_datalen(&c); + if (cdb_read(&c,nughde.s,nughde.len,cdb_datapos(&c)) == -1) _exit(QLX_CDB); + if (flagwild) + if (!stralloc_cats(&nughde,local + i - 1)) _exit(QLX_NOMEM); + if (!stralloc_0(&nughde)) _exit(QLX_NOMEM); + close(fd); + return; + } + } + --i; + flagwild = 1; + } while (i); + + close(fd); + } + + if (pipe(pi) == -1) _exit(QLX_SYS); + + args[0] = "bin/qmail-getpw"; + args[1] = local; + args[2] = 0; + switch (gpwpid = fork()) { + case -1: + _exit(QLX_SYS); + case 0: + if (prot_gid(auto_gidn) == -1) _exit(QLX_USAGE); + if (prot_uid(auto_uidp) == -1) _exit(QLX_USAGE); + close(pi[0]); + if (fd_move(1,pi[1]) == -1) _exit(QLX_SYS); + execv(*args,args); + _exit(QLX_EXECPW); + } + close(pi[1]); + + if (readclose_append(pi[0],&nughde,128) == -1) _exit(QLX_SYS); + + if (wait_pid(&gpwstat,gpwpid) != -1) { + if (wait_crashed(gpwstat)) _exit(QLX_SYS); + if (wait_exitcode(gpwstat) != 0) _exit(wait_exitcode(gpwstat)); + } +} + +int spawn(int fdmess,int fdout,const char *s,char *r,const int at) +{ + int f; + + if (!(f = fork())) { + char *(args[11]); + unsigned long u; + int n; + int uid; + int gid; + char *x; + unsigned int xlen; + + r[at] = 0; + if (!r[0]) _exit(0); /* <> */ + + if (chdir(auto_qmail) == -1) _exit(QLX_USAGE); + + nughde_get(r); + + x = nughde.s; + xlen = nughde.len; + + args[0] = "bin/qmail-local"; + args[1] = "--"; + args[2] = x; + n = byte_chr(x,xlen,0); + if (n++ == xlen) _exit(QLX_USAGE); + x += n; + xlen -= n; + + scan_ulong(x,&u); + uid = u; + n = byte_chr(x,xlen,0); + if (n++ == xlen) _exit(QLX_USAGE); + x += n; + xlen -= n; + + scan_ulong(x,&u); + gid = u; + n = byte_chr(x,xlen,0); + if (n++ == xlen) _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[3] = x; + n = byte_chr(x,xlen,0); + if (n++ == xlen) _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[4] = r; + args[5] = x; + n = byte_chr(x,xlen,0); + if (n++ == xlen) _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[6] = x; + n = byte_chr(x,xlen,0); + if (n++ == xlen) _exit(QLX_USAGE); + x += n; + xlen -= n; + + args[7] = r + at + 1; + args[8] = s; + args[9] = aliasempty; + args[10] = 0; + + if (fd_move(0,fdmess) == -1) _exit(QLX_SYS); + if (fd_move(1,fdout) == -1) _exit(QLX_SYS); + if (fd_copy(2,1) == -1) _exit(QLX_SYS); + if (prot_gid(gid) == -1) _exit(QLX_USAGE); + if (prot_uid(uid) == -1) _exit(QLX_USAGE); + if (!getuid()) _exit(QLX_ROOT); + + execv(*args,args); + if (errno) _exit(QLX_EXECSOFT); + _exit(QLX_EXECHARD); + } + return f; +} diff --git a/sqmail-4.3.07/src/qmail-mfrules.c b/sqmail-4.3.07/src/qmail-mfrules.c new file mode 100644 index 0000000..e8cfc94 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-mfrules.c @@ -0,0 +1,173 @@ +#include <sys/stat.h> +#include <stdio.h> // rename +#include "logmsg.h" +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "exit.h" +#include <unistd.h> +#include "open.h" +#include "auto_qmail.h" +#include "cdbmake.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "case.h" + +#define WHO "qmail-mfrules" + +int rename(const char *,const char *); // stdio.h + +stralloc address = {0}; +stralloc data = {0}; +stralloc key = {0}; +stralloc line = {0}; + +char inbuf[1024]; +buffer bi; + +int fd; +int fdtemp; +int match = 1; + +struct cdb_make cdb; + +void die_nomem() +{ + logmsg(WHO,112,FATAL,"out of memory"); +} +void die_parse() +{ + if (!stralloc_0(&line)) die_nomem(); + logmsg(WHO,100,ERROR,B("unable to parse this line: ",line.s)); +} +void die_read() +{ + logmsg(WHO,111,ERROR,"unable to read control/mailfromrules"); +} +void die_write() +{ + logmsg(WHO,111,ERROR,"unable to write to control/mailfromrules.tmp"); +} + +char strnum[FMT_ULONG]; +stralloc sanum = {0}; + +void getnum(char *buf,int len,unsigned long *u) +{ + if (!stralloc_copyb(&sanum,buf,len)) die_nomem(); + if (!stralloc_0(&sanum)) die_nomem(); + if (sanum.s[scan_ulong(sanum.s,u)]) die_parse(); +} + +void doaddressdata() +{ + int i; + int left; + int right; + unsigned long bot; + unsigned long top; + + if (byte_chr(address.s,address.len,'=') == address.len) + if (byte_chr(address.s,address.len,'@') == address.len) { + i = byte_chr(address.s,address.len,'-'); + if (i < address.len) { + left = byte_rchr(address.s,i,'.'); + if (left == i) left = 0; else ++left; + + ++i; + right = i + byte_chr(address.s + i,address.len - i,'.'); + + getnum(address.s + left,i - 1 - left,&bot); + getnum(address.s + i,right - i,&top); + if (top > 255) top = 255; + + while (bot <= top) { + if (!stralloc_copyb(&key,address.s,left)) die_nomem(); + if (!stralloc_catb(&key,strnum,fmt_ulong(strnum,bot))) die_nomem(); + if (!stralloc_catb(&key,address.s + right,address.len - right)) die_nomem(); + case_lowerb(key.s,key.len); + if (cdb_make_add(&cdb,key.s,key.len,data.s,data.len) == -1) die_write(); + ++bot; + } + + return; + } + } + + case_lowerb(address.s,address.len); + case_lowerb(data.s,data.len); + if (cdb_make_add(&cdb,address.s,address.len,data.s,data.len) == -1) die_write(); +} + +int main() +{ + int amper; + int i; + int len; + char *x; + char ch; + + umask(033); + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,ERROR,B("unable to chdir to: ",auto_qmail)); + + fd = open_read("control/mailfromrules"); + if (fd == -1) die_read(); + + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + fdtemp = open_trunc("control/mailfromrules.tmp"); + if (fdtemp == -1) die_write(); + + if (cdb_make_start(&cdb,fdtemp) == -1) die_write(); + + while (match) { + if (getln(&bi,&line,&match,'\n') != 0) die_read(); + + x = line.s; len = line.len; + + if (!len) break; + if (x[0] == '#') continue; + if (x[0] == '\n') continue; + + while (len) { + ch = x[len - 1]; + if (ch != '\n') if (ch != ' ') if (ch != '\t') break; + --len; + } + line.len = len; /* for die_parse() */ + + amper = byte_chr(x,len,'&'); + if (!amper) die_parse(); + if (amper) if (amper == len || amper < 2) die_parse(); + + if (!stralloc_copyb(&address,x,amper)) die_nomem(); + if (!stralloc_copys(&data,"")) die_nomem(); + + x = line.s + amper + 1; len = line.len - amper - 1; + + while (len) { + if (len < 3) die_parse(); /* input checks */ + if ( *x == ',' || *x == ' ' || *x == '\t') die_parse(); + i = byte_chr(x,len,','); /* &addr1,addr2,.. */ + if (i > 0 && i < len) { + if (!stralloc_catb(&data,"+",1)) die_nomem(); + if (!stralloc_catb(&data,x,i)) die_nomem(); + x += i + 1; len -= i + 1; } + else { + if (!stralloc_catb(&data,"+",1)) die_nomem(); + if (!stralloc_catb(&data,x,len)) die_nomem(); + len = 0; } + } + doaddressdata(); + } + + if (cdb_make_finish(&cdb) == -1) die_write(); + if (fsync(fdtemp) == -1) die_write(); + if (close(fdtemp) == -1) die_write(); /* NFS stupidity */ + if (rename("control/mailfromrules.tmp","control/mailfromrules.cdb") == -1) + logmsg(WHO,111,ERROR,"unable to move control/mailfromrules.tmp to control/mailfromrules.cdb"); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-mrtg-queue.sh b/sqmail-4.3.07/src/qmail-mrtg-queue.sh new file mode 100644 index 0000000..3ac0fb1 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-mrtg-queue.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cd HOME +echo `find queue/mess -type f -print | wc -l` +echo `find queue/todo/* -type f -print | wc -l` diff --git a/sqmail-4.3.07/src/qmail-mrtg.c b/sqmail-4.3.07/src/qmail-mrtg.c new file mode 100644 index 0000000..c956196 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-mrtg.c @@ -0,0 +1,322 @@ +#include <unistd.h> +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "exit.h" +#include "open.h" +#include "scan.h" +#include "fmt.h" +#include "case.h" +#include "now.h" +#include "str.h" +#include "datetime.h" +#include "logmsg.h" + +#define WHO "qmail-mrtg" +#define TAI64NLEN 24 + +/** @file qmail-mrtg.c + @return 0: ok + 1: Error: No TAI64N timestamp available + 2: Warning: Not enough time left between calls +*/ + +/* qmail-send */ + +int local = 0; +int remote = 0; +int success = 0; +int failure = 0; +int bytes = 0; +int tlstrans = 0; +int deferral = 0; +int bounces = 0; +int triples = 0; +int qmtp = 0; +int qmtps = 0; + +/* qmail-smtpd */ + +int asessions = 0; +int rsessions = 0; +int aorig = 0; +int arcpt = 0; +int rsend = 0; +int rhelo = 0; +int rorigbad = 0; +int rorigdns = 0; +int rrcptbad = 0; +int rrcptfail = 0; +int rsize = 0; +int rmime = 0; +int rloader = 0; +int rvirus = 0; +int rspam = 0; +int aauth = 0; +int rauth = 0; +int atls = 0; +int rtls = 0; +int spfpass = 0; +int spfail = 0; + +/* qmail-pop3d */ + +int apop = 0; +int rpop = 0; +int pok = 0; +int pdeny = 0; + +/* *server + rblsmtpd */ + +int sok = 0; +int sdeny = 0; +int greet = 0; +int grey = 0; +int rbl = 0; + +char bufsmall[BUFFER_SMALL]; +buffer bo = BUFFER_INIT(write,1,bufsmall,sizeof(bufsmall)); + +static void outs(char *s) +{ + if (buffer_puts(&bo,s) == -1) _exit(1); + if (buffer_puts(&bo,"\n") == -1) _exit(1); + if (buffer_flush(&bo) == -1) _exit(1); +} + +static void out(int i) +{ + char num[FMT_ULONG]; + + if (buffer_put(&bo,num,fmt_ulong(num,(unsigned long) i)) == -1) _exit(1); + if (buffer_puts(&bo,"\n") == -1) _exit(1); + if (buffer_flush(&bo) == -1) _exit(1); +} + +char bufspace[1024]; +buffer bi = BUFFER_INIT(read,0,bufspace,sizeof(bufspace)); + +void mrtg_results(char flag) +{ + switch (flag) { + case '1': out(success); out(tlstrans); break; + case '2': bytes = bytes/1024; out(bytes); out(bytes); break; + case '3': out(local); out(remote); break; + case '4': out(failure); out(deferral); break; + case '5': out(bounces); out(triples); break; + case '6': qmtps += qmtp; out(qmtp); out(qmtps); break; /* QMTP */ + + case 'a': out(asessions); out(rsessions); break; /* total */ + case 'b': out(aorig); out(arcpt); break; /* accepted */ + case 'c': out(rsend); out(rhelo); break; /* rejected MTA */ + case 'd': out(rorigbad); out(rorigdns); break; /* Orig */ + case 'e': out(rrcptbad); out(rrcptfail); break; /* Recipient */ + case 'f': out(rmime); out(rloader); break; /* Warlord */ + case 'g': out(rvirus); out(rspam); break; /* Infected/Spam */ + case 'h': out(aauth); out(rauth); break; /* Auth */ + case 'i': out(atls); out(rtls); break; /* TLS */ + case 'j': out(spfpass); out(spfail); break; /* SPF */ + case 'k': out(grey); break; /* Greylisted */ + case 'z': sdeny +=rbl; out(sok); out(sdeny); break; /* reject session */ + + case 'A': out(apop); out(rpop); break; + case 'B': out(pok); out(pdeny); break; + + default: break; + } +} + +void mrtg_sendlog(char *in, char flag) +{ + int i, j, k = 0; + + switch (flag) { + case '1': if (case_starts(in,"delivery")) { + i = str_chr(in,':') + 2; + if (case_starts(in + i,"success:")) success++; + i = str_chr(in,'T'); + if (case_starts(in + i,"TLS_")) tlstrans++; + }; break; + case '2': if (case_starts(in,"info msg")) { + i = str_chr(in,':') + 8; + if ((j = str_chr(in + i,' '))) in[i + j] = '\0'; + bytes += atoi(in + i); + }; break; + case '3': if (case_starts(in,"status:")) { + i = str_rchr(in,'c') + 4; + k = str_rchr(in,'r') + 7; + if ((j = str_chr(in + i,'/'))) in[i + j] = '\0'; + if (atoi(in + i) > local) local = atoi(in + i); + if ((j = str_chr(in + k,'/'))) in[k + j] = '\0'; + if (atoi(in + k) > remote) remote = atoi(in + k); + }; break; + case '4': if (case_starts(in,"delivery")) { + i = str_chr(in,':') + 2; + if (case_starts(in + i,"failure:")) failure++; + if (case_starts(in + i,"deferral:")) deferral++; + }; break; + case '5': if (case_starts(in,"bounce msg")) bounces++; + if (case_starts(in,"triple bounce:")) triples++; + break; + case '6': if (case_starts(in,"delivery")) { + i = str_chr(in,'q'); + if (case_starts(in + i,"qmtp:_ok")) qmtp++; + if (case_starts(in + i,"qmtps:_ok")) qmtps++; + }; break; + default: break; + } +} + +void mrtg_smtplog(char *in, char flag) +{ + int i, j, k = 0; + + i = str_chr(in,'A'); + j = str_chr(in,'R'); + k = str_chr(in,'P'); + + switch (flag) { + case 'a': if (case_starts(in + i,"Accept")) asessions++; + if (case_starts(in + j,"Reject")) rsessions++; + break; + case 'b': if (case_starts(in + i,"Accept::ORIG:")) aorig++; + if (case_starts(in + i,"Accept::RCPT:")) arcpt++; + break; + case 'c': if (case_starts(in + j,"Reject::SNDR::Invalid_Relay")) rsend++; + if (case_starts(in + j,"Reject::SNDR::Bad_Helo")) rhelo++; + if (case_starts(in + j,"Reject::SNDR::DNS_Helo")) rhelo++; + break; + case 'd': if (case_starts(in + j,"Reject::ORIG::Bad_Mailfrom")) rorigbad++; + if (case_starts(in + j,"Reject::ORIG::DNS_MF")) rorigdns++; + break; + case 'e': if (case_starts(in + j,"Reject::RCPT::Bad_Rcptto")) rrcptbad++; + if (case_starts(in + j,"Reject::RCPT::Failed_Rcptto")) rrcptfail++; + break; + case 'f': if (case_starts(in + j,"Reject::DATA::Invalid_Size")) rsize++; + if (case_starts(in + j,"Reject::DATA::Bad_MIME")) rmime++; + if (case_starts(in + j,"Reject::DATA::MIME_Attach")) rmime++; + if (case_starts(in + j,"Reject::DATA::Bad_Loader")) rloader++; + break; + case 'g': if (case_starts(in + j,"Reject::DATA::Spam_Message")) rspam++; + if (case_starts(in + j,"Reject::DATA::Virus_Infected")) rvirus++; + break; + case 'h': if (case_starts(in + i,"Accept::AUTH:")) aauth++; + if (case_starts(in + j,"Reject::AUTH:")) rauth++; + break; + case 'i': if (case_starts(in + k,"P:ESMTPS")) atls++; + if (case_starts(in + j,"Reject::TLS:")) rtls++; + break; + case 'j': if (case_starts(in + i,"Accept::SPF:")) spfpass++; + if (case_starts(in + j,"Reject::SPF:")) spfail++; + break; + case 'k': if (case_starts(in + i,"Deferred::SNDR::Grey_Listed")) grey++; + break; + case 'z': if (case_starts(in,"tcpserver") || case_starts(in,"sslserver") || case_starts(in,"rblsmtpd")) { + i = str_chr(in,':') + 2; + if (case_starts(in + i,"ok")) sok++; + if (case_starts(in + i,"deny")) sdeny++; + j = str_chr(in+i,':') + 2; + if (case_starts(in + i + j,"451")) rbl++; + if (case_starts(in + i + j,"553")) rbl++; + if (case_starts(in + i + j,"greetdelay:")) greet++; + } break; + default: break; + } +} + +void mrtg_pop3log(char *in, char flag) +{ + int i, j = 0; + + switch (flag) { + case 'A': i = str_chr(in,'A'); j = str_chr(in,'R'); + if (case_starts(in + i,"Accept::AUTH:")) apop++; + if (case_starts(in + j,"Reject::AUTH:")) rpop++; + break; + case 'B': if (case_starts(in,"tcpserver:") || case_starts(in,"sslserver:")) { + i = str_chr(in,':') + 2; + if (case_starts(in + i,"ok")) pok++; + if (case_starts(in + i,"deny")) pdeny++; + }; break; + default: break; + } +} + +int main(int argc, char **argv) +{ + int i; + int c; + int match; + int enoughtime = 0; + unsigned long u; + unsigned long calltime; + unsigned long seconds; + unsigned long nanoseconds; + unsigned int history = 305; + char flag; + + stralloc line = {0}; + calltime = now(); + + if (argc < 2) + logmsg(WHO,100,USAGE,"qmail-mrtg [ -1 | -2 | -3 | -4 | -5 | -6 |\ + -a | -b | -c | -d | -e | -f | -g | -h | -i | -j | -k | -z | -A | -B ] [time (min)] \n\ + qmail-mrtg needs to be called every [time] minutes (i.e. by crontab) - default 305 secs"); + + flag = *(argv[1] + 1); + if (argc == 3) { scan_ulong(argv[2],&u); history = 60 * u + 5; } + +/* Read input lines sequentially */ + + buffer_init(&bi,read,0,bufspace,sizeof(bufspace)); + + for (;;) { + if (getln(&bi,&line,&match,'\n') != 0) _exit(1); + if (!match) break; + if (!stralloc_0(&line)) _exit(1); + + seconds = 0; + nanoseconds = 0; + + if (line.s[0] == '@') { /* tai64 timestamp */ + for (i = 1; i <= TAI64NLEN; i++) { + c = (int)line.s[i]; + u = c - '0'; + if (u >= 10) { + u = c - 'a'; + if (u >= 6) break; + u += 10; + } + seconds <<= 4; + seconds += nanoseconds >> 28; + nanoseconds &= 0xfffffff; + nanoseconds <<= 4; + nanoseconds += u; + } + seconds -= 4611686018427387914ULL; + seconds = seconds > 0 ? seconds : 0; + } else { + outs("Error: No TAI64N timestamp available."); + _exit(1); + } + + if (enoughtime) { /* default history = 305 sec => evaluate logs every ~5 mins */ + if (seconds <= calltime && seconds >= (calltime - history)) { + if (flag >= '1' && flag <= '9') mrtg_sendlog(line.s + TAI64NLEN + 2,flag); + else if (flag >= 'a' && flag <= 'z') mrtg_smtplog(line.s + TAI64NLEN + 2,flag); + else if (flag >= 'A' && flag <= 'Z') mrtg_pop3log(line.s + TAI64NLEN + 2,flag); + } + } else { + if (seconds) { + enoughtime++; + } else { + outs("Warning: Not enough time left between calls"); + _exit(1); + } + } + } + + mrtg_results(flag); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-newmrh.c b/sqmail-4.3.07/src/qmail-newmrh.c new file mode 100644 index 0000000..4a74698 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-newmrh.c @@ -0,0 +1,75 @@ +#include <unistd.h> +#include <sys/stat.h> +#include <stdio.h> // rename +#include "logmsg.h" +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "exit.h" +#include "open.h" +#include "auto_qmail.h" +#include "cdbmake.h" +#include "case.h" + +#define WHO "qmail-newmrh" + +int rename(const char *,const char *); // stdio.h + +void die_read() +{ + logmsg(WHO,111,ERROR,"unable to read control/morercpthosts"); +} +void die_write() +{ + logmsg(WHO,111,ERROR,"unable to write to control/morercpthosts.tmp"); +} + +char inbuf[1024]; +buffer bi; + +int fd; +int fdtemp; + +struct cdb_make cdb; +stralloc line = {0}; +int match; + +int main() +{ + umask(033); + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,ERROR,B("unable to chdir to: ",auto_qmail)); + + fd = open_read("control/morercpthosts"); + if (fd == -1) die_read(); + + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + fdtemp = open_trunc("control/morercpthosts.tmp"); + if (fdtemp == -1) die_write(); + + if (cdb_make_start(&cdb,fdtemp) == -1) die_write(); + + for (;;) { + if (getln(&bi,&line,&match,'\n') != 0) die_read(); + case_lowerb(line.s,line.len); + while (line.len) { + if (line.s[line.len - 1] == ' ') { --line.len; continue; } + if (line.s[line.len - 1] == '\n') { --line.len; continue; } + if (line.s[line.len - 1] == '\t') { --line.len; continue; } + if (line.s[0] != '#') + if (cdb_make_add(&cdb,line.s,line.len,"",0) == -1) + die_write(); + break; + } + if (!match) break; + } + + if (cdb_make_finish(&cdb) == -1) die_write(); + if (fsync(fdtemp) == -1) die_write(); + if (close(fdtemp) == -1) die_write(); /* NFS stupidity */ + if (rename("control/morercpthosts.tmp","control/morercpthosts.cdb") == -1) + logmsg(WHO,111,ERROR,"unable to move control/morercpthosts.tmp to control/morercpthosts.cdb"); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-newu.c b/sqmail-4.3.07/src/qmail-newu.c new file mode 100644 index 0000000..d5e9baa --- /dev/null +++ b/sqmail-4.3.07/src/qmail-newu.c @@ -0,0 +1,132 @@ +#include <unistd.h> +#include <sys/stat.h> +#include <stdio.h> // rename +#include "stralloc.h" +#include "getln.h" +#include "buffer.h" +#include "cdbmake.h" +#include "exit.h" +#include "open.h" +#include "logmsg.h" +#include "case.h" +#include "byte.h" +#include "auto_qmail.h" + +#define WHO "qmail-newu" + +int rename(const char *,const char *); // stdio.h + +void die_chdir() +{ + logmsg(WHO,110,ERROR,"unable to chdir"); +} +void die_nomem() +{ + logmsg(WHO,111,FATAL,"fatal: out of memory"); +} +void die_opena() +{ + logmsg(WHO,112,ERROR,"unable to open users/assign"); +} +void die_reada() +{ + logmsg(WHO,110,ERROR,"unable to read users/assign"); +} +void die_format() +{ + logmsg(WHO,112,ERROR,"bad format in users/assign"); +} +void die_opent() +{ + logmsg(WHO,112,ERROR,"unable to open users/assign.cdb.tmp"); +} +void die_writet() +{ + logmsg(WHO,112,ERROR,"unable to write users/assign.cdb.tmp"); +} +void die_rename() +{ + logmsg(WHO,112,ERROR,"unable to move users/cdb.tmp to users/assign.cdb"); +} + +struct cdb_make cdb; +stralloc key = {0}; +stralloc data = {0}; + +char inbuf[1024]; +buffer bi; + +int fd; +int fdtemp; + +stralloc line = {0}; +int match; + +stralloc wildchars = {0}; + +int main() +{ + int i; + int numcolons; + + umask(033); + if (chdir(auto_qmail) == -1) die_chdir(); + + fd = open_read("users/assign"); + if (fd == -1) die_opena(); + + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + fdtemp = open_trunc("users/assign.cdb.tmp"); + if (fdtemp == -1) die_opent(); + + if (cdb_make_start(&cdb,fdtemp) == -1) die_writet(); + + if (!stralloc_copys(&wildchars,"")) die_nomem(); + + for (;;) { + if (getln(&bi,&line,&match,'\n') != 0) die_reada(); + if (line.len && (line.s[0] == '.')) break; + if (!match) die_format(); + + if (byte_chr(line.s,line.len,'\0') < line.len) die_format(); + i = byte_chr(line.s,line.len,':'); + if (i == line.len) die_format(); + if (i == 0) die_format(); + if (!stralloc_copys(&key,"!")) die_nomem(); + if (line.s[0] == '+') { + if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem(); + case_lowerb(key.s,key.len); + if (i >= 2) + if (byte_chr(wildchars.s,wildchars.len,line.s[i - 1]) == wildchars.len) + if (!stralloc_append(&wildchars,line.s + i - 1)) die_nomem(); + } + else { + if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem(); + if (!stralloc_0(&key)) die_nomem(); + case_lowerb(key.s,key.len); + } + + if (!stralloc_copyb(&data,line.s + i + 1,line.len - i - 1)) die_nomem(); + + numcolons = 0; + for (i = 0; i < data.len; ++i) + if (data.s[i] == ':') { + data.s[i] = 0; + if (++numcolons == 6) break; + } + if (numcolons < 6) die_format(); + data.len = i; + + if (cdb_make_add(&cdb,key.s,key.len,data.s,data.len) == -1) die_writet(); + } + + if (cdb_make_add(&cdb,"",0,wildchars.s,wildchars.len) == -1) die_writet(); + + if (cdb_make_finish(&cdb) == -1) die_writet(); + if (fsync(fdtemp) == -1) die_writet(); + if (close(fdtemp) == -1) die_writet(); /* NFS stupidity */ + if (rename("users/assign.cdb.tmp","users/assign.cdb") == -1) die_rename(); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-pop3d.c b/sqmail-4.3.07/src/qmail-pop3d.c new file mode 100644 index 0000000..ae4b6ea --- /dev/null +++ b/sqmail-4.3.07/src/qmail-pop3d.c @@ -0,0 +1,314 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "commands.h" +#include "sig.h" +#include "getln.h" +#include "stralloc.h" +#include "buffer.h" +#include "alloc.h" +#include "open.h" +#include "prioq.h" +#include "scan.h" +#include "fmt.h" +#include "str.h" +#include "exit.h" +#include "maildir.h" +#include "timeout.h" +#include "qmail.h" + +#define FDIN 0 +#define FDOUT 1 +#define POP3_TIMEOUT 1200 + +int rename(const char *,const char *); // stdio.h + +void die() { _exit(0); } + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + r = timeoutread(POP3_TIMEOUT,fd,buf,len); + if (r <= 0) die(); + return r; +} + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + r = timeoutwrite(POP3_TIMEOUT,fd,buf,len); + if (r <= 0) die(); + return r; +} + +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(saferead,FDIN,inbuf,sizeof(inbuf)); +char outbuf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(safewrite,FDOUT,outbuf,sizeof(outbuf)); + +void out(char *buf,int len) +{ + buffer_put(&bo,buf,len); +} +void outs(char *s) +{ + buffer_puts(&bo,s); +} +void flush() +{ + buffer_flush(&bo); +} +void err(char *s) +{ + outs("-ERR "); + outs(s); + outs("\r\n"); + flush(); +} + +void die_nomem() { err("out of memory"); die(); } +void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); } +void die_scan() { err("unable to scan $HOME/Maildir"); die(); } + +void err_syntax() { err("syntax error"); } +void err_unimpl() { err("unimplemented"); } +void err_deleted() { err("already deleted"); } +void err_nozero() { err("messages are counted from 1"); } +void err_toobig() { err("not that many messages"); } +void err_nosuch() { err("unable to open that message"); } +void err_nounlink() { err("unable to unlink all deleted messages"); } + +void okay() { outs("+OK \r\n"); flush(); } + +void printfn(char *fn) +{ + fn += 4; + out(fn,str_chr(fn,':')); +} + +char strnum[FMT_ULONG]; +stralloc line = {0}; + +void blast(buffer *bf,unsigned long limit) +{ + int match; + int inheaders = 1; + + for (;;) { + if (getln(bf,&line,&match,'\n') != 0) die(); + if (!match && !line.len) break; + if (match) --line.len; /* no way to pass this info over POP */ + if (limit) if (!inheaders) if (!--limit) break; + if (!line.len) + inheaders = 0; + else + if (line.s[0] == '.') + out(".",1); + out(line.s,line.len); + out("\r\n",2); + + if (!match) break; + } + out("\r\n.\r\n",5); + flush(); +} + +stralloc filenames = {0}; +prioq pq = {0}; + +struct message { + int flagdeleted; + unsigned long size; + char *fn; +} *m; + +int numm; +int last = 0; + +void getlist() +{ + struct prioq_elt pe; + struct stat st; + int i; + + maildir_clean(&line); + if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan(); + + numm = pq.p ? pq.len : 0; + m = (struct message *) alloc(numm * sizeof(struct message)); + if (!m) die_nomem(); + + for (i = 0; i < numm; ++i) { + if (!prioq_min(&pq,&pe)) { numm = i; break; } + prioq_delmin(&pq); + m[i].fn = filenames.s + pe.id; + m[i].flagdeleted = 0; + if (stat(m[i].fn,&st) == -1) + m[i].size = 0; + else + m[i].size = st.st_size; + } +} + +void pop3_stat() +{ + int i; + unsigned long total; + + total = 0; + for (i = 0; i < numm; ++i) + if (!m[i].flagdeleted) total += m[i].size; + + outs("+OK "); + out(strnum,fmt_uint(strnum,numm)); + outs(" "); + out(strnum,fmt_ulong(strnum,total)); + outs("\r\n"); + flush(); +} + +void pop3_rset() +{ + int i; + + for (i = 0; i < numm; ++i) + m[i].flagdeleted = 0; + last = 0; + okay(); +} + +void pop3_last() +{ + outs("+OK "); + out(strnum,fmt_uint(strnum,last)); + outs("\r\n"); + flush(); +} + +void pop3_quit() +{ + int i; + + for (i = 0; i < numm; ++i) + if (m[i].flagdeleted) { + if (unlink(m[i].fn) == -1) err_nounlink(); + } else { + if (str_start(m[i].fn,"new/")) { + if (!stralloc_copys(&line,"cur/")) die_nomem(); + if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem(); + if (!stralloc_cats(&line,":2,")) die_nomem(); + if (!stralloc_0(&line)) die_nomem(); + rename(m[i].fn,line.s); /* if it fails, bummer */ + } + } + okay(); + die(); +} + +int msgno(char *arg) +{ + unsigned long u; + + if (!scan_ulong(arg,&u)) { err_syntax(); return -1; } + if (!u) { err_nozero(); return -1; } + --u; + if (u >= numm) { err_toobig(); return -1; } + if (m[u].flagdeleted) { err_deleted(); return -1; } + return u; +} + +void pop3_dele(char *arg) +{ + int i; + + i = msgno(arg); + if (i == -1) return; + m[i].flagdeleted = 1; + if (i + 1 > last) last = i + 1; + okay(); +} + +void list(int i,int flaguidl) +{ + out(strnum,fmt_uint(strnum,i + 1)); + outs(" "); + if (flaguidl) printfn(m[i].fn); + else out(strnum,fmt_ulong(strnum,m[i].size)); + outs("\r\n"); +} + +void dolisting(char *arg,int flaguidl) +{ + unsigned int i; + + if (*arg) { + i = msgno(arg); + if (i == -1) return; + + outs("+OK "); + list(i,flaguidl); + } else { + okay(); + for (i = 0; i < numm; ++i) + if (!m[i].flagdeleted) list(i,flaguidl); + outs(".\r\n"); + } + flush(); +} + +void pop3_uidl(char *arg) { dolisting(arg,1); } +void pop3_list(char *arg) { dolisting(arg,0); } + +char msgbuf[BUFSIZE_MESS]; +buffer bm; + +void pop3_top(char *arg) +{ + int i; + unsigned long limit; + int fd; + + i = msgno(arg); + if (i == -1) return; + + arg += scan_ulong(arg,&limit); + while (*arg == ' ') ++arg; + if (scan_ulong(arg,&limit)) ++limit; + else limit = 0; + + fd = open_read(m[i].fn); + if (fd == -1) { err_nosuch(); return; } + okay(); + buffer_init(&bm,read,fd,msgbuf,sizeof(msgbuf)); + blast(&bm,limit); + close(fd); +} + +struct commands pop3commands[] = { + { "quit", pop3_quit, 0 } +, { "stat", pop3_stat, 0 } +, { "list", pop3_list, 0 } +, { "uidl", pop3_uidl, 0 } +, { "dele", pop3_dele, 0 } +, { "retr", pop3_top, 0 } +, { "rset", pop3_rset, 0 } +, { "last", pop3_last, 0 } +, { "top", pop3_top, 0 } +, { "noop", okay, 0 } +, { 0, err_unimpl, 0 } +} ; + +int main(int argc,char **argv) +{ + sig_alarmcatch(die); + sig_pipeignore(); + + if (!argv[1]) die_nomaildir(); + if (chdir(argv[1]) == -1) die_nomaildir(); + + getlist(); + + okay(); + commands(&bi,pop3commands); + die(); +} diff --git a/sqmail-4.3.07/src/qmail-popup.c b/sqmail-4.3.07/src/qmail-popup.c new file mode 100644 index 0000000..a2fd39d --- /dev/null +++ b/sqmail-4.3.07/src/qmail-popup.c @@ -0,0 +1,303 @@ +#include <unistd.h> +#include "commands.h" +#include "fd.h" +#include "sig.h" +#include "stralloc.h" +#include "buffer.h" +#include "alloc.h" +#include "wait.h" +#include "str.h" +#include "byte.h" +#include "now.h" +#include "fmt.h" +#include "case.h" +#include "exit.h" +#include "timeout.h" +#include "env.h" +#include "tls_start.h" +#include "ip.h" +#include "qmail.h" + +#define PORT_POP3S "995" // string +#define FDIN 0 +#define FDOUT 1 +#define FDAUTH 3 +#define FDLOG 5 +#define POP3_TIMEOUT 1200 + +void die() { _exit(1); } + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + r = timeoutread(POP3_TIMEOUT,fd,buf,len); + if (r <= 0) die(); + return r; +} + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + r = timeoutwrite(POP3_TIMEOUT,fd,buf,len); + if (r <= 0) die(); + return r; +} + +char inbuf[BUFSIZE_AUTH]; +buffer bi = BUFFER_INIT(saferead,FDIN,inbuf,sizeof(inbuf)); + +char outbuf[BUFSIZE_AUTH]; +buffer bo = BUFFER_INIT(safewrite,FDOUT,outbuf,sizeof(outbuf)); + +void outs(char *s) +{ + buffer_puts(&bo,s); +} +void flush() +{ + buffer_flush(&bo); +} +void err(char *s) +{ + outs("-ERR "); + outs(s); + outs("\r\n"); + flush(); +} + +/* Logging */ + +stralloc protocol = {0}; +stralloc auth = {0}; +char *localport; +char *remoteip; +char *remotehost; + +char strnum[FMT_ULONG]; +char logbuf[BUFSIZE_LOG]; +buffer bl = BUFFER_INIT(safewrite,FDLOG,logbuf,sizeof(logbuf)); + +void logs(char *s) { if (buffer_puts(&bl,s) == -1) _exit(1); } +void logp(char *s) { logs(" P:"); logs(s); } +void logh(char *s1, char *s2) { logs(" S:"); logs(s1); logs(":"); logs(s2); } +void logu(char *s) { logs(" ?~ '"); logs(s); logs("'"); } +void logn(char *s) { if (buffer_puts(&bl,s) == -1) _exit(1); if (buffer_flush(&bl) == -1) _exit(1); } +void logpid() { strnum[fmt_ulong(strnum,getpid())] = 0; logs("qmail-popup: pid "); logs(strnum); logs(" "); } +void log_pop(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6) + { logpid(); logs(s1); logs(s2); logp(s3); logh(s4,s5), logu(s6), logn("\n"); } + +void die_usage() { err("usage: popup hostname subprogram"); die(); } +void die_nomem() { err("out of memory"); die(); } +void die_pipe() { err("unable to open pipe"); die(); } +void die_write() { err("unable to write pipe"); die(); } +void die_fork() { err("unable to fork"); die(); } +void die_childcrashed() { err("aack, child crashed"); } +void die_badauth() { err("authorization failed"); } +void die_tls() { err("TLS startup failed"); die(); } +void die_notls() { + err("TLS required but not negotiated"); + log_pop("Reject::STLS::","Any","POP3",remoteip,remotehost,"unknown"); + die(); +} + +void err_syntax() { err("syntax error"); } +void err_wantuser() { err("USER first"); } +void err_authoriz() { err("authorization first"); } + +void okay() { outs("+OK \r\n"); flush(); } +void pop3_quit() { okay(); die(); } + +void poplog_init() +{ + if (!stralloc_copys(&protocol,"POP3")) die_nomem(); + localport = env_get("TCP6LOCALPORT"); + if (!localport) localport = env_get("TCPLOCALPORT"); + if (!localport) localport = "unknown"; + if (!case_diffs(localport,PORT_POP3S)) + if (!stralloc_cats(&protocol,"S")) die_nomem(); + remoteip = env_get("TCP6REMOTEIP"); + if (remoteip && byte_equal(remoteip,7,V4MAPPREFIX)) remoteip = remoteip + 7; + if (!remoteip) remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) remoteip = "unknown"; + remotehost = env_get("TCP6REMOTEHOST"); + if (!remotehost) remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; +} + +char unique[FMT_ULONG + FMT_ULONG + 3]; +char *hostname; +stralloc username = {0}; +int seenuser = 0; +char **childargs; +char authbuf[BUFSIZE_AUTH]; +buffer ba; +int stls = 0; +int seenstls = 0; +int apop = 0; + +void doanddie(char *user,unsigned int userlen,char *pass) /* userlen: including 0 byte */ +{ + int child; + int wstat; + int pi[2]; + + if (fd_copy(2,1) == -1) die_pipe(); + close(FDAUTH); + if (pipe(pi) == -1) die_pipe(); + if (pi[0] != FDAUTH) die_pipe(); + switch (child = fork()) { + case -1: + die_fork(); + case 0: + close(pi[1]); + sig_pipedefault(); + execvp(*childargs,childargs); + _exit(1); + } + close(pi[0]); + buffer_init(&ba,write,pi[1],authbuf,sizeof(authbuf)); + if (buffer_put(&ba,user,userlen) == -1) die_write(); + if (buffer_put(&ba,pass,str_len(pass) + 1) == -1) die_write(); + if (buffer_puts(&ba,"<") == -1) die_write(); + if (buffer_puts(&ba,unique) == -1) die_write(); + if (buffer_puts(&ba,hostname) == -1) die_write(); + if (buffer_put(&ba,">",2) == -1) die_write(); + if (buffer_flush(&ba) == -1) die_write(); + close(pi[1]); + byte_zero(pass,str_len(pass)); + byte_zero(authbuf,sizeof(authbuf)); + if (wait_pid(&wstat,child) == -1) die(); + if (wait_crashed(wstat)) die_childcrashed(); + if (!stralloc_0(&auth)) die_nomem(); + if (!stralloc_0(&protocol)) die_nomem(); + if (wait_exitcode(wstat)) { + die_badauth(); + log_pop("Reject::AUTH::",auth.s,protocol.s,remoteip,remotehost,user); + } + else + log_pop("Accept::AUTH::",auth.s,protocol.s,remoteip,remotehost,user); + die(); +} + +void pop3_greet() +{ + char *s; + s = unique; + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + + if (!apop) + outs("+OK\r\n"); + else { + outs("+OK <"); + outs(unique); + outs(hostname); + outs(">\r\n"); + } + flush(); +} + +void pop3_user(char *arg) +{ + if (stls == 2 && !seenstls) die_notls(); + if (!*arg) { err_syntax(); return; } + okay(); + seenuser = 1; + if (!stralloc_copys(&username,arg)) die_nomem(); + if (!stralloc_0(&username)) die_nomem(); +} + +void pop3_pass(char *arg) +{ + if (!seenuser) { err_wantuser(); return; } + if (!*arg) { err_syntax(); return; } + if (!stralloc_copys(&auth,"User")) die_nomem(); + doanddie(username.s,username.len,arg); +} + +void pop3_apop(char *arg) +{ + char *space; + + if (stls == 2 && !seenstls) die_notls(); + space = arg + str_chr(arg,' '); + if (!*space) { err_syntax(); return; } + *space++ = 0; + if (!stralloc_copys(&auth,"Apop")) die_nomem(); + doanddie(arg,space - arg,space); +} + +void pop3_capa(char *arg) +{ + outs("+OK capability list follows\r\n"); + outs("TOP\r\n"); + outs("USER\r\n"); + outs("UIDL\r\n"); + if (apop) + outs("APOP\r\n"); + if (stls > 0) + outs("STLS\r\n"); + outs(".\r\n"); + flush(); +} + +void pop3_stls(char *arg) +{ + if (stls == 0 || seenstls == 1) + return err("STLS not available"); + outs("+OK starting TLS negotiation\r\n"); + flush(); + + if (!starttls_init()) die_tls(); + buffer_init(&bi,saferead,FDIN,inbuf,sizeof(inbuf)); + seenstls = 1; + +/* reset state */ + seenuser = 0; + if (!stralloc_cats(&protocol,"S")) die_nomem(); +} + +struct commands pop3commands[] = { + { "user", pop3_user, 0 } +, { "pass", pop3_pass, 0 } +, { "apop", pop3_apop, 0 } +, { "quit", pop3_quit, 0 } +, { "capa", pop3_capa, 0 } +, { "stls", pop3_stls, 0 } +, { "noop", okay, 0 } +, { 0, err_authoriz, 0 } +}; + +int main(int argc,char **argv) +{ + char *pop3auth; + char *ucspitls; + + sig_alarmcatch(die); + sig_pipeignore(); + + hostname = argv[1]; + if (!hostname) die_usage(); + childargs = argv + 2; + if (!*childargs) die_usage(); + + ucspitls = env_get("UCSPITLS"); + if (ucspitls) { + stls = 1; + if (!case_diffs(ucspitls,"-")) stls = 0; + if (!case_diffs(ucspitls,"!")) stls = 2; + } + + pop3auth = env_get("POP3AUTH"); + if (pop3auth) { + if (case_starts(pop3auth,"apop")) apop = 2; + if (case_starts(pop3auth,"+apop")) apop = 1; + } + poplog_init(); + pop3_greet(); + commands(&bi,pop3commands); + die(); +} diff --git a/sqmail-4.3.07/src/qmail-postgrey.c b/sqmail-4.3.07/src/qmail-postgrey.c new file mode 100644 index 0000000..bd88389 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-postgrey.c @@ -0,0 +1,105 @@ +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "stralloc.h" +#include "logmsg.h" +#include "ip.h" +#include "case.h" +#include "str.h" +#include "exit.h" +#include "scan.h" +#include "timeout.h" +#include "timeoutconn.h" +#include "socket_if.h" + +#define WHO "qmail-postgrey" + +#define CT 10 /* Connect timeout */ +#define WT 10 /* Write timeout */ +#define RT 10 /* Read timeout */ + +unsigned int port = 60000; /* default port */ + +int main(int argc, char **argv) +{ + struct ip4_address ip4; + struct ip6_address ip6; + stralloc query = {0}; + char buf[64]; + char *remoteip = 0; + char *netif = 0; + uint32 ifidx = 0; + int pgfd; + int i, j, r; + + if (argc != 6) + logmsg(WHO,100,USAGE,"qmail-postgrey ip%ifidx;port sender recipient client_address client_name"); + + remoteip = argv[1]; + i = str_chr(remoteip,':'); + if (remoteip[i] == ':') { + j = str_chr(remoteip,'%'); /* IF index */ + if (remoteip[j] == '%') { + remoteip[j] = 0; + netif = &remoteip[j + 1]; + ifidx = socket_getifidx(netif); + } + if (!ip6_scan(remoteip,(char *)&ip6.d)) + logmsg(WHO,111,FATAL,B("No valid IPv6 address provided: ",remoteip)); + pgfd = socket(AF_INET6,SOCK_STREAM,0); + if (pgfd == -1) + logmsg(WHO,111,FATAL,"Can't bind to IPv6 socket."); + r = timeoutconn6(pgfd,(char *)&ip6.d,port,CT,ifidx); + } else { + if (!ip4_scan(remoteip,(char *)&ip4.d)) + logmsg(WHO,111,FATAL,B("No valid IPv6 address provided: ",remoteip)); + pgfd = socket(AF_INET,SOCK_STREAM,0); + if (pgfd == -1) + logmsg(WHO,111,FATAL,"Can't bind to IPv4 socket."); + r = timeoutconn4(pgfd,(char *)&ip4.d,port,CT); + } + if (r != 0) { + if (errno == ETIMEDOUT) + close(pgfd); + logmsg(WHO,111,FATAL,B("Can't communicate with postgrey server: ",remoteip)); + _exit(1); + } + + /* Provide SMTP connect vector to postgrey server */ + + if (!stralloc_copys(&query,"request=smtpd_access_policy\nclient_address=")) _exit(1); + if (!stralloc_cats(&query,argv[4])) _exit(1); + if (!stralloc_cats(&query,"\nclient_name=")) _exit(1); + if (!stralloc_cats(&query,argv[5])) _exit(1); + if (!stralloc_cats(&query,"\nsender=")) _exit(1); + if (!stralloc_cats(&query,argv[2])) _exit(1); + if (!stralloc_cats(&query,"\nrecipient=")) _exit(1); + if (!stralloc_cats(&query,argv[3])) _exit(1); + if (!stralloc_cats(&query,"\n\n")) _exit(1); + + do { + r = timeoutwrite(WT,pgfd,query.s,query.len); + } while (r == -1 && errno == EINTR); + + if (r != query.len) { close(pgfd); _exit(1); } + + /* Read response */ + + do { + r = timeoutread(RT,pgfd,buf,sizeof(buf)); + } while (r == -1 && errno == EINTR); + + if (r == -1) { close(pgfd); _exit(1); } + close(pgfd); + +// logmsg(WHO,0,INFO,buf); + + if (r >= 12) + if (!case_diffb(buf,12,"action=dunno")) _exit(0); + if (r >= 14) + if (!case_diffb(buf,14,"action=prepend")) _exit(0); + if (r >= 22) + if (!case_diffb(buf,22,"action=defer_if_permit")) _exit(10); + + exit(1); +} diff --git a/sqmail-4.3.07/src/qmail-pw2u.c b/sqmail-4.3.07/src/qmail-pw2u.c new file mode 100644 index 0000000..5a6cbec --- /dev/null +++ b/sqmail-4.3.07/src/qmail-pw2u.c @@ -0,0 +1,321 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "buffer.h" +#include "getoptb.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "fmt.h" +#include "str.h" +#include "scan.h" +#include "open.h" +#include "logmsg.h" +#include "exit.h" +#include "getln.h" +#include "byte.h" +#include "auto_break.h" +#include "auto_qmail.h" +#include "auto_usera.h" +#include "qmail.h" + +#define WHO "qmail-pw2u" + +void die_chdir() +{ + buffer_putsflush(buffer_2,"qmail-pw2u: fatal: unable to chdir\n"); + _exit(111); +} + +void die_nomem() +{ + buffer_putsflush(buffer_2,"qmail-pw2u: fatal: out of memory\n"); + _exit(111); +} + +void die_read() +{ + buffer_putsflush(buffer_2,"qmail-pw2u: fatal: unable to read input\n"); + _exit(111); +} + +void die_write() +{ + buffer_putsflush(buffer_2,"qmail-pw2u: fatal: unable to write output\n"); + _exit(111); +} + +void die_control() +{ + buffer_putsflush(buffer_2,"qmail-pw2u: fatal: unable to read controls\n"); + _exit(111); +} + +void die_alias() +{ + buffer_puts(buffer_2,"qmail-pw2u: fatal: unable to find "); + buffer_puts(buffer_2,auto_usera); + buffer_puts(buffer_2," user\n"); + buffer_flush(buffer_2); + _exit(111); +} + +void die_home(char *fn) +{ + buffer_puts(buffer_2,"qmail-pw2u: fatal: unable to stat "); + buffer_puts(buffer_2,fn); + buffer_puts(buffer_2,"\n"); + buffer_flush(buffer_2); + _exit(111); +} + +void die_user(char *s,unsigned int len) +{ + buffer_puts(buffer_2,"qmail-pw2u: fatal: unable to find "); + buffer_put(buffer_2,s,len); + buffer_puts(buffer_2," user for subuser\n"); + buffer_flush(buffer_2); + _exit(111); +} + +char *dashcolon = "-:"; +int flagalias = 0; +int flagnoupper = 1; +int homestrategy = 2; +/* 2: skip if home does not exist; skip if home is not owned by user */ +/* 1: stop if home does not exist; skip if home is not owned by user */ +/* 0: don't worry about home */ + +int okincl; stralloc incl = {0}; struct constmap mapincl; +int okexcl; stralloc excl = {0}; struct constmap mapexcl; +int okmana; stralloc mana = {0}; struct constmap mapmana; + +stralloc allusers = {0}; struct constmap mapuser; + +stralloc uugh = {0}; +stralloc user = {0}; +stralloc uidstr = {0}; +stralloc gidstr = {0}; +stralloc home = {0}; +unsigned long uid; + +stralloc line = {0}; + +void doaccount() +{ + struct stat st; + int i; + char *mailnames; + char *x; + unsigned int xlen; + + if (byte_chr(line.s,line.len,'\0') < line.len) return; + + x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&user,x,i)) die_nomem(); + if (!stralloc_0(&user)) die_nomem(); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&uidstr,x,i)) die_nomem(); + if (!stralloc_0(&uidstr)) die_nomem(); + scan_ulong(uidstr.s,&uid); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&gidstr,x,i)) die_nomem(); + if (!stralloc_0(&gidstr)) die_nomem(); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&home,x,i)) die_nomem(); + if (!stralloc_0(&home)) die_nomem(); + + if (!uid) return; + if (flagnoupper) + for (i = 0; i < user.len; ++i) + if ((user.s[i] >= 'A') && (user.s[i] <= 'Z')) + return; + if (okincl) + if (!constmap(&mapincl,user.s,user.len - 1)) + return; + if (okexcl) + if (constmap(&mapexcl,user.s,user.len - 1)) + return; + if (homestrategy) { + if (stat(home.s,&st) == -1) { + if (errno != ENOENT) die_home(home.s); + if (homestrategy == 1) die_home(home.s); + return; + } + if (st.st_uid != uid) return; + } + + if (!stralloc_copys(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,user.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,uidstr.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,gidstr.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + if (!stralloc_cats(&uugh,home.s)) die_nomem(); + if (!stralloc_cats(&uugh,":")) die_nomem(); + + /* XXX: avoid recording in allusers unlein sub actually needs it */ + if (!stralloc_cats(&allusers,user.s)) die_nomem(); + if (!stralloc_cats(&allusers,":")) die_nomem(); + if (!stralloc_catb(&allusers,uugh.s,uugh.len)) die_nomem(); + if (!stralloc_0(&allusers)) die_nomem(); + + if (str_equal(user.s,auto_usera)) { + if (buffer_puts(buffer_1,"+") == -1) die_write(); + if (buffer_put(buffer_1,uugh.s,uugh.len) == -1) die_write(); + if (buffer_puts(buffer_1,dashcolon) == -1) die_write(); + if (buffer_puts(buffer_1,":\n") == -1) die_write(); + flagalias = 1; + } + + mailnames = 0; + if (okmana) + mailnames = constmap(&mapmana,user.s,user.len - 1); + if (!mailnames) + mailnames = user.s; + + for (;;) { + while (*mailnames == ':') ++mailnames; + if (!*mailnames) break; + + i = str_chr(mailnames,':'); + + if (buffer_puts(buffer_1,"=") == -1) die_write(); + if (buffer_put(buffer_1,mailnames,i) == -1) die_write(); + if (buffer_put(buffer_1,uugh.s,uugh.len) == -1) die_write(); + if (buffer_puts(buffer_1,"::\n") == -1) die_write(); + + if (*auto_break) { + if (buffer_puts(buffer_1,"+") == -1) die_write(); + if (buffer_put(buffer_1,mailnames,i) == -1) die_write(); + if (buffer_put(buffer_1,auto_break,1) == -1) die_write(); + if (buffer_put(buffer_1,uugh.s,uugh.len) == -1) die_write(); + if (buffer_puts(buffer_1,dashcolon) == -1) die_write(); + if (buffer_puts(buffer_1,":\n") == -1) die_write(); + } + + mailnames += i; + } +} + +stralloc sub = {0}; + +void dosubuser() +{ + int i; + char *x; + unsigned int xlen; + char *uugh; + + x = line.s; xlen = line.len; i = byte_chr(x,xlen,':'); if (i == xlen) return; + if (!stralloc_copyb(&sub,x,i)) die_nomem(); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + uugh = constmap(&mapuser,x,i); + if (!uugh) die_user(x,i); + ++i; x += i; xlen -= i; i = byte_chr(x,xlen,':'); if (i == xlen) return; + + if (buffer_puts(buffer_1,"=") == -1) die_write(); + if (buffer_put(buffer_1,sub.s,sub.len) == -1) die_write(); + if (buffer_puts(buffer_1,uugh) == -1) die_write(); + if (buffer_puts(buffer_1,dashcolon) == -1) die_write(); + if (buffer_put(buffer_1,x,i) == -1) die_write(); + if (buffer_puts(buffer_1,":\n") == -1) die_write(); + + if (*auto_break) { + if (buffer_puts(buffer_1,"+") == -1) die_write(); + if (buffer_put(buffer_1,sub.s,sub.len) == -1) die_write(); + if (buffer_put(buffer_1,auto_break,1) == -1) die_write(); + if (buffer_puts(buffer_1,uugh) == -1) die_write(); + if (buffer_puts(buffer_1,dashcolon) == -1) die_write(); + if (buffer_put(buffer_1,x,i) == -1) die_write(); + if (buffer_puts(buffer_1,"-:\n") == -1) die_write(); + } +} + +int fd; +char inbuf[BUFSIZE_LINE]; +buffer in; + +int main(int argc,char **argv) +{ + int opt; + int match; + + while ((opt = getopt(argc,argv,"/ohHuUc:C")) != opteof) + switch (opt) { + case '/': dashcolon = "-/:"; break; + case 'o': homestrategy = 2; break; + case 'h': homestrategy = 1; break; + case 'H': homestrategy = 0; break; + case 'u': flagnoupper = 0; break; + case 'U': flagnoupper = 1; break; + case 'c': *auto_break = *optarg; break; + case 'C': *auto_break = 0; break; + case '?': + default: + _exit(100); + } + + if (chdir(auto_qmail) == -1) die_chdir(); + + /* no need for control_init() */ + + okincl = control_readfile(&incl,"users/include",0); + if (okincl == -1) die_control(); + if (okincl) if (!constmap_init(&mapincl,incl.s,incl.len,0)) die_nomem(); + + okexcl = control_readfile(&excl,"users/exclude",0); + if (okexcl == -1) die_control(); + if (okexcl) if (!constmap_init(&mapexcl,excl.s,excl.len,0)) die_nomem(); + + okmana = control_readfile(&mana,"users/mailnames",0); + if (okmana == -1) die_control(); + if (okmana) if (!constmap_init(&mapmana,mana.s,mana.len,1)) die_nomem(); + + if (!stralloc_copys(&allusers,"")) die_nomem(); + + for (;;) { + if (getln(buffer_0,&line,&match,'\n') == -1) die_read(); + doaccount(); + if (!match) break; + } + if (!flagalias) die_alias(); + + fd = open_read("users/subusers"); + if (fd == -1) { + if (errno != ENOENT) die_control(); + } + else { + buffer_init(&in,read,fd,inbuf,sizeof(inbuf)); + + if (!constmap_init(&mapuser,allusers.s,allusers.len,1)) die_nomem(); + + for (;;) { + if (getln(&in,&line,&match,'\n') == -1) die_read(); + dosubuser(); + if (!match) break; + } + + close(fd); + } + + fd = open_read("users/append"); + if (fd == -1) { + if (errno != ENOENT) die_control(); + } + else { + buffer_init(&in,read,fd,inbuf,sizeof(inbuf)); + for (;;) { + if (getln(&in,&line,&match,'\n') == -1) die_read(); + if (buffer_put(buffer_1,line.s,line.len) == -1) die_write(); + if (!match) break; + } + } + + if (buffer_puts(buffer_1,".\n") == -1) die_write(); + if (buffer_flush(buffer_1) == -1) die_write(); + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-qmaint.c b/sqmail-4.3.07/src/qmail-qmaint.c new file mode 100644 index 0000000..e83ab6f --- /dev/null +++ b/sqmail-4.3.07/src/qmail-qmaint.c @@ -0,0 +1,594 @@ +/* + Based on an implementation of queue-fix 1.2 by Eric Huss +*/ +#include <unistd.h> +#include <sys/stat.h> +#include <pwd.h> +#include <grp.h> +#include "stralloc.h" +#include "direntry.h" +#include "fmt.h" +#include "fmtqfn.h" +#include "error.h" +#include "buffer.h" +#include "getln.h" +#include "str.h" +#include "open.h" +#include "fifo.h" +#include "scan.h" +#include "readsubdir.h" +#include "logmsg.h" +#include "exit.h" +#include "auto_qmail.h" +#include "auto_split.h" +#include "auto_uids.h" + +#define WHO "qmail-qmaint" + +stralloc queue_dir = {0}; /*the root queue dir with trailing slash*/ +stralloc check_dir = {0}; /*the current directory being checked*/ +stralloc temp_dirname = {0}; /*temporary used for checking directories */ +stralloc temp_filename = {0}; /*temporary used for checking individuals*/ +stralloc old_name = {0}; /*used in rename*/ +stralloc new_name = {0}; /*used in rename*/ +stralloc mess_dir = {0}; /*used for renaming in mess dir*/ +stralloc query = {0}; /*used in interactive query function*/ + +char strnum[FMT_ULONG]; +int flag_interactive = 0; +int flag_dircreate = 0; +int flag_filecreate = 0; +int flag_permfix = 0; +int flag_namefix = 0; +int flag_delete = 0; + +int qmailq_uid; +int qmails_uid; +int qmailr_uid; +int qmail_gid; +int split_num; + +void die_make(char *name) +{ + logmsg(WHO,111,ERROR,B("Failed to make: ",name)); +} + +void die_user(char *user) +{ + logmsg(WHO,111,ERROR,B("Failed to determine uid of: ",user)); +} + +void die_group(char *group) +{ + logmsg(WHO,111,ERROR,B("Failed to determine gid of: ",group)); +} + +void die_check() +{ + logmsg(WHO,111,ERROR,"Failed while checking directory structure. \nEnsure the given queue exists and you have permission to access it."); +} + +void die_recon() +{ + logmsg(WHO,110,ERROR,"Failed to reconstruct queue. \nEnsure the queue exists and you have permission to modify it."); +} + +void die_nomem() +{ + logmsg(WHO,110,ERROR,"Out of memory."); +} + +/*returns 1==yes, 0==no*/ + +int confirm() +{ + int match; + + if (getln(buffer_0,&query,&match,'\n')) return 0; + if (!match) return 0; + if (query.s[0] == 'y' || query.s[0] == 'Y' || query.s[0] == '\n') return 1; + return 0; +} + +/*gid may be -1 on files for "unknown*/ + +#define DIRS logmsg(WHO,0,WARN,"It looks like some directories don't exist, should I create them? (Y/n)") +#define FILES logmsg(WHO,0,WARN,"It looks like some files don't exist, should I create them? (Y/n)") + +#define PERMS logmsg(WHO,0,WARN,B("It looks like permissions are wrong for ",name," should I fix them? (Y/n)")) +#define CPERMS logmsg(WHO,0,WARN,B("Changing permissions: ",name," => ",pnum)) + +#define OWNER logmsg(WHO,0,WARN,B("It looks like ownerships are wrong for ",name," should I fix them? (Y/n)")) +#define COWNER logmsg(WHO,0,WARN,B("Changing ownership: ",name," => ",unum,"/",gnum)) + +int check_item(char *name,int uid,int gid,int perm,char type,int size) +{ + struct stat st; + int fd; + char num[12]; + char unum[12]; + char gnum[12]; + char pnum[12]; + + /*check for existence and proper credentials*/ + + strnum[fmt_ulong(unum,uid)] = 0; + strnum[fmt_ulong(gnum,gid)] = 0; + strnum[fmt_ulong(pnum,perm)] = 0; + + switch (type) { + case 'd': /*directory*/ + if (stat(name,&st)) { + if (errno != ENOENT) return -1; + if (!flag_dircreate && flag_interactive) { + DIRS; if (!confirm()) return -1; + flag_dircreate = 1; + } + /*create it*/ + logmsg(WHO,0,INFO,B("Creating directory: ",name)); + if (mkdir(name,perm)) die_make(name); + CPERMS; if (chmod(name,perm)) die_make(name); + COWNER; if (chown(name,uid,gid)) die_make(name); + return 0; + } + /*check the values*/ + if (st.st_uid != uid || st.st_gid != gid) { + if (!flag_permfix && flag_interactive) { OWNER; if (!confirm()) return -1; flag_permfix = 1; } + COWNER; if (chown(name,uid,gid)) die_make(name); + } + if ((st.st_mode & 07777) != perm) { + if (!flag_permfix && flag_interactive) { PERMS; if (!confirm()) return -1; flag_permfix = 1; } + CPERMS; if (chmod(name,perm)) die_make(name); + } + return 0; + case 'f': /*regular file*/ + if (stat(name,&st)) return -1; + /*check the values*/ + if (st.st_uid != uid || (st.st_gid != gid && gid != -1)) { + if (!flag_permfix && flag_interactive) { OWNER; if (!confirm()) return -1; flag_permfix = 1; } + COWNER; if (chown(name,uid,gid)) die_make(name); + } + if ((st.st_mode & 07777) != perm) { + if (!flag_permfix && flag_interactive) { PERMS; if (!confirm()) return -1; flag_permfix = 1; } + CPERMS; if (chmod(name,perm)) die_make(name); + } + return 0; + case 'z': /*regular file with a size*/ + if (stat(name,&st)) { + if (errno != ENOENT) return -1; + if (!flag_filecreate && flag_interactive) { + FILES; if (!confirm()) return -1; + flag_filecreate = 1; + } + /*create it*/ + + strnum[fmt_ulong(num,size)] = 0; + logmsg(WHO,0,INFO,B("Creating: ",name," with size ",num)); + fd = open_trunc(name); + if (fd == -1) die_make(name); + while (size--) { if (write(fd,"",1)!=1) die_make(name); } + close(fd); + CPERMS; if (chmod(name,perm)) die_make(name); + COWNER; if (chown(name,uid,gid)) die_make(name); + return 0; + } + /*check the values*/ + if (st.st_uid != uid || (st.st_gid != gid && gid != -1)) { + if (!flag_permfix && flag_interactive) { OWNER; if (!confirm()) return -1; flag_permfix = 1; } + COWNER; if (chown(name,uid,gid)) die_make(name); + } + if ((st.st_mode & 07777) != perm) { + if (!flag_permfix && flag_interactive) { PERMS; if (!confirm()) return -1; flag_permfix = 1; } + CPERMS; if (chmod(name,perm)) die_make(name); + } + if (st.st_size != size) { + logmsg(WHO,0,WARN,B("File ",name," has not the right size. I will not fix it, please investigate.")); + } + return 0; + case 'p': /*a named pipe*/ + if (stat(name,&st)) { + if (errno != ENOENT) return -1; + if (!flag_filecreate && flag_interactive) { + FILES; if (!confirm()) return -1; + flag_filecreate = 1; + } + /*create it*/ + logmsg(WHO,INFO,0,B("Creating fifo: ",name)); + if (fifo_make(name,perm)) die_make(name); + CPERMS; if (chmod(name,perm)) die_make(name); + COWNER; if (chown(name,uid,gid)) die_make(name); + return 0; + } + /*check the values*/ + if (st.st_uid != uid || (st.st_gid != gid && gid != -1)) { + if (!flag_permfix && flag_interactive) { OWNER; if (!confirm()) return -1; flag_permfix = 1; } + COWNER; if (chown(name,uid,gid)) die_make(name); + } + if ((st.st_mode & 07777) != perm) { + if (!flag_permfix && flag_interactive) { PERMS; if (!confirm()) return -1; flag_permfix = 1; } + CPERMS; if (chmod(name,perm)) die_make(name); + } + return 0; + } + + return 0; +} + +int check_files(char * directory,int uid,int gid,int perm) +{ + DIR *dir; + direntry *d; + + dir = opendir(directory); + + if (!dir) return -1; + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') continue; + if (!stralloc_copys(&temp_filename,directory)) die_nomem(); + if (!stralloc_append(&temp_filename,"/")) die_nomem(); + if (!stralloc_cats(&temp_filename,d->d_name)) die_nomem(); + if (!stralloc_0(&temp_filename)) die_nomem(); + if (check_item(temp_filename.s,uid,gid,perm,'f',0)) { closedir(dir); return -1; } + } + closedir(dir); + return 0; +} + +void warn_files(char * directory) +{ + DIR *dir; + direntry *d; + int found = 0; + + dir = opendir(directory); + if (!dir) return; + + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') continue; + found = 1; + break; + } + + closedir(dir); + + if (found) + logmsg(WHO,0,WARN,B("Found files in ",directory," that shouldn't be there. I will not remove them. You should consider checking it out.")); +} + +int check_splits(char * directory,int dir_uid,int dir_gid,int dir_perm,int file_gid,int file_perm) +{ + DIR *dir; + direntry *d; + int i; + + for (i = 0; i < split_num ; i++) { + strnum[fmt_ulong(strnum,i)] = 0; + if (!stralloc_copys(&temp_dirname,directory)) die_nomem(); + if (!stralloc_append(&temp_dirname,"/")) die_nomem(); + if (!stralloc_cats(&temp_dirname,strnum)) die_nomem(); + if (!stralloc_0(&temp_dirname)) die_nomem(); + + /*check the split dir*/ + if (check_item(temp_dirname.s,dir_uid,dir_gid,dir_perm,'d',0)) return -1; + + /*check its contents*/ + dir = opendir(temp_dirname.s); + if (!dir) return -1; + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') continue; + if (!stralloc_copys(&temp_filename,temp_dirname.s)) die_nomem(); + if (!stralloc_append(&temp_filename,"/")) die_nomem(); + if (!stralloc_cats(&temp_filename,d->d_name)) die_nomem(); + if (!stralloc_0(&temp_filename)) die_nomem(); + if (check_item(temp_filename.s,dir_uid,file_gid,file_perm,'f',0)) { closedir(dir); return -1; } + } + closedir(dir); + } + + return 0; +} + +int rename_mess(char *dir, char *part, char *new_part, char *old_filename, char *new_filename) +{ + + if (flag_interactive && !flag_namefix) { + logmsg(WHO,0,INFO,"It looks like some files need to be renamed, should I rename them? (Y/n)\n"); + if (!confirm()) return -1; + flag_namefix = 1; + } + + /*prepare the old filename*/ + if (!stralloc_copy(&old_name,&queue_dir)) die_nomem(); + if (!stralloc_cats(&old_name,dir)) die_nomem(); + if (!stralloc_cats(&old_name,part)) die_nomem(); + if (!stralloc_append(&old_name,"/")) die_nomem(); + if (!stralloc_cats(&old_name,old_filename)) die_nomem(); + if (!stralloc_0(&old_name)) die_nomem(); + + /*prepare the new filename*/ + if (!stralloc_copy(&new_name,&queue_dir)) die_nomem(); + if (!stralloc_cats(&new_name,dir)) die_nomem(); + if (!stralloc_cats(&new_name,new_part)) die_nomem(); + if (!stralloc_append(&new_name,"/")) die_nomem(); + if (!stralloc_cats(&new_name,new_filename)) die_nomem(); + if (!stralloc_0(&new_name)) die_nomem(); + + logmsg(WHO,0,INFO,B("Renaming ",old_name.s," to ",new_name.s)); + if (rename(old_name.s,new_name.s)) { + if (errno != ENOENT) return -1; + } + + return 0; +} + +int fix_part(char *part) +{ + DIR *dir; + direntry *d; + struct stat st; + char inode[FMT_ULONG]; + char new_part[FMT_ULONG]; + int old_inode; + int part_num; + int correct_part_num; + + scan_uint(part,&part_num); + + if (!stralloc_copy(&mess_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&mess_dir,"mess/")) die_nomem(); + if (!stralloc_cats(&mess_dir,part)) die_nomem(); + if (!stralloc_0(&mess_dir)) die_nomem(); + + dir = opendir(mess_dir.s); + if (!dir) return -1; + + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') continue; + /*check from mess*/ + if (!stralloc_copys(&temp_filename,mess_dir.s)) die_nomem(); + if (!stralloc_append(&temp_filename,"/")) die_nomem(); + if (!stralloc_cats(&temp_filename,d->d_name)) die_nomem(); + if (!stralloc_0(&temp_filename)) die_nomem(); + if (stat(temp_filename.s,&st)) { closedir(dir); return -1; } + + /*check that filename == inode number*/ + /*check that inode%auto_split == part_num*/ + scan_uint(d->d_name,&old_inode); + correct_part_num = st.st_ino % split_num; + if (st.st_ino != old_inode || part_num != correct_part_num) { + /*rename*/ + inode[fmt_ulong(inode,st.st_ino)] = 0; + new_part[fmt_ulong(new_part,correct_part_num)] = 0; + if (rename_mess("mess/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if (rename_mess("info/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if (rename_mess("local/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if (rename_mess("remote/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if (rename_mess("todo/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if (rename_mess("intd/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + + if (rename_mess("bounce","","",d->d_name,inode)) { closedir(dir); return -1; } + } + } + + closedir(dir); + return 0; +} + +int fix_names() +{ + int i; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"mess")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + + for (i = 0; i < split_num; i++) { + strnum[fmt_ulong(strnum,i)] = 0; + if (fix_part(strnum)) return -1; + } + + return 0; +} + +int check_dirs() +{ + /*check root existence*/ + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailq_uid,qmail_gid,0750,'d',0)) return -1; + + /*check the bigtodo queue */ + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"info")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmails_uid,qmail_gid,0700,'d',0)) return -1; + if (check_splits(check_dir.s,qmails_uid,qmail_gid,0700,qmail_gid,0600)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"mess")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailq_uid,qmail_gid,0750,'d',0)) return -1; + if (check_splits(check_dir.s,qmailq_uid,qmail_gid,0750,-1,0644)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"remote")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmails_uid,qmail_gid,0700,'d',0)) return -1; + if (check_splits(check_dir.s,qmails_uid,qmail_gid,0700,qmail_gid,0600)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"local")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmails_uid,qmail_gid,0700,'d',0)) return -1; + if (check_splits(check_dir.s,qmails_uid,qmail_gid,0700,qmail_gid,0600)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"intd")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailq_uid,qmail_gid,0700,'d',0)) return -1; + if (check_splits(check_dir.s,qmailq_uid,qmail_gid,0700,qmail_gid,0600)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"todo")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailq_uid,qmail_gid,0750,'d',0)) return -1; + if (check_splits(check_dir.s,qmailq_uid,qmail_gid,0750,-1,0644)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"dkim")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailq_uid,qmail_gid,0750,'d',0)) return -1; + if (check_splits(check_dir.s,qmailq_uid,qmail_gid,0750,qmail_gid,0644)) return -1; + + /*check the others*/ + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"bounce")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmails_uid,qmail_gid,0700,'d',0)) return -1; + if (check_files(check_dir.s,qmails_uid,qmail_gid,0600)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"pid")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailq_uid,qmail_gid,0700,'d',0)) return -1; + + warn_files(check_dir.s); + + /*lock has special files that must exist*/ + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"lock")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailq_uid,qmail_gid,0750,'d',0)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"lock/sendmutex")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmails_uid,qmail_gid,0600,'z',0)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"lock/tcpto")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmailr_uid,qmail_gid,0644,'z',1024)) return -1; + + if (!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if (!stralloc_cats(&check_dir,"lock/trigger")) die_nomem(); + if (!stralloc_0(&check_dir)) die_nomem(); + if (check_item(check_dir.s,qmails_uid,qmail_gid,0622,'p',0)) return -1; + + return 0; +} + +/* stolen from qmail-send */ + +stralloc fn = {0}; + +void fnmake_init() { while (!stralloc_ready(&fn,FMTQFN)) die_nomem(); } +void fnmake_local(unsigned long id) { fn.len = fmtqfn(fn.s,"local/",id,1); } +void fnmake_remote(unsigned long id) { fn.len = fmtqfn(fn.s,"remote/",id,1); } +void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } +void fnmake_dkim(unsigned long id) { fn.len = fmtqfn(fn.s,"dkim/",id,1); } +void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } +void fnmake_bounce(unsigned long id) { fn.len = fmtqfn(fn.s,"bounce/",id,0); } + +void warn_unlink(unsigned long id) +{ + char foo[FMT_ULONG]; + foo[fmt_ulong(foo,id)] = 0; + logmsg(WHO,99,WARN,B("no such file to unlink #",foo)); +} + +void err_unlink(unsigned long id) +{ + char foo[FMT_ULONG]; + foo[fmt_ulong(foo,id)] = 0; + logmsg(WHO,100,ERROR,B("trouble with unlinking #",foo)); +} + +void err_chdir() +{ + logmsg(WHO,110,FATAL,"unable to chdir"); +} + +int delete_msg(unsigned long id) +{ + struct stat st; + int bounce = 1; + + if (chdir(auto_qmail) == -1) err_chdir(); + if (chdir("queue") == -1) err_chdir(); + fnmake_init(); + + fnmake_mess(id); // regular message pre-processed + if (stat(fn.s,&st) == -1) err_unlink(id); + else bounce = 0; + if (!bounce && unlink(fn.s) == -1) + if (errno != ENOENT) err_unlink(id); + + fnmake_info(id); // not delivered yet + if (!stat(fn.s,&st)) + if (unlink(fn.s) == -1) + if (errno != ENOENT) err_unlink(id); + + if (bounce) { + fnmake_bounce(id); + if (!stat(fn.s,&st)) { warn_unlink(id); return 1; } + if (unlink(fn.s) == -1) + if (errno != ENOENT) err_unlink(id); + } + + fnmake_remote(id); + if (!stat(fn.s,&st)) + if (unlink(fn.s) == -1) + if (errno != ENOENT) err_unlink(id); + + fnmake_local(id); + if (!stat(fn.s,&st)) + if (unlink(fn.s) == -1) + if (errno != ENOENT) err_unlink(id); + + return 0; +} + +int main(int argc, char **argv) +{ + char *mess = 0; + unsigned long id = 0; + + if (argc > 1) { + if (!str_diff(argv[1],"-i")) { + flag_interactive = 1; + } else if (!str_diff(argv[1],"-d")) { + if (!argv[2]) logmsg(WHO,111,USAGE,"qmail-qmaint [-i] || [-d messid]"); + mess = argv[2]; + flag_delete = 1; + scan_ulong(mess,&id); + } + } + + if (!stralloc_copys(&queue_dir,auto_qmail)) die_nomem(); + if (!stralloc_cats(&queue_dir,"/queue/")) die_nomem(); + + logmsg(WHO,0,INFO,B("Checking s/qmail queue at: ",auto_qmail,"/queue/")); + + /* get constants */ + + qmailq_uid = auto_uidq; + qmails_uid = auto_uids; + qmailr_uid = auto_uidr; + qmail_gid = auto_gidq; + split_num = auto_split; + + /*check that all the proper directories exist with proper credentials*/ + + if (check_dirs()) die_check(); + + if (flag_delete) { + if (!delete_msg(id)) + logmsg(WHO,0,INFO,B("file ",mess," from queue deleted.")); + } else + if (fix_names()) die_check(); + + logmsg(WHO,0,INFO,"done."); + + _exit (0); +} diff --git a/sqmail-4.3.07/src/qmail-qmqpc.c b/sqmail-4.3.07/src/qmail-qmqpc.c new file mode 100644 index 0000000..942b0de --- /dev/null +++ b/sqmail-4.3.07/src/qmail-qmqpc.c @@ -0,0 +1,180 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "buffer.h" +#include "getln.h" +#include "exit.h" +#include "stralloc.h" +#include "readclose.h" +#include "timeoutconn.h" +#include "logmsg.h" +#include "str.h" +#include "sig.h" +#include "ip.h" +#include "timeout.h" +#include "auto_qmail.h" +#include "qmail.h" +#include "control.h" +#include "fmt.h" +#include "uint_t.h" +#include "socket_if.h" + +#define PORT_QMQP 628 +#define TCP_TIMEOUT 60 + +void die_success() { _exit(0); } +void die_perm() { _exit(31); } +void nomem() { _exit(51); } +void die_read() { if (errno == ENOMEM) nomem(); _exit(54); } +void die_control() { _exit(55); } +void die_socket() { _exit(56); } +void die_home() { _exit(61); } +void die_temp() { _exit(71); } +void die_conn() { _exit(74); } +void die_format() { _exit(91); } + +int lasterror = 55; +int qmqpfd; + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + r = timeoutread(TCP_TIMEOUT,qmqpfd,buf,len); + if (r <= 0) die_conn(); + return r; +} + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + r = timeoutwrite(TCP_TIMEOUT,qmqpfd,buf,len); + if (r <= 0) die_conn(); + return r; +} + +char buf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(safewrite,-1,buf,sizeof(buf)); +buffer bi = BUFFER_INIT(saferead,-1,buf,sizeof(buf)); +buffer be = BUFFER_INIT(read,1,buf,sizeof(buf)); // envelope +/* WARNING: can use only one of these at a time! */ + +stralloc beforemessage = {0}; +stralloc message = {0}; +stralloc aftermessage = {0}; + +char strnum[FMT_ULONG]; +stralloc line = {0}; + +void getmess() +{ + int match; + + if (readclose_append(0,&message,BUFSIZE_LINE) == -1) die_read(); + + strnum[fmt_ulong(strnum,(unsigned long) message.len)] = 0; + if (!stralloc_copys(&beforemessage,strnum)) nomem(); + if (!stralloc_cats(&beforemessage,":")) nomem(); + if (!stralloc_copys(&aftermessage,",")) nomem(); + + if (getln(&be,&line,&match,'\0') == -1) die_read(); + if (!match) die_format(); + if (line.len < 2) die_format(); + if (line.s[0] != 'F') die_format(); + + strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0; + if (!stralloc_cats(&aftermessage,strnum)) nomem(); + if (!stralloc_cats(&aftermessage,":")) nomem(); + if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem(); + if (!stralloc_cats(&aftermessage,",")) nomem(); + + for (;;) { + if (getln(&be,&line,&match,'\0') == -1) die_read(); + if (!match) die_format(); + if (line.len < 2) break; + if (line.s[0] != 'T') die_format(); + + strnum[fmt_ulong(strnum,(unsigned long) line.len - 2)] = 0; + if (!stralloc_cats(&aftermessage,strnum)) nomem(); + if (!stralloc_cats(&aftermessage,":")) nomem(); + if (!stralloc_catb(&aftermessage,line.s + 1,line.len - 2)) nomem(); + if (!stralloc_cats(&aftermessage,",")) nomem(); + } +} + +void doit(char *server) +{ + struct ip4_address ip4s; + struct ip6_address ip6s; + char *netif = 0; + uint32 ifidx = 0; + char ch; + int i, j, r; + + i = str_chr(server,':'); + if (server[i] == ':') { + j = str_chr(server,'%'); /* IF index */ + if (server[j] == '%') { + server[j] = 0; + netif = &server[j + 1]; + ifidx = socket_getifidx(netif); + } + if (!ip6_scan(server,(char *)&ip6s.d)) return; + qmqpfd = socket(AF_INET6,SOCK_STREAM,0); + if (qmqpfd == -1) die_socket(); + r = timeoutconn6(qmqpfd,(char *)&ip6s.d,PORT_QMQP,10,ifidx); + } else { + if (!ip4_scan(server,(char *)&ip4s.d)) return; + qmqpfd = socket(AF_INET,SOCK_STREAM,0); + if (qmqpfd == -1) die_socket(); + r = timeoutconn4(qmqpfd,(char *)&ip4s.d,PORT_QMQP,10); + } + if (r != 0) { + lasterror = 73; + if (errno == ETIMEDOUT) lasterror = 72; + close(qmqpfd); + return; + } + + strnum[fmt_ulong(strnum,(unsigned long) (beforemessage.len + message.len + aftermessage.len))] = 0; + buffer_puts(&bo,strnum); + buffer_puts(&bo,":"); + buffer_put(&bo,beforemessage.s,beforemessage.len); + buffer_put(&bo,message.s,message.len); + buffer_put(&bo,aftermessage.s,aftermessage.len); + buffer_puts(&bo,","); + buffer_flush(&bo); + + for (;;) { + buffer_get(&bi,&ch,1); + if (ch == 'K') die_success(); + if (ch == 'Z') die_temp(); + if (ch == 'D') die_perm(); + } +} + +stralloc servers = {0}; + +int main() +{ + int i; + int j; + + sig_pipeignore(); + + if (chdir(auto_qmail) == -1) die_home(); + if (control_init() == -1) die_control(); + if (control_readfile(&servers,"control/qmqpservers",0) != 1) die_control(); + + getmess(); + + i = 0; + for (j = 0; j < servers.len; ++j) + if (!servers.s[j]) { + doit(servers.s + i); + i = j + 1; + } + + _exit(lasterror); +} diff --git a/sqmail-4.3.07/src/qmail-qmqpd.c b/sqmail-4.3.07/src/qmail-qmqpd.c new file mode 100644 index 0000000..4a2bc66 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-qmqpd.c @@ -0,0 +1,195 @@ +#include <unistd.h> +#include "auto_qmail.h" +#include "qmail.h" +#include "received.h" +#include "sig.h" +#include "buffer.h" +#include "exit.h" +#include "now.h" +#include "fmt.h" +#include "env.h" +#include "case.h" +#include "byte.h" +#include "ip.h" +#include "str.h" +#include "qmail.h" + +#define PORT_QMQP "628" +#define QMTP_SIZE 200000000 +#define QMTP_TIMEOUT 3600 + +void resources() { _exit(111); } + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + r = write(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + r = read(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} + +char inbuf[BUFSIZE_MESS]; +buffer bi = BUFFER_INIT(saferead,0,inbuf,sizeof(inbuf)); +char outbuf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(safewrite,1,outbuf,sizeof(outbuf)); + +unsigned long bytesleft = 100; + +void getbyte(char *ch) +{ + if (!bytesleft--) _exit(100); + buffer_get(&bi,ch,1); +} + +unsigned long getlen() +{ + unsigned long len = 0; + char ch; + + for (;;) { + getbyte(&ch); + if (ch == ':') return len; + if (len > QMTP_SIZE) resources(); + len = 10 * len + (ch - '0'); + } +} + +void getcomma() +{ + char ch; + getbyte(&ch); + if (ch != ',') _exit(100); +} + +struct qmail qq; + +void identify() +{ + char *remotehost; + char *remoteinfo; + char *remoteip; + char *local; + char *localport; + + remotehost = env_get("TCP6REMOTEHOST"); + if (!remotehost) remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCP6REMOTEINFO"); + if (!remoteinfo) remoteinfo = env_get("TCPREMOTEINFO"); + remoteip = env_get("TCP6REMOTEIP"); + if (remoteip && byte_equal(remoteip,7,V4MAPPREFIX)) remoteip=remoteip+7; + if (!remoteip) remoteip = env_get("TCPREMOTEIP"); + if (!remoteip) remoteip = "unknown"; + local = env_get("TCP6LOCALHOST"); + if (!local) local = env_get("TCPLOCALHOST"); + if (!local) local = env_get("TCP6LOCALIP"); + if (!local) local = env_get("TCPLOCALIP"); + if (!local) local = "unknown"; + localport = env_get("TCP6LOCALPORT"); + if (!localport) localport = env_get("TCPLOCALPORT"); + if (!localport) localport = "0"; + + received(&qq,"QMQP",local,remoteip,remotehost,remoteinfo,(char *) 0,(char *) 0,(char *) 0); +} + +char buf[BUFSIZE_LINE]; // sender/recipient buffer +char bufd[BUFSIZE_MESS]; // temporary data buffer +char strnum[FMT_ULONG]; + +int getbuf() +{ + unsigned long len; + int i; + + len = getlen(); + if (len >= BUFSIZE_LINE) { + for (i = 0; i < len; ++i) getbyte(buf); + getcomma(); + buf[0] = 0; + return 0; + } + + for (i = 0; i < len; ++i) getbyte(buf + i); + getcomma(); + buf[len] = 0; + return byte_chr(buf,len,'\0') == len; +} + +int flagok = 1; + +int main() +{ + char *result; + unsigned long qp; + unsigned long len; + unsigned long dlen; + + sig_pipeignore(); + sig_alarmcatch(resources); + alarm(QMTP_TIMEOUT); + + bytesleft = getlen(); + + len = getlen(); + + if (chdir(auto_qmail) == -1) resources(); + if (qmail_open(&qq) == -1) resources(); + qp = qmail_qp(&qq); + identify(); + + while (len > 0) { /* XXX: could speed this up; done */ + dlen = (len < BUFSIZE_LINE) ? len : BUFSIZE_LINE; + buffer_get(&bi,bufd,dlen); + qmail_put(&qq,bufd,dlen); + len -= dlen; + } + getcomma(); + + if (getbuf()) + qmail_from(&qq,buf); + else { + qmail_from(&qq,""); + qmail_fail(&qq); + flagok = 0; + } + + while (bytesleft) + if (getbuf()) + qmail_to(&qq,buf); + else { + qmail_fail(&qq); + flagok = 0; + } + + bytesleft = 1; + getcomma(); + + result = qmail_close(&qq); + + if (!*result) { + len = fmt_str(buf,"Kok "); + len += fmt_ulong(buf + len,(unsigned long) now()); + len += fmt_str(buf + len," qp "); + len += fmt_ulong(buf + len,qp); + buf[len] = 0; + result = buf; + } + + if (!flagok) + result = "Dsorry, I can't accept addresses like that (#5.1.3)"; + + buffer_put(&bo,strnum,fmt_ulong(strnum,(unsigned long) str_len(result))); + buffer_puts(&bo,":"); + buffer_puts(&bo,result); + buffer_puts(&bo,","); + buffer_flush(&bo); + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-qmtpd.c b/sqmail-4.3.07/src/qmail-qmtpd.c new file mode 100644 index 0000000..f2a49cb --- /dev/null +++ b/sqmail-4.3.07/src/qmail-qmtpd.c @@ -0,0 +1,358 @@ +#include <unistd.h> +#include "stralloc.h" +#include "buffer.h" +#include "qmail.h" +#include "now.h" +#include "str.h" +#include "fmt.h" +#include "env.h" +#include "sig.h" +#include "case.h" +#include "exit.h" +#include "scan.h" +#include "rcpthosts.h" +#include "auto_qmail.h" +#include "control.h" +#include "received.h" +#include "ip.h" +#include "byte.h" + +#define PORT_QMTPS "6209" // string compare +#define QMTP_SIZE 200000000 // 23 MB transfer limit +#define QMTP_TIMEOUT 3600 // 1 hour + +/** @file qmail-qmtpd.c -- QMTP/QMTPS server + @brief requires sslserver */ + +void badproto() { _exit(100); } +void resources() { _exit(111); } + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + r = write(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} + +char outbuf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(safewrite,1,outbuf,sizeof(outbuf)); + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + buffer_flush(&bo); + r = read(fd,buf,len); + if (r <= 0) _exit(0); + return r; +} + +char inbuf[BUFSIZE_MESS]; // at least 2*outbuf +buffer bi = BUFFER_INIT(saferead,0,inbuf,sizeof(inbuf)); + +unsigned long getlen() +{ + unsigned long len = 0; + char ch; + for (;;) { + buffer_get(&bi,&ch,1); + if (ch == ':') return len; + if (ch < '0' || ch > '9') resources(); + if (len > QMTP_SIZE) resources(); + len = 10 * len + (ch - '0'); + } +} + +void getcomma() +{ + char ch; + buffer_get(&bi,&ch,1); + if (ch != ',') badproto(); +} + +unsigned int databytes = 0; +unsigned int bytestooverflow = 0; +struct qmail qq; + +char buf[BUFSIZE_LINE]; // sender/recipient buffer +char bufd[BUFSIZE_MESS]; // temporary data buffer +char buf2[BUFFER_SMALL]; // QMTP message buffer + +char *remotehost; +char *remoteinfo; +char *remoteip; +char *localport; +char *local; + +stralloc failure = {0}; +stralloc protocol = {0}; +stralloc tlsinfo = {0}; + +char *relayclient; +int relayclientlen = 0; + +char *ucspitls; +char *tlsversion; +char *cipher; +char *cipherperm; +char *cipherused; +char *clientdn; +char *clientcn; +char *dnemail; + +int seentls = 0; + +int modssl_info() +{ + tlsversion = env_get("SSL_PROTOCOL"); + if (!tlsversion) return 0; + seentls = 1; + + cipher = env_get("SSL_CIPHER"); + if (!cipher) cipher = "unknown"; + cipherperm = env_get("SSL_CIPHER_ALGKEYSIZE"); + if (!cipherperm) cipherperm = "unknown"; + cipherused = env_get("SSL_CIPHER_USEKEYSIZE"); + if (!cipherused) cipherused = "unknown"; + clientdn = env_get("SSL_CLIENT_S_DN"); + if (clientdn) seentls = 2; + else + clientdn = "none"; + + if (!stralloc_copys(&tlsinfo,tlsversion)) resources(); + if (!stralloc_cats(&tlsinfo,": ")) resources(); + if (!stralloc_cats(&tlsinfo,cipher)) resources(); + if (!stralloc_cats(&tlsinfo," [")) resources(); + if (!stralloc_cats(&tlsinfo,cipherused)) resources(); + if (!stralloc_cats(&tlsinfo,"/")) resources(); + if (!stralloc_cats(&tlsinfo,cipherperm)) resources(); + if (!stralloc_cats(&tlsinfo,"] \n")) resources(); + if (!stralloc_cats(&tlsinfo," DN=")) resources(); + if (!stralloc_cats(&tlsinfo,clientdn)) resources(); + if (!stralloc_0(&tlsinfo)) resources(); + + if (!stralloc_append(&protocol,"S")) resources(); + + if (seentls == 2) { + clientcn = env_get("SSL_CLIENT_S_DN_CN"); + remoteinfo = clientcn ? clientcn : clientdn; + dnemail = env_get("SSL_CLIENT_S_DN_Email"); + if (!dnemail) dnemail = "unknown"; + if (!stralloc_append(&protocol,"A")) resources(); + relayclient = ""; + } + return 1; +} + +int main() +{ + char ch; + int i; + unsigned long biglen; + unsigned long dlen; + unsigned long len; + int flagdos; + int flagsenderok; + int flagbother; + unsigned long qp; + char *result; + char *x; + unsigned long u; + + sig_pipeignore(); + sig_alarmcatch(resources); + alarm(QMTP_TIMEOUT); + + if (chdir(auto_qmail) == -1) resources(); + + if (control_init() == -1) resources(); + if (rcpthosts_init() == -1) resources(); + + if (control_readint(&databytes,"control/databytes") == -1) resources(); + x = env_get("DATABYTES"); + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; + + relayclient = env_get("RELAYCLIENT"); + remotehost = env_get("TCP6REMOTEHOST"); + if (!remotehost) remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCP6REMOTEINFO"); + if (!remoteinfo) remoteinfo = env_get("TCPREMOTEINFO"); + remoteip = env_get("TCP6REMOTEIP"); + if (!remoteip) remoteip = env_get("TCPREMOTEIP"); + if (remoteip && byte_equal(remoteip,7,V4MAPPREFIX)) remoteip = remoteip + 7; + if (!remoteip) remoteip = "unknown"; + local = env_get("TCP6LOCALHOST"); + if (!local) local = env_get("TCPLOCALHOST"); + if (!local) local = env_get("TCP6LOCALIP"); + if (!local) local = env_get("TCPLOCALIP"); + if (!local) local = "unknown"; + localport = env_get("TCP6LOCALPORT"); + if (!localport) localport = env_get("TCPLOCALPORT"); + if (!localport) localport = "0"; + + if (!stralloc_copys(&protocol,"QMTP")) resources(); + if (!case_diffs(localport,PORT_QMTPS)) + if (!modssl_info()) resources(); + + if (relayclient) + relayclientlen = str_len(relayclient); + + for (;;) { // https://cr.yp.to/proto/qmtp.txt + if (!stralloc_copys(&failure,"")) resources(); + flagsenderok = 1; + + len = getlen(); // package to read + if (len == 0) badproto(); + + if (databytes) bytestooverflow = databytes + 1; + if (qmail_open(&qq) == -1) resources(); + qp = qmail_qp(&qq); + + buffer_get(&bi,&ch,1); + --len; + if (ch == 10) flagdos = 0; + else if (ch == 13) flagdos = 1; + else badproto(); + + /* no fakehelo, no spfinfo */ + + received(&qq,protocol.s,local,remoteip,remotehost,remoteinfo,(char *) 0,tlsinfo.s,(char *) 0); + + /* XXX: check for loops? only if len is big? - message */ + + if (flagdos) + while (len > 0) { + buffer_get(&bi,&ch,1); + --len; + while ((ch == 13) && len) { + buffer_get(&bi,&ch,1); + --len; + if (ch == 10) break; + if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq); + qmail_put(&qq,"\015",1); + } + if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qq); + qmail_put(&qq,&ch,1); + } + else { + if (databytes) + if (len > databytes) { + bytestooverflow = 0; + qmail_fail(&qq); + } + while (len > 0) { /* XXX: DJB: could speed this up, obviously; FEH: done */ + dlen = (len < BUFSIZE_LINE) ? len : BUFSIZE_LINE; + buffer_get(&bi,bufd,dlen); + qmail_put(&qq,bufd,dlen); + len -= dlen; + } + } + getcomma(); + + len = getlen(); // QMTP sender + + if (len >= BUFSIZE_LINE) { + buf[0] = 0; + flagsenderok = 0; + for (i = 0; i < len; ++i) + buffer_get(&bi,&ch,1); + } + else { + for (i = 0; i < len; ++i) { + buffer_get(&bi,buf + i,1); + if (!buf[i]) flagsenderok = 0; + } + buf[len] = 0; + } + getcomma(); + + flagbother = 0; + qmail_from(&qq,buf); + if (!flagsenderok) qmail_fail(&qq); + + biglen = getlen(); // QMTP recipients + while (biglen > 0) { + if (!stralloc_append(&failure,"")) resources(); + + len = 0; + for (;;) { + if (!biglen) badproto(); + buffer_get(&bi,&ch,1); + --biglen; + if (ch == ':') break; + if (ch < '0' || ch > '9') resources(); + if (len > QMTP_SIZE) resources(); + len = 10 * len + (ch - '0'); + } + if (len >= biglen) badproto(); + if (len + relayclientlen >= BUFSIZE_LINE) { + failure.s[failure.len - 1] = 'L'; + for (i = 0; i < len; ++i) + buffer_get(&bi,&ch,1); + } + else { + for (i = 0; i < len; ++i) { + buffer_get(&bi,buf + i,1); + if (!buf[i]) failure.s[failure.len - 1] = 'N'; + } + buf[len] = 0; + + if (relayclientlen) + str_copy(buf + len,relayclient); + if (!relayclient) + switch (rcpthosts(buf,len)) { + case -1: resources(); + case 0: failure.s[failure.len - 1] = 'D'; + } + + if (!failure.s[failure.len - 1]) { + qmail_to(&qq,buf); + flagbother = 1; + } + } + getcomma(); + biglen -= (len + 1); + } + getcomma(); + + if (!flagbother) qmail_fail(&qq); + result = qmail_close(&qq); + if (!flagsenderok) result = "D Unacceptable sender (#5.1.7)"; + if (databytes) if (!bytestooverflow) result = "D Sorry, that message size exceeds my databytes limit (#5.3.4)"; + + if (*result) + len = str_len(result); + else { + /* success! */ + len = 0; + len += fmt_str(buf2 + len,"K Ok "); + len += fmt_ulong(buf2 + len,(unsigned long) now()); + len += fmt_str(buf2 + len," qp "); + len += fmt_ulong(buf2 + len,qp); + buf2[len] = 0; + result = buf2; + } + + len = fmt_ulong(buf,len); + buf[len++] = ':'; + len += fmt_str(buf + len,result); + buf[len++] = ','; + + for (i = 0; i < failure.len; ++i) + switch (failure.s[i]) { + case 0: + buffer_put(&bo,buf,len); + break; + case 'D': + buffer_puts(&bo,"66:D Sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1),"); + break; + default: + buffer_puts(&bo,"46:D Sorry, I can't handle that recipient (#5.1.3),"); + break; + } + + /* bo will be flushed when we read from the network again */ + } +} diff --git a/sqmail-4.3.07/src/qmail-qread.c b/sqmail-4.3.07/src/qmail-qread.c new file mode 100644 index 0000000..888c14a --- /dev/null +++ b/sqmail-4.3.07/src/qmail-qread.c @@ -0,0 +1,162 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "stralloc.h" +#include "fmt.h" +#include "str.h" +#include "getln.h" +#include "fmtqfn.h" +#include "readsubdir.h" +#include "auto_qmail.h" +#include "open.h" +#include "datetime.h" +#include "date822fmt.h" +#include "error.h" +#include "exit.h" + +readsubdir rs; + +void die(int n) { buffer_flush(buffer_1); _exit(n); } + +void warn(char *s1,char *s2) +{ + char *x; + x = error_str(errno); + buffer_puts(buffer_1,s1); + buffer_puts(buffer_1,s2); + buffer_puts(buffer_1,": "); + buffer_puts(buffer_1,x); + buffer_puts(buffer_1,"\n"); +} + +void die_nomem() { buffer_puts(buffer_1,"fatal: out of memory\n"); die(111); } +void die_chdir() { warn("fatal: unable to chdir",""); die(110); } +void die_opendir(fn) char *fn; { warn("fatal: unable to opendir ",fn); die(110); } + +void err(unsigned long id) +{ + char foo[FMT_ULONG]; + foo[fmt_ulong(foo,id)] = 0; + warn("warning: trouble with #",foo); +} + +char fnmess[FMTQFN]; +char fninfo[FMTQFN]; +char fnlocal[FMTQFN]; +char fnremote[FMTQFN]; +char fnbounce[FMTQFN]; + +char inbuf[1024]; +stralloc sender = {0}; + +unsigned long id; +datetime_sec qtime; +int flagbounce; +unsigned long size; + +unsigned int fmtstats(char *s) +{ + struct datetime dt; + unsigned int len; + unsigned int i; + + len = 0; + datetime_tai(&dt,qtime); + i = date822fmt(s,&dt) - 7/*XXX*/; len += i; if (s) s += i; + i = fmt_str(s," GMT #"); len += i; if (s) s += i; + i = fmt_ulong(s,id); len += i; if (s) s += i; + i = fmt_str(s," "); len += i; if (s) s += i; + i = fmt_ulong(s,size); len += i; if (s) s += i; + i = fmt_str(s," <"); len += i; if (s) s += i; + i = fmt_str(s,sender.s + 1); len += i; if (s) s += i; + i = fmt_str(s,"> "); len += i; if (s) s += i; + if (flagbounce) { + i = fmt_str(s," bouncing"); len += i; if (s) s += i; + } + + return len; +} + +stralloc stats = {0}; + +void out(char *s,unsigned int n) +{ + while (n > 0) { + buffer_put(buffer_1,((*s >= 32) && (*s <= 126)) ? s : "_",1); + --n; + ++s; + } +} +void outs(char *s) { out(s,str_len(s)); } +void outok(char *s) { buffer_puts(buffer_1,s); } + +void putstats() +{ + if (!stralloc_ready(&stats,fmtstats(FMT_LEN))) die_nomem(); + stats.len = fmtstats(stats.s); + out(stats.s,stats.len); + outok("\n"); +} + +stralloc line = {0}; + +int main() +{ + int channel; + int match; + struct stat st; + int fd; + buffer b; + int x; + + if (chdir(auto_qmail) == -1) die_chdir(); + if (chdir("queue") == -1) die_chdir(); + readsubdir_init(&rs,"info",die_opendir); + + while ((x = readsubdir_next(&rs,&id))) + if (x > 0) { + fmtqfn(fnmess,"mess/",id,1); + fmtqfn(fninfo,"info/",id,1); + fmtqfn(fnlocal,"local/",id,1); + fmtqfn(fnremote,"remote/",id,1); + fmtqfn(fnbounce,"bounce/",id,0); + + if (stat(fnmess,&st) == -1) { err(id); continue; } + size = st.st_size; + flagbounce = !stat(fnbounce,&st); + + fd = open_read(fninfo); + if (fd == -1) { err(id); continue; } + buffer_init(&b,read,fd,inbuf,sizeof(inbuf)); + if (getln(&b,&sender,&match,0) == -1) die_nomem(); + if (fstat(fd,&st) == -1) { close(fd); err(id); continue; } + close(fd); + qtime = st.st_mtime; + + putstats(); + + for (channel = 0; channel < 2; ++channel) { + fd = open_read(channel ? fnremote : fnlocal); + if (fd == -1) { + if (errno != ENOENT) err(id); + } else { + for (;;) { + if (getln(&b,&line,&match,0) == -1) die_nomem(); + if (!match) break; + switch (line.s[0]) { + case 'D': + outok(" done"); + case 'T': + outok(channel ? "\tremote\t" : "\tlocal\t"); + outs(line.s + 1); + outok("\n"); + break; + } + } + close(fd); + } + } + } + + die(0); +} diff --git a/sqmail-4.3.07/src/qmail-qstat.sh b/sqmail-4.3.07/src/qmail-qstat.sh new file mode 100755 index 0000000..b8971e5 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-qstat.sh @@ -0,0 +1,12 @@ +cd HOME +messdirs=`echo queue/mess/* | wc -w` +messfiles=`find queue/mess/* -print | wc -w` +tododirs=`echo queue/todo/* | wc -w` +todofiles=`find queue/todo/* -print 2>/dev/null | wc -w` +echo messages in queue: `expr $messfiles - $messdirs` +if [ $tododirs -gt 1 ] +then + echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs` +else + echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs + 1` +fi diff --git a/sqmail-4.3.07/src/qmail-queue.c b/sqmail-4.3.07/src/qmail-queue.c new file mode 100644 index 0000000..250d556 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-queue.c @@ -0,0 +1,306 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "sig.h" +#include "exit.h" +#include "open.h" +#include "seek.h" +#include "fmt.h" +#include "alloc.h" +#include "buffer.h" +#include "datetime.h" +#include "now.h" +#include "triggerpull.h" +#include "extra.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "date822fmt.h" +#include "fmtqfn.h" +#include "env.h" +#include "wait.h" +#include "scan.h" +#include "qmail.h" + +#define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */ +#define ADDR 1003 + +char inbuf[BUFSIZE_MESS]; +struct buffer bi; +char outbuf[BUFSIZE_LINE]; +struct buffer bo; + +datetime_sec starttime; +struct datetime dt; +unsigned long mypid; +unsigned long uid; +char *pidfn; +struct stat pidst; +unsigned long messnum; +char *messfn; +char *todofn; +char *intdfn; +int messfd; +int intdfd; +int flagmademess = 0; +int flagmadeintd = 0; + +void cleanup() +{ + if (flagmadeintd) { + seek_trunc(intdfd,0); + if (unlink(intdfn) == -1) return; + } + if (flagmademess) { + seek_trunc(messfd,0); + if (unlink(messfn) == -1) return; + } +} + +void die(int e) { _exit(e); } +void die_qhpsi() { cleanup(); die(71); } +void die_write() { cleanup(); die(53); } +void die_read() { cleanup(); die(54); } +void sigalrm() { /* thou shalt not clean up here */ die(52); } +void sigbug() { die(81); } + +unsigned int receivedlen; +char *received; +/* "Received: (qmail-queue invoked by alias); 26 Sep 1995 04:46:54 -0000\n" */ + +static unsigned int receivedfmt(char *s) +{ + unsigned int i; + unsigned int len; + len = 0; + + i = fmt_str(s,"Received: (qmail "); len += i; if (s) s += i; + i = fmt_ulong(s,mypid); len += i; if (s) s += i; + i = fmt_str(s," invoked "); len += i; if (s) s += i; + if (uid == auto_uida) { + i = fmt_str(s,"by alias"); len += i; if (s) s += i; + } else if (uid == auto_uidd) { + i = fmt_str(s,"from network"); len += i; if (s) s += i; + } else if (uid == auto_uids) { + i = fmt_str(s,"for bounce"); len += i; if (s) s += i; + } else { + i = fmt_str(s,"by uid "); len += i; if (s) s += i; + i = fmt_ulong(s,uid); len += i; if (s) s += i; + } + i = fmt_str(s,"); "); len += i; if (s) s += i; + i = date822fmt(s,&dt); len += i; if (s) s += i; + return len; +} + +void received_setup() +{ + receivedlen = receivedfmt((char *) 0); + received = alloc(receivedlen + 1); + if (!received) die(51); + receivedfmt(received); +} + +unsigned int pidfmt(char *s,unsigned long seq) +{ + unsigned int i; + unsigned int len; + + len = 0; + i = fmt_str(s,"pid/"); len += i; if (s) s += i; + i = fmt_ulong(s,mypid); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,starttime); len += i; if (s) s += i; + i = fmt_str(s,"."); len += i; if (s) s += i; + i = fmt_ulong(s,seq); len += i; if (s) s += i; + ++len; if (s) *s++ = 0; + + return len; +} + +char *fnnum(char *dirslash,int flagsplit) +{ + char *s; + + s = alloc(fmtqfn((char *) 0,dirslash,messnum,flagsplit)); + if (!s) die(51); + fmtqfn(s,dirslash,messnum,flagsplit); + return s; +} + +void pidopen(void) +{ + unsigned int len; + unsigned long seq; + + seq = 1; + len = pidfmt((char *) 0,seq); + pidfn = alloc(len); + if (!pidfn) die(51); + + for (seq = 1; seq < 10; ++seq) { + if (pidfmt((char *) 0,seq) > len) die(81); /* paranoia */ + pidfmt(pidfn,seq); + messfd = open_excl(pidfn); + if (messfd != -1) return; + } + + die(63); +} + +char *qhpsi; + +void qhpsiprog(char *arg) +{ + int wstat; + int child; + char *qhpsiargs[6] = { 0, 0, 0, 0, 0, 0 }; + char *x; + unsigned long u; + int childrc; + int qhpsirc = 1; + unsigned int size; + unsigned int qhpsiminsize = 0; + unsigned int qhpsimaxsize = 0; + + struct stat st; + + if (stat(messfn,&st) == -1) die(63); + size = (unsigned int) st.st_size; + + x = env_get("QHPSIMINSIZE"); + if (x) { scan_ulong(x,&u); qhpsiminsize = (int) u; } + if (qhpsiminsize) if (size < qhpsiminsize) return; + x = env_get("QHPSIMAXSIZE"); + if (x) { scan_ulong(x,&u); qhpsimaxsize = (int) u; } + if (qhpsimaxsize) if (size > qhpsimaxsize) return; + + if (*arg) { + switch (child = fork()) { + case -1: + die_qhpsi(); + case 0: + qhpsiargs[0] = arg; + qhpsiargs[1] = messfn; + qhpsiargs[2] = env_get("QHPSIARG1"); + if (!qhpsiargs[2]) qhpsiargs[2] = 0; + qhpsiargs[3] = env_get("QHPSIARG2"); + if (!qhpsiargs[3]) qhpsiargs[3] = 0; + qhpsiargs[4] = env_get("QHPSIARG3"); + if (!qhpsiargs[4]) qhpsiargs[4] = 0; + x = env_get("QHPSIRC"); + if (x) { scan_ulong(x,&u); qhpsirc = (int) u; } + execvp(*qhpsiargs,qhpsiargs); + die_qhpsi(); + } + if (wait_pid(&wstat,child) == -1) die_qhpsi(); + if (wait_crashed(wstat)) die_qhpsi(); + childrc = wait_exitcode(wstat); + if (childrc == qhpsirc) { cleanup(); die(32); } + else if (childrc != 0) die_qhpsi(); + } +} + +char tmp[FMT_ULONG]; + +int main() +{ + unsigned int len; + char ch; + int fd; + + sig_blocknone(); + umask(033); + if (chdir(auto_qmail) == -1) die(61); + if (chdir("queue") == -1) die(62); + + mypid = getpid(); + uid = getuid(); + starttime = now(); + datetime_tai(&dt,starttime); + qhpsi = env_get("QHPSI"); + + received_setup(); + + sig_pipeignore(); + sig_miscignore(); + sig_alarmcatch(sigalrm); + sig_bugcatch(sigbug); + + alarm(DEATH); + + pidopen(); + if (fstat(messfd,&pidst) == -1) die(63); + + messnum = pidst.st_ino; + messfn = fnnum("mess/",1); + todofn = fnnum("todo/",1); + intdfn = fnnum("intd/",1); + + if (link(pidfn,messfn) == -1) die(64); + if (unlink(pidfn) == -1) die(63); + flagmademess = 1; + + buffer_init(&bo,write,messfd,outbuf,sizeof(outbuf)); + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + + if (buffer_put(&bo,received,receivedlen) == -1) die_write(); + + switch (buffer_copy(&bo,&bi)) { + case -2: die_read(); + case -3: die_write(); + } + if (buffer_flush(&bo) == -1) die_write(); + if (fsync(messfd) == -1) die_write(); + + intdfd = open_excl(intdfn); + if (intdfd == -1) die(65); + flagmadeintd = 1; + + buffer_init(&bo,write,intdfd,outbuf,sizeof(outbuf)); + buffer_init(&bi,read,1,inbuf,sizeof(inbuf)); + + if (buffer_put(&bo,"u",1) == -1) die_write(); + if (buffer_put(&bo,tmp,fmt_ulong(tmp,uid)) == -1) die_write(); + if (buffer_put(&bo,"",1) == -1) die_write(); + + if (buffer_put(&bo,"p",1) == -1) die_write(); + if (buffer_put(&bo,tmp,fmt_ulong(tmp,mypid)) == -1) die_write(); + if (buffer_put(&bo,"",1) == -1) die_write(); + + if (buffer_get(&bi,&ch,1) < 1) die_read(); + if (ch != 'F') die(91); + if (buffer_put(&bo,&ch,1) == -1) die_write(); + for (len = 0; len < ADDR; ++len) { + if (buffer_get(&bi,&ch,1) < 1) die_read(); + if (buffer_put(&bo,&ch,1) == -1) die_write(); + if (!ch) break; + } + if (len >= ADDR) die(11); + + if (buffer_put(&bo,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write(); + + for (;;) { + if (buffer_get(&bi,&ch,1) < 1) die_read(); + if (!ch) break; + if (ch == 'Q') { qhpsi = 0; break; } + if (ch != 'T') die(91); + if (buffer_put(&bo,&ch,1) == -1) die_write(); + for (len = 0; len < ADDR; ++len) { + if (buffer_get(&bi,&ch,1) < 1) die_read(); + if (buffer_put(&bo,&ch,1) == -1) die_write(); + if (!ch) break; + } + if (len >= ADDR) die(11); + } + + if (qhpsi) qhpsiprog(qhpsi); + + if (buffer_flush(&bo) == -1) die_write(); + if (fsync(intdfd) == -1) die_write(); + + if (link(intdfn,todofn) == -1) die(66); + if ((fd = open(todofn,O_RDONLY)) < 0 || fsync(fd) < 0 || close(fd)) die(66); + + triggerpull(); + die(0); +} diff --git a/sqmail-4.3.07/src/qmail-recipients.c b/sqmail-4.3.07/src/qmail-recipients.c new file mode 100644 index 0000000..058994a --- /dev/null +++ b/sqmail-4.3.07/src/qmail-recipients.c @@ -0,0 +1,77 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "logmsg.h" +#include "stralloc.h" +#include "buffer.h" +#include "getln.h" +#include "exit.h" +#include "open.h" +#include "case.h" +#include "auto_qmail.h" +#include "cdbmake.h" + +#define WHO "qmail-recipients" + +int rename(const char *,const char *); // stdio.h + +void die_read() +{ + logmsg(WHO,111,ERROR,"unable to read users/recipients"); +} +void die_write() +{ + logmsg(WHO,111,ERROR,"unable to write to users/recipients.tmp"); +} + +char inbuf[1024]; +buffer b; + +int fd; +int fdtemp; + +struct cdb_make cdb; +stralloc line = {0}; +stralloc key = {0}; +int match; + +int main() +{ + umask(033); + if (chdir(auto_qmail) == -1) + logmsg(WHO,110,ERROR,B("unable to chdir to: ",auto_qmail)); + + fd = open_read("users/recipients"); + if (fd == -1) die_read(); + + buffer_init(&b,read,fd,inbuf,sizeof(inbuf)); + + fdtemp = open_trunc("users/recipients.tmp"); + if (fdtemp == -1) die_write(); + + if (cdb_make_start(&cdb,fdtemp) == -1) die_write(); + + for (;;) { + stralloc_copys(&key,":"); + if (getln(&b,&line,&match,'\n') != 0) die_read(); + while (line.len) { + if (line.s[line.len - 1] == ' ') { --line.len; continue; } + if (line.s[line.len - 1] == '\n') { --line.len; continue; } + if (line.s[line.len - 1] == '\t') { --line.len; continue; } + if (line.s[0] != '#' && stralloc_cat(&key,&line)) { + case_lowerb(key.s,key.len); + if (cdb_make_add(&cdb,key.s,key.len,"",0) == -1) + die_write(); + } + break; + } + if (!match) break; + } + + if (cdb_make_finish(&cdb) == -1) die_write(); + if (fsync(fdtemp) == -1) die_write(); + if (close(fdtemp) == -1) die_write(); /* NFS stupidity */ + if (rename("users/recipients.tmp","users/recipients.cdb") == -1) + logmsg(WHO,111,ERROR,"unable to move users/recipients.tmp to users/recipients.cdb"); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-remote.c b/sqmail-4.3.07/src/qmail-remote.c new file mode 100644 index 0000000..d2f08dd --- /dev/null +++ b/sqmail-4.3.07/src/qmail-remote.c @@ -0,0 +1,1476 @@ +#ifdef IDN2 +#include <idn2.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include "sig.h" +#include "stralloc.h" +#include "buffer.h" +#include "scan.h" +#include "case.h" +#include "byte.h" +#include "logmsg.h" +#include "qmail.h" +#include "auto_qmail.h" +#include "control.h" +#include "dns.h" +#include "alloc.h" +#include "genalloc.h" +#include "quote.h" +#include "fmt.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "str.h" +#include "now.h" +#include "exit.h" +#include "constmap.h" +#include "tcpto.h" +#include "timeout.h" +#include "timeoutconn.h" +#include "base64.h" +#include "socket_if.h" +#include "ucspissl.h" +#include "hmac_md5.h" +#include "tls_remote.h" +#include "tls_errors.h" +#include "tls_timeoutio.h" +#include "uint_t.h" + +#define WHO "qmail-remote" + +#define QMTP_MAX 200000000 /* 190 MB for QMTP */ +#define HUGESMTPTEXT 1000 /* RFC 5322; was 5000 chars/line */ +#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ +#define PORT_QMTP 209 +#define PORT_SMTPS 465 +#define PORT_SUBMISSION 587 +#define PORT_QMTPS 6209 +#define VERIFYDEPTH 1 +#define TCP_TIMEOUT 60 +#define SMTP_TIMEOUT 1200 + +unsigned long port = PORT_SMTP; + +/** @file qmail-remote.c -- versatile SMTP(S)/QMTP(S) client */ + +int flagauth = 0; /* 1 = login; 2 = plain; 3 = crammd5 */ +int flagsmtps = 0; /* RFC 8314 - 'implicit TLS' */ +int flagtlsdomain = 0; /* 0 = no; 1 = yes; 2 = cert */ +int flagtls = 0; /* flagtls: XYZ + (mode) Z: -2 = rejected; -1 = not; 0 = no, default; Z > 0 see tls_remote.c + (prot) Y: 0 = StartTLS; 1 = SMTPS; 2 = QMTPS + (active) X: 1 = running TLS connection (after DNS lookup) + (done) Z: 1: CA chain; 2: Cert wildname; 3: Cert exactname; + 4: Cert fingerprint; 5: TLSA record */ +int flagverify = 0; /* 1 = verify Cert against CA; 2 = verify against Dir; 3 = triggerd by TLSA; + -2 = Cert pinning; -1 = no TLSA validation */ +int flagutf8 = 0; + +GEN_ALLOC_typedef(saa,stralloc,sa,len,a) +GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) +static stralloc sauninit = {0}; + +stralloc helohost = {0}; +stralloc eaihost = {0}; +stralloc host = {0}; +stralloc idnhost = {0}; +stralloc sender = {0}; +stralloc canonhost = {0}; +stralloc remotehost = {0}; +stralloc canonbox = {0}; +stralloc senddomain = {0}; +stralloc sendip = {0}; + +stralloc domainips = {0}; +struct constmap mapdomainips; +char ip4[4]; +char ip6[16]; +uint32 ifidx = 0; +char *authsender = 0; + +stralloc smtproutes = {0}; +struct constmap mapsmtproutes; +stralloc qmtproutes = {0}; +struct constmap mapqmtproutes; + +saa reciplist = {0}; +stralloc recip = {0}; + +char msgsize[FMT_ULONG]; +unsigned long msize = 0; +struct ip_mx partner; + +SSL *ssl; +SSL_CTX *ctx; + +char bufsmall[BUFFER_SMALL]; +buffer bs = BUFFER_INIT(write,1,bufsmall,sizeof(bufsmall)); + +void out(char *s) +{ + if (buffer_puts(&bs,s) == -1) + _exit(0); + } +void zero() +{ + if (buffer_put(&bs,"\0",1) == -1) + _exit(0); +} +void zerodie() +{ + zero(); + buffer_flush(&bs); + if (ssl) tls_exit(ssl); + _exit(0); +} + +void outsafe(stralloc *sa) +{ + int i; + char ch; + for (i = 0; i < sa->len; ++i) { + ch = sa->s[i]; + if (ch == 0) continue; + if (ch < 33) ch = '?'; + if (ch > 126) ch = '?'; + if (buffer_put(&bs,&ch,1) == -1) _exit(0); + } +} + +void temp_noip() +{ + out("ZInvalid ipaddr in control/domainips (#4.3.0)\n"); + zerodie(); +} +void temp_nomem() +{ + out("ZOut of memory. (#4.3.0)\n"); + zerodie(); +} +void temp_oserr() +{ + out("ZSystem resources temporarily unavailable. (#4.3.0)\n"); + zerodie(); +} +void temp_osip() +{ + out("ZCan't bind to local ip address: "); + outsafe(&sendip); + out(". (#4.3.0)\n"); + zerodie(); +} +void temp_noconn() +{ + out("ZSorry, I wasn't able to establish an SMTP connection: "); + outsafe(&canonhost); + out(". (#4.3.0)\n"); + zerodie(); +} +void temp_qmtpnoc() +{ + out("ZSorry, I wasn't able to establish an QMTP connection: "); + outsafe(&canonhost); + out(". (#4.3.1)\n"); + zerodie(); +} +void temp_read() +{ + out("ZUnable to read message. (#4.3.0)\n"); + zerodie(); +} +void temp_dnscanon() +{ + out("ZCNAME lookup failed temporarily for: "); + outsafe(&canonhost); + out(". (#4.4.3)\n"); + zerodie(); +} +void temp_dns() +{ + out("ZSorry, I couldn't find any host named: "); + outsafe(&host); + out(". (#4.1.2)\n"); + zerodie(); +} +void temp_nomx() +{ + out("ZSorry, I couldn't find a mail exchanger or IP address for: "); + outsafe(&host); + out(". Will try again. (#4.1.2)\n"); + zerodie(); +} +void temp_chdir() +{ + out("ZUnable to switch to home directory. (#4.3.0)\n"); + zerodie(); +} +void temp_control() +{ + out("ZUnable to read control files. (#4.3.0)\n"); + zerodie(); +} +void perm_partialline() +{ + out("DSMTP cannot transfer messages with partial final line. (#5.6.2)\n"); + zerodie(); +} +void temp_proto() +{ + out("ZRecipient did not talk proper QMTP. (#4.3.0)\n"); + zerodie(); +} +void perm_usage() +{ + out("Dqmail-remote was invoked improperly. (#5.3.5)\n"); + zerodie(); +} +void perm_dns() +{ + out("DSorry, I couldn't find any host named: "); + outsafe(&host); + out(". (#5.1.2)\n"); + zerodie(); +} +void perm_nomx() +{ + out("DSorry, I couldn't find a mail exchanger or IP address for: "); + outsafe(&host); + out(". (#5.4.4)\n"); + zerodie(); +} +void perm_ambigmx() +{ + out("DSorry. Although I'm listed as a best-preference MX or A for that host,\n\ +it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n"); + zerodie(); +} +void err_authprot() +{ + out("ZSorry, no supported AUTH method found, trying later again. (#4.7.1)\n"); + zerodie(); +} + +void outhost() +{ + char ipaddr[IPFMT]; + int len; + + switch (partner.af) { + case AF_INET: + len = ip4_fmt(ipaddr,(char *)&partner.addr.ip4.d); break; + case AF_INET6: + len = ip6_fmt(ipaddr,(char *)&partner.addr.ip6.d); break; + } + if (buffer_put(&bs,ipaddr,len) == -1) _exit(0); +} + +int flagcritical = 0; + +void dropped() +{ + out("ZConnected to "); + outhost(); + out(" but connection died. "); + if (flagcritical) out("Possible duplicate! "); + out("(#4.4.2)\n"); + zerodie(); +} + +int timeoutconnect = TCP_TIMEOUT; +int smtpfd; +int timeout = SMTP_TIMEOUT; + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + if (ssl) { + r = tls_timeoutread(timeout,smtpfd,smtpfd,ssl,buf,len); + if (r < 0) temp_tlserr(); + } else { + r = timeoutread(timeout,smtpfd,buf,len); + } + if (r <= 0) dropped(); + return r; +} + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + if (ssl) { + r = tls_timeoutwrite(timeout,smtpfd,smtpfd,ssl,buf,len); + if (r < 0) temp_tlserr(); + } else { + r = timeoutwrite(timeout,smtpfd,buf,len); + } + if (r <= 0) dropped(); + return r; +} + +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); +char outbuf[BUFSIZE_MESS]; +buffer bo = BUFFER_INIT(safewrite,-1,outbuf,sizeof(outbuf)); +char frombuf[BUFFER_SMALL]; +buffer bf = BUFFER_INIT(saferead,-1,frombuf,sizeof(frombuf)); + +static stralloc smtptext = {0}; +static stralloc header = {0}; + +void get(char *ch) +{ + buffer_get(&bf,ch,1); + if (*ch != '\r') + if (smtptext.len < HUGESMTPTEXT) + if (!stralloc_append(&smtptext,ch)) temp_nomem(); +} + +unsigned long smtpcode() +{ + unsigned char ch; + unsigned long code; + + if (!stralloc_copys(&smtptext,"")) temp_nomem(); + + get(&ch); code = ch - '0'; + get(&ch); code = code * 10 + (ch - '0'); + get(&ch); code = code * 10 + (ch - '0'); + for (;;) { + get(&ch); + if (ch != '-') break; + while (ch != '\n') get(&ch); + get(&ch); + get(&ch); + get(&ch); + } + while (ch != '\n') get(&ch); + + return code; +} + +void outsmtptext() +{ + int i; + if (smtptext.s) if (smtptext.len) { + out("Remote host said: "); + for (i = 0; i < smtptext.len; ++i) + if (!smtptext.s[i]) smtptext.s[i] = '?'; + if (buffer_put(&bs,smtptext.s,smtptext.len) == -1) _exit(0); + smtptext.len = 0; + } +} + +void quit(char *prepend,char *append) +{ + buffer_putsflush(&bo,"QUIT\r\n"); + /* waiting for remote side is just too ridiculous */ + out(prepend); + outhost(); + out(append); + out(".\n"); + outsmtptext(); + zerodie(); +} + +void blast() +{ + int r; + int in; + int out; + int eom = 1; // end-of-message <CRLF>.<CRLF> + char tmpbuf[BUFSIZE_MESS + 2]; // intermediate write buffer + +// New blast code; inspired by Bruce Guenter's 'fastremote patch' (2005) + + while ((r = buffer_get(&bi,inbuf,sizeof(inbuf)))) { // read into buffer + if (r == -1) temp_read(); + + for (in = out = 0; in < r;) { + if (eom && inbuf[in] == '.') { + tmpbuf[out++] = '.'; + tmpbuf[out++] = inbuf[in++]; + } + eom = 0; + while (in < r) { + if (inbuf[in] == '\r') { in++; continue; } // CR is DKIM input + if (inbuf[in] == '\n') { + eom = 1; + in++; + tmpbuf[out++] = '\r'; + tmpbuf[out++] = '\n'; + break; + } + tmpbuf[out++] = inbuf[in++]; + } + } + if (out) buffer_put(&bo,tmpbuf,out); + } + + if (!eom) perm_partialline(); + flagcritical = 1; + buffer_put(&bo,".\r\n",3); // LF seen; finish with .<CRLF> + buffer_flush(&bo); +} + +/* this file is too long -------------------------------------- client TLS */ + +stralloc cafile = {0}; +stralloc cadir = {0}; +stralloc certfile = {0}; +stralloc keyfile = {0}; +stralloc keypwd = {0}; +stralloc ciphers = {0}; + +char *tlsdestinfo = 0; +char *tlsdomaininfo = 0; + +stralloc domaincerts = {0}; +struct constmap mapdomaincerts; +stralloc tlsdestinations = {0}; +struct constmap maptlsdestinations; +unsigned long verifydepth = VERIFYDEPTH; + +void tls_init() +{ + ctx = ssl_client(); + ssl_errstr(); + if (!ctx) temp_tlsctx(); + +/* Fetch CA infos for dest */ + + if (flagverify > 0) + if (cafile.len || cadir.len) + if (!ssl_ca(ctx,cafile.s,cadir.s,(int) verifydepth)) temp_tlsca(); + + if (ciphers.len) + if (!ssl_ciphers(ctx,ciphers.s)) temp_tlscipher(); + +/* Prepare for Certificate Request */ + + if (flagtlsdomain == 2) + switch (tls_certkey(ctx,certfile.s,keyfile.s,keypwd.s)) { + case 0: break; + case -1: temp_tlscert(); + case -2: temp_tlskey(); + case -3: temp_tlschk(); + } + +/* Set SSL Context */ + + ssl = ssl_new(ctx,smtpfd); + if (!ssl) temp_tlsctx(); + +/* Setup SSL FDs */ + + if (!tls_conn(ssl,smtpfd)) temp_tlscon(); + +/* Go on in none-blocking mode */ + + if (tls_timeoutconn(timeout,smtpfd,smtpfd,ssl) <= 0) + temp_tlserr(); +} + +int starttls_peer() +{ + int i = 0; + + if (smtptext.len > 16) + for (i = 0; i < smtptext.len - 8; ++i) + if (case_starts(smtptext.s + i,"STARTTLS")) return 1; + + return 0; +} + +void tls_peercheck() +{ + X509 *cert; + STACK_OF(X509) *certs; + + cert = SSL_get_peer_certificate(ssl); + if (!cert) { flagtls = 100; return; } + + if ((certs = SSL_get_peer_cert_chain(ssl)) == NULL) { + certs = sk_X509_new_null(); + sk_X509_push(certs, cert); + } + + if (flagverify == -2) { // fingerprinting is silent + if (cafile.len) case_lowerb(cafile.s,cafile.len); + switch (tls_fingerprint(cert,cafile.s + 1,cafile.len - 2)) { + case -1: temp_tlspeercert(); + case -2: temp_tlsdigest(); + case -3: temp_invaliddigest(); + case 0: temp_tlscertfp(); + case 1: flagtls = 104; break; + } + } + + if (flagverify >= 0) { // TLSA is default + switch (tlsa_check(certs,remotehost,port)) { + case -4: temp_tlsamissing(); break; /* FIXME */ + case -3: temp_tlsainvalid(); break; + case -2: break; // unsupported type; may happen + case -1: break; // weird TLSA record + case 0: break; // no TLSA record given + case 1: case 2: flagtls = 107; flagverify = 3; break; // full certchain available (-PKIX) + case 3: flagtls = 106; flagverify = 0; break; // TA-CA; verify wont work + case 4: flagtls = 105; flagverify = 0; break; // Endpoint only + } + } + + if (flagverify > 0) { + switch (tls_checkpeer(ssl,cert,remotehost,flagtls,flagverify)) { + case -1: temp_tlspeercert(); + case -2: temp_tlspeerverify(); + case -3: temp_tlspeervalid(); + case 1: flagtls = 101; break; + case 2: flagtls = 102; break; + case 3: flagtls = 103; break; + } + } + + if (flagtls < 100) flagtls = 100; + + X509_free(cert); + X509_free(certs); + + return; +} + +/* this file is too long --------------------------------------- smtp UTF8 */ + +int utf8string(unsigned char *ch,int len) +{ + int i = 0; + while (i < len) + if (ch[i++] > 127) return 1; + return 0; +} + +int utf8received() +{ + int r; + int i; + int received = 0; + char ch; + stralloc receivedline = {0}; + +/* we consider only our own last written header */ + + for (;;) { + r = buffer_get(&bi,&ch,1); + if (r == 0) break; + if (r == -1) temp_read(); + if (ch == '\r') continue; // DKIM + + if (ch == '\n') { + if (!stralloc_append(&header,"\r")) temp_nomem(); /* received.c does not add '\r' */ + if (!stralloc_append(&header,"\n")) temp_nomem(); + if (case_starts(receivedline.s,"Date:")) return 0; /* header to quit asap */ + if (case_starts(receivedline.s,"Received: from")) received++; /* found Received header */ + if (received) { + if (case_starts(receivedline.s," by ")) { + for (i = 6; i < receivedline.len - 6; ++i) + if (*(receivedline.s + i) == ' ') + if (case_starts(receivedline.s + i + 1,"with UTF8")) return 1; + return 0; + } + } + if (!stralloc_copys(&receivedline,"")) temp_nomem(); + } else { + if (!stralloc_append(&header,&ch)) temp_nomem(); + if (!stralloc_catb(&receivedline,&ch,1)) temp_nomem(); + } + } + return 0; +} + +/* this file is too long -------------------------------------- smtp client */ + +unsigned long code; +int flagsize = 0; + +int smtp_size() +{ + int i; + if (smtptext.len > 10) + for (i = 0; i < smtptext.len; ++i) { + if (case_starts(smtptext.s + i,"SIZE ")) return 1; + } + return 0;; +} + +void smtp_greeting() +{ + buffer_puts(&bo,"EHLO "); + buffer_put(&bo,helohost.s,helohost.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + + if (smtpcode() != 250) { + buffer_puts(&bo,"HELO "); + buffer_put(&bo,helohost.s,helohost.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + + code = smtpcode(); + authsender = 0; + if (code >= 500) quit("DConnected to "," but my name was rejected"); + if (code != 250) quit("ZConnected to "," but my name was rejected"); + } + flagsize = smtp_size(); +} + +void smtp_starttls() +{ + buffer_puts(&bo,"STARTTLS\r\n"); + buffer_flush(&bo); + + if (smtpcode() == 220) { + tls_init(); + tls_peercheck(); + smtp_greeting(); + } + else { + flagtls = -2; + quit("ZConnected to "," but STARTTLS was rejected"); + } +} + +void mailfrom() +{ + buffer_puts(&bo,"MAIL FROM:<"); + buffer_put(&bo,sender.s,sender.len); + buffer_puts(&bo,">"); + if (flagutf8 || utf8received()) + buffer_puts(&bo," SMTPUTF8"); + if (flagsize && msize) { + buffer_puts(&bo," SIZE="); + buffer_puts(&bo,msgsize); + } + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); +} + +/* this file is too long -------------------------------------- client auth */ + +stralloc authsenders = {0}; +struct constmap mapauthsenders; + +stralloc user = {0}; +stralloc pass = {0}; +stralloc auth = {0}; +stralloc chal = {0}; +stralloc slop = {0}; +stralloc plain = {0}; +stralloc xuser = {0}; + +static const char hextab[] = "0123456789abcdef"; + +int xtext(stralloc *sa,char *s,int len) +{ + int i; + unsigned char c; + char xch[2]; + + if (!stralloc_copys(sa,"")) temp_nomem(); + + for (i = 0; i < len; i++) { + c = s[i]; + if (c < 33 || c > 126 || c == '=' || c == '+') { + xch[0] = hextab[(c >> 4) & 0x0f]; + xch[1] = hextab[c & 0x0f]; + if (!stralloc_catb(sa,xch,2)) temp_nomem(); + } else + if (!stralloc_catb(sa,s + i,1)) temp_nomem(); + } + + return sa->len; +} + +void mailfrom_xtext() +{ + if (!xtext(&xuser,user.s,user.len)) temp_nomem(); + buffer_puts(&bo,"MAIL FROM:<"); + buffer_put(&bo,sender.s,sender.len); + buffer_puts(&bo,"> AUTH="); + buffer_put(&bo,xuser.s,xuser.len); + if (flagutf8 || utf8received()) + buffer_puts(&bo," SMTPUTF8"); + if (flagsize && msize) { + buffer_puts(&bo," SIZE="); + buffer_puts(&bo,msgsize); + } + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); +} + +int mailfrom_plain() +{ + buffer_puts(&bo,"AUTH PLAIN\r\n"); + buffer_flush(&bo); + + if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH PLAIN)"); + + if (!stralloc_cats(&plain,"")) temp_nomem(); /* RFC 4616 section 2 */ + if (!stralloc_0(&plain)) temp_nomem(); + if (!stralloc_cat(&plain,&user)) temp_nomem(); /* user-id */ + if (!stralloc_0(&plain)) temp_nomem(); + if (!stralloc_cat(&plain,&pass)) temp_nomem(); /* password */ + if (b64encode(&plain,&auth)) quit("ZConnected to "," but unable to base64encode (plain)"); + buffer_put(&bo,auth.s,auth.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + + switch (smtpcode()) { + case 235: mailfrom_xtext(); break; + case 432: quit("DConnected to "," but password expired"); + case 534: quit("ZConnected to "," but authentication mechamism too weak (plain)"); + default: quit("ZConnected to "," but authentication was rejected (plain)"); + } + return 0; +} + +int mailfrom_login() +{ + buffer_puts(&bo,"AUTH LOGIN\r\n"); + buffer_flush(&bo); + + if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)"); + if (!stralloc_copys(&auth,"")) temp_nomem(); + if (b64encode(&user,&auth)) quit("ZConnected to "," but unable to base64encode user"); + + buffer_put(&bo,auth.s,auth.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + + if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username)"); + + if (!stralloc_copys(&auth,"")) temp_nomem(); + if (b64encode(&pass,&auth)) quit("ZConnected to "," but unable to base64encode pass"); + buffer_put(&bo,auth.s,auth.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + + switch (smtpcode()) { + case 235: mailfrom_xtext(); break; + case 432: quit("DConnected to "," but password expired"); + case 534: quit("ZConnected to "," but authentication mechanism is too weak (login)"); + default: quit("ZConnected to "," but authentication was rejected (login)"); + } + return 0; +} + +int mailfrom_cram() +{ + int j; + unsigned char digest[16]; + unsigned char digascii[33]; + + buffer_puts(&bo,"AUTH CRAM-MD5\r\n"); + buffer_flush(&bo); + + if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (AUTH CRAM-MD5)"); + if (str_chr(smtptext.s + 4,' ')) { /* Challenge */ + if (!stralloc_copys(&slop,"")) temp_nomem(); + if (!stralloc_copyb(&slop,smtptext.s + 4,smtptext.len - 5)) temp_nomem(); + if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge"); + } + + hmac_md5((unsigned char *)chal.s,chal.len,pass.s,pass.len,digest); + + for (j = 0; j < 16; j++) { /* HEX => ASCII */ + digascii[2 * j] = hextab[digest[j] >> 4]; + digascii[2 * j + 1] = hextab[digest[j] & 0x0f]; + } + digascii[32]=0; + + if (!stralloc_copys(&slop,"")) temp_nomem(); + if (!stralloc_cat(&slop,&user)) temp_nomem(); /* user-id */ + if (!stralloc_cats(&slop," ")) temp_nomem(); + if (!stralloc_catb(&slop,digascii,32)) temp_nomem(); /* digest */ + + if (!stralloc_copys(&auth,"")) temp_nomem(); + if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest"); + + buffer_put(&bo,auth.s,auth.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + + switch (smtpcode()) { + case 235: mailfrom_xtext(); break; + case 432: quit("DConnected to "," but password expired"); + case 534: quit("ZConnected to "," but authentication mechamism too weak (cram)"); + default: quit("ZConnected to "," but authentication was rejected (cram)"); + } + return 0; +} + +void smtp_auth() +{ + int i; + + if (smtptext.len > 8) + for (i = 4; i < smtptext.len - 5; ++i) { + if (case_starts(smtptext.s + i,"CRAM")) + if (mailfrom_cram() >= 0) return; + if (case_starts(smtptext.s + i,"LOGIN")) + if (mailfrom_login() >= 0) return; + if (case_starts(smtptext.s + i,"PLAIN")) + if (mailfrom_plain() >= 0) return; + } + err_authprot(); + mailfrom(); +} + +/* this file is too long ------------------------------------------- GO ON */ + +void smtp() +{ + int flagbother; + int i; + + if (flagtls > 10 && flagtls < 20) { /* SMTPS */ + tls_init(); + tls_peercheck(); + } + + code = smtpcode(); + if (code >= 500) quit("DConnected to "," but sender was rejected"); + if (code == 421 || code == 450) quit("ZConnected to "," but probably greylisted"); /* RFC 6647 */ + if (code >= 400) quit("ZConnected to "," but sender was rejected"); + if (code != 220) quit("ZConnected to "," but greeting failed"); + + smtp_greeting(); + + if (flagtls > 0 && flagtls < 10) { /* STARTTLS */ + if (starttls_peer()) + smtp_starttls(); + else if (flagtls > 3 && flagtls != 9) { + if (!stralloc_0(&host)) temp_nomem(); + temp_tlshost(); + } + } + if (user.len && pass.len) /* AUTH */ + smtp_auth(); + else + mailfrom(); /* Mail From */ + + code = smtpcode(); + if (code >= 500) quit("DConnected to "," but sender was rejected"); + if (code >= 400) quit("ZConnected to "," but sender was probably greylisted"); + + flagbother = 0; /* Rcpt To */ + for (i = 0; i < reciplist.len; ++i) { + buffer_puts(&bo,"RCPT TO:<"); + buffer_put(&bo,reciplist.sa[i].s,reciplist.sa[i].len); + buffer_puts(&bo,">\r\n"); + buffer_flush(&bo); + + code = smtpcode(); /* Data */ + if (flagsize) { + if (code == 552) quit("DConnected to "," but message size is too large"); + if (code == 452) quit("ZConnected to "," however insufficient storage space available"); + } + if (code == 421 || code == 450) { // Postfix merde ;-) + out("s"); outhost(); out(" sender is greylisting.\n"); + outsmtptext(); zero(); + } else if (code >= 500) { + out("h"); outhost(); out(" does not like recipient.\n"); + outsmtptext(); zero(); + } else if (code >= 400) { + out("s"); outhost(); out(" does not like recipient.\n"); + outsmtptext(); zero(); + } else { + out("r"); zero(); + flagbother = 1; + } + } + if (!flagbother) quit("DGiving up on ",""); + + buffer_putsflush(&bo,"DATA\r\n"); + + code = smtpcode(); + if (code >= 500) quit("D"," failed on DATA command"); + if (code >= 400) quit("Z"," failed on DATA command"); + + buffer_putflush(&bo,header.s,header.len); + + blast(); + code = smtpcode(); + flagcritical = 0; + if (code >= 500) quit("D"," failed after I sent the message"); + if (code >= 400) quit("Z"," failed after I sent the message"); + switch (flagtls) { // StartTLS + SMTPS + case 100: case 110: quit("K"," TLS transmitted message accepted"); break; + case 101: case 111: quit("K"," TLS (verified CA) transmitted message accepted"); break; + case 102: case 112: quit("K"," TLS (validated CA+DN*) transmitted message accepted"); break; + case 103: case 113: quit("K"," TLS (validated CA+DN) transmitted message accepted"); break; + case 104: case 114: quit("K"," TLS (CERT pinning) transmitted message accepted"); break; + case 105: case 115: quit("K"," TLS (TLSA EE validated) transmitted message accepted"); break; + case 106: case 116: quit("K"," TLS (TLSA TA validated) transmitted message accepted"); break; + case 107: case 117: quit("K"," TLS (TLSA PKIX verified) transmitted message accepted"); break; + default: quit("K"," accepted message"); break; + } +} + +/* this file is too long -------------------------------------- qmtp client */ + +int qmtpsend = 0; + +void qmtp() +{ + unsigned long len; + char *x; + int i; + int n; + unsigned char ch; + char num[FMT_ULONG]; + int flagallok; + + if (qmtpsend == 2) { /* QMTPS */ + tls_init(); + tls_peercheck(); + } + +/* the following code was substantially taken from serialmail's serialqmtp.c */ + + scan_ulong(msgsize,&len); + buffer_put(&bo,num,fmt_ulong(num,len + 1)); + buffer_put(&bo,":\n",2); + while (len > 0) { + n = buffer_feed(&bi); + if (n <= 0) _exit(1); /* wise guy again */ + x = buffer_PEEK(&bi); + buffer_put(&bo,x,n); + buffer_SEEK(&bi,n); + len -= n; + } + buffer_put(&bo,",",1); + + len = sender.len; + buffer_put(&bo,num,fmt_ulong(num,len)); + buffer_put(&bo,":",1); + buffer_put(&bo,sender.s,sender.len); + buffer_put(&bo,",",1); + + len = 0; + for (i = 0; i < reciplist.len; ++i) + len += fmt_ulong(num,reciplist.sa[i].len) + 1 + reciplist.sa[i].len + 1; + buffer_put(&bo,num,fmt_ulong(num,len)); + buffer_put(&bo,":",1); + for (i = 0; i < reciplist.len; ++i) { + buffer_put(&bo,num,fmt_ulong(num,reciplist.sa[i].len)); + buffer_put(&bo,":",1); + buffer_put(&bo,reciplist.sa[i].s,reciplist.sa[i].len); + buffer_put(&bo,",",1); + } + buffer_put(&bo,",",1); + buffer_flush(&bo); + + flagallok = 1; + + for (i = 0; i < reciplist.len; ++i) { + len = 0; + for (;;) { + get(&ch); + if (ch == ':') break; + if (len > QMTP_MAX) temp_proto(); + if (ch - '0' > 9) temp_proto(); + len = 10 * len + (ch - '0'); + } + if (!len) temp_proto(); + get(&ch); --len; + if ((ch != 'Z') && (ch != 'D') && (ch != 'K')) temp_proto(); + + if (!stralloc_copyb(&smtptext,&ch,1)) temp_proto(); + if (flagtls == 100) { + if (!stralloc_cats(&smtptext,"qmtps:")) temp_nomem(); + } else { + if (!stralloc_cats(&smtptext,"qmtp:")) temp_nomem(); + } + + while (len > 0) { + get(&ch); + --len; + } + + for (len = 0; len < smtptext.len; ++len) { + ch = smtptext.s[len]; + if ((ch < 32) || (ch > 126)) smtptext.s[len] = '?'; + } + get(&ch); + if (ch != ',') temp_proto(); + smtptext.s[smtptext.len - 1] = '\n'; + + if (smtptext.s[0] == 'K') out("r"); + else if (smtptext.s[0] == 'D') { + out("h"); + flagallok = 0; + } + else { /* if (smtptext.s[0] == 'Z') */ + out("s"); + flagallok = 0; + } + if (buffer_put(&bs,smtptext.s + 1,smtptext.len - 1) == -1) temp_qmtpnoc(); + zero(); + } + if (!flagallok) { + out("DGiving up on "); outhost(); out("\n"); + } else { + out("KAll received okay by "); outhost(); out("\n"); + } + zerodie(); +} + +/* this file is too long -------------------------------------- common */ + +/* host has to be canonical [A/AAAA record], box has to be quoted */ + +void addrmangle(stralloc *saout,char *address,int *flagalias,int flagcname) +{ + int at; + int r = 0; + stralloc cn = {0}; + + *flagalias = flagcname; /* saout + flagalias are output */ + if (!flagutf8) + flagutf8 = utf8string(address,str_len(address)); + + at = str_rchr(address,'@'); + if (!address[at]) { + if (!stralloc_copys(saout,address)) temp_nomem(); + return; + } + + if (!stralloc_copys(&canonbox,address)) temp_nomem(); + canonbox.len = at; + if (!quote(saout,&canonbox)) temp_nomem(); /* saout = 'inbox' name without quotes ;-) */ + if (!stralloc_cats(saout,"@")) temp_nomem(); + + if (!stralloc_copys(&canonhost,address + at + 1)) temp_nomem(); + if (flagcname) { /* no relayhost */ + DNS_INIT + switch ((r = dns_cname(&cn,&canonhost))) { + case DNS_MEM: temp_nomem(); + case DNS_SOFT: temp_dnscanon(); + case DNS_HARD: ; /* alias loop, not our problem */ + default: if (r > 0) *flagalias = 0; + } + } + if (!stralloc_cat(saout,&canonhost)) temp_nomem(); +} + +void getcontrols() +{ + if (control_init() == -1) temp_control(); + if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); + if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) + temp_control(); + if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1) + temp_control(); + switch (control_readfile(&smtproutes,"control/smtproutes",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&mapsmtproutes,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&mapsmtproutes,smtproutes.s,smtproutes.len,1)) temp_nomem(); break; + } + switch (control_readfile(&domainips,"control/domainips",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&mapdomainips,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&mapdomainips,domainips.s,domainips.len,1)) temp_nomem(); break; + } + switch (control_readfile(&authsenders,"control/authsenders",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&mapauthsenders,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&mapauthsenders,authsenders.s,authsenders.len,1)) temp_nomem(); break; + } + switch (control_readfile(&qmtproutes,"control/qmtproutes",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&mapqmtproutes,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&mapqmtproutes,qmtproutes.s,qmtproutes.len,1)) temp_nomem(); break; + } + switch (control_readfile(&domaincerts,"control/domaincerts",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&mapdomaincerts,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&mapdomaincerts,domaincerts.s,domaincerts.len,1)) temp_nomem(); break; + } + switch (control_readfile(&tlsdestinations,"control/tlsdestinations",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&maptlsdestinations,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&maptlsdestinations,tlsdestinations.s,tlsdestinations.len,1)) temp_nomem(); break; + } +} + +int main(int argc,char **argv) +{ + static ipalloc ip = {0}; + stralloc netif = {0}; + struct stat st; + int i, j, k; + int p; /* reserved for port */ + int r; /* reserved for return code */ + unsigned long random; + char **recips; + unsigned long prefme; + int flagallaliases; + int flagalias; + char *relayhost; + char *localip; + int ip6flag = 0; + + sig_pipeignore(); + if (argc < 4) perm_usage(); + if (chdir(auto_qmail) == -1) temp_chdir(); + + getcontrols(); + if (!stralloc_copys(&host,argv[1])) temp_nomem(); + + authsender = 0; + relayhost = 0; + + addrmangle(&sender,argv[2],&flagalias,0); + + if (sender.len > 1) { + i = str_chr(sender.s,'@'); + if (sender.s[i] == '@') + if (!stralloc_copyb(&senddomain,sender.s + i + 1,sender.len - i - 1)) temp_nomem(); // un-terminated + } + +/* this file is too long -------------------------------------- set domain ip + helohost */ + + localip = 0; + + for (i = 0; i <= senddomain.len; ++i) + if ((i == 0) || (senddomain.s[i] == '.')) + if ((localip = constmap(&mapdomainips,senddomain.s + i,senddomain.len - i))) + break; + + if (!localip) + localip = constmap(&mapdomainips,"*",1); /* one for all */ + + if (localip) { + j = str_chr(localip,'%'); + if (localip[j] != '%') j = 0; + k = str_chr(localip,'|'); + if (localip[k] != '|') k = 0; + if (k) { /* helohost */ + if (!stralloc_copys(&helohost,localip + k + 1)) temp_nomem(); + if (!stralloc_0(&helohost)) temp_nomem(); + localip[k] = 0; + } + if (j) { /* IF index */ + localip[j] = 0; + if (!stralloc_copys(&netif,localip + j + 1)) temp_nomem(); + if (!stralloc_0(&netif)) temp_nomem(); + } + } + +/* this file is too long -------------------------------------- authsender routes */ + + for (i = 0; i <= sender.len; ++i) + if ((i == 0) || (i == sender.len) || (sender.s[i] == '.') || (sender.s[i] == '@')) + if ((authsender = constmap(&mapauthsenders,sender.s + i,sender.len - i))) + break; + + if (authsender && !*authsender) authsender = 0; + + if (authsender) { + i = str_chr(authsender,'|'); + if (authsender[i] == '|') { + j = str_chr(authsender + i + 1,'|'); + if (authsender[i + j + 1] == '|') { + authsender[i] = 0; + authsender[i + j + 1] = 0; + if (!stralloc_copys(&user,"")) temp_nomem(); + if (!stralloc_copys(&user,authsender + i + 1)) temp_nomem(); + if (!stralloc_copys(&pass,"")) temp_nomem(); + if (!stralloc_copys(&pass,authsender + i + j + 2)) temp_nomem(); + } + } + p = str_chr(authsender,';'); + if (authsender[p] == ';') { + if (authsender[p + 1] == 's') { flagsmtps = 1, p++; } + scan_ulong(authsender + p + 1,&port); + authsender[p] = 0; + } + relayhost = authsender; + if (!stralloc_copys(&host,authsender)) temp_nomem(); + } + +/* this file is too long -------------------------------------- standard routes */ + + if (!authsender) { + if (sender.len == 0) { /* bounce routes */ + if ((relayhost = constmap(&mapqmtproutes,"!@",2))) { + qmtpsend = 1; port = PORT_QMTP; + } else + relayhost = constmap(&mapsmtproutes,"!@",2); + } + + if (relayhost && !*relayhost) relayhost = 0; + + if (!relayhost) { + for (i = 0; i <= host.len; ++i) { /* qmtproutes */ + if ((i == 0) || (i == host.len) || (host.s[i] == '.')) + if ((relayhost = constmap(&mapqmtproutes,host.s + i,host.len - i))) { + qmtpsend = 1; port = PORT_QMTP; + break; + } /* default smtproutes */ + if ((relayhost = constmap(&mapsmtproutes,host.s + i,host.len - i))) + break; + } + } + if (relayhost && !*relayhost) relayhost = 0; + + if (relayhost) { /* default smtproutes -- authenticated */ + i = str_chr(relayhost,'|'); + if (relayhost[i] == '|') { + j = str_chr(relayhost + i + 1,'|'); // authenticate + if (relayhost[i + j + 1] == '|') { + relayhost[i] = 0; + relayhost[i + j + 1] = 0; + if (!stralloc_copys(&user,"")) temp_nomem(); + if (!stralloc_copys(&user,relayhost + i + 1)) temp_nomem(); + if (!stralloc_copys(&pass,"")) temp_nomem(); + k = str_chr(relayhost + i + j + 2,'|'); // local ip + if (relayhost[i + j + k + 2] == '|') { + relayhost[i + j + k + 2] = 0; + localip = relayhost + i + j + k + 3; + } + if (!stralloc_copys(&pass,relayhost + i + j + 2)) temp_nomem(); + } + } + p = str_chr(relayhost,';'); + if (relayhost[p] == ';') { + if (relayhost[p + 1] == 's') { flagsmtps = 1; p++; } // RFC 8314 + scan_ulong(relayhost + p + 1,&port); + if (qmtpsend && port == PORT_QMTPS) qmtpsend = 2; + relayhost[p] = 0; + } + if (!stralloc_copys(&host,relayhost)) temp_nomem(); +#ifdef IDN2 + } else { + char *asciihost = 0; + if (!stralloc_0(&host)) temp_nomem(); + switch (idn2_lookup_u8(host.s,(uint8_t**)&asciihost,IDN2_NFC_INPUT)) { + case IDN2_OK: break; + case IDN2_MALLOC: temp_nomem(); + default: perm_dns(); + } + if (!stralloc_copys(&idnhost,asciihost)) temp_nomem(); +#endif + } + } + +/* this file is too long -------------------------------------- TLS destinations */ + + + flagtls = tls_destination((const stralloc) host); // host may not be 0-terminated + + if (flagtls > 0) { + if (tlsdestinfo) { + i = str_chr(tlsdestinfo,'|'); /* ca file/dir or cert fingerprint */ + if (tlsdestinfo[i] == '|') { + tlsdestinfo[i] = 0; + j = str_chr(tlsdestinfo + i + 1,'|'); /* cipher */ + if (tlsdestinfo[i + j + 1] == '|') { + tlsdestinfo[i + j + 1] = 0; + k = str_chr(tlsdestinfo + i + j + 2,'|'); /* cone domain */ + if (tlsdestinfo[i + j + k + 2] == '|') { + tlsdestinfo[i + j + k + 2] = 0; + if (str_diffn(tlsdestinfo + j + k + 3,canonhost.s,canonhost.len)) flagtls = 0; + } + p = str_chr(tlsdestinfo + i + j + 2,';'); /* verifydepth;port */ + if (tlsdestinfo[i + j + p + 2] == ';') { + tlsdestinfo[i + j + p + 2] = 0; + if (p > 0) scan_ulong(tlsdestinfo + i + j + 2,&verifydepth); + if (tlsdestinfo[i + j + p + 3] == 's') { flagsmtps = 1; p++; } /* RFC 8314 */ + scan_ulong(tlsdestinfo + i + j + p + 3,&port); + } + } + if (j) + if (!stralloc_copys(&ciphers,tlsdestinfo + i + 1)) temp_nomem(); + } + + /* either ':[=]cafile/cadir' -or- ':;port' */ + + if (tlsdestinfo[0] == ';') + scan_ulong(tlsdestinfo + 1,&port); + else + if (!stralloc_copys(&cafile,tlsdestinfo)) temp_nomem(); + } + +/* cafile starts with '=' => it is a fingerprint + cafile ends with '/' => consider it as cadir + cafile and cadir are now 0-terminated + ciphers are alway 0-terminated if given */ + + if (cafile.len > 2) { + flagverify = 1; + if (cafile.s[cafile.len] == '/') { + cafile.len = 0; + flagverify = 2; + if (!stralloc_copys(&cadir,tlsdestinfo)) temp_nomem(); + if (!stralloc_0(&cadir)) temp_nomem(); + } else { + if (cafile.s[0] == '=') flagverify = -2; + if (!stralloc_0(&cafile)) temp_nomem(); + } + } else + cafile.len = cadir.len = 0; + + if (ciphers.len > 4) /* otherwise garbage */ + if (!stralloc_0(&ciphers)) temp_nomem(); + else + ciphers.len = 0; + + if (port == PORT_SMTPS || flagsmtps) flagtls += 10; + if (port == PORT_QMTPS) flagtls += 20; + } + + if (flagtls == 8) flagverify = -1; + if (!flagtls && qmtpsend == 2) flagtls = 20; /* QMTPS */ + + +/* this file is too long -------------------------------------- Our Certs - per senddomain */ + + if (flagtls > 0) { + flagtlsdomain = tls_domaincerts((const stralloc) senddomain); // senddomain un-terminated + + if (flagtlsdomain && tlsdomaininfo) { + i = str_chr(tlsdomaininfo,'|'); + if (tlsdomaininfo[i] == '|') { + tlsdomaininfo[i] = 0; + j = str_chr(tlsdomaininfo + i + 1,'|'); + if (tlsdomaininfo[i + j + 1] == '|') { + tlsdomaininfo[i + j + 1] = 0; + if (!stralloc_copys(&keypwd,"")) temp_nomem(); + if (!stralloc_copys(&keypwd,tlsdomaininfo + i + j + 2)) temp_nomem(); + if (!stralloc_0(&keypwd)) temp_nomem(); + } + if (!stralloc_copys(&keyfile,tlsdomaininfo + i + 1)) temp_nomem(); + if (!stralloc_0(&keyfile)) temp_nomem(); + } + if (!stralloc_copys(&certfile,tlsdomaininfo)) temp_nomem(); + if (!stralloc_0(&certfile)) temp_nomem(); + flagtlsdomain = 2; + } + } + +/* this file is too long -------------------------------------- work thru reciplist */ + + if (!saa_readyplus(&reciplist,0)) temp_nomem(); + if (ipme_init() != 1) temp_oserr(); + + flagallaliases = 1; + recips = argv + 3; + + if (fstat(0,&st) == -1) quit("Z", " unable to fstat stdin"); + msize = st.st_size; + fmt_ulong(msgsize,msize); + + while (*recips) { + if (!saa_readyplus(&reciplist,1)) temp_nomem(); + reciplist.sa[reciplist.len] = sauninit; + addrmangle(reciplist.sa + reciplist.len,*recips,&flagalias,!relayhost); + if (!flagalias) flagallaliases = 0; + ++reciplist.len; + ++recips; + } + + random = now() + (getpid() << 16); +#ifdef IDN2 + switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&idnhost,random)) { +#else + switch (relayhost ? dns_ip(&ip,&host) : dns_mxip(&ip,&host,random)) { +#endif + case DNS_MEM: temp_nomem(); + case DNS_ERR: temp_dns(); + case DNS_COM: temp_dns(); + case DNS_SOFT: temp_dns(); +#ifdef DEFERREDBOUNCES + default: if (!ip.len) temp_nomx(); +#else + default: if (!ip.len) perm_nomx(); +#endif + } + + prefme = 100000; + for (i = 0; i < ip.len; ++i) + if (ipme_is(&ip.ix[i])) + if (ip.ix[i].pref < prefme) + prefme = ip.ix[i].pref; + + if (relayhost) prefme = 300000; + if (flagallaliases) prefme = 500000; + + if (localip) { + i = str_chr(localip,':'); + if (localip[i] == ':') ip6flag = 1; + else ip6flag = -1; + } + + for (i = 0; i < ip.len; ++i) { /* MX with smallest distance */ + if (ip6flag == -1 && ip.ix[i].af == AF_INET6) continue; + if (ip6flag == 1 && ip.ix[i].af == AF_INET) continue; + if (ip.ix[i].pref < prefme) break; + } + + if (i >= ip.len) + perm_ambigmx(); + + if (!stralloc_copys(&remotehost,ip.ix[i].mxh)) temp_nomem(); /* take MX hostname for TLSA */ + if (!stralloc_0(&remotehost)) temp_nomem(); + + for (i = 0; i < ip.len; ++i) { + if (ip.ix[i].pref < prefme) { + if (ip6flag == -1 && ip.ix[i].af == AF_INET6) continue; /* explicit binding */ + if (ip6flag == 1 && ip.ix[i].af == AF_INET) continue; + if (tcpto(&ip.ix[i])) continue; + + smtpfd = socket(ip.ix[i].af,SOCK_STREAM,0); + if (smtpfd == -1) continue; + + if (localip) { /* set domain ip */ + if (!stralloc_copyb(&sendip,localip,str_len(localip))) temp_nomem(); + j = str_chr(localip,':'); + if (localip[j] == ':') { + if (!ip6_scan(localip,ip6)) temp_noip(); /* IPv6 */ + if (byte_equal(ip.ix[i].addr.ip6.d,16,ip6)) continue; + ifidx = socket_getifidx(netif.s); + if (socket_bind6(smtpfd,ip6,0,ifidx) < 0) temp_osip(); + } else { + if (!ip4_scan(localip,ip4)) temp_noip(); /* IPv4 */ + if (byte_equal(ip.ix[i].addr.ip4.d,4,ip4)) continue; + if (socket_bind4(smtpfd,ip4,0) < 0) temp_osip(); + } + } + + + AGAIN: + if (ip.ix[i].af == AF_INET6) + r = timeoutconn6(smtpfd,(char *)&ip.ix[i].addr.ip6.d,(unsigned int) port,timeoutconnect,ifidx); + else + r = timeoutconn4(smtpfd,(char *)&ip.ix[i].addr.ip4.d,(unsigned int) port,timeoutconnect); + if (r == 0) { + tcpto_err(&ip.ix[i],0); + partner = ip.ix[i]; + if (qmtpsend) + qmtp(); + else + smtp(); /* read qmail/THOUGHTS; section 6 */ + } + if (flagtls == 9 && errno == EPROTO) { + flagtls = 0; goto AGAIN; + } + if (errno == ETIMEDOUT || errno == ECONNREFUSED || errno == EPROTO) + tcpto_err(&ip.ix[i],1); + close(smtpfd); + } + } + temp_noconn(); +} diff --git a/sqmail-4.3.07/src/qmail-rspawn.c b/sqmail-4.3.07/src/qmail-rspawn.c new file mode 100644 index 0000000..a9b0a1a --- /dev/null +++ b/sqmail-4.3.07/src/qmail-rspawn.c @@ -0,0 +1,99 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "fd.h" +#include "wait.h" +#include "buffer.h" +#include "exit.h" +#include "error.h" +#include "ipalloc.h" +#include "tcpto.h" +#include "auto_qmail.h" +#include "open.h" +#include "pathexec.h" + +void initialize(int argc,char **argv) { tcpto_clean(); } + +int truncreport = 0; + +void report(buffer *log,int wstat,char *s,int len) +{ + int j; + int k; + int result; + int orr; + + if (wait_crashed(wstat)) { buffer_putsflush(log,"Zqmail-spawn: qmail-remote crashed.\n"); return; } + + switch (wait_exitcode(wstat)) { + case 0: break; + case 111: buffer_putsflush(log,"Zqmail-rspawn: Unable to run qmail-remote.\n"); break; + default: buffer_putsflush(log,"Dqmail-rspawn: Unable to run qmail-remote. \n"); return; + } + + if (!len) { buffer_putsflush(log,"Zqmail-rspawn: qmail-remote produced no output.\n"); return; } + + result = -1; + j = 0; + + for (k = 0; k < len; ++k) + if (!s[k]) { + if (s[j] == 'K') { result = 1; break; } + if (s[j] == 'Z') { result = 0; break; } + if (s[j] == 'D') break; + j = k + 1; + } + + orr = result; + + switch (s[0]) { + case 's': orr = 0; break; + case 'h': orr = -1; + } + + switch (orr) { + case 1: buffer_put(log,"K",1); break; + case 0: buffer_put(log,"Z",1); break; + case -1: buffer_put(log,"D",1); break; + } + + for (k = 1; k < len;) + if (!s[k++]) { + buffer_puts(log,s + 1); + if (result <= orr) + if (k < len) + switch (s[k]) { + case 'Z': case 'D': case 'K': + buffer_puts(log,s + k + 1); + } + break; + } +} + +int spawn(int fdmess,int fdout,const char *s,char *r,const int at) +{ + int f; + char *(args[5]); + struct stat st; + + if (chdir(auto_qmail) == -1) _exit(110); + if (stat("control/dkimdomains",&st) !=1) + args[0] = "qmail-dksign"; + else + args[0] = "qmail-remote"; + args[1] = r + at + 1; + args[2] = s; + args[3] = r; + args[4] = 0; + + if (chdir("queue/mess") == -1) _exit(110); + + if (!(f = vfork())) { + if (fd_move(0,fdmess) == -1) _exit(111); + if (fd_move(1,fdout) == -1) _exit(111); + if (fd_copy(2,1) == -1) _exit(111); + pathexec(args); + if (errno) _exit(111); + _exit(100); + } + return f; +} diff --git a/sqmail-4.3.07/src/qmail-send.c b/sqmail-4.3.07/src/qmail-send.c new file mode 100644 index 0000000..fa241bf --- /dev/null +++ b/sqmail-4.3.07/src/qmail-send.c @@ -0,0 +1,1440 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> +#include "error.h" +#include "sig.h" +#include "direntry.h" +#include "control.h" +#include "select.h" +#include "open.h" +#include "seek.h" +#include "exit.h" +#include "lock.h" +#include "ndelay.h" +#include "now.h" +#include "getln.h" +#include "buffer.h" +#include "alloc.h" +#include "genalloc.h" +#include "stralloc.h" +#include "logmsg.h" +#include "str.h" +#include "byte.h" +#include "fmt.h" +#include "scan.h" +#include "case.h" +#include "auto_qmail.h" +#include "trigger.h" +#include "newfield.h" +#include "quote.h" +#include "qmail.h" +#include "qsutil.h" +#include "prioq.h" +#include "constmap.h" +#include "fmtqfn.h" +#include "readsubdir.h" +#include "sendtodo.h" + +int lifetime = 604800; +int bouncemaxbytes = 0; + +stralloc percenthack = {0}; +struct constmap mappercenthack; +stralloc locals = {0}; +struct constmap maplocals; +stralloc vdoms = {0}; +struct constmap mapvdoms; +stralloc envnoathost = {0}; +stralloc bouncefrom = {0}; +stralloc bouncehost = {0}; +stralloc doublebounceto = {0}; +stralloc doublebouncehost = {0}; + +char strnum2[FMT_ULONG]; +char strnum3[FMT_ULONG]; + +#define CHANNELS 2 +char *chanaddr[CHANNELS] = { "local/", "remote/" }; +char *chanstatusmsg[CHANNELS] = { " local ", " remote " }; +char *tochan[CHANNELS] = { " to local ", " to remote " }; +int chanfdout[CHANNELS] = { 1, 3 }; +int chanfdin[CHANNELS] = { 2, 4 }; +int chanskip[CHANNELS] = { 10, 20 }; + +int flagexitasap = 0; void sigterm() { flagexitasap = 1; } +int flagrunasap = 0; void sigalrm() { flagrunasap = 1; } +int flagreadasap = 0; void sighup() { flagreadasap = 1; } + +void cleandied() +{ + log1s("alert: lost connection to qmail-clean ... exiting\n"); + flagexitasap = 1; +} + +int flagspawnalive[CHANNELS]; + +void spawndied(int c) +{ + log1s("alert: oh no! lost spawn connection! dying...\n"); + flagspawnalive[c] = 0; + flagexitasap = 1; +} + +#define REPORTMAX 10000 + +datetime_sec recent; + + +/* this file is too long ---------------------------------------- FILE CREATE */ + +stralloc fn = {0}; +stralloc fn2 = {0}; +char fnmake_strnum[FMT_ULONG]; + +void fnmake_init() +{ + while (!stralloc_ready(&fn,FMTQFN)) nomem(); + while (!stralloc_ready(&fn2,FMTQFN)) nomem(); +} + +void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } +void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,1); } +void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } +void fnmake_foop(unsigned long id) { fn.len = fmtqfn(fn.s,"foop/",id,0); } +void fnmake_split(unsigned long id) { fn.len = fmtqfn(fn.s,"",id,1); } +void fnmake2_bounce(unsigned long id) { fn2.len = fmtqfn(fn2.s,"bounce/",id,0); } +void fnmake_chanaddr(unsigned long id,int c) { fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } + + +/* this file is too long ----------------------------------------- REWRITING */ + +void senderadd(stralloc *sa,char *sender,char *recip) +{ + int i; + int j; + int k; + + i = str_len(sender); + if (i >= 4) + if (str_equal(sender + i - 4,"-@[]")) { + j = byte_rchr(sender,i - 4,'@'); + k = str_rchr(recip,'@'); + if (recip[k] && (j + 5 <= i)) { + /* owner-@host-@[] -> owner-recipbox=reciphost@host */ + while (!stralloc_catb(sa,sender,j)) nomem(); + while (!stralloc_catb(sa,recip,k)) nomem(); + while (!stralloc_cats(sa,"=")) nomem(); + while (!stralloc_cats(sa,recip + k + 1)) nomem(); + while (!stralloc_cats(sa,"@")) nomem(); + while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem(); + return; + } + } + while (!stralloc_cats(sa,sender)) nomem(); +} + + +/* this file is too long ---------------------------------------------- INFO */ + +int getinfo(stralloc *sa,datetime_sec *dt,unsigned long id) +{ + int fdnumber; + struct stat st; + static stralloc line = {0}; + int match; + char buf[128]; + buffer b; + + fnmake_info(id); + fdnumber = open_read(fn.s); + if (fdnumber == -1) return 0; + if (fstat(fdnumber,&st) == -1) { close(fdnumber); return 0; } + buffer_init(&b,read,fdnumber,buf,sizeof(buf)); + if (getln(&b,&line,&match,'\0') == -1) { close(fdnumber); return 0; } + close(fdnumber); + if (!match) return 0; + if (line.s[0] != 'F') return 0; + + *dt = st.st_mtime; + while (!stralloc_copys(sa,line.s + 1)) nomem(); + while (!stralloc_0(sa)) nomem(); + return 1; +} + + +/* this file is too long ------------------------------------- COMMUNICATION */ + +buffer toqc; char toqcbuf[1024]; +buffer fromqc; char fromqcbuf[1024]; +stralloc comm_buf[CHANNELS] = { {0}, {0} }; +int comm_pos[CHANNELS]; + +void comm_init() +{ + int c; + + buffer_init(&toqc,write,5,toqcbuf,sizeof(toqcbuf)); + buffer_init(&fromqc,read,6,fromqcbuf,sizeof(fromqcbuf)); + for (c = 0; c < CHANNELS; ++c) + if (ndelay_on(chanfdout[c]) == -1) + /* this is so stupid: NDELAY semantics should be default on write */ + spawndied(c); /* drastic, but better than risking deadlock */ +} + +int comm_canwrite(int c) +{ + /* XXX: could allow a bigger buffer; say 10 recipients */ + if (comm_buf[c].s && comm_buf[c].len) return 0; + return 1; +} + +void comm_write(int c,int delnum,unsigned long id,char *sender,char *recip) +{ + char ch; + + if (comm_buf[c].s && comm_buf[c].len) return; + while (!stralloc_copys(&comm_buf[c],"")) nomem(); + ch = delnum; + while (!stralloc_append(&comm_buf[c],&ch)) nomem(); + fnmake_split(id); + while (!stralloc_cats(&comm_buf[c],fn.s)) nomem(); + while (!stralloc_0(&comm_buf[c])) nomem(); + senderadd(&comm_buf[c],sender,recip); + while (!stralloc_0(&comm_buf[c])) nomem(); + while (!stralloc_cats(&comm_buf[c],recip)) nomem(); + while (!stralloc_0(&comm_buf[c])) nomem(); + comm_pos[c] = 0; +} + +void comm_selprep(int *nfds,fd_set *wfds) +{ + int c; + + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) + if (comm_buf[c].s && comm_buf[c].len) { + FD_SET(chanfdout[c],wfds); + if (*nfds <= chanfdout[c]) + *nfds = chanfdout[c] + 1; + } +} + +void comm_do(fd_set *wfds) +{ + int c; + + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) + if (comm_buf[c].s && comm_buf[c].len) + if (FD_ISSET(chanfdout[c],wfds)) { + int w; + int len; + len = comm_buf[c].len; + + w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]); + if (w <= 0) { + if ((w == -1) && (errno == EPIPE)) + spawndied(c); + else + continue; /* kernel select() bug; can't avoid busy-looping */ + } else { + comm_pos[c] += w; + if (comm_pos[c] == len) + comm_buf[c].len = 0; + } + } +} + + +/* this file is too long ------------------------------------------ CLEANUPS */ + +int flagcleanup; /* if 1, cleanupdir is initialized and ready */ +readsubdir cleanupdir; +datetime_sec cleanuptime; + +void cleanup_init() +{ + flagcleanup = 0; + cleanuptime = now(); +} + +void cleanup_selprep(datetime_sec *wakeup) +{ + if (flagcleanup) *wakeup = 0; + if (*wakeup > cleanuptime) *wakeup = cleanuptime; +} + +void cleanup_do() +{ + char ch; + struct stat st; + unsigned long id; + + if (!flagcleanup) { + if (recent < cleanuptime) return; + readsubdir_init(&cleanupdir,"mess",pausedir); + flagcleanup = 1; + } + + switch (readsubdir_next(&cleanupdir,&id)) { + case 1: break; + case 0: flagcleanup = 0; cleanuptime = recent + SLEEP_CLEANUP; + default: return; + } + + fnmake_mess(id); + if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */ + if (recent <= st.st_atime + OSSIFIED) return; + + fnmake_info(id); + if (stat(fn.s,&st) == 0) return; + if (errno != ENOENT) return; + + fnmake_todo(id); + if (stat(fn.s,&st) == 0) return; + if (errno != ENOENT) return; + + fnmake_foop(id); + if (buffer_putflush(&toqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (buffer_get(&fromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') + log3s("warning: qmail-clean unable to clean up ",fn.s,"\n"); +} + + +/* this file is too long ----------------------------------- PRIORITY QUEUES */ + +prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */ +prioq pqchan[CHANNELS] = { {0}, {0} }; +/* pqchan 0: -todo +info +local ?remote */ +/* pqchan 1: -todo +info ?local +remote */ +prioq pqfail = {0}; /* stat() failure; has to be pqadded again */ + +void pqadd(unsigned long id) +{ + struct prioq_elt pe; + struct prioq_elt pechan[CHANNELS]; + int flagchan[CHANNELS]; + struct stat st; + int c; + +#define CHECKSTAT if (errno != ENOENT) goto FAIL; + + fnmake_info(id); + if (stat(fn.s,&st) == -1) { + CHECKSTAT + return; /* someone yanking our chain */ + } + + fnmake_todo(id); + if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */ + CHECKSTAT + + for (c = 0; c < CHANNELS; ++c) { + fnmake_chanaddr(id,c); + if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT } + else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; } + } + + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c],&pechan[c])) nomem(); + + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) break; + + if (c == CHANNELS) { + pe.id = id; pe.dt = now(); + while (!prioq_insert(&pqdone,&pe)) nomem(); + } + + return; + + FAIL: + log3s("warning: unable to stat ",fn.s,"; will try again later\n"); + pe.id = id; pe.dt = now() + SLEEP_SYSFAIL; + while (!prioq_insert(&pqfail,&pe)) nomem(); +} + +void pqstart() +{ + readsubdir rs; + int x; + unsigned long id; + + readsubdir_init(&rs,"info",pausedir); + + while ((x = readsubdir_next(&rs,&id))) + if (x > 0) pqadd(id); +} + +void pqfinish() +{ + int c; + struct prioq_elt pe; + time_t ut[2]; /* XXX: more portable than utimbuf, but still worrisome */ + + for (c = 0; c < CHANNELS; ++c) + while (prioq_min(&pqchan[c],&pe)) { + prioq_delmin(&pqchan[c]); + fnmake_chanaddr(pe.id,c); + ut[0] = ut[1] = pe.dt; + if (utime(fn.s,ut) == -1) + log3s("warning: unable to utime ",fn.s,"; message will be retried too soon\n"); + } +} + +void pqrun() +{ + int c; + int i; + + for (c = 0; c < CHANNELS; ++c) + if (pqchan[c].p) + if (pqchan[c].len) + for (i = 0; i < pqchan[c].len; ++i) + pqchan[c].p[i].dt = recent; +} + + +/* this file is too long ---------------------------------------------- JOBS */ + +struct job +{ + int refs; /* if 0, this struct is unused */ + unsigned long id; + int channel; + datetime_sec retry; + stralloc sender; + int numtodo; + int flaghiteof; + int flagdying; +}; + +int numjobs; +struct job *jo; + +void job_init() +{ + int j; + + while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem(); + for (j = 0; j < numjobs; ++j) { + jo[j].refs = 0; + jo[j].sender.s = 0; + } +} + +int job_avail() +{ + int j; + + for (j = 0; j < numjobs; ++j) + if (!jo[j].refs) return 1; + return 0; +} + +int job_open(unsigned long id,int channel) +{ + int j; + + for (j = 0; j < numjobs; ++j) + if (!jo[j].refs) break; + if (j == numjobs) return -1; + jo[j].refs = 1; + jo[j].id = id; + jo[j].channel = channel; + jo[j].numtodo = 0; + jo[j].flaghiteof = 0; + return j; +} + +void job_close(int j) +{ + struct prioq_elt pe; + struct stat st; + int c; + + if (0 < --jo[j].refs) return; + + pe.id = jo[j].id; + pe.dt = jo[j].retry; + + if (jo[j].flaghiteof && !jo[j].numtodo) { + fnmake_chanaddr(jo[j].id,jo[j].channel); + if (unlink(fn.s) == -1) { + log3s("warning: unable to unlink ",fn.s,"; will try again later\n"); + pe.dt = now() + SLEEP_SYSFAIL; + } else { + for (c = 0; c < CHANNELS; ++c) if (c != jo[j].channel) { + fnmake_chanaddr(jo[j].id,c); + if (stat(fn.s,&st) == 0) return; /* more channels going */ + if (errno != ENOENT) { + log3s("warning: unable to stat ",fn.s,"\n"); + break; /* this is the only reason for HOPEFULLY */ + } + } + pe.dt = now(); + while (!prioq_insert(&pqdone,&pe)) nomem(); + return; + } + } + + while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem(); +} + + +/* this file is too long ------------------------------------------- BOUNCES */ + +char *stripvdomprepend(char *recip) +{ + int i; + char *domain; + int domainlen; + char *prepend; + + i = str_rchr(recip,'@'); + if (!recip[i]) return recip; + domain = recip + i + 1; + domainlen = str_len(domain); + + for (i = 0; i <= domainlen; ++i) + if ((i == 0) || (i == domainlen) || (domain[i] == '.')) + if ((prepend = constmap(&mapvdoms,domain + i,domainlen - i))) { + if (!*prepend) break; + i = str_len(prepend); + if (str_diffn(recip,prepend,i)) break; + if (recip[i] != '-') break; + return recip + i + 1; + } + + return recip; +} + +stralloc bouncetext = {0}; + +void addbounce(unsigned long id,char *recip,char *report) +{ + int fd; + int pos; + int w; + + while (!stralloc_copys(&bouncetext,"<")) nomem(); + while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem(); + + for (pos = 0; pos < bouncetext.len; ++pos) + if (bouncetext.s[pos] == '\n') + bouncetext.s[pos] = '_'; + + while (!stralloc_cats(&bouncetext,">:\n")) nomem(); + while (!stralloc_cats(&bouncetext,report)) nomem(); + + if (report[0]) + if (report[str_len(report) - 1] != '\n') + while (!stralloc_cats(&bouncetext,"\n")) nomem(); + + for (pos = bouncetext.len - 2; pos > 0; --pos) + if (bouncetext.s[pos] == '\n') + if (bouncetext.s[pos - 1] == '\n') + bouncetext.s[pos] = '/'; + + while (!stralloc_cats(&bouncetext,"\n")) nomem(); + fnmake2_bounce(id); + + for (;;) { + fd = open_append(fn2.s); + if (fd != -1) break; + log1s("alert: unable to append to bounce message; HELP! sleeping...\n"); + sleep(10); + } + + pos = 0; + + while (pos < bouncetext.len) { + w = write(fd,bouncetext.s + pos,bouncetext.len - pos); + if (w <= 0) { + log1s("alert: unable to append to bounce message; HELP! sleeping...\n"); + sleep(10); + } + else + pos += w; + } + close(fd); +} + +int injectbounce(unsigned long id) +{ + struct qmail qqt; + struct stat st; + char *bouncesender; + char *bouncerecip; + int r; + int fd; + buffer bi; + char buf[128]; + char inbuf[128]; + static stralloc sender = {0}; + static stralloc quoted = {0}; + datetime_sec birth; + unsigned long qp; + int bytestogo; + int bytestoget; + + if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */ + + /* owner-@host-@[] -> owner-@host */ + if (sender.len >= 5) + if (str_equal(sender.s + sender.len - 5,"-@[]")) { + sender.len -= 4; + sender.s[sender.len - 1] = 0; + } + + fnmake2_bounce(id); + fnmake_mess(id); + + if (stat(fn2.s,&st) == -1) { + if (errno == ENOENT) return 1; + log3s("warning: unable to stat ",fn2.s,"\n"); + return 0; + } + + if (str_equal(sender.s,"#@[]")) + log3s("triple bounce: discarding ",fn2.s,"\n"); + else if (!*sender.s && *doublebounceto.s == '@') + log3s("double bounce: discarding ",fn2.s,"\n"); + else { + if (qmail_open(&qqt) == -1) + { log1s("warning: unable to start qmail-queue, will try later\n"); return 0; } + qp = qmail_qp(&qqt); + + if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; } + else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; } + + while (!newfield_datemake(now())) nomem(); + qmail_put(&qqt,newfield_date.s,newfield_date.len); + qmail_puts(&qqt,"From: "); + while (!quote("ed,&bouncefrom)) nomem(); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,"@"); + qmail_put(&qqt,bouncehost.s,bouncehost.len); + qmail_puts(&qqt,"\nTo: "); + while (!quote2("ed,bouncerecip)) nomem(); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,"\n\ +Subject: failure notice\n\ +\n\ +Hi. This is the qmail-send program at "); + qmail_put(&qqt,bouncehost.s,bouncehost.len); + qmail_puts(&qqt,*sender.s ? ".\n\ +I'm afraid I wasn't able to deliver your message to the following addresses.\n\ +This is a permanent error; I've given up. Sorry it didn't work out.\n\ +\n\ +" : ".\n\ +I tried to deliver a bounce message to this address, but the bounce bounced!\n\ +\n\ +"); + + fd = open_read(fn2.s); + if (fd == -1) + qmail_fail(&qqt); + else { + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + while ((r = buffer_get(&bi,buf,sizeof(buf))) > 0) + qmail_put(&qqt,buf,r); + + close(fd); + if (r == -1) qmail_fail(&qqt); + } + + qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n"); + qmail_puts(&qqt,"Return-Path: <"); + while (!quote2("ed,sender.s)) nomem(); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,">\n"); + + fd = open_read(fn.s); + if (fd == -1) + qmail_fail(&qqt); + else { + if (bouncemaxbytes) { + bytestogo = bouncemaxbytes; + bytestoget = (bytestogo < sizeof(buf)) ? bytestogo : sizeof(buf); + + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + while (bytestoget > 0 && (r = buffer_get(&bi,buf,bytestoget)) > 0) { + qmail_put(&qqt,buf,r); + bytestogo -= bytestoget; + bytestoget = (bytestogo < sizeof(buf)) ? bytestogo : sizeof(buf); + } + if (r > 0) + qmail_puts(&qqt,"\n\n--- Rest of message truncated.\n"); + } else { /* preserve default behavior */ + buffer_init(&bi,read,fd,inbuf,sizeof(inbuf)); + + while ((r = buffer_get(&bi,buf,sizeof(buf))) > 0) + qmail_put(&qqt,buf,r); + } + close(fd); + if (r == -1) qmail_fail(&qqt); + } + + qmail_from(&qqt,bouncesender); + qmail_to(&qqt,bouncerecip); + + if (*qmail_close(&qqt)) { + log1s("warning: trouble injecting bounce message, will try later\n"); + return 0; + } + + strnum2[fmt_ulong(strnum2,id)] = 0; + log2s("bounce msg ",strnum2); + strnum2[fmt_ulong(strnum2,qp)] = 0; + log3s(" qp ",strnum2,"\n"); + } + + if (unlink(fn2.s) != 0) { + log3s("warning: unable to unlink ",fn2.s,"\n"); + return 0; + } + + return 1; +} + + +/* this file is too long ---------------------------------------- DELIVERIES */ + +struct del { + int used; + int j; + unsigned long delid; + seek_pos mpos; + stralloc recip; +}; + +unsigned long masterdelid = 1; +unsigned int concurrency[CHANNELS] = { 10, 20 }; +unsigned int concurrencyused[CHANNELS] = { 0, 0 }; +struct del *d[CHANNELS]; +stralloc dline[CHANNELS]; +char delbuf[2048]; + +void del_status() +{ + int c; + + log1s("status:"); + for (c = 0; c < CHANNELS; ++c) { + strnum2[fmt_ulong(strnum2,(unsigned long) concurrencyused[c])] = 0; + strnum3[fmt_ulong(strnum3,(unsigned long) concurrency[c])] = 0; + log2s(chanstatusmsg[c],strnum2); + log2s("/",strnum3); + } + if (flagexitasap) log1s(" exitasap"); + log1s("\n"); +} + +void del_init() +{ + int c; + int i; + + for (c = 0; c < CHANNELS; ++c) { + flagspawnalive[c] = 1; + while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del)))) + nomem(); + for (i = 0; i < concurrency[c]; ++i) + { d[c][i].used = 0; d[c][i].recip.s = 0; } + dline[c].s = 0; + while (!stralloc_copys(&dline[c],"")) nomem(); + } + + del_status(); +} + +int del_canexit() +{ + int c; + + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */ + if (concurrencyused[c]) return 0; + return 1; +} + +int del_avail(int c) +{ + return flagspawnalive[c] && comm_canwrite(c) && (concurrencyused[c] < concurrency[c]); +} + +void del_start(int j,seek_pos mpos,char *recip) +{ + int i; + int c; + + c = jo[j].channel; + if (!flagspawnalive[c]) return; + if (!comm_canwrite(c)) return; + + for (i = 0; i < concurrency[c]; ++i) + if (!d[c][i].used) break; + if (i == concurrency[c]) return; + + if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; } + if (!stralloc_0(&d[c][i].recip)) { nomem(); return; } + d[c][i].j = j; ++jo[j].refs; + d[c][i].delid = masterdelid++; + d[c][i].mpos = mpos; + d[c][i].used = 1; ++concurrencyused[c]; + + comm_write(c,i,jo[j].id,jo[j].sender.s,recip); + + strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0; + strnum3[fmt_ulong(strnum3,jo[j].id)] = 0; + log2s("starting delivery ",strnum2); + log3s(": msg ",strnum3,tochan[c]); + logsafe(recip); + log1s("\n"); + del_status(); +} + +void markdone(int c,unsigned long id,seek_pos pos) +{ + struct stat st; + int fd; + + fnmake_chanaddr(id,c); + + for (;;) { + fd = open_write(fn.s); + if (fd == -1) break; + if (fstat(fd,&st) == -1) { close(fd); break; } + if (seek_set(fd,pos) == -1) { close(fd); break; } + if (write(fd,"D",1) != 1) { close(fd); break; } + /* further errors -> double delivery without us knowing about it, oh well */ + close(fd); + return; + } + log3s("warning: trouble marking ",fn.s,"; message will be delivered twice!\n"); +} + +void del_dochan(int c) +{ + int r; + char ch; + int i; + int delnum; + + r = read(chanfdin[c],delbuf,sizeof(delbuf)); + if (r == -1) return; + if (r == 0) { spawndied(c); return; } + + for (i = 0; i < r; ++i) { + ch = delbuf[i]; + while (!stralloc_append(&dline[c],&ch)) nomem(); + + if (dline[c].len > REPORTMAX) + dline[c].len = REPORTMAX; + /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */ + /* but from a security point of view, we don't trust rspawn */ + + if (!ch && (dline[c].len > 1)) { + delnum = (unsigned int) (unsigned char) dline[c].s[0]; + if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used) + log1s("warning: internal error: delivery report out of range\n"); + else { + strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0; + if (dline[c].s[1] == 'Z') + if (jo[d[c][delnum].j].flagdying) { + dline[c].s[1] = 'D'; + --dline[c].len; + while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem(); + while (!stralloc_0(&dline[c])) nomem(); + } + + switch (dline[c].s[1]) { + case 'K': + log3s("delivery ",strnum3,": success: "); + logsafe(dline[c].s + 2); + log1s("\n"); + markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; + case 'Z': + log3s("delivery ",strnum3,": deferral: "); + logsafe(dline[c].s + 2); + log1s("\n"); + break; + case 'D': + log3s("delivery ",strnum3,": failure: "); + logsafe(dline[c].s + 2); + log1s("\n"); + addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2); + markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos); + --jo[d[c][delnum].j].numtodo; + break; + default: + log3s("delivery ",strnum3,": report mangled, will defer\n"); + } + + job_close(d[c][delnum].j); + d[c][delnum].used = 0; --concurrencyused[c]; + del_status(); + + } + dline[c].len = 0; + } + } +} + +void del_selprep(int *nfds,fd_set *rfds) +{ + int c; + + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) { + FD_SET(chanfdin[c],rfds); + if (*nfds <= chanfdin[c]) + *nfds = chanfdin[c] + 1; + } +} + +void del_do(fd_set *rfds) +{ + int c; + + for (c = 0; c < CHANNELS; ++c) + if (flagspawnalive[c]) + if (FD_ISSET(chanfdin[c],rfds)) + del_dochan(c); +} + + +/* this file is too long -------------------------------------------- PASSES */ + +struct +{ + unsigned long id; /* if 0, need a new pass */ + int j; /* defined if id; job number */ + int fd; /* defined if id; reading from {local,remote} */ + seek_pos mpos; /* defined if id; mark position */ + buffer b; + char buf[128]; +} +pass[CHANNELS]; + +void pass_init() +{ + int c; + + for (c = 0; c < CHANNELS; ++c) pass[c].id = 0; +} + +void pass_selprep(datetime_sec *wakeup) +{ + int c; + struct prioq_elt pe; + if (flagexitasap) return; + + for (c = 0; c < CHANNELS; ++c) + if (pass[c].id) + if (del_avail(c)) + { *wakeup = 0; return; } + + if (job_avail()) + for (c = 0; c < CHANNELS; ++c) + if (!pass[c].id) + if (prioq_min(&pqchan[c],&pe)) + if (*wakeup > pe.dt) *wakeup = pe.dt; + + if (prioq_min(&pqfail,&pe)) + if (*wakeup > pe.dt) + *wakeup = pe.dt; + + if (prioq_min(&pqdone,&pe)) + if (*wakeup > pe.dt)*wakeup = pe.dt; +} + +static datetime_sec squareroot(datetime_sec x) /* result^2 <= x < (result + 1)^2 ; assuming: >= 0 */ +{ + datetime_sec y; + datetime_sec yy; + datetime_sec y21; + int j; + + y = 0; yy = 0; + for (j = 15; j >= 0; --j) { + y21 = (y << (j + 1)) + (1 << (j + j)); + if (y21 <= x - yy) { y += (1 << j); yy += y21; } + } + return y; +} + +datetime_sec nextretry(datetime_sec birth,int c) +{ + int n; + + if (birth > recent) n = 0; + else n = squareroot(recent - birth); /* no need to add fuzz to recent */ + + n += chanskip[c]; + return birth + n * n; +} + +void pass_dochan(int c) +{ + datetime_sec birth; + struct prioq_elt pe; + static stralloc line = {0}; + int match; + + if (flagexitasap) return; + + if (!pass[c].id) { + if (!job_avail()) return; + if (!prioq_min(&pqchan[c],&pe)) return; + if (pe.dt > recent) return; + fnmake_chanaddr(pe.id,c); + + prioq_delmin(&pqchan[c]); + pass[c].mpos = 0; + pass[c].fd = open_read(fn.s); + if (pass[c].fd == -1) goto trouble; + if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; } + pass[c].id = pe.id; + buffer_init(&pass[c].b,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf)); + pass[c].j = job_open(pe.id,c); + jo[pass[c].j].retry = nextretry(birth,c); + jo[pass[c].j].flagdying = (recent > birth + lifetime); + while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem(); + } + + if (!del_avail(c)) return; + + if (getln(&pass[c].b,&line,&match,'\0') == -1) { + fnmake_chanaddr(pass[c].id,c); + log3s("warning: trouble reading ",fn.s,"; will try again later\n"); + close(pass[c].fd); + job_close(pass[c].j); + pass[c].id = 0; + return; + } + + if (!match) { + close(pass[c].fd); + jo[pass[c].j].flaghiteof = 1; + job_close(pass[c].j); + pass[c].id = 0; + return; + } + + switch (line.s[0]) { + case 'T': + ++jo[pass[c].j].numtodo; + del_start(pass[c].j,pass[c].mpos,line.s + 1); + break; + case 'D': + break; + default: + fnmake_chanaddr(pass[c].id,c); + log3s("warning: unknown record type in ",fn.s,"!\n"); + close(pass[c].fd); + job_close(pass[c].j); + pass[c].id = 0; + return; + } + + pass[c].mpos += line.len; + return; + + trouble: + log3s("warning: trouble opening ",fn.s,"; will try again later\n"); + pe.dt = recent + SLEEP_SYSFAIL; + while (!prioq_insert(&pqchan[c],&pe)) nomem(); +} + +void messdone(unsigned long id) +{ + char ch; + int c; + struct prioq_elt pe; + struct stat st; + + for (c = 0; c < CHANNELS; ++c) { + fnmake_chanaddr(id,c); + if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */ + if (errno != ENOENT) { + log3s("warning: unable to stat ",fn.s,"; will try again later\n"); + goto FAIL; + } + } + + fnmake_todo(id); + if (stat(fn.s,&st) == 0) return; + if (errno != ENOENT) { + log3s("warning: unable to stat ",fn.s,"; will try again later\n"); + goto FAIL; + } + + fnmake_info(id); + if (stat(fn.s,&st) == -1) { + if (errno == ENOENT) return; + log3s("warning: unable to stat ",fn.s,"; will try again later\n"); + goto FAIL; + } + + /* -todo +info -local -remote ?bounce */ + if (!injectbounce(id)) + goto FAIL; /* injectbounce() produced error message */ + + strnum3[fmt_ulong(strnum3,id)] = 0; + log3s("end msg ",strnum3,"\n"); + + /* -todo +info -local -remote -bounce */ + fnmake_info(id); + if (unlink(fn.s) == -1) { + log3s("warning: unable to unlink ",fn.s,"; will try again later\n"); + goto FAIL; + } + + /* -todo -info -local -remote -bounce; we can relax */ + fnmake_foop(id); + if (buffer_putflush(&toqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (buffer_get(&fromqc,&ch,1) != 1) { cleandied(); return; } + if (ch != '+') log3s("warning: qmail-clean unable to clean up ",fn.s,"\n"); + + return; + + FAIL: + pe.id = id; pe.dt = now() + SLEEP_SYSFAIL; + while (!prioq_insert(&pqdone,&pe)) nomem(); +} + +void pass_do() +{ + int c; + struct prioq_elt pe; + + for (c = 0; c < CHANNELS; ++c) + pass_dochan(c); + + if (prioq_min(&pqfail,&pe)) + if (pe.dt <= recent) { + prioq_delmin(&pqfail); + pqadd(pe.id); + } + + if (prioq_min(&pqdone,&pe)) + if (pe.dt <= recent) { + prioq_delmin(&pqdone); + messdone(pe.id); + } +} + + +/* this file is too long ------------------------------------- EXTERNAL TODO */ + +stralloc todoline = {0}; +char todobuf[2048]; +int todofdin; +int todofdout; +int flagtodoalive; + +void tododied() { + log1s("alert: lost connection to qmail-todo ... exiting\n"); + flagexitasap = 1; + flagtodoalive = 0; +} + +void todo_init() +{ + todofdout = 7; + todofdin = 8; + flagtodoalive = 1; + /* sync with external todo */ + if (write(todofdout,"S",1) != 1) tododied(); + + return; +} + +void todo_selprep(int *nfds,fd_set *rfds,datetime_sec *wakeup) +{ + if (flagexitasap) { + if (flagtodoalive) { + write(todofdout,"X",1); + } + } + if (flagtodoalive) { + FD_SET(todofdin,rfds); + if (*nfds <= todofdin) + *nfds = todofdin + 1; + } +} + +void todo_del(char* s) +{ + int flagchan[CHANNELS]; + struct prioq_elt pe; + unsigned long id; + unsigned int len; + int c; + + for (c = 0; c < CHANNELS; ++c) + flagchan[c] = 0; + + switch (*s++) { + case 'L': + flagchan[0] = 1; + break; + case 'R': + flagchan[1] = 1; + break; + case 'B': + flagchan[0] = 1; + flagchan[1] = 1; + break; + case 'X': + break; + default: + log1s("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + len = scan_ulong(s,&id); + if (!len || s[len]) { + log1s("warning: qmail-send unable to understand qmail-todo\n"); + return; + } + + pe.id = id; pe.dt = now(); + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) + while (!prioq_insert(&pqchan[c],&pe)) nomem(); + + for (c = 0; c < CHANNELS; ++c) + if (flagchan[c]) break; + + if (c == CHANNELS) + while (!prioq_insert(&pqdone,&pe)) nomem(); + + return; +} + +void todo_do(fd_set *rfds) +{ + int r; + char ch; + int i; + + if (!flagtodoalive) return; + if (!FD_ISSET(todofdin,rfds)) return; + + r = read(todofdin,todobuf,sizeof(todobuf)); + if (r == -1) return; + if (r == 0) { + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + return; + } + + for (i = 0; i < r; ++i) { + ch = todobuf[i]; + while (!stralloc_append(&todoline,&ch)) nomem(); + if (todoline.len > REPORTMAX) + todoline.len = REPORTMAX; + /* qmail-todo is responsible for keeping it short */ + if (!ch && (todoline.len > 1)) { + switch (todoline.s[0]) { + case 'D': + if (flagexitasap) break; + todo_del(todoline.s + 1); + break; + case 'L': + log1s(todoline.s + 1); + break; + case 'X': + if (flagexitasap) + flagtodoalive = 0; + else + tododied(); + break; + default: + log1s("warning: qmail-send unable to understand qmail-todo: report mangled\n"); + break; + } + todoline.len = 0; + } + } +} + +/* this file is too long ---------------------------------------------- MAIN */ + +int getcontrols() +{ + if (control_init() == -1) return 0; + if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0; + if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0; + if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0; + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; + if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0; + if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0; + if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0; + if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0; + if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0; + if (!stralloc_cats(&doublebounceto,"@")) return 0; + if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0; + if (!stralloc_0(&doublebounceto)) return 0; + if (control_readfile(&locals,"control/locals",1) != 1) return 0; + if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; + switch (control_readfile(&percenthack,"control/percenthack",0)) { + case -1: return 0; + case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; + case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; + } + switch (control_readfile(&vdoms,"control/virtualdomains",0)) { + case -1: return 0; + case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; + } + return 1; +} + +stralloc newlocals = {0}; +stralloc newvdoms = {0}; + +void regetcontrols() +{ + int r; + + if (control_readfile(&newlocals,"control/locals",1) != 1) { log1s("alert: unable to reread control/locals\n"); return; } + if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) { log1s("alert: unable to reread control/concurrencylocal\n"); return; } + if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) { log1s("alert: unable to reread control/concurrencyremote\n"); return; } + if (control_readint(&lifetime,"control/queuelifetime") == -1) { log1s("alert: unable to reread control/queuelifetime\n"); return; } + + r = control_readfile(&newvdoms,"control/virtualdomains",0); + if (r == -1) { log1s("alert: unable to reread control/virtualdomains\n"); return; } + + constmap_free(&maplocals); + constmap_free(&mapvdoms); + + while (!stralloc_copy(&locals,&newlocals)) nomem(); + while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); + + if (r) { + while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); + while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); + } else + while (!constmap_init(&mapvdoms,"",0,1)) nomem(); +} + +void reread() +{ + if (chdir(auto_qmail) == -1) { + log1s("alert: unable to reread controls: unable to switch to home directory\n"); + return; + } + write(todofdout,"H",1); + regetcontrols(); + + while (chdir("queue") == -1) { + log1s("alert: unable to switch back to queue directory; HELP! sleeping...\n"); + sleep(10); + } +} + +int main() +{ + int fd; + datetime_sec wakeup; + fd_set rfds; + fd_set wfds; + int nfds; + struct timeval tv; + int c; + int u; + int r; + char ch; + + if (chdir(auto_qmail) == -1) { log1s("alert: cannot start: unable to switch to home directory\n"); _exit(110); } + if (!getcontrols()) { log1s("alert: cannot start: unable to read controls\n"); _exit(111); } + if (chdir("queue") == -1) { log1s("alert: cannot start: unable to switch to queue directory\n"); _exit(110); } + sig_pipeignore(); + sig_termcatch(sigterm); + sig_alarmcatch(sigalrm); + sig_hangupcatch(sighup); + sig_childdefault(); + umask(077); + + fd = open_write("lock/sendmutex"); + if (fd == -1) { log1s("alert: cannot start: unable to open mutex\n"); _exit(111); } + if (lock_exnb(fd) == -1) { log1s("alert: cannot start: qmail-send is already running\n"); _exit(111); } + + numjobs = 0; + for (c = 0;c < CHANNELS;++c) { + do + r = read(chanfdin[c],&ch,1); + + while ((r == -1) && (errno == EINTR)); + if (r < 1) { log1s("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); } + + u = (unsigned int) (unsigned char) ch; + if (concurrency[c] > u) concurrency[c] = u; + numjobs += concurrency[c]; + } + + fnmake_init(); + + comm_init(); + + pqstart(); + job_init(); + del_init(); + pass_init(); + todo_init(); + cleanup_init(); + + while (!flagexitasap || !del_canexit() || flagtodoalive) { + recent = now(); + + if (flagrunasap) { flagrunasap = 0; pqrun(); } + if (flagreadasap) { flagreadasap = 0; reread(); } + + wakeup = recent + SLEEP_FOREVER; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + nfds = 1; + + comm_selprep(&nfds,&wfds); + del_selprep(&nfds,&rfds); + pass_selprep(&wakeup); + todo_selprep(&nfds,&rfds,&wakeup); + cleanup_selprep(&wakeup); + + if (wakeup <= recent) tv.tv_sec = 0; + else tv.tv_sec = wakeup - recent + SLEEP_FUZZ; + tv.tv_usec = 0; + + if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1) + if (errno == EINTR) + ; + else + log1s("warning: trouble in select\n"); + else { + recent = now(); + + comm_do(&wfds); + del_do(&rfds); + todo_do(&rfds); + pass_do(); + cleanup_do(); + } + } + + pqfinish(); + log1s("status: exiting\n"); + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-showctl.c b/sqmail-4.3.07/src/qmail-showctl.c new file mode 100644 index 0000000..2b4cc0a --- /dev/null +++ b/sqmail-4.3.07/src/qmail-showctl.c @@ -0,0 +1,372 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "spf.h" +#include "buffer.h" +#include "exit.h" +#include "fmt.h" +#include "str.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "direntry.h" +#include "auto_uids.h" +#include "auto_qmail.h" +#include "auto_break.h" +#include "auto_patrn.h" +#include "auto_spawn.h" +#include "auto_split.h" + +stralloc me = {0}; +int meok; + +stralloc line = {0}; +char num[FMT_ULONG]; + +void safeput(char *buf,unsigned int len) +{ + char ch; + + while (len > 0) { + ch = *buf; + if ((ch < 32) || (ch > 126)) ch = '?'; + buffer_put(buffer_1,&ch,1); + ++buf; + --len; + } +} + +void do_int(char *fn,char *def,char *pre,char *post) +{ + int i; + buffer_puts(buffer_1,"\n"); + buffer_puts(buffer_1,fn); + buffer_puts(buffer_1,": "); + switch (control_readint(&i,fn)) { + case 0: + buffer_puts(buffer_1,"(Default.) "); + buffer_puts(buffer_1,pre); + buffer_puts(buffer_1,def); + buffer_puts(buffer_1,post); + buffer_puts(buffer_1,".\n"); + break; + case 1: + if (i < 0) i = 0; + buffer_puts(buffer_1,pre); + buffer_put(buffer_1,num,fmt_uint(num,i)); + buffer_puts(buffer_1,post); + buffer_puts(buffer_1,".\n"); + break; + default: + buffer_puts(buffer_1,"Oops! Trouble reading this file.\n"); + break; + } +} + +void do_str(char *fn,int flagme,char *def,char *pre) +{ + buffer_puts(buffer_1,"\n"); + buffer_puts(buffer_1,fn); + buffer_puts(buffer_1,": "); + switch (control_readline(&line,fn)) { + case 0: + buffer_puts(buffer_1,"(Default.) "); + if (!stralloc_copys(&line,def)) { + buffer_puts(buffer_1,"Oops! Out of memory.\n"); + break; + }; + if (flagme && meok) + if (!stralloc_copy(&line,&me)) { + buffer_puts(buffer_1,"Oops! Out of memory.\n"); + break; + }; + case 1: + buffer_puts(buffer_1,pre); + safeput(line.s,line.len); + buffer_puts(buffer_1,".\n"); + break; + default: + buffer_puts(buffer_1,"Oops! Trouble reading this file.\n"); + break; + } +} + +int do_lst(char *fn,char *def,char *pre,char *post) +{ + int i; + int j; + + buffer_puts(buffer_1,"\n"); + buffer_puts(buffer_1,fn); + buffer_puts(buffer_1,": "); + switch (control_readfile(&line,fn,0)) { + case 0: + buffer_puts(buffer_1,"(Default.) "); + buffer_puts(buffer_1,def); + buffer_puts(buffer_1,"\n"); + return 0; + case 1: + buffer_puts(buffer_1,"\n"); + i = 0; + for (j = 0; j < line.len; ++j) + if (!line.s[j]) { + buffer_puts(buffer_1,pre); + safeput(line.s + i,j - i); + buffer_puts(buffer_1,post); + buffer_puts(buffer_1,"\n"); + i = j + 1; + } + return 1; + default: + buffer_puts(buffer_1,"Oops! Trouble reading this file.\n"); + return -1; + } +} + +int main() +{ + DIR *dir; + direntry *d; + struct stat stmrh; + struct stat stmrhcdb; + + buffer_puts(buffer_1,"s/qmail home directory: "); + buffer_puts(buffer_1,auto_qmail); + buffer_puts(buffer_1,".\n"); + + buffer_puts(buffer_1,"user-ext delimiter: "); + buffer_puts(buffer_1,auto_break); + buffer_puts(buffer_1,".\n"); + + buffer_puts(buffer_1,"paternalism (in decimal): "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_patrn)); + buffer_puts(buffer_1,".\n"); + + buffer_puts(buffer_1,"silent concurrency limit: "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_spawn)); + buffer_puts(buffer_1,".\n"); + + buffer_puts(buffer_1,"subdirectory split: "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_split)); + buffer_puts(buffer_1,".\n"); + + buffer_puts(buffer_1,"user ids: "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uida)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uidd)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uidl)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uido)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uidp)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uidq)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uidr)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_uids)); + buffer_puts(buffer_1,".\n"); + + buffer_puts(buffer_1,"group ids: "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_gidn)); + buffer_puts(buffer_1,", "); + buffer_put(buffer_1,num,fmt_ulong(num,(unsigned long) auto_gidq)); + buffer_puts(buffer_1,".\n"); + + if (chdir(auto_qmail) == -1) { + buffer_puts(buffer_1,"Oops! Unable to chdir to "); + buffer_puts(buffer_1,auto_qmail); + buffer_puts(buffer_1,".\n"); + buffer_flush(buffer_1); + _exit(110); + } + if (chdir("control") == -1) { + buffer_puts(buffer_1,"Oops! Unable to chdir to control.\n"); + buffer_flush(buffer_1); + _exit(110); + } + + dir = opendir("."); + if (!dir) { + buffer_puts(buffer_1,"Oops! Unable to open current directory.\n"); + buffer_flush(buffer_1); + _exit(110); + } + + meok = control_readline(&me,"me"); + if (meok == -1) { + buffer_puts(buffer_1,"Oops! Trouble reading control/me."); + buffer_flush(buffer_1); + _exit(112); + } + + do_lst("authsenders","No authenticated SMTP senders.","Authenticated SMTP senders: ",""); + do_lst("badhelo","Any HELO/EHLO greeting is allowed.",""," not accepted in HELO/EHLO; exception token is '!'."); + do_lst("badmailfrom","Any MAIL FROM is allowed.",""," are rejected or treated special in MAIL FROM depending on tokens: '!', '?', '=', '~', '+'."); + do_lst("badloadertypes","Any loader types are accepted.",""," not accepted as loader type."); + /* XXX: check badloadertypes.cdb contents */ + buffer_puts(buffer_1,"\nbadloadertypes.cdb: "); + if (stat("badloadertypes",&stmrh) == -1) + if (stat("badloadertypes.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"(Default.) No effect.\n"); + else + buffer_puts(buffer_1,"Oops! badloadertypes.cdb exists but badloadertypes doesn't.\n"); + else + if (stat("badloadertypes.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"Oops! badloadertypes exists but badloadertypes.cdb doesn't.\n"); + else + if (stmrh.st_mtime > stmrhcdb.st_mtime) + buffer_puts(buffer_1,"Oops! badloadertypes.cdb is older than badloadertypes.\n"); + else + buffer_puts(buffer_1,"Modified recently enough; hopefully up to date.\n"); + do_lst("badmimetypes","Any MIME types are accepted.",""," not accepted as MIME type."); + /* XXX: check badmimetypes.cdb contents */ + buffer_puts(buffer_1,"\nbadmimetypes.cdb: "); + if (stat("badmimetypes",&stmrh) == -1) + if (stat("badmimetypes.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"(Default.) No effect.\n"); + else + buffer_puts(buffer_1,"Oops! badmimetypes.cdb exists but badmimetypes doesn't.\n"); + else + if (stat("badmimetypes.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"Oops! badmimetypes exists but badmimetypes.cdb doesn't.\n"); + else + if (stmrh.st_mtime > stmrhcdb.st_mtime) + buffer_puts(buffer_1,"Oops! badmimetypes.cdb is older than badmimetypes.\n"); + else + buffer_puts(buffer_1,"Modified recently enough; hopefully up to date.\n"); + do_lst("badrcptto","Any RCPT TO is allowed.",""," not accepted in RCPT TO."); + do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is "); + do_str("bouncehost",1,"bouncehost","Bounce host name is "); + do_int("bouncemaxbytes","0","Bounce size limit is "," bytes"); + do_int("concurrencylocal","10","Local concurrency is ",""); + do_int("concurrencyremote","20","Remote concurrency is ",""); + do_int("databytes","0","SMTP DATA limit is "," bytes"); + do_str("defaultdomain",1,"defaultdomain","Default domain name is "); + do_str("defaulthost",1,"defaulthost","Default host name is "); + do_lst("dkimdomains","No DKIM domains defined for signing.","DKIM domains: ",""); + do_lst("domaincerts","No domain certs defined.","Domain certs: ",""); + do_lst("domainips","No domain ip mappings defined.","Mappping sender domain part to local ip: ",""); + do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: "); + do_str("doublebounceto",0,"postmaster","2B recipient user: "); + do_str("envnoathost",1,"envnoathost","Presumed domain name is "); + do_str("helohost",1,"helohost","SMTP client HELO host name is "); + do_str("idhost",1,"idhost","Message-ID host name is "); + do_str("localiphost",1,"localiphost","Local IP address becomes "); + do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally."); + do_str("me",0,"undefined! Uh-oh","My name is "); + + do_lst("mailfromrules","Any envelope sender are accepted.",""," (MAV rule)."); + /* XXX: check mailfromrules.cdb contents */ + buffer_puts(buffer_1,"\nmailfromrules.cdb: "); + if (stat("mailfromrules",&stmrh) == -1) + if (stat("mailfromrules.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"(Default.) No effect.\n"); + else + buffer_puts(buffer_1,"Oops! mailfromrules.cdb exists but mailfromrules doesn't.\n"); + else + if (stat("mailfromrules.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"Oops! mailfromrules exists but mailfromrules.cdb doesn't.\n"); + else + if (stmrh.st_mtime > stmrhcdb.st_mtime) + buffer_puts(buffer_1,"Oops! mailfromrules.cdb is older than mailfromrules.\n"); + else + buffer_puts(buffer_1,"Modified recently enough; hopefully up to date.\n"); + + do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@","."); + do_str("plusdomain",1,"plusdomain","Plus domain name is "); + do_lst("qmqpservers","No QMQP servers.","QMQP server: ","."); + do_int("queuelifetime","604800","Message lifetime in the queue is "," seconds"); + + if (do_lst("rcpthosts","SMTP clients may send messages to any recipient.","SMTP clients may send messages to recipients at ",".")) + do_lst("morercpthosts","No effect.","SMTP clients may send messages to recipients at ","."); + else + do_lst("morercpthosts","No rcpthosts; morercpthosts is irrelevant.","No rcpthosts; doesn't matter that morercpthosts has ","."); + /* XXX: check morercpthosts.cdb contents */ + buffer_puts(buffer_1,"\nmorercpthosts.cdb: "); + if (stat("morercpthosts",&stmrh) == -1) + if (stat("morercpthosts.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"(Default.) No effect.\n"); + else + buffer_puts(buffer_1,"Oops! morercpthosts.cdb exists but morercpthosts doesn't.\n"); + else + if (stat("morercpthosts.cdb",&stmrhcdb) == -1) + buffer_puts(buffer_1,"Oops! morercpthosts exists but morercpthosts.cdb doesn't.\n"); + else + if (stmrh.st_mtime > stmrhcdb.st_mtime) + buffer_puts(buffer_1,"Oops! morercpthosts.cdb is older than morercpthosts.\n"); + else + buffer_puts(buffer_1,"Modified recently enough; hopefully up to date.\n"); + do_lst("recipients","SMTP clients may send messages to any recipient.","SMTP clients may send messages to local recipients listed in ","."); + do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 "); + do_lst("qmtproutes","No additional QMTP routes.","QMTP route: ",""); + do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ",""); + do_str("spfexplain",0,SPF_DEFEXP,"SPF default explanation is: 550 "); + do_str("spflocalrules",0,"(None)","Defined local SPF rules are: "); + do_lst("srsrdomains","No SRS fowarding rules.","SRS rules: ",""); + do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds"); + do_int("timeoutremote","1200","SMTP client data timeout is "," seconds"); + do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds"); + do_lst("tlsdestinations","No TLS destinations defined.","TLS destination: ",""); + do_lst("virtualdomains","No virtual domains.","Virtual domain: ",""); + + while ((d = readdir(dir))) { + if (str_equal(d->d_name,".")) continue; + if (str_equal(d->d_name,"..")) continue; + if (str_equal(d->d_name,"authsenders")) continue; + if (str_equal(d->d_name,"badhelo")) continue; + if (str_equal(d->d_name,"badrcptto")) continue; + if (str_equal(d->d_name,"badmailfrom")) continue; + if (str_equal(d->d_name,"badloadertypes")) continue; + if (str_equal(d->d_name,"badloadertypes.cdb")) continue; + if (str_equal(d->d_name,"badmimetypes")) continue; + if (str_equal(d->d_name,"badmimetypes.cdb")) continue; + if (str_equal(d->d_name,"bouncefrom")) continue; + if (str_equal(d->d_name,"bouncehost")) continue; + if (str_equal(d->d_name,"bouncemaxbytes")) continue; + if (str_equal(d->d_name,"concurrencylocal")) continue; + if (str_equal(d->d_name,"concurrencyremote")) continue; + if (str_equal(d->d_name,"databytes")) continue; + if (str_equal(d->d_name,"defaultdomain")) continue; + if (str_equal(d->d_name,"defaulthost")) continue; + if (str_equal(d->d_name,"dkimdomains")) continue; + if (str_equal(d->d_name,"domainips")) continue; + if (str_equal(d->d_name,"domaincerts")) continue; + if (str_equal(d->d_name,"doublebouncehost")) continue; + if (str_equal(d->d_name,"doublebounceto")) continue; + if (str_equal(d->d_name,"envnoathost")) continue; + if (str_equal(d->d_name,"helohost")) continue; + if (str_equal(d->d_name,"idhost")) continue; + if (str_equal(d->d_name,"localiphost")) continue; + if (str_equal(d->d_name,"locals")) continue; + if (str_equal(d->d_name,"me")) continue; + if (str_equal(d->d_name,"mailfromrules")) continue; + if (str_equal(d->d_name,"mailfromrules.cdb")) continue; + if (str_equal(d->d_name,"morercpthosts")) continue; + if (str_equal(d->d_name,"morercpthosts.cdb")) continue; + if (str_equal(d->d_name,"percenthack")) continue; + if (str_equal(d->d_name,"plusdomain")) continue; + if (str_equal(d->d_name,"qmqpservers")) continue; + if (str_equal(d->d_name,"queuelifetime")) continue; + if (str_equal(d->d_name,"rcpthosts")) continue; + if (str_equal(d->d_name,"recipients")) continue; + if (str_equal(d->d_name,"smtpgreeting")) continue; + if (str_equal(d->d_name,"qmtproutes")) continue; + if (str_equal(d->d_name,"smtproutes")) continue; + if (str_equal(d->d_name,"spfexplain")) continue; + if (str_equal(d->d_name,"spflocalrules")) continue; + if (str_equal(d->d_name,"srsdomains")) continue; + if (str_equal(d->d_name,"timeoutconnect")) continue; + if (str_equal(d->d_name,"timeoutremote")) continue; + if (str_equal(d->d_name,"timeoutsmtpd")) continue; + if (str_equal(d->d_name,"tlsdestinations")) continue; + if (str_equal(d->d_name,"virtualdomains")) continue; + buffer_puts(buffer_1,"\n"); + buffer_puts(buffer_1,d->d_name); + buffer_puts(buffer_1,": I have no idea what this file does.\n"); + } + + buffer_flush(buffer_1); + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-smtpam.c b/sqmail-4.3.07/src/qmail-smtpam.c new file mode 100755 index 0000000..e9566e0 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-smtpam.c @@ -0,0 +1,633 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include "sig.h" +#include "genalloc.h" +#include "stralloc.h" +#include "buffer.h" +#include "scan.h" +#include "case.h" +#include "byte.h" +#include "error.h" +#include "auto_qmail.h" +#include "control.h" +#include "dns.h" +#include "alloc.h" +#include "quote.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "str.h" +#include "now.h" +#include "exit.h" +#include "constmap.h" +#include "tcpto.h" +#include "socket_if.h" +#include "ucspissl.h" +#include "timeout.h" +#include "timeoutconn.h" +#include "tls_remote.h" +#include "tls_errors.h" +#include "tls_timeoutio.h" +#include "uint_t.h" + +#define MAX_SIZE 200000000 +#define HUGESMTPTEXT 5000 +#define PORT_SMTP 25 /* silly rabbit, /etc/services is for users */ +#define PORT_SMTPS 465 +#define VERIFYDEPTH 1 +#define FDPAM 3 +#define TCP_TIMEOUT 60 +#define SMTP_TIMEOUT 1200 + +#define WHO "qmail-smtpam" + +/** @file qmail-smtpam.c -- TLS enabled SMTP PAM to check mailbox at remote MX + */ + +int flagauth = 0; /* 1 = login; 2 = plain; 3 =crammd5 */ +int flagsmtps = 0; /* RFC 8314 - 'implicit TLS' */ +int flagtls = 0; /* -2 = rejected; -1 = not; 0 = no, default; + > 0 see tls_remote.c + +10 = SMTPS; +20 = QMTPS; 100 = active TLS connection */ +int flagverify = 0; /* 1 = verify Cert against CA ; -1 = Cert pinning */ +int flagutf8mail = 0; + +unsigned long port = PORT_SMTP; + +GEN_ALLOC_typedef(saa,stralloc,sa,len,a) +GEN_ALLOC_readyplus(saa,stralloc,sa,len,a,i,n,x,10,saa_readyplus) + +stralloc helohost = {0}; +stralloc host = {0}; +stralloc ports = {0}; +stralloc remotehost = {0}; +stralloc sender = {0}; +stralloc canonhost = {0}; +stralloc canonbox = {0}; +stralloc sendip = {0}; +stralloc recipient = {0}; + +stralloc domainips = {0}; +struct constmap mapdomainips; +char ip4[4]; +char ip6[16]; +uint32 ifidx = 0; + +stralloc routes = {0}; +struct constmap maproutes; + +struct ip_mx partner; + +SSL *ssl; +SSL_CTX *ctx; + +void out(char *s) { if (buffer_puts(buffer_1small,s) == -1) _exit(111); } +void zero() { if (buffer_put(buffer_1small,"\0",1) == -1) _exit(111); } +void zerodie() { zero(); buffer_flush(buffer_1small); _exit(111); } +void outsafe(stralloc *sa) +{ + int i; + char ch; + for (i = 0; i < sa->len; ++i) { + ch = sa->s[i]; + if (ch < 33) ch = '?'; + if (ch > 126) ch = '?'; + if (buffer_put(buffer_1small,&ch,1) == -1) _exit(111); + } +} + +void temp_noip() +{ + out("Zinvalid ipaddr in control/domainips (#4.3.0)\n"); + zerodie(); +} +void temp_nomem() +{ + out("ZOut of memory. (#4.3.0)\n"); + zerodie(); +} +void temp_oserr() +{ + out("ZSystem resources temporarily unavailable. (#4.3.0)\n"); + zerodie(); +} +void temp_osip() +{ + out("ZCan't bind to local ip address: "); + outsafe(&sendip); + out(". (#4.3.0)\n"); + zerodie(); +} +void temp_noconn() +{ + out("ZSorry, I wasn't able to establish an SMTP connection. (#4.4.1)\n"); + zerodie(); +} +void temp_dnscanon() +{ + out("ZCNAME lookup failed temporarily for: "); + outsafe(&canonhost); + out(". (#4.4.3)\n"); + zerodie(); +} +void temp_dns() +{ + out("ZSorry, I couldn't find any host named: "); + outsafe(&host); + out(". (#4.1.2)\n"); + zerodie(); +} +void temp_chdir() +{ + out("ZUnable to switch to home directory. (#4.3.0)\n"); + zerodie(); +} +void temp_control() +{ + out("ZUnable to read control files. (#4.3.0)\n"); + zerodie(); +} +void perm_usage() +{ + out("Dqmail-smtpam was invoked improperly. (#5.3.5)\n"); + zerodie(); +} +void perm_dns() +{ + out("DSorry, I couldn't find any host named: "); + outsafe(&host); + out(". (#5.1.2)\n"); + zerodie(); +} +void outhost() +{ + char ipaddr[IPFMT]; + int len; + + switch (partner.af) { + case AF_INET: + len = ip4_fmt(ipaddr,(char *)&partner.addr.ip4.d); break; + case AF_INET6: + len = ip6_fmt(ipaddr,(char *)&partner.addr.ip6.d); break; + } + if (buffer_put(buffer_1small,ipaddr,len) == -1) _exit(0); +} + +int flagcritical = 0; + +void dropped() +{ + out("ZConnected to "); + outhost(); + out(" but connection died. "); + if (flagcritical) out("Possible duplicate! "); + out("(#4.4.2)\n"); + zerodie(); +} + +int timeoutconnect = TCP_TIMEOUT; +int smtpfd; +int timeout = SMTP_TIMEOUT; + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + r = timeoutread(timeout,smtpfd,buf,len); + if (r <= 0) dropped(); + return r; +} + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + r = timeoutwrite(timeout,smtpfd,buf,len); + if (r <= 0) dropped(); + return r; +} + +char outbuf[1450]; +buffer bo = BUFFER_INIT(safewrite,-1,outbuf,sizeof(outbuf)); +char frombuf[128]; +buffer bi = BUFFER_INIT(saferead,-1,frombuf,sizeof(frombuf)); + +stralloc smtptext = {0}; + +void get(char *ch) +{ + buffer_get(&bi,ch,1); + if (*ch != '\r') + if (smtptext.len < HUGESMTPTEXT) + if (!stralloc_append(&smtptext,ch)) temp_nomem(); +} + +unsigned long smtpcode() +{ + unsigned char ch; + unsigned long code; + + if (!stralloc_copys(&smtptext,"")) temp_nomem(); + + get(&ch); code = ch - '0'; + get(&ch); code = code * 10 + (ch - '0'); + get(&ch); code = code * 10 + (ch - '0'); + for (;;) { + get(&ch); + if (ch != '-') break; + while (ch != '\n') get(&ch); + get(&ch); + get(&ch); + get(&ch); + } + while (ch != '\n') get(&ch); + + return code; +} + +void outsmtptext() +{ + int i; + if (smtptext.s) if (smtptext.len) { + out("Remote host said: "); + for (i = 0; i < smtptext.len; ++i) + if (!smtptext.s[i]) smtptext.s[i] = '?'; + if (buffer_put(buffer_1small,smtptext.s,smtptext.len) == -1) _exit(111); + smtptext.len = 0; + } +} + +void quit(char *prepend,char *append) +{ + buffer_putsflush(&bo,"QUIT\r\n"); + /* waiting for remote side is just too ridiculous */ + out(prepend); + outhost(); + out(append); + out(".\n"); + outsmtptext(); + zerodie(); +} + +stralloc recip = {0}; + +/* this file is too long -------------------------------------- client TLS */ + +stralloc cafile = {0}; +stralloc cadir = {0}; +stralloc certfile = {0}; +stralloc keyfile = {0}; +stralloc keypwd = {0}; +stralloc ciphers = {0}; +stralloc tlsdest = {0}; + +char *tlsdestinfo = 0; +char *tlsdomaininfo = 0; + +stralloc domaincerts = {0}; +struct constmap mapdomaincerts; +stralloc tlsdestinations = {0}; +struct constmap maptlsdestinations; +unsigned long verifydepth = VERIFYDEPTH; + +void tls_init() +{ +/* Client CTX */ + + ctx = ssl_client(); + ssl_errstr(); + if (!ctx) temp_tlsctx(); + +/* Fetch CA infos for dest */ + + if (flagverify > 0) + if (cafile.len || cadir.len) + if (!ssl_ca(ctx,cafile.s,cadir.s,(int) verifydepth)) temp_tlsca(); + + if (ciphers.len) + if (!ssl_ciphers(ctx,ciphers.s)) temp_tlscipher(); + +/* Set SSL Context */ + + ssl = ssl_new(ctx,smtpfd); + if (!ssl) temp_tlsctx(); + +/* Setup SSL FDs */ + + if (!tls_conn(ssl,smtpfd)) temp_tlscon(); + +/* Go on in none-blocking mode */ + + if (tls_timeoutconn(timeout,smtpfd,smtpfd,ssl) <= 0) + temp_tlserr(); +} + +int starttls_peer() +{ + int i = 0; + + while ( (i += str_chr(smtptext.s + i,'\n') + 1) && + (i < smtptext.len - 8) ) { + if (!str_diffn(smtptext.s + i + 4,"STARTTLS",8)) return 1; } + + return 0; +} + +void tls_peercheck() +{ + X509 *cert; + + cert = SSL_get_peer_certificate(ssl); + if (!cert) { flagtls = 100; return; } + + if (flagverify < 0) { + if (cafile.len) case_lowerb(cafile.s,cafile.len); + switch (tls_fingerprint(cert,cafile.s + 1,cafile.len - 1)) { + case -1: temp_tlspeercert(); + case -2: temp_tlsdigest(); + case -3: temp_invaliddigest(); + case 1: temp_tlscertfp(); + } + } else { + switch (tls_checkpeer(ssl,cert,remotehost,flagtls,flagverify)) { + case -1: temp_tlspeercert(); + case -2: temp_tlspeerverify(); + case -3: temp_tlspeervalid(); + case 1: flagtls = 101; break; + case 2: flagtls = 102; break; + case 3: flagtls = 103; break; + } + } + + if (flagtls < 100) flagtls = 100; + + X509_free(cert); + + return; +} + +int utf8flag(unsigned char *ch,int len) +{ + int i = 0; + while (i < len) + if (ch[i++] > 127) return 1; + return 0; +} + +/* this file is too long -------------------------------------- SMTP connection */ + +unsigned long code; + +void smtp_greeting() +{ + buffer_puts(&bo,"EHLO "); + buffer_put(&bo,helohost.s,helohost.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + + if (smtpcode() != 250) { + buffer_puts(&bo,"HELO "); + buffer_put(&bo,helohost.s,helohost.len); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + code = smtpcode(); + if (code >= 500) quit("DConnected to"," but my name was rejected"); + if (code != 250) quit("ZConnected to"," but my name was rejected"); + } +} + +void smtp_starttls() +{ + buffer_puts(&bo,"STARTTLS\r\n"); + buffer_flush(&bo); + if (smtpcode() == 220) { + tls_init(); + tls_peercheck(); + smtp_greeting(); + } else { + flagtls = -2; + quit("ZConnected to"," but STARTTLS was rejected"); + } +} + +void smtp() +{ + + if (flagtls > 10 && flagtls < 20) { /* SMTPS */ + tls_init(); + tls_peercheck(); + } + + code = smtpcode(); + if (code >= 500) quit("DConnected to "," but sender was rejected"); + if (code >= 400) quit("ZConnected to "," but sender was probably greylisted"); + + smtp_greeting(); + + if (flagutf8mail) buffer_puts(&bo," SMTPUTF8"); + + if (flagtls > 0 && flagtls < 10) /* STARTTLS */ + if (starttls_peer()) { + smtp_starttls(); + } else if (flagtls > 2) { + temp_tlshost(); + } + + buffer_puts(&bo,"MAIL FROM:<>"); + if (flagutf8mail) + buffer_puts(&bo," SMTPUTF8"); + buffer_puts(&bo,"\r\n"); + buffer_flush(&bo); + code = smtpcode(); + if (code >= 500) quit("DConnected to "," but sender was rejected"); + if (code >= 400) quit("ZConnected to "," but sender was rejected"); + + buffer_puts(&bo,"RCPT TO:<"); + buffer_put(&bo,recipient.s,recipient.len); + buffer_puts(&bo,">\r\n"); + buffer_flush(&bo); + code = smtpcode(); + close(smtpfd); + if (code == 250) _exit(0); + _exit(1); +} + +void getcontrols() +{ + if (control_init() == -1) temp_control(); + if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control(); + if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1) + temp_control(); + if (control_rldef(&helohost,"control/helohost",1,(char *) 0) != 1) + temp_control(); + switch (control_readfile(&domainips,"control/domainips",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&mapdomainips,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&mapdomainips,domainips.s,domainips.len,1)) temp_nomem(); break; + } + switch (control_readfile(&tlsdestinations,"control/tlsdestinations",0)) { + case -1: temp_control(); + case 0: if (!constmap_init(&maptlsdestinations,"",0,1)) temp_nomem(); break; + case 1: if (!constmap_init(&maptlsdestinations,tlsdestinations.s,tlsdestinations.len,1)) temp_nomem(); break; + } + +} + +char up[513]; +int uplen; + +int main(int argc,char **argv) +{ + static ipalloc ip = {0}; + stralloc netif = {0}; + int i, j, k; + int r; /* reserved for return code */ + int p; /* reserved for port */ + char *localip = 0; + char *tlsdestinfo = 0; + + sig_pipeignore(); + if (argc < 2) perm_usage(); + if (chdir(auto_qmail) == -1) temp_chdir(); + getcontrols(); + + if (!stralloc_copys(&host,argv[1])) temp_nomem(); + + if (argv[2]) { + if (!stralloc_copys(&ports,argv[2])) temp_nomem(); + if (*ports.s == 's') { ports.s++; flagsmtps = 1; } + scan_ulong(ports.s,&port); + } + + if (ipme_init() != 1) temp_oserr(); + +/* this file is too long -------------------------------------- set domain ip + helohost */ + + if (!localip) + localip = constmap(&mapdomainips,"*",1); /* one for all */ + + if (localip) { + j = str_chr(localip,'%'); + if (localip[j] != '%') j = 0; + k = str_chr(localip,'|'); + if (localip[k] != '|') k = 0; + if (k) { /* helohost */ + if (!stralloc_copys(&helohost,localip + k + 1)) temp_nomem(); + localip[k] = 0; + } + if (j) { /* if index */ + localip[j] = 0; + if (!stralloc_copys(&netif,localip + j + 1)) temp_nomem(); + if (!stralloc_0(&netif)) temp_nomem(); + } + } + + +/* this file is too long -------------------------------------- TLS destinations */ + + flagtls = tls_destination((const stralloc) host); // un-terminated + + if (flagtls > 0) { + if (tlsdestinfo) { + i = str_chr(tlsdestinfo,'|'); /* ca file or cert fingerprint */ + if (tlsdestinfo[i] == '|') { + tlsdestinfo[i] = 0; + j = str_chr(tlsdestinfo+i+1,'|'); /* cipher */ + if (tlsdestinfo[i + j + 1] == '|') { + tlsdestinfo[i + j + 1] = 0; + k = str_chr(tlsdestinfo + i + j + 2,'|'); /* cone domain */ + if (tlsdestinfo[i + j + k + 2] == '|') { + tlsdestinfo[i + j + k + 2] = 0; + if (str_diffn(tlsdestinfo + j + k + 3,canonhost.s,canonhost.len)) flagtls = 0; + } + p = str_chr(tlsdestinfo + i + j + 2,';'); /* verifydepth;port */ + if (tlsdestinfo[i + j + p + 2] == ';') { + if (tlsdestinfo[i + j + p + 3] == 's') { flagsmtps = 1; p++; } + tlsdestinfo[i + j + p + 2] = 0; + if (p > 0) scan_ulong(tlsdestinfo+i+j + 2,&verifydepth); + scan_ulong(tlsdestinfo+i+j + p + 3,&port); + } + } + if (!stralloc_copys(&ciphers,tlsdestinfo + i + 1)) temp_nomem(); + } + if (!stralloc_copys(&cafile,tlsdestinfo)) temp_nomem(); + } + +/* cafile starts with '=' => it is a fingerprint + cafile ends with '/' => consider it as cadir */ + + if (cafile.len) { + flagverify = 1; + if (cafile.s[cafile.len] == '/') { + cafile.len = 0; + flagverify = 2; + if (!stralloc_copys(&cadir,tlsdestinfo)) temp_nomem(); + if (!stralloc_0(&cadir)) temp_nomem(); + } else { + if (cafile.s[0] == '%') flagverify = -1; + if (!stralloc_0(&cafile)) temp_nomem(); + } + } else { + cafile.len = cadir.len = ciphers.len = p = 0; + } + + if (port == PORT_SMTPS || flagsmtps) flagtls = flagtls + 10; + } + +/* this file is too long -------------------------------------- Setup connection */ + + uplen = 0; + for (;;) { + do + r = read(FDPAM,up + uplen,sizeof(up) - uplen); + while ((r == -1) && (errno == EINTR)); + if (r == -1) _exit(111); + if (r == 0) break; + uplen += r; + if (uplen >= sizeof(up)) _exit(111); + } + close(FDPAM); + + if (!stralloc_copyb(&recipient,up,uplen)) temp_nomem(); + if (!stralloc_0(&recipient)) temp_nomem(); + if (!stralloc_0(&host)) temp_nomem(); + if (!stralloc_copys(&remotehost,host.s)) temp_nomem(); + + flagutf8mail = utf8flag(recipient.s,recipient.len); + + switch (dns_ip(&ip,&remotehost)) { + case DNS_MEM: temp_nomem(); + case DNS_ERR: temp_dns(); + case DNS_COM: temp_dnscanon(); + default: if (ip.len <= 0) perm_dns(); + } + + smtpfd = socket(ip.ix[i].af,SOCK_STREAM,0); + if (smtpfd == -1) temp_oserr(); + + if (localip) { /* set domain ip */ + if (!stralloc_copyb(&sendip,localip,str_len(localip))) temp_nomem(); + j = str_chr(localip,':'); + if (j && localip[j] == ':') { /* IPv6 */ + if (!ip6_scan(localip,ip6)) temp_noip(); + ifidx = socket_getifidx(netif.s); + if (socket_bind6(smtpfd,ip6,0,ifidx) < 0) temp_osip(); + } else { /* IPv4 */ + if (!ip4_scan(localip,ip4)) temp_noip(); + if (socket_bind4(smtpfd,ip4,0) < 0) temp_osip(); + } + } + + r = timeoutconn(smtpfd,&ip.ix[i].addr,(unsigned int) port,timeoutconnect,ifidx); + if (r == 0) { + tcpto_err(&ip.ix[i],0); + partner = ip.ix[i]; + smtp(); /* does not return */ + } + tcpto_err(&ip.ix[i],errno == ETIMEDOUT); + close(smtpfd); + + temp_noconn(); +} diff --git a/sqmail-4.3.07/src/qmail-smtpd.c b/sqmail-4.3.07/src/qmail-smtpd.c new file mode 100755 index 0000000..b629948 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-smtpd.c @@ -0,0 +1,1720 @@ +#include <unistd.h> +#include "wildmat.h" +#include "buffer.h" +#include "stralloc.h" +#include "genalloc.h" +#include "alloc.h" +#include "qmail.h" +#include "auto_qmail.h" +#include "control.h" +#include "received.h" +#include "constmap.h" +#include "logmsg.h" +#include "ipme.h" +#include "fd.h" +#include "ip.h" +#include "qmail.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "case.h" +#include "env.h" +#include "now.h" +#include "exit.h" +#include "rcpthosts.h" +#include "recipients.h" +#include "mfrules.h" +#include "tls_start.h" +#include "smtpdlog.h" +#include "timeout.h" +#include "commands.h" +#include "cdbread.h" +#include "dns.h" +#include "wait.h" +#include "sig.h" +#include "close.h" +#include "open.h" +#include "base64.h" +#include "spf.h" + +/** @file qmail-smtpd.c -- authenticating ESMTP/ESMTPS server + @brief requires sslserver or tcpserver */ + +#define PAM111421 +#define AUTHSLEEP 5 +#define PORT_SMTPS "465" +#define SMTP_TIMEOUT 1200 + +#define MIMETYPE_LEN 9 +#define LOADER_LEN 5 +#define BASE64MESSAGE "content-transfer-encoding: base64" +#define FDIN 0 +#define FDOUT 1 +#define FDLOG 2 +#define FDAUTH 3 + +#define MAXHOPS 100 +unsigned long databytes = 0; +int timeout = SMTP_TIMEOUT; + +int modssl_info(); + +ssize_t safewrite(int fd,char *buf,int len) +{ + int r; + r = timeoutwrite(timeout,fd,buf,len); + if (r <= 0) _exit(1); + return r; +} + +ssize_t saferead(int fd,char *buf,int len) +{ + int r; + flush(); + r = timeoutread(timeout,fd,buf,len); + if (r == -1) if (errno == ETIMEDOUT) die_alarm(); + if (r <= 0) die_read(); + return r; +} + +char inbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(saferead,FDIN,inbuf,sizeof(inbuf)); + +char outbuf[BUFSIZE_LINE]; +buffer bo = BUFFER_INIT(safewrite,FDOUT,outbuf,sizeof(outbuf)); + +char buflog[BUFSIZE_LOG]; +buffer bl = BUFFER_INIT(write,FDLOG,buflog,sizeof(buflog)); + +void flush() { buffer_flush(&bo); } // this triggers writing to STDIO +void out(char *s) { buffer_puts(&bo,s); } + +stralloc sa = {0}; +ipalloc ia = {0}; + +int bhelocheck(void); + +/* this file is too long -------------------------------------- DNS helper */ + +int dnsq(char *arg,char type) +{ + unsigned int random; + int at; + int r = -2; + int len; + + len = str_len(arg); + if (len < 1) return r; + + if (arg[len-1] == ' ') len--; /* trailing blank */ + if (len < 1) return r; + + at = byte_rchr(arg,len,'@'); + if (at < len) { + if (!stralloc_copyb(&sa,arg + at + 1,len - at - 1)) die_nomem(); + } else + if (!stralloc_copyb(&sa,arg,len)) die_nomem(); + + random = now() + (getpid() << 16); + + switch (type) { /* Common for IPv4 and IPv6 */ + case 'A': r = dns_ip(&ia,&sa); break; + case 'M': r = dns_mxip(&ia,&sa,random); break; + } + + switch (r) { + case DNS_ERR: out("451 DNS temporary failure (#4.3.0)\r\n"); return -1; + case DNS_MEM: die_nomem(); + default: if (ia.len) return 0; + } + + return 1; +} + +/* this file is too long -------------------------------------- Greeting */ + +static stralloc greeting = {0}; + +void smtp_greet(char *code) +{ + buffer_puts(&bo,code); + buffer_put(&bo,greeting.s,greeting.len); +} +void smtp_help() +{ + out("214 s/qmail home page: https://www.fehcom.de/sqmail.html\r\n"); +} +void smtp_quit() +{ + smtp_greet("221 "); out("\r\n"); flush(); _exit(0); +} + +char *remoteip; +char *remotehost; +char *remoteinfo; +char *local; +char *localport; +char *relayclient; +int flagutf8 = 0; + +stralloc protocol = {0}; +stralloc helohost = {0}; +char *fakehelo; /* pointer into helohost, or 0 */ +stralloc tlsinfo = {0}; + +char *helocheck; +int flagbadhelo; +int flagdnshelo; +int seenhelo = 0; + +char *badmailcond; +char *badhelocond; + +void dohelo(char *helo) +{ + if (!stralloc_copys(&helohost,helo)) die_nomem(); + if (!stralloc_0(&helohost)) die_nomem(); + fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0; + if (helocheck) { + if (str_len(helocheck) == 1) { + switch (*helocheck) { + case '=': flagbadhelo = bhelocheck(); + if (fakehelo) { flagdnshelo = 1; badhelocond = "="; } + break; + case 'A': flagbadhelo = bhelocheck(); + if (flagbadhelo == 0) { flagdnshelo = dnsq(helohost.s,'A'); badhelocond = "A"; } + break; + case 'M': flagbadhelo = bhelocheck(); + if (flagbadhelo == 0) { flagdnshelo = dnsq(helohost.s,'M'); badhelocond = "M"; } + break; + case '.': flagbadhelo = bhelocheck(); + if (!str_len(helo)) flagbadhelo = -2; + break; + case '!': if (!str_len(helo)) flagbadhelo = -2; + break; + } + } else { + flagbadhelo = bhelocheck(); + } + if (flagbadhelo == -3) flagbadhelo = 0; + } + if (!env_put("HELOHOST",helohost.s)) die_nomem(); +} + +int liphostok = 0; +stralloc liphost = {0}; + +int bmfok = 0; +stralloc bmf = {0}; +struct constmap mapbmf; + +int brtok= 0; +stralloc brt = {0}; +struct constmap mapbrt; + +int badhelook = 0; +stralloc badhelo = {0}; +struct constmap mapbhlo; + +static struct cdb cdbm; +static struct cdb cdbl; + +static int fdbmt; +int flagmimetype = 0; /* 1: white; 2: cdb; 3: white+cdb; 4: !relay+white; 6: !relay+white+cdb; -1: found in cdb; -2: found white */ +char *badmimeinit; + +static int fdblt; +int flagloadertype = 0; /* 1: cdb; 2: !relay+cdb; -1: found in cdb */ +char *badloaderinit; + +static int fdmav; +int flagmav = 0; +int localmf = 0; /* 1: domainpart->rcpthosts; 2: ->mailformrules; 3: ->remoteinfo; 4: ->DN(Email) */ +char *localmfcheck; + +char *mfdnscheck; +char *qhpsi; +char *base64; + +int maxrcptcount = 0; +int flagerrcpts = 0; +int flagnotorious = 0; + +int tarpitcount = 0; +int tarpitdelay = 0; + +int greylist = 0; +stralloc pgbind = {0}; + +char *auth; +int smtpauth = 0; /* -1:Cert 0:none 1:login/plain 2:cram 3:login/plain/cram 11:must_login/plain 12:must_2 13:must_3 */ +int seenauth = 0; /* 1:ESMTPA 2:~CLIENTDN */ +stralloc authmethod = {0}; + +int starttls = 0; /* -1:TLS 0:none 1:STARTTLS 2:require_STARTTLS 3:relay_if_CLIENTDN 4:require_+_relay_if_CLIENTDN */ +int seentls = 0; /* 1:~STARTTLS 2:~TLS 3:~CLIENTDN */ +char *ucspitls = 0; +char *tlsversion; +char *cipher; +char *cipherperm; +char *cipherused; +char *clientdn; +char *clientcn; +char *dnemail; + +stralloc mailto = {0}; +stralloc deliverto = {0}; +char *delivermailto; +stralloc rblinfo = {0}; +char *rblsmtpd; + +int flagspf = 0; +static stralloc spfbounce = {0}; + +void setup() +{ + char *x; + unsigned long u; + int r; + flagip6 = 1; // GCC 10 implicit int declarition if global var + + if (control_init() == -1) die_control(); + if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1) + die_control(); + liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0); + if (liphostok == -1) die_control(); + if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control(); + if (timeout <= 0) timeout = 1; + + if (rcpthosts_init() == -1) die_control(); + if (recipients_init() == -1) die_control(); + + bmfok = control_readfile(&bmf,"control/badmailfrom",0); + if (bmfok == -1) die_control(); + if (bmfok) + if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem(); + + brtok = control_readfile(&brt,"control/badrcptto",0); + if (brtok == -1) die_control(); + if (brtok) + if (!constmap_init(&mapbrt,brt.s,brt.len,0)) die_nomem(); + + if (control_readint(&databytes,"control/databytes") == -1) die_control(); + x = env_get("DATABYTES"); + if (x) { scan_ulong(x,&u); databytes = u; } + if (!(databytes + 1)) --databytes; + + if (!stralloc_copys(&protocol,"ESMTP")) die_nomem(); /* RFC 3848 */ + remoteip = env_get("TCP6REMOTEIP"); /* compactified IPv6 */ + if (!remoteip) remoteip = env_get("TCPREMOTEIP"); /* allow other tcpserver/sslserver */ + if (remoteip && (str_chr(remoteip,':') < str_len(remoteip))) { + if (byte_equal(remoteip,7,V4MAPPREFIX)) + { remoteip = remoteip + 7; flagip6 = 0; } + } else flagip6 = 0; + if (!remoteip) { remoteip = "unknown"; flagip6 = -1; } + local = env_get("TCP6LOCALHOST"); + if (!local) local = env_get("TCPLOCALHOST"); + if (!local) local = env_get("TCP6LOCALIP"); + if (!local) local = env_get("TCPLOCALIP"); + if (!local) local = "unknown"; + localport = env_get("TCP6LOCALPORT"); + if (!localport) localport = env_get("TCPLOCALPORT"); + if (!localport) localport = "0"; + remotehost = env_get("TCP6REMOTEHOST"); + if (!remotehost) remotehost = env_get("TCPREMOTEHOST"); + if (!remotehost) remotehost = "unknown"; + remoteinfo = env_get("TCP6REMOTEINFO"); + if (!remoteinfo) remoteinfo = env_get("TCPREMOTEINFO"); + relayclient = env_get("RELAYCLIENT"); + + if (!case_diffs(localport,PORT_SMTPS)) { + if (!modssl_info()) die_starttls(); + starttls = -1; + } + + mfdnscheck = env_get("MFDNSCHECK"); + x = env_get("MAXRECIPIENTS"); + if (x) { scan_ulong(x,&u); maxrcptcount = u; }; + if (!(maxrcptcount + 1)) --maxrcptcount; + + helocheck = env_get("HELOCHECK"); + if (helocheck) { + badhelook = control_readfile(&badhelo,"control/badhelo",0); + if (badhelook == -1) die_control(); + if (badhelook) + if (!constmap_init(&mapbhlo,badhelo.s,badhelo.len,0)) die_nomem(); + } + + x = env_get("TARPITCOUNT"); + if (x) { scan_ulong(x,&u); tarpitcount = u; }; + x = env_get("TARPITDELAY"); + if (x) { scan_ulong(x,&u); tarpitdelay = u; }; + + x = env_get("POSTGREY"); // RFC 6647 + if (x) { + if (case_diffs(x,"-")) { + greylist = 1; + if (!stralloc_copys(&pgbind,x)) die_nomem(); + if (!stralloc_append(&pgbind,"")) die_nomem(); + } + } + + localmfcheck = env_get("LOCALMFCHECK"); + if (localmfcheck) { + localmf = 1; + if (str_len(localmfcheck) == 1 && *localmfcheck == '!') { + localmf = 2; + fdmav = open_read("control/mailfromrules.cdb"); + if (fdmav == -1 ) localmf = 1; + } + if (str_len(localmfcheck) == 1 && *localmfcheck == '=') { + localmf = 3; + } + if (str_len(localmfcheck) == 1 && *localmfcheck == '?') { + localmf = 4; + } + } + + badmimeinit = env_get("BADMIMETYPE"); + if (badmimeinit) { + fdbmt = open_read("control/badmimetypes.cdb"); + if (str_len(badmimeinit) == 1) { + if (*badmimeinit == '-') + flagmimetype = 0; + else { + if (*badmimeinit == '!') flagmimetype = 1; + if (*badmimeinit == '+') flagmimetype = 4; + } + } + if (fdbmt != -1 ) flagmimetype = flagmimetype + 2; + } + + badloaderinit = env_get("BADLOADERTYPE"); + if (badloaderinit) { + if (str_len(badloaderinit) == 1) { + if (*badloaderinit == '-') + flagloadertype = 0; + else { + flagloadertype = 1; + if (*badloaderinit == '+') flagloadertype = 2; + fdblt = open_read("control/badloadertypes.cdb"); + if (fdblt == -1 ) flagloadertype = 0; + } + } + } + + base64 = env_get("BASE64"); + qhpsi = env_get("QHPSI"); + auth = env_get("SMTPAUTH"); + if (auth) { + smtpauth = 1; + if (!case_diffs(auth,"-")) smtpauth = 0; + if (!case_diffs(auth,"!")) smtpauth = 11; + if (case_starts(auth,"cram")) smtpauth = 2; + if (case_starts(auth,"+cram")) smtpauth = 3; + if (case_starts(auth,"!cram")) smtpauth = 12; + if (case_starts(auth,"!+cram")) smtpauth = 13; + } + + if (!seentls) { + ucspitls = env_get("UCSPITLS"); + if (ucspitls) { + starttls = 1; + if (!case_diffs(ucspitls,"-")) starttls = 0; + if (!case_diffs(ucspitls,"!")) starttls = 2; + if (!case_diffs(ucspitls,"?")) starttls = 3; + if (!case_diffs(ucspitls,"@")) starttls = 4; + } + } + + delivermailto = env_get("DELIVERTO"); + if (delivermailto) { + if (!stralloc_cats(&mailto,delivermailto)) die_nomem(); + if (!stralloc_cats(&mailto," ")) die_nomem(); + } + + rblsmtpd = env_get("RBLSMTPD"); + if (rblsmtpd) { + if (!stralloc_cats(&rblinfo,rblsmtpd)) die_nomem(); + if (!stralloc_0(&rblinfo)) die_nomem(); + } + + x = env_get("SPF"); + if (x) { scan_ulong(x,&u); flagspf = u; } + if (flagspf < 0 || flagspf > 6) die_control(); + if (flagspf) { + r = control_readline(&spflocalrules,"control/spflocalrules"); + if (r == -1) die_control(); + if (!stralloc_0(&spflocalrules)) die_nomem(); + if (control_rldef(&spfexplain,"control/spfexplain",0,SPF_DEFEXP) == -1) die_control(); + if (!stralloc_0(&spfexplain)) die_nomem(); + } + + x = env_get("UTF8"); + if (x) flagutf8 = 1; + + if (!stralloc_copys(&helohost,"")) die_nomem(); // helohost is initially empty + if (!stralloc_0(&helohost)) die_nomem(); + fakehelo = 0; + +} + +void auth_info(char *method) +{ + if (!env_put("AUTHPROTOCOL",method)) die_nomem(); + if (!env_put("AUTHUSER",remoteinfo)) die_nomem(); + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_unset("TCP6REMOTEINFO")) die_read(); + if (!env_put("TCP6REMOTEINFO",remoteinfo)) die_nomem(); + + if (!stralloc_append(&protocol,"A")) die_nomem(); +} + +int modssl_info() +{ + tlsversion = env_get("SSL_PROTOCOL"); + if (!tlsversion) return 0; + + cipher = env_get("SSL_CIPHER"); + if (!cipher) cipher = "unknown"; + cipherperm = env_get("SSL_CIPHER_ALGKEYSIZE"); + if (!cipherperm) cipherperm = "unknown"; + cipherused = env_get("SSL_CIPHER_USEKEYSIZE"); + if (!cipherused) cipherused = "unknown"; + clientdn = env_get("SSL_CLIENT_S_DN"); + if (!clientdn) clientdn = "none"; + else { + seentls = 3; + seenauth = 2; + smtpauth = -1; + relayclient = ""; + } + + if (!stralloc_copys(&tlsinfo,tlsversion)) die_nomem(); + if (!stralloc_cats(&tlsinfo,": ")) die_nomem(); + if (!stralloc_cats(&tlsinfo,cipher)) die_nomem(); + if (!stralloc_cats(&tlsinfo," [")) die_nomem(); + if (!stralloc_cats(&tlsinfo,cipherused)) die_nomem(); + if (!stralloc_cats(&tlsinfo,"/")) die_nomem(); + if (!stralloc_cats(&tlsinfo,cipherperm)) die_nomem(); + if (!stralloc_cats(&tlsinfo,"] \n")) die_nomem(); + if (!stralloc_cats(&tlsinfo," DN=")) die_nomem(); + if (!stralloc_cats(&tlsinfo,clientdn)) die_nomem(); + if (!stralloc_0(&tlsinfo)) die_nomem(); + + if (!stralloc_append(&protocol,"S")) die_nomem(); + + if (seentls == 3 && starttls == 4) { + clientcn = env_get("SSL_CLIENT_S_DN_CN"); + remoteinfo = clientcn ? clientcn : clientdn; + dnemail = env_get("SSL_CLIENT_S_DN_Email"); + if (!dnemail) dnemail = "unknown"; + if (!stralloc_cats(&authmethod,"cert")) die_nomem(); + auth_info(authmethod.s); + } + return 1; +} + +/* this file is too long --------------------------------- Address checks */ + +stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */ +stralloc eddr = {0}; /* extended address; used for smart address recognition */ +stralloc rddr = {0}; /* test anti-spoofing host name */ +stralloc mailfrom = {0}; +stralloc rcptto = {0}; +stralloc user = {0}; +stralloc fuser = {0}; +stralloc mfparms = {0}; + +int seenmail = 0; +int flagaddr; /* defined if seenmail */ +int flagrcpt; +int flagdnsmf = 0; +int flagsize; +int rcptcount = 0; + +int addrparse(char *arg) +{ + int i; + char ch; + char terminator; + struct ip4_address ip4s; + struct ip6_address ip6s; + int flagesc; + int flagquoted; + + terminator = '>'; + i = str_chr(arg,'<'); + if (arg[i]) + arg += i + 1; + else + return 0; + + /* strip source route */ + if (*arg == '@') while (*arg) if (*arg++ == ':') break; + + if (!stralloc_copys(&addr,"")) die_nomem(); + flagesc = 0; + flagquoted = 0; + for (i = 0; ch = arg[i]; ++i) { /* copy arg to addr, stripping quotes */ + if (flagesc) { + if (!stralloc_append(&addr,&ch)) die_nomem(); + flagesc = 0; + } + else { + if (!flagquoted && (ch == terminator)) break; + switch (ch) { + case '\\': flagesc = 1; break; + case '"': flagquoted = !flagquoted; break; + default: if (!stralloc_append(&addr,&ch)) die_nomem(); + } + } + } + /* could check for termination failure here, but why bother? */ + if (!stralloc_append(&addr,"")) die_nomem(); + + if (liphostok) { + i = byte_rchr(addr.s,addr.len,'@'); + if (i < addr.len) /* if not, partner should go read rfc 821 */ + if (addr.s[i + 1] == '[') { + if (byte_rchr(addr.s + i + 2,addr.len - i - 2,':') < str_len(addr.s)) { /* @[IPv6::] */ + if (!addr.s[i + 1 + ip6_scanbracket(addr.s + i + 1,(char *)&ip6s.d)]) + if (ipme_is6(&ip6s)) { + addr.len = i + 1; + if (!stralloc_cat(&addr,&liphost)) die_nomem(); + } + } else { /* @[IPv4] */ + if (!addr.s[i + 1 + ip4_scanbracket(addr.s + i + 1,(char *)&ip4s.d)]) + if (ipme_is4(&ip4s)) { + addr.len = i + 1; + if (!stralloc_cat(&addr,&liphost)) die_nomem(); + } + } + } + if (!stralloc_0(&addr)) die_nomem(); + } + + if (addr.len > 900) return 0; + return 1; +} + +int bhelocheck() +{ + int i; + int j; + int k = 0; + char subvalue; + + if (badhelook && helohost.len > 1) { /* helohost! */ + if (!stralloc_copyb(&eddr,helohost.s,helohost.len - 1)) die_nomem(); + if (!stralloc_append(&eddr,"!")) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + if (constmap(&mapbhlo,eddr.s,eddr.len - 1)) return -3; + + if (constmap(&mapbhlo,helohost.s,helohost.len - 1)) return -1; + + i = 0; + for (j = 0; j < badhelo.len; ++j) + if (!badhelo.s[j]) { + subvalue = badhelo.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(helohost.s,badhelo.s + i)) k = subvalue; + i = j + 1; + } + return k; + } + return 0; +} + +int bmfcheck() +{ + int i = 0; + int j = 0; + int k = 0; + int at = 0; + int dlen; + int rlen; + char subvalue; + + if (bmfok && mailfrom.len > 1) { + rlen = str_len(remotehost); + at = byte_rchr(mailfrom.s,mailfrom.len,'@'); + +/* '?' enhanced address to skip all other tests including MFDNSCHECK */ + + if (!stralloc_copys(&eddr,"?")) die_nomem(); + if (!stralloc_cat(&eddr,&mailfrom)) die_nomem(); + case_lowerb(eddr.s,eddr.len); + if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -110; + +/* '+' extended address for none-RELAYCLIENTS */ + + if (at && !relayclient) { + if (!stralloc_copyb(&eddr,mailfrom.s,mailfrom.len - 1)) die_nomem(); + if (!stralloc_append(&eddr,"+")) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + case_lowerb(eddr.s,eddr.len); + if (constmap(&mapbmf,eddr.s + at,eddr.len - at - 1)) return -5; + } + +/* '-' extended address from UNKNOWN */ + + if (at && !case_diffs(remotehost,"unknown")) { + if (!stralloc_copyb(&eddr,mailfrom.s,mailfrom.len - 1)) die_nomem(); + if (!stralloc_append(&eddr,"-")) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + case_lowerb(eddr.s,eddr.len); + if (constmap(&mapbmf,eddr.s + at,eddr.len - at - 1)) return -4; + } + +/* '=' extended address for WELLKNOWN senders */ + + else if (at && rlen >= mailfrom.len - at - 1) { + dlen = mailfrom.len - at - 2; + if (!stralloc_copyb(&eddr,mailfrom.s,mailfrom.len - 1)) die_nomem(); + if (!stralloc_append(&eddr,"=")) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + case_lowerb(eddr.s,eddr.len); + if (str_diffn(remotehost + rlen - dlen,eddr.s + at + 1,dlen)) + if (constmap(&mapbmf,eddr.s + at,eddr.len - at - 1)) return -3; + +/* '~' extended address for MISMATCHED Domains */ + + if (case_diffrs(remotehost,mailfrom.s + at + 1)) { + j = 0; + do { + if (!stralloc_copys(&eddr,"~")) die_nomem(); + if (!stralloc_cats(&eddr,remotehost + j)) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return -2; + j = byte_chr(remotehost + j,rlen - j,'.') + j + 1; + } while (j > 0 && rlen - j > 0); + } + } + +/* Standard */ + + if (constmap(&mapbmf,mailfrom.s,mailfrom.len - 1)) return -1; + if (at && at < mailfrom.len) + if (constmap(&mapbmf,mailfrom.s + at,mailfrom.len - at - 1)) return -1; + +/* Wildmating */ + + i = k = 0; + for (j = 0; j < bmf.len; ++j) { + if (!bmf.s[j]) { + subvalue = bmf.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(mailfrom.s,bmf.s + i)) k = subvalue; + i = j + 1; + } + } + return k; + } + + return 0; +} + +int brtcheck() +{ + int i; + int j = 0; + int k = 0; + char subvalue; + + if (brtok) { + if (constmap(&mapbrt,addr.s,addr.len - 1)) return -2; + + int at = byte_rchr(addr.s,addr.len,'@'); + if (at < addr.len) + if (constmap(&mapbrt,addr.s + at,addr.len - at - j)) return -1; + +/* '#' enhanced address to consider invalid rcptto addresses for none-relayclients */ + + if (!relayclient) { + if (!stralloc_copys(&eddr,"+")) die_nomem(); + if (!stralloc_cat(&eddr,&addr)) die_nomem(); + if (!stralloc_0(&eddr)) die_nomem(); + case_lowerb(eddr.s,eddr.len); + if (constmap(&mapbmf,eddr.s,eddr.len - 1)) return 110; + } + + i = 0; + for (j = 0; j < brt.len; ++j) + if (!brt.s[j]) { + subvalue = brt.s[i] != '!'; + if (!subvalue) i++; + if ((k != subvalue) && wildmat(addr.s,brt.s + i)) k = subvalue; + i = j + 1; + } + return k; + } + return 0; +} + +int addrallowed(char *arg) +{ + int r; + r = rcpthosts(arg,str_len(arg)); + if (r == -1) die_control(); + return r; +} + +int rcptallowed() +{ + int r; + r = recipients(addr.s,str_len(addr.s)); +#ifdef PAM111421 + if (r == 111) die_recipients(); +#endif + if (r == -3) die_recipients(); + if (r == -2) die_nomem(); + if (r == -1) die_control(); + return r; +} + +int localaddr(char *mf) +{ + int at; + int mflen; + + mflen = str_len(mf); + if (mflen < 1 ) return 0; + + if (localmf == 4) { + if (!case_diffs(dnemail,mf)) return 2; + return -4; + } + if (localmf == 3) { + if (!case_diffs(remoteinfo,mf)) return 2; + return -3; + } + else if (localmf == 2) + return mfrules(fdmav,remoteip,remotehost,remoteinfo,mf); + else { + if (str_len(localmfcheck) > 1) { + case_lowerb(localmfcheck,str_len(localmfcheck)); + at = byte_rchr(mf,mflen,'@'); + if (at < mflen) + if (!str_diffn(localmfcheck,mf + at + 1,mflen - at - 1)) return 2; + } + if (addrallowed(mf)) return 3; + return -2; + } +} + +int spf_check(int flag6) +{ + int r; + + if (mailfrom.len <= 1) { flagspf = 0; return 0; } + + DNS_INIT + r = spf_query(remoteip,helohost.s,mailfrom.s,local,flag6); + if (r == SPF_NOMEM) die_nomem(); + if (!stralloc_0(&spfinfo)) die_nomem(); + + switch (r) { + case SPF_ME: + case SPF_OK: + if (!env_put("SPFRESULT","pass")) die_nomem(); + flagspf = 10; + break; + case SPF_LOOP: + case SPF_ERROR: + case SPF_SYNTAX: + case SPF_EXHAUST: + if (!env_put("SPFRESULT","error")) die_nomem(); + if (flagspf < 2) { flagspf = 0; break; } + out("451 SPF lookup failure (#4.3.0)\r\n"); + return -1; + case SPF_NONE: + if (!env_put("SPFRESULT","none")) die_nomem(); + flagspf = 0; + break; + case SPF_UNKNOWN: + if (!env_put("SPFRESULT","unknown")) die_nomem(); + if (flagspf < 6) break; + else return 4; + case SPF_NEUTRAL: + if (!env_put("SPFRESULT","neutral")) die_nomem(); + if (flagspf < 5) break; + else return 3; + case SPF_SOFTFAIL: + if (!env_put("SPFRESULT","softfail")) die_nomem(); + if (flagspf < 4) break; + else return 2; + case SPF_FAIL: + if (!env_put("SPFRESULT","fail")) die_nomem(); + if (flagspf < 3) break; + if (!spf_parse(&spfbounce,spfexpmsg.s,expdomain.s)) die_nomem(); + return 1; + } + + return 0; +} + +/* this file is too long --------------------------------- MF parser */ + +int mailfrom_size(char *arg) +{ + unsigned long r; + unsigned long sizebytes = 0; + + scan_ulong(arg,&r); + sizebytes = r; + if (databytes) if (sizebytes > databytes) return 1; + return 0; +} + +void mailfrom_auth(char *arg,int len) +{ + if (!stralloc_copys(&fuser,"")) die_nomem(); + if (case_starts(arg,"<>")) { + if (!stralloc_cats(&fuser,"unknown")) die_nomem(); + } else { + while (len) { + if (*arg == '+') { + if (case_starts(arg,"+3D")) { + arg = arg + 2; len = len - 2; + if (!stralloc_cats(&fuser,"=")) die_nomem(); + } + if (case_starts(arg,"+2B")) { + arg = arg + 2; len = len - 2; + if (!stralloc_cats(&fuser,"+")) die_nomem(); + } + } else { + if (!stralloc_catb(&fuser,arg,1)) die_nomem(); + } + arg++; len--; + } + } + + if (!stralloc_0(&fuser)) die_nomem(); + if (!remoteinfo) { + remoteinfo = fuser.s; + if (!env_unset("TCPREMOTEINFO")) die_read(); + if (!env_put("TCPREMOTEINFO",remoteinfo)) die_nomem(); + if (!env_unset("TCP6REMOTEINFO")) die_read(); + if (!env_put("TCP6REMOTEINFO",remoteinfo)) die_nomem(); + } +} + +void mailfrom_parms(char *arg) +{ + int len; + + if ((len = str_len(arg))) { + if (!stralloc_copys(&mfparms,"")) die_nomem(); + while (len) { + arg++; len--; + if (*arg == ' ' || *arg == '\0' ) { + if (flagutf8) if (case_starts(mfparms.s,"SMTPUTF8")) flagutf8 = 2; + if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s + 5)) { flagsize = 1; return; } + if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s + 5,mfparms.len - 5); + if (!stralloc_copys(&mfparms,"")) die_nomem(); + } + else + if (!stralloc_catb(&mfparms,arg,1)) die_nomem(); + } + } +} + +/* this file is too long --------------------------------- SMTP dialog */ + +void smtp_helo(char *arg) +{ + smtp_greet("250 "); out("\r\n"); flush(); + seenmail = 0; rcptcount = 0; seenhelo++; + dohelo(arg); +} + +void smtp_ehlo(char *arg) +{ + char size[FMT_ULONG]; + + smtp_greet("250-"); out("\r\n"); + out("250-PIPELINING\r\n250-8BITMIME\r\n"); + if (flagutf8) out("250-SMTPUTF8\r\n"); + if (starttls > 0 && !seentls) out("250-STARTTLS\r\n"); + + switch (smtpauth) { + case 1: case 11: out("250-AUTH LOGIN PLAIN\r\n"); break; + case 2: case 12: out("250-AUTH CRAM-MD5\r\n"); break; + case 3: case 13: out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n"); break; + } + + size[fmt_ulong(size,(unsigned long) databytes)] = 0; + out("250 SIZE "); out(size); out("\r\n"); + + seenhelo++; seenmail = 0; rcptcount = 0; + dohelo(arg); +} + +void smtp_rset(void) +{ + seenmail = 0; rcptcount = 0; /* RFC 5321: seenauth + seentls stay */ + + if (!stralloc_copys(&mailfrom,"")) die_nomem(); + if (!stralloc_copys(&rcptto,"")) die_nomem(); + out("250 flushed\r\n"); +} + +void smtp_starttls() +{ + if (starttls == 0) err_starttls(); + + out("220 Ready to start TLS (#5.7.0)\r\n"); + flush(); + + if (!starttls_init()) die_starttls(); + buffer_init(&bi,saferead,FDIN,inbuf,sizeof(inbuf)); + seentls = 2; + + if (!starttls_info()) die_starttls(); + if (!modssl_info()) die_starttls(); + +/* reset SMTP state */ + + seenhelo = 0; seenmail = 0; rcptcount = 0; + if (!stralloc_copys(&addr,"")) die_nomem(); + if (!stralloc_copys(&helohost,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,"")) die_nomem(); + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (seenauth == 1) seenauth = 0; /* Otherwise Auth by client Cert */ +} + +void smtp_mail(char *arg) +{ + if (flagutf8) if (!stralloc_cats(&protocol,"UTF8")) die_nomem(); + if (!stralloc_0(&protocol)) die_nomem(); + + if ((starttls > 1) && !seentls) { + err_tlsreq("Reject::TLS::missing",protocol.s,remoteip,remotehost,helohost.s); + return; + } + if (smtpauth > 10 && !seenauth) { + err_authreq("Reject::AUTH::missing",protocol.s,remoteip,remotehost,helohost.s); + return; + } + if (!addrparse(arg)) { err_syntax(); return; } + + flagsize = 0; + rcptcount = 0; + mailfrom_parms(arg); + seenmail++; + if (relayclient && localmf) { + flagmav = localaddr(addr.s); + if (flagmav > 0) if (!stralloc_append(&protocol,"M")) die_nomem(); + } + if (!stralloc_copys(&rcptto,"")) die_nomem(); + if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); + if (!stralloc_0(&mailfrom)) die_nomem(); + if (!env_put("MAILFROM",mailfrom.s)) die_nomem(); + + flagaddr = bmfcheck(); + if (flagaddr != -110) + if (mfdnscheck) flagdnsmf = dnsq(mailfrom.s,'M'); + + out("250 ok\r\n"); +} + +/* this file is too long --------------------------------- Greylisting */ + +int postgrey_scanner() +{ + int child; + int wstat; + + char *postgrey_scannerarg[] = {"bin/qmail-postgrey",pgbind.s,mailfrom.s,addr.s,remoteip,remotehost,0}; + + switch (child = fork()) { + case -1: + return err_forkgl(); + case 0: + execv(*postgrey_scannerarg,postgrey_scannerarg); + _exit(1); + } + + wait_pid(&wstat,child); + if (wait_crashed(wstat)) return err_postgl(); + + switch (wait_exitcode(wstat)) { + case 10: return 1; + default: return 0; + } +} + +void smtp_rcpt(char *arg) +{ + char *rcptok = 0; + if (!seenmail) { err_wantmail(); return; } + if (!addrparse(arg)) { err_syntax(); return; } + rcptcount++; + +/* this file is too long --------------------------------- Split Horizon envelope checks */ + + if (!relayclient) { + if (!seenhelo && helocheck) /* Helo rejects */ + if (str_len(helocheck) == 1) { + err_helo("Reject::SNDR::Bad_Helo",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"0"); + return; + } + if (flagbadhelo) { + switch (flagbadhelo) { + case -2: badhelocond = "!"; break; + case -1: badhelocond = "."; break; + default: badhelocond = "*"; break; + } + err_helo("Reject::SNDR::Bad_Helo",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badhelocond); + return; + } + if (flagdnshelo > 0) { + err_helo("Reject::SNDR::DNS_Helo",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badhelocond); + return; + } + + if (flagdnsmf > 0) { /* Mail from rejects */ + err_mfdns("Reject::ORIG::DNS_MF",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + + if (!addrallowed(addr.s)) { /* Relaying rejects */ + err_nogateway("Reject::SNDR::Invalid_Relay",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + + if (greylist && (postgrey_scanner() == 1)) { /* Greylisting */ + postgrey("Deferred::SNDR::Grey_Listed",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + + if (tarpitcount && flagerrcpts >= tarpitcount) { /* Tarpitting et al. */ + if (tarpitdelay == 999) flagnotorious++; + err_rcpts("Reject::RCPT::Toomany_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + if (tarpitcount && rcptcount >= tarpitcount) + if (tarpitdelay > 0 && tarpitdelay < 999) sleep(tarpitdelay); + + flagrcpt = rcptallowed(); /* Rcpt to rejects */ + if (!flagrcpt) { + err_recipient("Reject::RCPT::Failed_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + flagerrcpts++; + return; + } + + if (flagspf) /* SPF rejects */ + if (spf_check(flagip6) > 0) { + if (!stralloc_0(&spfbounce)) die_nomem(); + err_spf("Reject::SPF::Fail",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,spfbounce.s); + return; + } + } + +/* this file is too long --------------------------------- Local checks */ + + else { + if (flagmimetype == 4 || flagmimetype == 6) flagmimetype = 0; + if (flagloadertype == 2) flagloadertype = 0; + + if (flagmav < 0) { + err_mav("Reject::ORIG::Invalid_Mailfrom",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + --addr.len; + if (!stralloc_cats(&addr,relayclient)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + } + +/* this file is too long --------------------------------- Common checks */ + + if (flagmimetype == 2 || flagmimetype == 3 || flagmimetype == 6) cdb_init(&cdbm,fdbmt); + if (flagloadertype == 1) cdb_init(&cdbl,fdblt); + + if (flagaddr && flagaddr != -110) { + switch (flagaddr) { + case -1: badmailcond = "@"; break; + case -2: badmailcond = "~"; break; + case -3: badmailcond = "="; break; + case -4: badmailcond = "-"; break; + case -5: badmailcond = "+"; break; + default: badmailcond = "*"; break; + } + err_bmf("Reject::ORIG::Bad_Mailfrom",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmailcond); + return; + } + + flagrcpt = brtcheck(); + if (flagrcpt == 110) { + err_brt("Reject::RCPT::Invalid_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } else if (flagrcpt > 0) { + err_brt("Reject::RCPT::Bad_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + + if (flagsize) { + err_size("Reject::DATA::Invalid_Size",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + + if (maxrcptcount && rcptcount > maxrcptcount) { + err_rcpts("Reject::RCPT::Toomany_Rcptto",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + +/* this file is too long --------------------------------- Checks done; mailfrom/recipient accepted */ + + if (!stralloc_cats(&rcptto,"T")) die_nomem(); + if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + + if (!stralloc_cats(&mailto,addr.s)) die_nomem(); + if (!stralloc_cats(&mailto," ")) die_nomem(); + if (!stralloc_copys(&deliverto,mailto.s)) die_nomem(); + if (!stralloc_0(&deliverto)) die_nomem(); + if (!env_put("RCPTTO",deliverto.s)) die_nomem(); + +/* this file is too long --------------------------------- Additional logging */ + + switch (flagrcpt) { + case 1: rcptok = "Recipients_Cdb"; break; + case 2: rcptok = "Recipients_Pam"; break; + case 3: rcptok = "Recipients_Users"; break; + case 4: rcptok = "Recipients_Wild"; break; + default: rcptok = "Rcpthosts_Rcptto"; break; + } + if (seenauth) + smtp_loga("Accept::AUTH::",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,remoteinfo,authmethod.s); + else if (flagmav > 0) + smtp_logg("Accept::ORIG::Local_Sender",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + else if (relayclient) + smtp_logg("Accept::SNDR::Relay_Client",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + else if (flagspf == 10) + smtp_logr("Accept::SPF::",rcptok,protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + else + smtp_logr("Accept::RCPT::",rcptok,protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + + out("250 ok\r\n"); +} + +struct qmail qqt; +unsigned long bytestooverflow = 0; + +stralloc line = {0}; +stralloc base64types = {0}; +stralloc badmimetype = {0}; +stralloc badloadertype = {0}; + +unsigned int nolines = 0; +unsigned int flagb64 = 0; /* lineno with BASE64MESSAGE */ +unsigned int flagbase = 0; /* lineno with actual base64 content */ +unsigned int flagblank = 0; + +static void queue_put(char *ch) +{ + int i; + + if (flagmimetype > 0 || flagloadertype > 0 ) { + if (line.len <= BUFSIZE_LINE) + if (!stralloc_catb(&line,ch,1)) die_nomem(); /* Reassamble chars to line; prepend with 'L' */ + + if (*ch == '\n') { + nolines++; + if (line.len == 2) { flagblank = nolines; flagbase = 0; } + + if (*(line.s + 1) == 'C' || *(line.s + 1) == 'c') + if (case_startb(line.s + 1,line.len - 2,BASE64MESSAGE)) flagb64 = nolines; + if (flagb64 && nolines == flagblank + 1 && line.len > MIMETYPE_LEN + 2) flagbase = nolines; + if (*(line.s + 1) == '-') { flagb64 = 0; flagbase = 0; } + + if (flagmimetype > 0 && flagbase == nolines) { /* badmimetype */ + if (!stralloc_catb(&base64types,line.s + 1,MIMETYPE_LEN)) die_nomem(); + if (!stralloc_0(&base64types)) die_nomem(); + + if (flagmimetype == 2 || flagmimetype == 3 || flagmimetype == 6) { + if (cdb_find(&cdbm,line.s + 1,MIMETYPE_LEN)) { + cdb_free(&cdbm); close(fdbmt); + if (!stralloc_copyb(&badmimetype,line.s + 1,MIMETYPE_LEN)) die_nomem(); + if (!stralloc_0(&badmimetype)) die_nomem(); + if (!stralloc_cats(&rcptto,"M")) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + qmail_fail(&qqt); + flagmimetype = -1; + } + } + } + + if (flagbase && line.len > LOADER_LEN + 2) { + if (flagloadertype >= 1 || flagmimetype >= 1) { + for (i = 0; i < line.len - LOADER_LEN; ++i) { + if (flagloadertype == 1 && *(line.s+i) == *badloaderinit) { /* badloadertype */ + if (cdb_find(&cdbl,line.s + i,LOADER_LEN)) { + cdb_free(&cdbl); close(fdbmt); + if (!stralloc_copyb(&badloadertype,line.s + i,LOADER_LEN)) die_nomem(); + if (!stralloc_0(&badloadertype)) die_nomem(); + if (!stralloc_cats(&rcptto,"L")) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + qmail_fail(&qqt); + flagloadertype = -1; + } + } + if (flagmimetype == 1 || flagmimetype == 3 || flagmimetype == 4) { + if (*(line.s + i) == ' ' || *(line.s + i) == '\t') { /* white spaces */ + if (!stralloc_copyb(&badmimetype,line.s + i - 2,MIMETYPE_LEN)) die_nomem(); + if (!stralloc_0(&badmimetype)) die_nomem(); + if (!stralloc_cats(&rcptto,"M")) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + qmail_fail(&qqt); + flagmimetype = -2; + } + } + } + } + } + line.len = 0; + if (!stralloc_copys(&line,"L")) die_nomem(); + } + } + + if (bytestooverflow) + if (!--bytestooverflow) + qmail_fail(&qqt); + qmail_put(&qqt,ch,1); +} + +void blast(int *hops) +{ + char ch; + int state; + int flaginheader; + int pos; /* number of bytes since most recent \n, if fih */ + int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ + int flagmaybey; /* 1 if this line might match \r\n, if fih */ + int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ +#ifdef BARELF + int seencr = 0; +#endif + + state = 1; + *hops = 0; + flaginheader = 1; + pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; + for (;;) { + buffer_get(&bi,&ch,1); +#ifdef BARELF + if (ch == '\n') { + if (seencr == 0) { buffer_seek(&bi,-1); ch = '\r'; } + } + if (ch == '\r') seencr = 1; else seencr = 0; +#endif + if (flaginheader) { + if (pos < 9) { + if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; + if (flagmaybez) if (pos == 8) ++*hops; + if (pos < 8) + if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0; + if (flagmaybex) if (pos == 7) ++*hops; + if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; + if (flagmaybey) if (pos == 1) flaginheader = 0; + ++pos; + } + if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } + } + switch (state) { + case 0: + if (ch == '\n') straynewline(); + if (ch == '\r') { state = 4; continue; } + break; + case 1: /* \r\n */ + if (ch == '\n') straynewline(); + if (ch == '.') { state = 2; continue; } + if (ch == '\r') { state = 4; continue; } + state = 0; + break; + case 2: /* \r\n + . */ + if (ch == '\n') straynewline(); + if (ch == '\r') { state = 3; continue; } + state = 0; + break; + case 3: /* \r\n + .\r */ + if (ch == '\n') return; + queue_put("."); + queue_put("\r"); + if (ch == '\r') { state = 4; continue; } + state = 0; + break; + case 4: /* + \r */ + if (ch == '\n') { state = 1; break; } + if (ch != '\r') { queue_put("\r"); state = 0; } + } + queue_put(&ch); + } +} + +char accept_buf[FMT_ULONG]; + +void acceptmessage(unsigned long qp) +{ + datetime_sec when; + when = now(); + out("250 ok "); + accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; + out(accept_buf); + out(" qp "); + accept_buf[fmt_ulong(accept_buf,qp)] = 0; + out(accept_buf); + out("\r\n"); +} + +void smtp_data() +{ + int hops; + unsigned long qp; + char *qqx; + + if (!seenmail) { err_wantmail(); return; } + if (!rcptto.len) { err_wantrcpt(); return; } + if (flagnotorious) { err_notorious(); } + seenmail = 0; + if (databytes) bytestooverflow = databytes + 1; + + if (!stralloc_copys(&addr,"")) die_nomem(); + if (!stralloc_cats(&addr,rcptto.s + 1)) die_nomem(); + if (!stralloc_0(&addr)) die_nomem(); + + if (qmail_open(&qqt) == -1) { err_qqt(); return; } + qp = qmail_qp(&qqt); + + out("354 go ahead\r\n"); + + if (flagspf && !relayclient) spfheader(&qqt,spfinfo.s,local,remoteip,helohost.s,mailfrom.s); + received(&qqt,protocol.s,local,remoteip,remotehost,remoteinfo,fakehelo,tlsinfo.s,rblinfo.s); + blast(&hops); + hops = (hops >= MAXHOPS); + if (hops) qmail_fail(&qqt); + if (base64 && base64types.len == 0) { + if (!stralloc_cats(&rcptto,"Q")) die_nomem(); + if (!stralloc_0(&rcptto)) die_nomem(); + } + qmail_from(&qqt,mailfrom.s); + qmail_put(&qqt,rcptto.s,rcptto.len); + + qqx = qmail_close(&qqt); + if (!*qqx) { acceptmessage(qp); return; } + if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } + if (databytes) + if (!bytestooverflow) { + err_size("Reject::DATA::Invalid_Size",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s); + return; + } + if (flagmimetype < 0) { + err_data("Reject::DATA::Bad_MIME",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badmimetype.s); + return; + } + if (flagloadertype < 0) { + err_data("Reject::DATA::Bad_Loader",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,badloadertype.s); + return; + } + if (*qqx == 'I') { + err_data("Reject::DKIM::Signature",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"fail"); + return; + } + if (*qqx == 'S') { + err_data("Reject::DATA::Spam_Message",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"spam"); + return; + } + if (*qqx == 'A') { + err_data("Reject::DATA::MIME_Attach",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"MIME"); + return; + } + if (*qqx == 'V') { + if (qhpsi) + err_data("Reject::DATA::Virus_Infected",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,qhpsi); + else + err_data("Reject::DATA::Virus_Infected",protocol.s,remoteip,remotehost,helohost.s,mailfrom.s,addr.s,"AV scanner"); + return; + } + if (*qqx == 'D') out("554 "); else out("451 "); + out(qqx + 1); + out("\r\n"); +} + +/* this file is too long --------------------------------- SMTP Auth */ + +char unique[FMT_ULONG + FMT_ULONG + 3]; +static stralloc authin = {0}; /* input from SMTP client */ +static stralloc pass = {0}; /* plain passwd or digest */ +static stralloc resp = {0}; /* b64 response */ +static stralloc chal = {0}; /* CRAM-MD5 plain challenge */ +static stralloc slop = {0}; /* CRAM-MD5 b64 challenge */ + +char **childargs; +char authbuf[BUFSIZE_AUTH]; +buffer ba = BUFFER_INIT(safewrite,FDAUTH,authbuf,sizeof(authbuf)); + +int authgetl(void) +{ + int i; + + if (!stralloc_copys(&authin,"")) die_nomem(); + for (;;) { + if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */ + i = buffer_get(&bi,authin.s + authin.len,1); + if (i != 1) die_read(); + if (authin.s[authin.len] == '\n') break; + ++authin.len; + } + + if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len; + authin.s[authin.len] = 0; + if (*authin.s == '*' && *(authin.s + 1) == 0) return err_authabort(); + if (authin.len == 0) return err_authinput(); + return authin.len; +} + +int authenticate(void) +{ + int child; + int wstat; + int pi[2]; + + if (!stralloc_0(&user)) die_nomem(); + if (!stralloc_0(&pass)) die_nomem(); + if (!stralloc_0(&chal)) die_nomem(); + if (!env_put("AUTHUSER",user.s)) die_nomem(); + + if (pipe(pi) == -1) return err_pipe(); + switch (child = fork()) { + case -1: + return err_fork(); + case 0: + close(pi[1]); + if (fd_copy(FDAUTH,pi[0]) == -1) return err_pipe(); + sig_pipedefault(); + execvp(*childargs,childargs); + _exit(1); + } + close(pi[0]); + + buffer_init(&ba,write,pi[1],authbuf,sizeof(authbuf)); + if (buffer_put(&ba,user.s,user.len) == -1) return err_write(); + if (buffer_put(&ba,pass.s,pass.len) == -1) return err_write(); + if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13) + if (buffer_put(&ba,chal.s,chal.len) == -1) return err_write(); + if (buffer_flush(&ba) == -1) return err_write(); + + close(pi[1]); + if (!stralloc_copys(&chal,"")) die_nomem(); + if (!stralloc_copys(&slop,"")) die_nomem(); + byte_zero(authbuf,sizeof(authbuf)); + if (wait_pid(&wstat,child) == -1) return err_child(); + if (wait_crashed(wstat)) return err_child(); + if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */ + return 0; /* yes */ +} + +int auth_login(char *arg) +{ + int r; + if (smtpauth == 2 || smtpauth == 12) return 1; /* only login/plain */ + + if (*arg) { + if ((r = b64decode((unsigned char *)arg,str_len(arg),&user)) == 1) return err_authinput(); + } + else { + out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */ + if (authgetl() < 0) return -1; + if ((r = b64decode((unsigned char *)authin.s,authin.len,&user)) == 1) return err_authinput(); + } + if (r == -1) die_nomem(); + + out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */ + + if (authgetl() < 0) return -1; + if ((r = b64decode((unsigned char *)authin.s,authin.len,&pass)) == 1) return err_authinput(); + if (r == -1) die_nomem(); + + if (!user.len || !pass.len) return err_authinput(); + return authenticate(); +} + +int auth_plain(char *arg) +{ + int r, id = 0; + if (smtpauth == 2 || smtpauth == 12) return 1; /* only login/plain */ + + if (*arg) { + if ((r = b64decode((unsigned char *)arg,str_len(arg),&resp)) == 1) return err_authinput(); + } + else { + out("334 \r\n"); flush(); + if (authgetl() < 0) return -1; + if ((r = b64decode((unsigned char *)authin.s,authin.len,&resp)) == 1) return err_authinput(); + } + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + while (resp.s[id]) id++; /* "authorize-id\0userid\0passwd\0" */ + + if (resp.len > id + 1) + if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem(); + if (resp.len > id + user.len + 2) + if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem(); + + if (!user.len || !pass.len) return err_authinput(); + return authenticate(); +} + +int auth_cram() +{ + int i, r; + char *s; + if (smtpauth == 1 || smtpauth == 11) return 1; /* no challenge if login/plain */ + + s = unique; /* generate challenge */ + s += fmt_uint(s,getpid()); + *s++ = '.'; + s += fmt_ulong(s,(unsigned long) now()); + *s++ = '@'; + *s++ = 0; + if (!stralloc_copys(&chal,"<")) die_nomem(); + if (!stralloc_cats(&chal,unique)) die_nomem(); + if (!stralloc_cats(&chal,local)) die_nomem(); + if (!stralloc_cats(&chal,">")) die_nomem(); + if (b64encode(&chal,&slop) < 0) die_nomem(); + if (!stralloc_0(&slop)) die_nomem(); + + out("334 "); /* "334 base64_challenge \r\n" */ + out(slop.s); + out("\r\n"); + flush(); + + if (authgetl() < 0) return -1; /* got response */ + if ((r = b64decode((unsigned char *)authin.s,authin.len,&resp)) == 1) return err_authinput(); + if (r == -1 || !stralloc_0(&resp)) die_nomem(); + + i = str_rchr(resp.s,' '); + s = resp.s + i; + while (*s == ' ') ++s; + resp.s[i] = 0; + if (!stralloc_copys(&user,resp.s)) die_nomem(); /* userid */ + if (!stralloc_copys(&pass,s)) die_nomem(); /* digest */ + + if (!user.len || !pass.len) return err_authinput(); + return authenticate(); +} + +struct authcmd { + char *text; + int (*fun)(); +} authcmds[] = { + { "login", auth_login } +, { "plain", auth_plain } +, { "cram-md5", auth_cram } +, { 0, err_noauth } +}; + +void smtp_auth(char *arg) +{ + int i; + char *cmd = arg; + + /* prevent users to expose userid + password over unencrypted connection */ + + if ((starttls > 1) && !seentls) { + if (!stralloc_append(&protocol,"A")) die_nomem(); + if (!stralloc_0(&protocol)) die_nomem(); + err_authsetup("Reject::TLS::required",protocol.s,remoteip,remotehost,helohost.s); + return; + } + + if ((starttls > 1) && !seenhelo) { + if (!stralloc_append(&protocol,"A")) die_nomem(); + if (!stralloc_0(&protocol)) die_nomem(); + err_tlsreq("Reject::AUTH::invalid",protocol.s,remoteip,remotehost,helohost.s); + return; + } + + if (!smtpauth) { out("503 auth not available (#5.3.3)\r\n"); flush(); _exit(0); } + if (smtpauth && !*childargs) { + err_authsetup("Reject::AUTH::setup",protocol.s,remoteip,remotehost,helohost.s); + flush(); _exit(1); + } + if (seenauth) { err_authd(); return; } + if (seenmail) { err_authmail(); return; } + + if (!stralloc_copys(&user,"")) die_nomem(); + if (!stralloc_copys(&pass,"")) die_nomem(); + if (!stralloc_copys(&resp,"")) die_nomem(); + if (!stralloc_copys(&chal,"")) die_nomem(); /* only needed for CRAM-MD5 */ + + i = str_chr(cmd,' '); /* get AUTH type */ + arg = cmd + i; + while (*arg == ' ') ++arg; + cmd[i] = 0; + + for (i = 0; authcmds[i].text; ++i) + if (case_equals(authcmds[i].text,cmd)) break; + + if (!authcmds[i].text) { /* invalid auth cmd */ + if (!stralloc_append(&protocol,"A")) die_nomem(); + if (!stralloc_0(&protocol)) die_nomem(); + err_authinvalid("Reject::AUTH::Method",protocol.s,remoteip,remotehost,helohost.s); + return; + } + + if (!stralloc_copys(&authmethod,authcmds[i].text)) die_nomem(); + if (!stralloc_0(&authmethod)) die_nomem(); + + switch (authcmds[i].fun(arg)) { + case 0: + seenauth = 1; + relayclient = ""; + remoteinfo = user.s; + auth_info(authmethod.s); + out("235 ok, go ahead (#2.0.0)\r\n"); + break; + case 1: + if (!stralloc_append(&protocol,"A")) die_nomem(); + if (!stralloc_0(&protocol)) die_nomem(); + err_authfail("Reject::AUTH::",protocol.s,remoteip,remotehost,helohost.s,user.s,authmethod.s); + return; + } +} + +/* this file is too long --------------------------------- GO ON */ + +struct commands smtpcommands[] = { + { "rcpt", smtp_rcpt, 0 } +, { "mail", smtp_mail, 0 } +, { "data", smtp_data, flush } +, { "auth", smtp_auth, flush } +, { "quit", smtp_quit, flush } +, { "helo", smtp_helo, flush } +, { "ehlo", smtp_ehlo, flush } +, { "rset", smtp_rset, flush } +, { "help", smtp_help, flush } +, { "noop", err_noop, flush } +, { "vrfy", err_vrfy, flush } +, { "starttls", smtp_starttls, flush } +, { 0, err_unimpl, flush } +} ; + +int main(int argc, char **argv) +{ + childargs = argv + 1; + sig_pipeignore(); + if (chdir(auto_qmail) == -1) die_control(); + setup(); + smtpdlog_init(); + if (ipme_init() != 1) die_ipme(); + smtp_greet("220 "); + out(" ESMTP\r\n"); + flush(); + if (commands(&bi,&smtpcommands) == 0) die_read(); + die_nomem(); + + return 0; +} diff --git a/sqmail-4.3.07/src/qmail-start.c b/sqmail-4.3.07/src/qmail-start.c new file mode 100644 index 0000000..7a7342c --- /dev/null +++ b/sqmail-4.3.07/src/qmail-start.c @@ -0,0 +1,165 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "fd.h" +#include "prot.h" +#include "exit.h" +#include "auto_uids.h" + +char *(qsargs[]) = { "qmail-send", 0 }; +char *(qcargs[]) = { "qmail-clean", 0 }; +char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 }; +char *(qrargs[]) = { "qmail-rspawn", 0 }; +char *(qtargs[]) = { "qmail-todo", 0}; + +void die() { _exit(111); } + +int pi0[2]; +int pi1[2]; +int pi2[2]; +int pi3[2]; +int pi4[2]; +int pi5[2]; +int pi6[2]; +int pi7[2]; +int pi8[2]; +int pi9[2]; +int pi10[2]; + +void closefds() +{ + close(2); close(3); close(4); close(5); close(6); + close(7); close(8); +} + +void closepipes() +{ + close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]); + close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]); + close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]); + close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]); + close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]); +} + +int main(int argc,char **argv) +{ + if (chdir("/") == -1) die(); + umask(077); + if (prot_gid(auto_gidq) == -1) die(); + + if (fd_copy(2,0) == -1) die(); + if (fd_copy(3,0) == -1) die(); + if (fd_copy(4,0) == -1) die(); + if (fd_copy(5,0) == -1) die(); + if (fd_copy(6,0) == -1) die(); + if (fd_copy(7,0) == -1) die(); + if (fd_copy(8,0) == -1) die(); + + if (argv[1]) { + qlargs[1] = argv[1]; + ++argv; + } + + if (argv[1]) { + if (pipe(pi0) == -1) die(); + switch (fork()) { + case -1: + die(); + case 0: + if (prot_gid(auto_gidn) == -1) die(); + if (prot_uid(auto_uidl) == -1) die(); + close(pi0[1]); + if (fd_move(0,pi0[0]) == -1) die(); + closefds(); + execvp(argv[1],argv + 1); + die(); + } + close(pi0[0]); + if (fd_move(1,pi0[1]) == -1) die(); + } + + if (pipe(pi1) == -1) die(); + if (pipe(pi2) == -1) die(); + if (pipe(pi3) == -1) die(); + if (pipe(pi4) == -1) die(); + if (pipe(pi5) == -1) die(); + if (pipe(pi6) == -1) die(); + if (pipe(pi7) == -1) die(); + if (pipe(pi8) == -1) die(); + if (pipe(pi9) == -1) die(); + if (pipe(pi10) == -1) die(); + + switch (fork()) { + case -1: die(); + case 0: + if (fd_copy(0,pi1[0]) == -1) die(); + if (fd_copy(1,pi2[1]) == -1) die(); + closefds(); + closepipes(); + execvp(*qlargs,qlargs); + die(); + } + + switch (fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidr) == -1) die(); + if (fd_copy(0,pi3[0]) == -1) die(); + if (fd_copy(1,pi4[1]) == -1) die(); + closefds(); + closepipes(); + execvp(*qrargs,qrargs); + die(); + } + + switch (fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidq) == -1) die(); + if (fd_copy(0,pi5[0]) == -1) die(); + if (fd_copy(1,pi6[1]) == -1) die(); + closefds(); + closepipes(); + execvp(*qcargs,qcargs); + die(); + } + + switch (fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uids) == -1) die(); + if (fd_copy(0,pi7[0]) == -1) die(); + if (fd_copy(1,pi8[1]) == -1) die(); + closefds(); + if (fd_copy(2,pi9[1]) == -1) die(); + if (fd_copy(3,pi10[0]) == -1) die(); + closepipes(); + execvp(*qtargs,qtargs); + die(); + } + + switch (fork()) { + case -1: die(); + case 0: + if (prot_uid(auto_uidq) == -1) die(); + if (fd_copy(0,pi9[0]) == -1) die(); + if (fd_copy(1,pi10[1]) == -1) die(); + closefds(); + closepipes(); + execvp(*qcargs,qcargs); + die(); + } + + if (prot_uid(auto_uids) == -1) die(); + if (fd_copy(0,1) == -1) die(); + if (fd_copy(1,pi1[1]) == -1) die(); + if (fd_copy(2,pi2[0]) == -1) die(); + if (fd_copy(3,pi3[1]) == -1) die(); + if (fd_copy(4,pi4[0]) == -1) die(); + if (fd_copy(5,pi5[1]) == -1) die(); + if (fd_copy(6,pi6[0]) == -1) die(); + if (fd_copy(7,pi7[1]) == -1) die(); + if (fd_copy(8,pi8[0]) == -1) die(); + closepipes(); + execvp(*qsargs,qsargs); + die(); +} diff --git a/sqmail-4.3.07/src/qmail-tcpok.c b/sqmail-4.3.07/src/qmail-tcpok.c new file mode 100644 index 0000000..2935f17 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-tcpok.c @@ -0,0 +1,36 @@ +#include "logmsg.h" +#include "buffer.h" +#include "lock.h" +#include "open.h" +#include <unistd.h> +#include "auto_qmail.h" +#include "exit.h" + +#define WHO "qmail-tcpok" + +char buf[1024]; /* XXX: must match size in tcpto_clean.c, tcpto.c */ +buffer bo; + +int main() +{ + int fd; + int i; + + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",auto_qmail)); + if (chdir("queue/lock") == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to ",auto_qmail,"/queue/lock: ")); + + fd = open_write("tcpto"); + if (fd == -1) + logmsg(WHO,111,FATAL,B("unable to write ",auto_qmail,"/queue/lock/tcpto: ")); + if (lock_ex(fd) == -1) + logmsg(WHO,111,FATAL,B("unable to lock ",auto_qmail,"/queue/lock/tcpto: ")); + + buffer_init(&bo,write,fd,buf,sizeof(buf)); + for (i = 0; i < sizeof(buf); ++i) + buffer_put(&bo,"",1); + if (buffer_flush(&bo) == -1) + logmsg(WHO,111,FATAL,B("unable to clear ",auto_qmail,"/queue/lock/tcpto: ")); + _exit(0); +} diff --git a/sqmail-4.3.07/src/qmail-tcpto.c b/sqmail-4.3.07/src/qmail-tcpto.c new file mode 100644 index 0000000..e148c55 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-tcpto.c @@ -0,0 +1,95 @@ +/* XXX: this program knows quite a bit about tcpto's internals */ + +#include <sys/socket.h> +#include <unistd.h> +#include "buffer.h" +#include "auto_qmail.h" +#include "fmt.h" +#include "ip.h" +#include "lock.h" +#include "error.h" +#include "exit.h" +#include "datetime.h" +#include "now.h" +#include "stralloc.h" +#include "open.h" +#include "logmsg.h" + +#define WHO "qmail-tcpto" + +void die(n) int n; { buffer_flush(buffer_1); _exit(n); } + +void warn(s) char *s; +{ + char *x; + x = error_str(errno); + buffer_puts(buffer_1,s); + buffer_puts(buffer_1,": "); + buffer_puts(buffer_1,x); + buffer_puts(buffer_1,"\n"); +} + +void die_chdir() { logmsg(WHO,110,FATAL,"unable to chdir"); } +void die_open() { logmsg(WHO,112,FATAL,"unable to open tcpto"); } +void die_lock() { logmsg(WHO,112,FATAL,"unable to lock tcpto"); } +void die_read() { logmsg(WHO,112,FATAL,"unable to read tcpto"); } + +char tcpto_buf[1024]; + +char tmp[FMT_ULONG + IPFMT]; + +int main(void) +{ + int fdlock; + int fd; + int r; + int i; + char *record; + char ip4[4]; + char ip6[16]; + datetime_sec when; + datetime_sec start; + + if (chdir(auto_qmail) == -1) die_chdir(); + if (chdir("queue/lock") == -1) die_chdir(); + + fdlock = open_write("tcpto"); + if (fdlock == -1) die_open(); + fd = open_read("tcpto"); + if (fd == -1) die_open(); + if (lock_ex(fdlock) == -1) die_lock(); + r = read(fd,tcpto_buf,sizeof(tcpto_buf)); + close(fd); + close(fdlock); + + if (r == -1) die_read(); + r >>= 5; /* 32 bit read */ + + start = now(); + record = tcpto_buf; + + for (i = 0; i < r; ++i) { + if (record[4] >= 1) { + when = (unsigned long) (unsigned char) record[11]; + when = (when << 8) + (unsigned long) (unsigned char) record[10]; + when = (when << 8) + (unsigned long) (unsigned char) record[9]; + when = (when << 8) + (unsigned long) (unsigned char) record[8]; + + if (record[0] == AF_INET) { + byte_copy(&ip4,4,record + 16); + buffer_put(buffer_1,tmp,ip4_fmt(tmp,ip4)); + } else { + byte_copy(&ip6,16,record + 16); + buffer_put(buffer_1,tmp,ip6_fmt(tmp,ip6)); + } + buffer_puts(buffer_1," timed out "); + buffer_put(buffer_1,tmp,fmt_ulong(tmp,(unsigned long) (start - when))); + buffer_puts(buffer_1," seconds ago; # recent timeouts: "); + buffer_put(buffer_1,tmp,fmt_ulong(tmp,(unsigned long) (unsigned char) record[4])); + buffer_puts(buffer_1,"\n"); + } + record += 32; + } + + die(0); +} diff --git a/sqmail-4.3.07/src/qmail-todo.c b/sqmail-4.3.07/src/qmail-todo.c new file mode 100644 index 0000000..6b6e1d4 --- /dev/null +++ b/sqmail-4.3.07/src/qmail-todo.c @@ -0,0 +1,642 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "alloc.h" +#include "auto_qmail.h" +#include "byte.h" +#include "constmap.h" +#include "control.h" +#include "direntry.h" +#include "error.h" +#include "exit.h" +#include "fmt.h" +#include "fmtqfn.h" +#include "getln.h" +#include "open.h" +#include "ndelay.h" +#include "now.h" +#include "readsubdir.h" +#include "buffer.h" +#include "scan.h" +#include "select.h" +#include "str.h" +#include "sig.h" +#include "stralloc.h" +#include "trigger.h" +#include "qsutil.h" +#include "sendtodo.h" +#include "qmail.h" + +stralloc percenthack = {0}; +struct constmap mappercenthack; +stralloc locals = {0}; +struct constmap maplocals; +stralloc vdoms = {0}; +struct constmap mapvdoms; +stralloc envnoathost = {0}; + +char strnum[FMT_ULONG]; + +/* XXX not good, if qmail-send.c changes this has to be updated */ +#define CHANNELS 2 +char *chanaddr[CHANNELS] = { "local/", "remote/" }; + +datetime_sec recent; +int flagquitasap = 0; + +void sendlog1(char *x); +void sendlog3(char *x,char *y,char *z); + +void sigterm(void) +{ + if (flagquitasap == 0) + sendlog1("status: qmail-todo stop processing asap\n"); + flagquitasap = 1; +} + +int flagreadasap = 0; void sighup(void) { flagreadasap = 1; } +int flagsendalive = 1; void senddied(void) { flagsendalive = 0; } + +void cleandied() +{ + sendlog1("alert: qmail-todo lost connection to qmail-clean ... exiting\n"); + flagquitasap = 1; +} + + +/* this file is not so long ------------------------------------- FILENAMES */ + +stralloc fn = {0}; + +void fnmake_init(void) +{ + while (!stralloc_ready(&fn,FMTQFN)) nomem(); +} + +void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } +void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,1); } +void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } +void fnmake_chanaddr(unsigned long id,int c) { fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } + + +/* this file is not so long ------------------------------------- REWRITING */ + +stralloc rwline = {0}; + +/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */ +/* may trash recip. must set up rwline, between a T and a \0. */ + +int rewrite(char *recip) +{ + int i; + int j; + char *x; + static stralloc addr = {0}; + int at; + + if (!stralloc_copys(&rwline,"T")) return 0; + if (!stralloc_copys(&addr,recip)) return 0; + + i = byte_rchr(addr.s,addr.len,'@'); + if (i == addr.len) { + if (!stralloc_cats(&addr,"@")) return 0; + if (!stralloc_cat(&addr,&envnoathost)) return 0; + } + + while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { + j = byte_rchr(addr.s,i,'%'); + if (j == i) break; + addr.len = i; + i = j; + addr.s[i] = '@'; + } + + at = byte_rchr(addr.s,addr.len,'@'); + + if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + for (i = 0; i <= addr.len; ++i) + if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) + if ((x = constmap(&mapvdoms,addr.s + i,addr.len - i))) { + if (!*x) break; + if (!stralloc_cats(&rwline,x)) return 0; + if (!stralloc_cats(&rwline,"-")) return 0; + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 2; +} + +/* this file is not so long --------------------------------- COMMUNICATION */ + +buffer toqc; char toqcbuf[1024]; +buffer fromqc; char fromqcbuf[1024]; +stralloc comm_buf = {0}; +int comm_pos; +int fdout = -1; +int fdin = -1; + +void sendlog1(char* x) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto FAIL; + if (!stralloc_cats(&comm_buf,x)) goto FAIL; + if (!stralloc_0(&comm_buf)) goto FAIL; + return; + + FAIL: + /* either all or nothing */ + comm_buf.len = pos; +} + +void sendlog3(char* x, char *y, char *z) +{ + int pos; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"L")) goto FAIL; + if (!stralloc_cats(&comm_buf,x)) goto FAIL; + if (!stralloc_cats(&comm_buf,y)) goto FAIL; + if (!stralloc_cats(&comm_buf,z)) goto FAIL; + if (!stralloc_0(&comm_buf)) goto FAIL; + return; + + FAIL: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_init(void) +{ + buffer_init(&toqc,write,2,toqcbuf,sizeof(toqcbuf)); + buffer_init(&fromqc,read,3,fromqcbuf,sizeof(fromqcbuf)); + + fdout = 1; /* stdout */ + fdin = 0; /* stdin */ + if (ndelay_on(fdout) == -1) + /* this is so stupid: NDELAY semantics should be default on write */ + senddied(); /* drastic, but better than risking deadlock */ + + while (!stralloc_ready(&comm_buf,1024)) nomem(); +} + +int comm_canwrite(void) +{ + /* XXX: could allow a bigger buffer; say 10 recipients */ + /* XXX: returns true if there is something in the buffer */ + if (!flagsendalive) return 0; + if (comm_buf.s && comm_buf.len) return 1; + return 0; +} + +void comm_write(unsigned long id, int local, int remote) +{ + int pos; + char *s; + + if (local && remote) s="B"; + else if (local) s="L"; + else if (remote) s="R"; + else s="X"; + + pos = comm_buf.len; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,"D")) goto FAIL; + if (!stralloc_cats(&comm_buf,s)) goto FAIL; + if (!stralloc_cats(&comm_buf,strnum)) goto FAIL; + if (!stralloc_0(&comm_buf)) goto FAIL; + return; + + FAIL: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid) +{ + int pos; + int i; + + pos = comm_buf.len; + if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto FAIL; + strnum[fmt_ulong(strnum,id)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto FAIL; + if (!stralloc_cats(&comm_buf,": bytes ")) goto FAIL; + strnum[fmt_ulong(strnum,size)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto FAIL; + if (!stralloc_cats(&comm_buf," from <")) goto FAIL; + i = comm_buf.len; + if (!stralloc_cats(&comm_buf,from)) goto FAIL; + + for (; i < comm_buf.len; ++i) + if (comm_buf.s[i] == '\n') + comm_buf.s[i] = '/'; + else + if (!issafe(comm_buf.s[i])) + comm_buf.s[i] = '_'; + + if (!stralloc_cats(&comm_buf,"> qp ")) goto FAIL; + strnum[fmt_ulong(strnum,pid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto FAIL; + if (!stralloc_cats(&comm_buf," uid ")) goto FAIL; + strnum[fmt_ulong(strnum,uid)] = 0; + if (!stralloc_cats(&comm_buf,strnum)) goto FAIL; + if (!stralloc_cats(&comm_buf,"\n")) goto FAIL; + if (!stralloc_0(&comm_buf)) goto FAIL; + return; + + FAIL: + /* either all or nothing */ + comm_buf.len = pos; +} + +void comm_exit(void) +{ + /* if it FAILs exit, we have already stoped */ + if (!stralloc_cats(&comm_buf,"X")) _exit(1); + if (!stralloc_0(&comm_buf)) _exit(1); +} + +void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds) +{ + if (flagsendalive) { + if (flagquitasap && comm_canwrite() == 0) + comm_exit(); + if (comm_canwrite()) { + FD_SET(fdout,wfds); + if (*nfds <= fdout) + *nfds = fdout + 1; + } + FD_SET(fdin,rfds); + if (*nfds <= fdin) + *nfds = fdin + 1; + } +} + +void comm_do(fd_set *wfds, fd_set *rfds) +{ + /* first write then read */ + if (flagsendalive) + if (comm_canwrite()) + if (FD_ISSET(fdout,wfds)) { + int w; + int len; + len = comm_buf.len; + w = write(fdout,comm_buf.s + comm_pos,len - comm_pos); + if (w <= 0) { + if ((w == -1) && (errno == EPIPE)) + senddied(); + } else { + comm_pos += w; + if (comm_pos == len) { + comm_buf.len = 0; + comm_pos = 0; + } + } + } + if (flagsendalive) + if (FD_ISSET(fdin,rfds)) { + /* there are only two messages 'H' and 'X' */ + char c; + int r; + r = read(fdin, &c, 1); + if (r <= 0) { + if ((r == -1) && (errno != EINTR)) + senddied(); + } else { + switch (c) { + case 'H': + sighup(); + break; + case 'X': + sigterm(); + break; + default: + sendlog1("warning: qmail-todo: qmail-send speaks an obscure dialect\n"); + break; + } + } + } +} + +/* this file is not so long ------------------------------------------ TODO */ + +datetime_sec nexttodorun; +int flagtododir; /* if 0, have to opendir again */ +readsubdir todosubdir; +stralloc todoline = {0}; +char todobuf[BUFSIZE_MESS]; +char todobufinfo[BUFSIZE_MESS]; +char todobufchan[CHANNELS][1024]; + +void todo_init(void) +{ + flagtododir = 0; + nexttodorun = now(); + trigger_set(); +} + +void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup) +{ + if (flagquitasap) return; + trigger_selprep(nfds,rfds); + if (flagtododir) *wakeup = 0; + if (*wakeup > nexttodorun) *wakeup = nexttodorun; +} + +void todo_do(fd_set *rfds) +{ + struct stat st; + buffer bi; + int fd; + buffer bo; + int fdnumber; + buffer bchan[CHANNELS]; + int fdchan[CHANNELS]; + int flagchan[CHANNELS]; + char ch; + int match; + unsigned long id; + int c; + unsigned long uid; + unsigned long pid; + + fd = -1; + fdnumber = -1; + for (c = 0; c < CHANNELS; ++c) + fdchan[c] = -1; + + if (flagquitasap) return; + + if (!flagtododir) { + if (!trigger_pulled(rfds)) { + if (recent < nexttodorun) return; + } + trigger_set(); + readsubdir_init(&todosubdir,"todo",pausedir); + flagtododir = 1; + nexttodorun = recent + SLEEP_TODO; + } + + switch (readsubdir_next(&todosubdir,&id)) { + case 1: break; + case 0: flagtododir = 0; + default: return; + } + + fnmake_todo(id); + + fd = open_read(fn.s); + if (fd == -1) { sendlog3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; } + + fnmake_mess(id); + /* just for the statistics */ + if (stat(fn.s,&st) == -1) + { sendlog3("warning: qmail-todo: unable to stat ",fn.s," for mess\n"); goto FAIL; } + + for (c = 0; c < CHANNELS; ++c) { + fnmake_chanaddr(id,c); + if (unlink(fn.s) == -1) if (errno != ENOENT) + { sendlog3("warning: qmail-todo: unable to unlink ",fn.s," for mess\n"); goto FAIL; } + } + + fnmake_info(id); + if (unlink(fn.s) == -1) if (errno != ENOENT) + { sendlog3("warning: qmail-todo: unable to unlink ",fn.s," for info\n"); goto FAIL; } + + fdnumber = open_excl(fn.s); + if (fdnumber == -1) + { sendlog3("warning: qmail-todo: unable to create ",fn.s," for info\n"); goto FAIL; } + + strnum[fmt_ulong(strnum,id)] = 0; + sendlog3("new msg ",strnum,"\n"); + + for (c = 0; c < CHANNELS; ++c) + flagchan[c] = 0; + + buffer_init(&bi,read,fd,todobuf,sizeof(todobuf)); + buffer_init(&bo,write,fdnumber,todobufinfo,sizeof(todobufinfo)); + + uid = 0; + pid = 0; + + for (;;) { + if (getln(&bi,&todoline,&match,'\0') == -1) { + /* perhaps we're out of memory, perhaps an I/O error */ + fnmake_todo(id); + sendlog3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto FAIL; + } + if (!match) break; + + switch (todoline.s[0]) { + case 'u': + scan_ulong(todoline.s + 1,&uid); break; + case 'p': + scan_ulong(todoline.s + 1,&pid); break; + case 'F': + if (buffer_putflush(&bo,todoline.s,todoline.len) == -1) { + fnmake_info(id); + sendlog3("warning: qmail-todo: trouble writing to ",fn.s," for todo\n"); goto FAIL; + } + comm_info(id,(unsigned long) st.st_size,todoline.s + 1,pid,uid); + break; + case 'T': + switch (rewrite(todoline.s + 1)) { + case 0: nomem(); goto FAIL; + case 2: c = 1; break; + default: c = 0; break; + } + if (fdchan[c] == -1) { + fnmake_chanaddr(id,c); + fdchan[c] = open_excl(fn.s); + if (fdchan[c] == -1) + { sendlog3("warning: qmail-todo: unable to create ",fn.s," for delivery\n"); goto FAIL; } + buffer_init(&bchan[c],write,fdchan[c],todobufchan[c],sizeof(todobufchan[c])); + flagchan[c] = 1; + } + if (buffer_put(&bchan[c],rwline.s,rwline.len) == -1) { + fnmake_chanaddr(id,c); + sendlog3("warning: qmail-todo: trouble writing to ",fn.s," for delivery\n"); goto FAIL; + } + break; + default: + fnmake_todo(id); + sendlog3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto FAIL; + } + } + + close(fd); fd = -1; + + fnmake_info(id); + if (buffer_flush(&bo) == -1) + { sendlog3("warning: qmail-todo: trouble writing to ",fn.s," for info\n"); goto FAIL; } + if (fsync(fdnumber) == -1) + { sendlog3("warning: qmail-todo: trouble fsyncing ",fn.s," for info\n"); goto FAIL; } + close(fdnumber); fdnumber = -1; + + for (c = 0; c < CHANNELS; ++c) + if (fdchan[c] != -1) { + fnmake_chanaddr(id,c); + if (buffer_flush(&bchan[c]) == -1) { sendlog3("warning: qmail-todo: trouble writing to ",fn.s," in channel\n"); goto FAIL; } + if (fsync(fdchan[c]) == -1) { sendlog3("warning: qmail-todo: trouble fsyncing ",fn.s," in channel\n"); goto FAIL; } + close(fdchan[c]); fdchan[c] = -1; + } + + fnmake_todo(id); + if (buffer_putflush(&toqc,fn.s,fn.len) == -1) { cleandied(); return; } + if (buffer_get(&fromqc,&ch,1) != 1) { cleandied(); return; } + + if (ch != '+') { + sendlog3("warning: qmail-clean unable to clean up ",fn.s,"\n"); + return; + } + + comm_write(id,flagchan[0],flagchan[1]); + return; + + FAIL: + if (fd != -1) close(fd); + if (fdnumber != -1) close(fdnumber); + for (c = 0; c < CHANNELS; ++c) + if (fdchan[c] != -1) close(fdchan[c]); +} + +/* this file is too long ---------------------------------------------- MAIN */ + +int getcontrols(void) +{ + if (control_init() == -1) return 0; + if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; + if (control_readfile(&locals,"control/locals",1) != 1) return 0; + if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; + switch (control_readfile(&percenthack,"control/percenthack",0)) { + case -1: return 0; + case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; + case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; + } + switch (control_readfile(&vdoms,"control/virtualdomains",0)) { + case -1: return 0; + case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; + } + return 1; +} + +stralloc newlocals = {0}; +stralloc newvdoms = {0}; + +void regetcontrols(void) +{ + int r; + + if (control_readfile(&newlocals,"control/locals",1) != 1) + { sendlog1("alert: qmail-todo: unable to reread control/locals\n"); return; } + r = control_readfile(&newvdoms,"control/virtualdomains",0); + if (r == -1) + { sendlog1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; } + + constmap_free(&maplocals); + constmap_free(&mapvdoms); + + while (!stralloc_copy(&locals,&newlocals)) nomem(); + while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); + + if (r) { + while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); + while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); + } + else + while (!constmap_init(&mapvdoms,"",0,1)) nomem(); +} + +void reread(void) +{ + if (chdir(auto_qmail) == -1) { + sendlog1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n"); + return; + } + + regetcontrols(); + while (chdir("queue") == -1) { + sendlog1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n"); + sleep(10); + } +} + +int main() +{ + datetime_sec wakeup; + fd_set rfds; + fd_set wfds; + int nfds; + struct timeval tv; + int r; + char c; + + if (chdir(auto_qmail) == -1) + { sendlog1("alert: qmail-todo: cannot start: unable to switch to home directory\n"); _exit(110); } + if (!getcontrols()) + { sendlog1("alert: qmail-todo: cannot start: unable to read controls\n"); _exit(112); } + if (chdir("queue") == -1) + { sendlog1("alert: qmail-todo: cannot start: unable to switch to queue directory\n"); _exit(110); } + sig_pipeignore(); + umask(077); + + fnmake_init(); + todo_init(); + comm_init(); + + do { + r = read(fdin, &c, 1); + if ((r == -1) && (errno != EINTR)) + _exit(100); /* read failed probably qmail-send died */ + } while ((r =! 1)); /* we assume it is a 'S' */ + + for (;;) { + recent = now(); + if (flagreadasap) { flagreadasap = 0; reread(); } + if (!flagsendalive) { + /* qmail-send finaly exited, so do the same. */ + if (flagquitasap) _exit(0); + /* qmail-send died. We can not log and we can not work therefor _exit(1). */ + _exit(1); + } + + wakeup = recent + SLEEP_FOREVER; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + nfds = 1; + + todo_selprep(&nfds,&rfds,&wakeup); + comm_selprep(&nfds,&wfds,&rfds); + + if (wakeup <= recent) tv.tv_sec = 0; + else tv.tv_sec = wakeup - recent + SLEEP_FUZZ; + tv.tv_usec = 0; + + if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1) + if (errno == EINTR) + ; + else + sendlog1("warning: qmail-todo: trouble in select\n"); + else { + recent = now(); + + todo_do(&rfds); + comm_do(&wfds, &rfds); + } + } + /* NOTREACHED */ + _exit(1); +} + diff --git a/sqmail-4.3.07/src/qmail-upq.sh b/sqmail-4.3.07/src/qmail-upq.sh new file mode 100755 index 0000000..f0c5dfc --- /dev/null +++ b/sqmail-4.3.07/src/qmail-upq.sh @@ -0,0 +1,14 @@ +cd QMAIL +cd queue +for dir in mess info local remote todo +do + ( cd $dir; find . -type f -print ) | ( + cd $dir + while read path + do + id=`basename "$path"` + sub=`expr "$id" % SPLIT` + mv "$path" "$sub"/"$id" + done + ) +done diff --git a/sqmail-4.3.07/src/qmail-vmailuser.c b/sqmail-4.3.07/src/qmail-vmailuser.c new file mode 100644 index 0000000..a65135f --- /dev/null +++ b/sqmail-4.3.07/src/qmail-vmailuser.c @@ -0,0 +1,148 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "global.h" +#include "qmail.h" +#include "auto_qmail.h" +#include "stralloc.h" +#include "case.h" +#include "control.h" +#include "constmap.h" +#include "direntry.h" +#include "error.h" +#include "str.h" +#include "fmt.h" +#include "open.h" +#include "byte.h" +#include "scan.h" +#include "str.h" + +#define FDAUTH 3 +#define RESPECT_CASE "-C" + +/** @file qmail-vmailuser.c + @return 0: virtual user exists + 1: virtual user dir not accessible + 2: qmail-vmailuser is misused + 110: can't read controls + 111: temporary problem +*/ + +char inputbuf[BUFSIZE_AUTH]; +struct constmap mapvdoms; +stralloc vdoms = {0}; +stralloc vdomdir = {0}; +stralloc vuser = {0}; +stralloc vuserdir = {0}; + +void pam_exit(int fail) +{ + int i; + + close(FDAUTH); + for (i = 0; i < sizeof(inputbuf); ++i) inputbuf[i] = 0; + _exit(fail); +} + +int main(int argc,char **argv) +{ + DIR *dir; + char *vdomuser; + char *domain = 0; + int buflen = 0; + int domlen = 0; + int flagrespect = 0; + int i, r; + char ch; + char *homedir = "/home"; + + if (argv[1]) + if (!case_diffs(argv[1],RESPECT_CASE)) { + flagrespect = 1; + } else { + homedir = argv[1]; + dir = opendir(homedir); + if (!dir) pam_exit(2); + } + + if (argv[2]) + if (!case_diffs(argv[2],RESPECT_CASE)) flagrespect = 1; + + if (chdir(auto_qmail) == -1) pam_exit(110); + + switch (control_readfile(&vdoms,"control/virtualdomains",0)) { + case -1: pam_exit(110); + case 0: if (!constmap_init(&mapvdoms,"",0,1)) pam_exit(111); + case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) pam_exit(111); + } + + for (;;) { /* read input */ + do + r = read(FDAUTH,inputbuf + buflen,sizeof(inputbuf) - buflen); + while ((r == -1) && (errno == EINTR)); + if (r == -1) pam_exit(111); + if (r == 0) break; + buflen += r; + if (buflen >= sizeof(inputbuf)) pam_exit(2); + } + close(FDAUTH); + + if ((r = byte_rchr(inputbuf,buflen,'@'))) /* @domain */ + if (r < buflen && inputbuf[r] == '@') { + domain = inputbuf + r + 1; + domlen = str_len(domain); + if (!flagrespect) + case_lowerb(inputbuf,buflen); + else + case_lowerb(domain,domlen); + } + vdomuser = constmap(&mapvdoms,domain,domlen); + if (!vdomuser) pam_exit(1); + + if (!stralloc_copys(&vuser,"")) pam_exit(111); /* user */ + for (i = 0; i < r; ++i) { + ch = inputbuf[i]; + if (ch == '.') ch = ':'; + if (!stralloc_append(&vuser,&ch)) pam_exit(111); + } + if (!stralloc_0(&vuser)) pam_exit(111); + + if (!stralloc_copys(&vdomdir,homedir)) pam_exit(111); /* vpopmail */ + if (!stralloc_cats(&vdomdir,"/")) pam_exit(111); + if (!stralloc_cats(&vdomdir,"vpopmail")) pam_exit(111); + if (!stralloc_copy(&vuserdir,&vdomdir)) pam_exit(111); + if (!stralloc_cats(&vuserdir,"/domains/")) pam_exit(111); + if (!stralloc_cats(&vuserdir,vdomuser)) pam_exit(111); + if (!stralloc_copy(&vdomdir,&vuserdir)) pam_exit(111); + if (!stralloc_0(&vdomdir)) pam_exit(111); + + dir = opendir(vdomdir.s); + if (dir) { + if (!stralloc_cats(&vuserdir,"/")) pam_exit(111); + if (!stralloc_cat(&vuserdir,&vuser)) pam_exit(111); + if (!stralloc_0(&vuserdir)) pam_exit(111); + + dir = opendir(vuserdir.s); + if (dir) pam_exit(0); + } + + if (!stralloc_copys(&vdomdir,homedir)) pam_exit(111); /* vmailmgr */ + if (!stralloc_cats(&vdomdir,"/")) pam_exit(111); + if (!stralloc_copy(&vuserdir,&vdomdir)) pam_exit(111); + if (!stralloc_cats(&vuserdir,vdomuser)) pam_exit(111); + if (!stralloc_cats(&vuserdir,"/users")) pam_exit(111); + if (!stralloc_copy(&vdomdir,&vuserdir)) pam_exit(111); + if (!stralloc_0(&vdomdir)) pam_exit(111); + + dir = opendir(vdomdir.s); + if (dir) { + if (!stralloc_cats(&vuserdir,"/")) pam_exit(111); + if (!stralloc_cat(&vuserdir,&vuser)) pam_exit(111); + if (!stralloc_0(&vuserdir)) pam_exit(111); + + dir = opendir(vuserdir.s); + if (dir) pam_exit(0); + } + + pam_exit(1); +} diff --git a/sqmail-4.3.07/src/qmail.c b/sqmail-4.3.07/src/qmail.c new file mode 100644 index 0000000..7bdfd29 --- /dev/null +++ b/sqmail-4.3.07/src/qmail.c @@ -0,0 +1,139 @@ +#include <unistd.h> +#include "readwrite.h" +#include "buffer.h" +#include "wait.h" +#include "exit.h" +#include "fd.h" +#include "qmail.h" +#include "auto_qmail.h" +#include "env.h" + +static char *binqqargs[2] = { 0, 0 } ; + +static void setup_qqargs() +{ + if (!binqqargs[0]) + binqqargs[0] = env_get("QMAILQUEUE"); + if (!binqqargs[0]) + binqqargs[0] = "bin/qmail-queue"; +} + +int qmail_open(struct qmail *qq) +{ + int pim[2]; + int pie[2]; + + setup_qqargs(); + + if (pipe(pim) == -1) return -1; + if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; } + + switch (qq->pid = vfork()) { + case -1: + close(pim[0]); close(pim[1]); + close(pie[0]); close(pie[1]); + return -1; + case 0: + close(pim[1]); + close(pie[1]); + if (fd_move(0,pim[0]) == -1) _exit(120); + if (fd_move(1,pie[0]) == -1) _exit(120); + if (chdir(auto_qmail) == -1) _exit(61); + execv(*binqqargs,binqqargs); + _exit(120); + } + + qq->fdm = pim[1]; close(pim[0]); + qq->fde = pie[1]; close(pie[0]); + buffer_init(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf)); + qq->flagerr = 0; + return 0; +} + +unsigned long qmail_qp(struct qmail *qq) +{ + return qq->pid; +} + +void qmail_fail(struct qmail *qq) +{ + qq->flagerr = 1; +} + +void qmail_put(struct qmail *qq,char *s,int len) +{ + if (!qq->flagerr) if (buffer_put(&qq->ss,s,len) == -1) qq->flagerr = 1; +} + +void qmail_puts(struct qmail *qq,char *s) +{ + if (!qq->flagerr) if (buffer_puts(&qq->ss,s) == -1) qq->flagerr = 1; +} + +void qmail_from(struct qmail *qq,char *s) +{ + if (buffer_flush(&qq->ss) == -1) qq->flagerr = 1; + close(qq->fdm); + buffer_init(&qq->ss,write,qq->fde,qq->buf,sizeof(qq->buf)); + qmail_put(qq,"F",1); + qmail_puts(qq,s); + qmail_put(qq,"",1); +} + +void qmail_to(struct qmail *qq,char *s) +{ + qmail_put(qq,"T",1); + qmail_puts(qq,s); + qmail_put(qq,"",1); +} + +char *qmail_close(struct qmail *qq) +{ + int wstat; + int exitcode; + + qmail_put(qq,"",1); + if (!qq->flagerr) + if (buffer_flush(&qq->ss) == -1) qq->flagerr = 1; + close(qq->fde); + + if (wait_pid(&wstat,qq->pid) != qq->pid) + return "Zqq waitpid surprise (#4.3.0)"; + if (wait_crashed(wstat)) + return "Zqq crashed (#4.3.0)"; + exitcode = wait_exitcode(wstat); + + switch (exitcode) { + case 0: if (!qq->flagerr) return ""; /* fall through */ + case 11: return "Denvelope address too long for qq (#5.1.3)"; + case 31: return "Dmail server permanently rejected message (#5.3.0)"; + case 32: return "Vmail server does not accept message (#5.3.0)"; + case 33: return "Smail server does not accept message (#5.3.0)"; + case 34: return "Amail server does not accept message (#5.3.0)"; + case 35: return "Imail server fails to verify DKIM signed message (#5.3.0)"; + case 51: return "Zqq out of memory (#4.3.0)"; + case 52: return "Zqq timeout (#4.3.0)"; + case 53: return "Zqq write error or disk full (#4.3.0)"; + case 54: return "Zqq read error (#4.3.0)"; + case 55: return "Zqq unable to read configuration (#4.3.0)"; + case 56: return "Zqq trouble making network connection (#4.3.0)"; + case 61: return "Zqq trouble in home directory (#4.3.0)"; + case 62: return "Zqq trouble creating files in queue (#4.3.0)"; + case 63: /* qmail-queue: fstat/unlinking problem */ + case 64: /* qmail-queue: linking pidfn -> messfn */ + case 65: /* qmail-queue: exclusive open failed */ + case 66: /* qmail-queue: linking intdfn -> todofn */ + case 71: return "Zmail server temporarily rejected message (#4.3.0)"; + case 72: return "Zconnection to mail server timed out (#4.4.1)"; + case 73: return "Zconnection to mail server rejected (#4.4.1)"; + case 74: return "Zcommunication with mail server failed (#4.4.2)"; + case 91: /* fall through */ + case 81: return "Zqq internal bug (#4.3.0)"; + case 115: /* compatibility */ + case 120: return "Zunable to exec qq (#4.3.0)"; + default: + if ((exitcode >= 11) && (exitcode <= 40)) + return "Dqq permanent problem (#5.3.0)"; + return "Zqq temporary problem (#4.3.0)"; + } +} diff --git a/sqmail-4.3.07/src/qreceipt.c b/sqmail-4.3.07/src/qreceipt.c new file mode 100644 index 0000000..8dacf40 --- /dev/null +++ b/sqmail-4.3.07/src/qreceipt.c @@ -0,0 +1,130 @@ +#include <unistd.h> +#include "sig.h" +#include "env.h" +#include "error.h" +#include "buffer.h" +#include "stralloc.h" +#include "getln.h" +#include "alloc.h" +#include "str.h" +#include "hfield.h" +#include "token822.h" +#include "headerbody.h" +#include "exit.h" +#include "open.h" +#include "quote.h" +#include "qmail.h" + +#define WHO "qreceipt" + +void die_noreceipt() { _exit(0); } +void die() { _exit(100); } +void die_temp() { _exit(111); } +void die_nomem() { + buffer_putsflush(buffer_2,"qreceipt: fatal: out of memory\n"); die_temp(); } +void die_fork() { + buffer_putsflush(buffer_2,"qreceipt: fatal: unable to fork\n"); die_temp(); } +void die_qqperm() { + buffer_putsflush(buffer_2,"qreceipt: fatal: permanent qmail-queue error\n"); die(); } +void die_qqtemp() { + buffer_putsflush(buffer_2,"qreceipt: fatal: temporary qmail-queue error\n"); die_temp(); } +void die_usage() { + buffer_putsflush(buffer_2, + "qreceipt: usage: qreceipt deliveryaddress\n"); die(); } +void die_read() { + if (errno == ENOMEM) die_nomem(); + buffer_putsflush(buffer_2,"qreceipt: fatal: read error\n"); die_temp(); } +void doordie(sa,r) stralloc *sa; int r; { + if (r == 1) return; if (r == -1) die_nomem(); + buffer_putsflush(buffer_2,"qreceipt: fatal: unable to parse this: "); + buffer_putflush(buffer_2,sa->s,sa->len); die(); } + +char *target; + +int flagreceipt = 0; + +char *returnpath; +stralloc messageid = {0}; +stralloc sanotice = {0}; + +int rwnotice(token822_alloc *addr) +{ + token822_reverse(addr); + if (token822_unquote(&sanotice,addr) != 1) die_nomem(); + if (sanotice.len == str_len(target)) + if (!str_diffn(sanotice.s,target,sanotice.len)) + flagreceipt = 1; + token822_reverse(addr); + return 1; +} + +struct qmail qqt; + +stralloc quoted = {0}; + +void finishheader() +{ + char *qqx; + + if (!flagreceipt) die_noreceipt(); + if (str_equal(returnpath,"")) die_noreceipt(); + if (str_equal(returnpath,"#@[]")) die_noreceipt(); + + if (!quote2("ed,returnpath)) die_nomem(); + + if (qmail_open(&qqt) == -1) die_fork(); + + qmail_puts(&qqt,"From: DELIVERY NOTICE SYSTEM <"); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,">\n"); + qmail_puts(&qqt,"To: <"); + qmail_put(&qqt,quoted.s,quoted.len); + qmail_puts(&qqt,">\n"); + qmail_puts(&qqt,"Subject: success notice\n\ +\n\ +Hi! This is the qreceipt program. Your message was delivered to the\n\ +following address: "); + qmail_puts(&qqt,target); + qmail_puts(&qqt,". Thanks for asking.\n"); + if (messageid.s) { + qmail_puts(&qqt,"Your "); + qmail_put(&qqt,messageid.s,messageid.len); + } + + qmail_from(&qqt,""); + qmail_to(&qqt,returnpath); + qqx = qmail_close(&qqt); + + if (*qqx) + if (*qqx == 'D') die_qqperm(); + else die_qqtemp(); +} + +stralloc hfbuf = {0}; +token822_alloc hfin = {0}; +token822_alloc hfrewrite = {0}; +token822_alloc hfaddr = {0}; + +void doheaderfield(stralloc *h) +{ + switch (hfield_known(h->s,h->len)) { + case H_MESSAGEID: + if (!stralloc_copy(&messageid,h)) die_nomem(); + break; + case H_NOTICEREQUESTEDUPONDELIVERYTO: + doordie(h,token822_parse(&hfin,h,&hfbuf)); + doordie(h,token822_addrlist(&hfrewrite,&hfaddr,&hfin,rwnotice)); + break; + } +} + +void dobody(stralloc *h) { ; } + +int main(int argc, char **argv) +{ + sig_pipeignore(); + if (!(target = argv[1])) die_usage(); + if (!(returnpath = env_get("SENDER"))) die_usage(); + if (headerbody(buffer_0,doheaderfield,finishheader,dobody) == -1) die_read(); + die_noreceipt(); +} diff --git a/sqmail-4.3.07/src/qsutil.c b/sqmail-4.3.07/src/qsutil.c new file mode 100644 index 0000000..9c438ea --- /dev/null +++ b/sqmail-4.3.07/src/qsutil.c @@ -0,0 +1,85 @@ +#include <unistd.h> +#include "stralloc.h" +#include "buffer.h" +#include "qsutil.h" + +static stralloc foo = {0}; + +static char errbuf[1]; +static struct buffer be = BUFFER_INIT(write,0,errbuf,1); + + +void logsa(stralloc *sa) +{ + buffer_putflush(&be,sa->s,sa->len); +} + +void log1s(char *s1) +{ + buffer_putsflush(&be,s1); +} + +void log2s(char *s1,char *s2) +{ + buffer_putsflush(&be,s1); + buffer_putsflush(&be,s2); +} + +void log3s(char *s1,char *s2,char *s3) +{ + buffer_putsflush(&be,s1); + buffer_putsflush(&be,s2); + buffer_putsflush(&be,s3); +} + +void log4s(char *s1,char *s2,char *s3,char *s4) +{ + buffer_putsflush(&be,s1); + buffer_putsflush(&be,s2); + buffer_putsflush(&be,s3); + buffer_putsflush(&be,s4); +} + +void log5s(char *s1,char *s2,char *s3,char *s4,char *s5) +{ + buffer_putsflush(&be,s1); + buffer_putsflush(&be,s2); + buffer_putsflush(&be,s3); + buffer_putsflush(&be,s4); + buffer_putsflush(&be,s5); +} + +void nomem() +{ + log1s("alert: out of memory, sleeping...\n"); + sleep(10); +} + +void pausedir(char *dir) +{ + log3s("alert: unable to opendir ",dir,", sleeping...\n"); + sleep(10); +} + +int issafe(char ch) +{ + if (ch == '%') return 0; /* general principle: allman's code is crap */ + if (ch < 33) return 0; + if (ch > 126) return 0; + return 1; +} + +void logsafe(char *s) +{ + int i; + + while (!stralloc_copys(&foo,s)) nomem(); + for (i = 0; i < foo.len; ++i) + if (foo.s[i] == '\n') + foo.s[i] = '/'; + else + if (!issafe(foo.s[i])) + foo.s[i] = '_'; + + logsa(&foo); +} diff --git a/sqmail-4.3.07/src/quote.c b/sqmail-4.3.07/src/quote.c new file mode 100644 index 0000000..ef1bf45 --- /dev/null +++ b/sqmail-4.3.07/src/quote.c @@ -0,0 +1,81 @@ +#include "stralloc.h" +#include "str.h" +#include "quote.h" + +/* +quote() encodes a box as per rfc 821 and rfc 822, +while trying to do as little quoting as possible. +no, 821 and 822 don't have the same encoding. they're not even close. +no special encoding here for bytes above 127. +*/ + +static char ok[128] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +,0,7,0,7,7,7,7,7,0,0,7,7,0,7,7,7 ,7,7,7,7,7,7,7,7,7,7,0,0,0,7,0,7 +,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,0,0,0,7,7 +,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0 +} ; + +static int doit(stralloc *saout,stralloc *sain) +{ + char ch; + int i; + int j; + + if (!stralloc_ready(saout,sain->len * 2 + 2)) return 0; + j = 0; + saout->s[j++] = '"'; + + for (i = 0; i < sain->len; ++i) { + ch = sain->s[i]; + if ((ch == '\r') || (ch == '\n') || (ch == '"') || (ch == '\\')) + saout->s[j++] = '\\'; + saout->s[j++] = ch; + } + saout->s[j++] = '"'; + saout->len = j; + + return 1; +} + +int quote_need(char *s,unsigned int n) +{ + unsigned char uch; + int i; + if (!n) return 1; + + for (i = 0; i < n; ++i) { + uch = s[i]; + if (uch >= 128) return 1; + if (!ok[uch]) return 1; + } + if (s[0] == '.') return 1; + if (s[n - 1] == '.') return 1; + + for (i = 0; i < n - 1; ++i) + if (s[i] == '.') + if (s[i + 1] == '.') return 1; + + return 0; +} + +int quote(stralloc *saout,stralloc *sain) +{ + if (quote_need(sain->s,sain->len)) return doit(saout,sain); + return stralloc_copy(saout,sain); +} + +static stralloc foo = {0}; + +int quote2(stralloc *sa,char *s) +{ + int j; + if (!*s) return stralloc_copys(sa,s); + j = str_rchr(s,'@'); + if (!stralloc_copys(&foo,s)) return 0; + if (!s[j]) return quote(sa,&foo); + foo.len = j; + if (!quote(sa,&foo)) return 0; + + return stralloc_cats(sa,s + j); +} diff --git a/sqmail-4.3.07/src/rcpthosts.c b/sqmail-4.3.07/src/rcpthosts.c new file mode 100644 index 0000000..2242249 --- /dev/null +++ b/sqmail-4.3.07/src/rcpthosts.c @@ -0,0 +1,70 @@ +#include "cdbread.h" +#include "byte.h" +#include "open.h" +#include "error.h" +#include "exit.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "case.h" +#include "close.h" +#include "fd.h" +#include "rcpthosts.h" + +static int flagrh = 0; +static int flagmrh = 0; +static stralloc rh = {0}; +static struct constmap maprh; +static int fdmrh; + +static struct cdb cdb; + +int rcpthosts_init() +{ + flagrh = control_readfile(&rh,"control/rcpthosts",0); + if (flagrh != 1) return flagrh; + if (!constmap_init(&maprh,rh.s,rh.len,0)) return flagrh = -1; + fdmrh = open_read("control/morercpthosts.cdb"); + if (fdmrh == -1) if (errno != ENOENT) return flagmrh = -1; + if (fdmrh > 0) flagmrh = 1; + return 0; +} + +static stralloc host = {0}; + +int rcpthosts(char *buf, int len) +{ + int j; + int r; + + if (flagrh != 1) return 1; + + j = byte_rchr(buf,len,'@'); + if (j >= len) return 1; /* presumably envnoathost is acceptable */ + + ++j; buf += j; len -= j; + + if (!stralloc_copyb(&host,buf,len)) return -1; + buf = host.s; + case_lowerb(buf,len); + + for (j = 0; j < len; ++j) + if (!j || (buf[j] == '.')) + if (constmap(&maprh,buf + j,len - j)) return 1; + + if (flagmrh == 1) { + fdmrh = open_read("control/morercpthosts.cdb"); + if (fdmrh == -1) if (errno == ENOENT) return 0; + cdb_init(&cdb,fdmrh); + + for (j = 0; j < len ;++j) + if (!j || (buf[j] == '.')) { + r = cdb_find(&cdb,buf + j,len - j); + if (r) { cdb_free(&cdb); close(fdmrh); return r; } + } + cdb_free(&cdb); + close(fdmrh); + } + + return 0; +} diff --git a/sqmail-4.3.07/src/readsubdir.c b/sqmail-4.3.07/src/readsubdir.c new file mode 100644 index 0000000..754aa36 --- /dev/null +++ b/sqmail-4.3.07/src/readsubdir.c @@ -0,0 +1,44 @@ +#include "readsubdir.h" +#include "fmt.h" +#include "scan.h" +#include "str.h" +#include "auto_split.h" + +void readsubdir_init(readsubdir *rs, char *name, void (*pause)()) +{ + rs->name = name; + rs->pause = pause; + rs->dir = 0; + rs->pos = 0; +} + +static char namepos[FMT_ULONG + 4 + READSUBDIR_NAMELEN]; + +int readsubdir_next(readsubdir *rs, unsigned long *id) +{ + direntry *d; + unsigned int len; + + if (!rs->dir) { + if (rs->pos >= auto_split) return 0; + if (str_len(rs->name) > READSUBDIR_NAMELEN) { rs->pos++; return -1; } + len = 0; + len += fmt_str(namepos + len,rs->name); + namepos[len++] = '/'; + len += fmt_ulong(namepos + len,(unsigned long) rs->pos); + namepos[len] = 0; + while (!(rs->dir = opendir(namepos))) rs->pause(namepos); + rs->pos++; + return -1; + } + + d = readdir(rs->dir); + if (!d) { closedir(rs->dir); rs->dir = 0; return -1; } + + if (str_equal(d->d_name,".")) return -1; + if (str_equal(d->d_name,"..")) return -1; + len = scan_ulong(d->d_name,id); + if (!len || d->d_name[len]) return -2; + + return 1; +} diff --git a/sqmail-4.3.07/src/received.c b/sqmail-4.3.07/src/received.c new file mode 100644 index 0000000..51339a9 --- /dev/null +++ b/sqmail-4.3.07/src/received.c @@ -0,0 +1,172 @@ +#include "fmt.h" +#include "qmail.h" +#include "now.h" +#include "datetime.h" +#include "date822fmt.h" +#include "received.h" +#include "str.h" +#include "stralloc.h" +#include "byte.h" +#include "str.h" + +static int issafe(char ch) +{ + if (ch == ' ') return 1; /* accept empty spaces */ + if (ch == '.') return 1; + if (ch == '@') return 1; + if (ch == '%') return 1; + if (ch == '+') return 1; + if (ch == '/') return 1; + if (ch == '=') return 1; + if (ch == ':') return 1; + if (ch == '-') return 1; + if ((ch >= 'a') && (ch <= 'z')) return 1; + if ((ch >= 'A') && (ch <= 'Z')) return 1; + if ((ch >= '0') && (ch <= '9')) return 1; + return 0; +} + +void safeput(struct qmail *qqt,char *s) +{ + char ch; + while ((ch = *s++)) { + if (!issafe(ch)) ch = '?'; + qmail_put(qqt,&ch,1); + } +} + +static char buf[DATE822FMT]; + +/* "Received: from relay1.uu.net ([E]HELO uunet.uu.net) (user@192.48.96.5)" */ +/* " de/crypted with tls-version: cipher [used/perm] DN=dn" */ +/* " by silverton.berkeley.edu with [UTF8][E]SMTP[SA]; 26 Sep 1995 04:46:54 -0000" */ +/* "X-RBL-Info: http://www.spamhaus.org/query/bl?ip=127.0.0.2 */ + +void received(struct qmail *qqt,char *protocol,char *local,char *remoteip,char *remotehost,char *remoteinfo,char *helo,char *tlsinfo,char *rblinfo) +{ + struct datetime dt; + int i; + + qmail_puts(qqt,"Received: from "); + safeput(qqt,remotehost); + if (helo) { + qmail_puts(qqt," (HELO "); + safeput(qqt,helo); + qmail_puts(qqt,")"); + } + qmail_puts(qqt," ("); + if (remoteinfo) { + safeput(qqt,remoteinfo); + qmail_puts(qqt,"@"); + } + safeput(qqt,remoteip); + qmail_puts(qqt,")"); + + if (tlsinfo) { + qmail_puts(qqt,"\n de/crypted with "); + qmail_puts(qqt,tlsinfo); + } + qmail_puts(qqt,"\n by "); + safeput(qqt,local); + qmail_puts(qqt," with "); + qmail_puts(qqt,protocol); + qmail_puts(qqt,"; "); + datetime_tai(&dt,now()); + qmail_put(qqt,buf,date822fmt(buf,&dt)); + + if (rblinfo) { + i = str_chr(rblinfo,']'); + if (rblinfo[i] == ']') { + qmail_puts(qqt,"X-RBL-Info: "); + safeput(qqt,rblinfo + i + 2); + qmail_puts(qqt,"\n"); + } + } +} + +/* "Received-SPF: pass (Helogreeting: domain of Identity " */ +/* " designates Clientip as permitted sender) receiver=Hostname " */ +/* " client-ip=Clientip; envelope-from=Mailfrom; " */ + +void spfheader(struct qmail *qqt,char *spfinfo,char *local,char *remoteip,char *helohost,char *mailfrom) +{ + char *result = 0; + char *identity = 0; + char *clientip = 0; + char *helo = 0; + char *envelopefrom = 0; + char *receiver = 0; + char *problem = 0; + char *mechanism = 0; + int i, j = 0; + int len; + + len = str_len(spfinfo); + if (!len) return; + + for (i = 0; i < len; i++) + if (spfinfo[i] == ' ') spfinfo[i] = '\0'; + + for (i = 0; i < len; i++) { + if (spfinfo[i] == '\0') { + switch (spfinfo[i + 1]) { + case 'S': clientip = spfinfo + i + 3; break; + case 'O': envelopefrom = spfinfo + i + 3; break; + case 'C': identity = spfinfo + i + 3; break; + case 'H': helo = spfinfo + i + 3; break; + case 'T': receiver = spfinfo + i + 3; break; + case 'P': problem = spfinfo + i + 3; break; + case 'M': if ((j = str_chr(spfinfo + i,'='))) spfinfo[i + j] = '\0'; + mechanism = spfinfo + i + 1; break; + case 'R': result = spfinfo + i + 3; break; + default: break; + } + } + } + + if (!result || *result == 0) result = "o"; + if (!clientip || *clientip == 0) clientip = remoteip; + if (!helo || *helo == 0) helo = helohost; + if (!envelopefrom || *envelopefrom == 0) envelopefrom = mailfrom; + if (!receiver || *receiver == 0) receiver = local; + if (!problem || *problem == 0) problem = "unknown"; + if (!mechanism || *mechanism == 0) mechanism = "unknown"; + if (!identity || *identity == 0) { + j = str_rchr(envelopefrom,'@'); + if (envelopefrom[j] == '@') identity = envelopefrom + j + 1; + else identity = "unknown"; + } + + qmail_puts(qqt,"Received-SPF: "); + switch (*result) { + case '+': qmail_puts(qqt," pass ("); safeput(qqt,helo); + qmail_puts(qqt,": domain of "); safeput(qqt,identity); qmail_puts(qqt,"\n"); + qmail_puts(qqt," designates "); safeput(qqt,clientip); qmail_puts(qqt," as permitted sender)\n"); + qmail_puts(qqt," receiver="); safeput(qqt,receiver); + qmail_puts(qqt,"; client-ip="); safeput(qqt,clientip); qmail_puts(qqt,"\n"); + qmail_puts(qqt," envelope-from="); safeput(qqt,envelopefrom); qmail_puts(qqt,";\n"); break; + case '-': qmail_puts(qqt," fail ("); safeput(qqt,helo); + qmail_puts(qqt,": domain of "); safeput(qqt,identity); qmail_puts(qqt,"\n"); + qmail_puts(qqt," does not designate "); safeput(qqt,clientip); qmail_puts(qqt," as permitted sender)\n"); break; + case '~': qmail_puts(qqt," softfail ("); safeput(qqt,helo); + qmail_puts(qqt,": domain of transitioning "); safeput(qqt,identity); qmail_puts(qqt,"\n"); + qmail_puts(qqt," does not designate "); safeput(qqt,clientip); qmail_puts(qqt," as permitted sender)\n"); break; + case '?': qmail_puts(qqt," neutral ("); safeput(qqt,helo); qmail_puts(qqt,"; client-ip="); safeput(qqt,clientip); + qmail_puts(qqt," is neither permitted \n"); qmail_puts(qqt," nor denied by domain of "); safeput(qqt,identity), + qmail_puts(qqt,")\n"); break; + case 'o': qmail_puts(qqt," none ("); safeput(qqt,helo); + qmail_puts(qqt,": domain of "); safeput(qqt,identity); qmail_puts(qqt," does\n"); + qmail_puts(qqt," not designate permitted sender hosts)\n"); break; + case 't': qmail_puts(qqt," temperror ("); safeput(qqt,helo); + qmail_puts(qqt,": domain of "); safeput(qqt,identity); qmail_puts(qqt," evaluated\n"); + qmail_puts(qqt," with error: "); safeput(qqt,problem); qmail_puts(qqt," for mechanism: "); safeput(qqt,mechanism); + qmail_puts(qqt,")\n"); break; + case 'e': qmail_puts(qqt," permerror ("); safeput(qqt,helo); + qmail_puts(qqt,": domain of "); safeput(qqt,identity); qmail_puts(qqt," evaluated\n"); + qmail_puts(qqt," with error: "); safeput(qqt,problem); qmail_puts(qqt," for mechanism: "); safeput(qqt,mechanism); + qmail_puts(qqt,")\n"); break; + default: qmail_puts(qqt," unknown (results for "); safeput(qqt,helo); + qmail_puts(qqt,": domain of "); safeput(qqt,identity); + qmail_puts(qqt," follow an unknown mechanism: "); safeput(qqt,mechanism); qmail_puts(qqt,")\n"); break; + } +} diff --git a/sqmail-4.3.07/src/recipients.c b/sqmail-4.3.07/src/recipients.c new file mode 100644 index 0000000..955dbd0 --- /dev/null +++ b/sqmail-4.3.07/src/recipients.c @@ -0,0 +1,288 @@ +#include <unistd.h> +#include "cdbread.h" +#include "byte.h" +#include "open.h" +#include "control.h" +#include "constmap.h" +#include "stralloc.h" +#include "recipients.h" +#include "wait.h" +#include "str.h" +#include "fd.h" +#include "sig.h" +#include "case.h" +#include "buffer.h" +#include "auto_break.h" +#define FDAUTH 3 + +static stralloc key = {0}; +static stralloc domain = {0}; +static stralloc wildhost = {0}; +static stralloc address = {0}; +static stralloc rcptline = {0}; +static stralloc vkey = {0}; +static stralloc verp = {0}; +static stralloc user = {0}; +static stralloc ukey = {0}; +static int flagrcpts = 0; +static int fdrcps; +static struct cdb cdb; + +/** @file recipients.c + @brief functions recipients_init, recipients, recipients_parse, callapam + @param pointer to address, length of address + @return -3: problem with PAM + -2: out of memory + -1: error reading control file + 0: address not found; unsuccessful + 1: CDB lookup; successful + 2: PAM lookup; successful + 3: USERS lookup; successful + 4: Wildcarded domain; successful + 5: Pass-thru; neutral + 10: none existing control file; pass-thru +*/ + +int recipients_init() +{ + flagrcpts = control_readfile(&rcptline,"control/recipients",0); + if (flagrcpts != 1) return flagrcpts; + return 0; +} + +char rcptbuf[512]; +buffer br = BUFFER_INIT(safewrite,FDAUTH,rcptbuf,sizeof(rcptbuf)); + +int callapam(char *pam,char *addr) +{ + int i; + int j=0; + int wstat; + int pi[2]; + int child; + char ch; + static stralloc mailaddress = {0}; + + char *childargs[7] = {0, 0, 0, 0, 0, 0, 0}; + stralloc pamarg = {0}; + stralloc pamname = {0}; + stralloc pamarg1 = {0}; + stralloc pamarg2 = {0}; + stralloc pamarg3 = {0}; + stralloc pamarg4 = {0}; + stralloc pamarg5 = {0}; + + for (i = 0; (ch = pam[i]); i++) { + if (j < 6) { + if (ch != ' ') + if (!stralloc_append(&pamarg,&ch)) return -2; + if (ch == ' ' || ch == '\n' || i == str_len(pam) - 1) { + if (!stralloc_0(&pamarg)) return -2; + switch (j) { + case 0: + if (!stralloc_copy(&pamname,&pamarg)) return -2; + childargs[0] = pamname.s; + case 1: + if (!stralloc_copy(&pamarg1,&pamarg)) return -2; + childargs[1] = pamarg1.s; + case 2: + if (!stralloc_copy(&pamarg2,&pamarg)) return -2; + childargs[2] = pamarg2.s; + case 3: + if (!stralloc_copy(&pamarg3,&pamarg)) return -2; + childargs[3] = pamarg3.s; + case 4: + if (!stralloc_copy(&pamarg4,&pamarg)) return -2; + childargs[4] = pamarg4.s; + case 5: + if (!stralloc_copy(&pamarg5,&pamarg)) return -2; + childargs[5] = pamarg5.s; + } + j++; + if (!stralloc_copys(&pamarg,"")) return -2; + } + } + } + childargs[j] = 0; + + close(FDAUTH); + if (pipe(pi) == -1) return -3; + if (pi[0] != FDAUTH) return -3; + + switch (child = fork()) { + case -1: + return -3; + case 0: + close(pi[1]); + if (fd_copy(FDAUTH,pi[0]) == -1) return -3; + sig_pipedefault(); + execvp(childargs[0],childargs); + return 111; + } + close(pi[0]); + +/* checkpassword compliant form: address\0\0\0 */ + + if (!stralloc_copys(&mailaddress,addr)) return -2; + if (!stralloc_0(&mailaddress)) return -2; + if (!stralloc_0(&mailaddress)) return -2; + if (!stralloc_0(&mailaddress)) return -2; + + buffer_init(&br,write,pi[1],rcptbuf,sizeof(rcptbuf)); + if (buffer_put(&br,mailaddress.s,mailaddress.len) == -1) return -3; + if (buffer_flush(&br) == -1) return -3; + close(pi[1]); + + if (wait_pid(&wstat,child) == -1) return -3; + if (wait_crashed(wstat)) return -3; + return wait_exitcode(wstat); +} + +int recipients_parse(char *rhost,int rlen,char *addr,char *rkey,int klen,char *vaddr,char *vkey,int vlen,char *ukey,int ulen) +{ + int i; + int r; + int j = 0; + int k = 0; + int u = 0; + static stralloc line = {0}; + int seenhost = 0; + + if (!stralloc_copys(&line,"")) return -2; + if (!stralloc_copys(&wildhost,"!")) return -2; + if (!stralloc_cats(&wildhost,rhost)) return -2; + if (!stralloc_0(&wildhost)) return -2; + + for (i = 0; i < rcptline.len; ++i) { + if (!stralloc_append(&line,&rcptline.s[i])) return -2; + + if (rcptline.s[i] == '\0') { + if (!stralloc_0(&line)) return -2; + + j = byte_chr(line.s,line.len,':'); /* cdb */ + k = byte_chr(line.s,line.len,'|'); /* pam */ + u = byte_chr(line.s,line.len,'='); /* assign users */ + + if (!str_diffn(line.s,wildhost.s,wildhost.len - 1)) return 4; /* wilddomain */ + if ((j && j < line.len) || (k && k < line.len) || (u && u < line.len)) + if (!str_diffn(line.s,"@",1)) /* exact */ + if (!str_diffn(line.s + 1,rhost,rlen - 1)) seenhost = 1; + + if (!seenhost) { /* domain */ + if (j && rlen >= j) + if (!str_diffn(line.s,rhost + rlen - j - 1,j - 1)) seenhost = 2; + if (k && rlen >= k) + if (!str_diffn(line.s,rhost + rlen - k - 1,k - 1)) seenhost = 3; + if (u && rlen >= u) + if (!str_diffn(line.s,rhost + rlen - u - 2,u - 2)) seenhost = 4; + } + if (!seenhost) /* pass-thru */ + if (!str_diffn(line.s,"!*",2)) return 5; + + if (k && k < line.len) /* pam */ + if (seenhost || !str_diffn(line.s,"*",1)) { + r = callapam(line.s + k + 1,addr); + if (vlen > 0 && r != 0) + r = callapam(line.s + k + 1,vaddr); + if (r == 0) return 2; + if (r == 111) return r; + } + + if (u && u < line.len) /* qmail-users */ + if (seenhost || !str_diffn(line.s,"*",1)) { + fdrcps = open_read("users/assign.cdb"); + if (fdrcps != -1) { + cdb_init(&cdb,fdrcps); + r = cdb_find(&cdb,ukey,ulen - 2); + cdb_free(&cdb); + close(fdrcps); + if (r) return 3; + } + } + + if (j && j < line.len) /* cdb */ + if (seenhost || !str_diffn(line.s,"*",1)) { + fdrcps = open_read(line.s + j + 1); + if (fdrcps != -1) { + cdb_init(&cdb,fdrcps); + r = cdb_find(&cdb,rkey,klen - 2); + if (vlen > 0 && r == 0) + r = cdb_find(&cdb,vkey,vlen - 2); + cdb_free(&cdb); + close(fdrcps); + if (r) return 1; + } + } + + if (!seenhost) { + fdrcps = open_read(line.s); /* legacy cdb */ + if (fdrcps != -1) { + cdb_init(&cdb,fdrcps); + r = cdb_find(&cdb,rkey,klen - 2); + if (vlen > 0 && r == 0) + r = cdb_find(&cdb,vkey,vlen - 2); + cdb_free(&cdb); + close(fdrcps); + if (r) return 1; + } + } + + if (!stralloc_copys(&line,"")) return -2; + } + } + return 0; +} + +int recipients(char *buf,int len) +{ + int at; + int i; + int r; + + if (flagrcpts != 1) return 10; + + at = byte_rchr(buf,len,'@'); + if (at && at < len) { + if (!stralloc_copyb(&domain,buf + at + 1,len - at - 1)) return -2; + if (!stralloc_copyb(&address,buf,len)) return -2; + } else { + if (!stralloc_copyb(&address,buf,len)) return -2; + if (!stralloc_append(&address,"@")) return -2; + if (!stralloc_copys(&domain,"localhost")) return -2; + if (!stralloc_cat(&address,&domain)) return -2; + } + if (!stralloc_copyb(&user,buf,at - 1)) return -2; + + if (!stralloc_0(&user)) return -2; + if (!stralloc_0(&address)) return -2; + if (!stralloc_0(&domain)) return -2; + + if (!stralloc_copys(&key,":")) return -2; + if (!stralloc_cat(&key,&address)) return -2; + if (!stralloc_0(&key)) return -2; /* \0\0 terminated */ + case_lowerb(key.s,key.len); + case_lowerb(domain.s,domain.len); + + if (!stralloc_copys(&ukey,"!=")) return -2; + if (!stralloc_cat(&ukey,&user)) return -2; + if (!stralloc_0(&ukey)) return -2; /* \0\0 terminated */ + case_lowerb(ukey.s,ukey.len); + + + for (i = 0; i < at; i++) { /* VERP addresses */ + if (buf[i] == *auto_break || buf[i] == '=' || buf[i] == '+') { /* SRS delimiter */ + if (!stralloc_copyb(&verp,buf,i + 1)) return -2; + if (!stralloc_append(&verp,"@")) return -2; + if (!stralloc_cat(&verp,&domain)) return -2; + if (!stralloc_copys(&vkey,":")) return -2; + if (!stralloc_cat(&vkey,&verp)) return -2; + if (!stralloc_0(&vkey)) return -2; /* \0\0 terminated */ + case_lowerb(vkey.s,vkey.len); + break; + } + } + + r = recipients_parse(domain.s,domain.len,address.s,key.s,key.len,verp.s,vkey.s,vkey.len,ukey.s,ukey.len); + if (r) return r; + return 0; +} diff --git a/sqmail-4.3.07/src/recipients.sh b/sqmail-4.3.07/src/recipients.sh new file mode 100644 index 0000000..0e520a8 --- /dev/null +++ b/sqmail-4.3.07/src/recipients.sh @@ -0,0 +1,16 @@ +awk ' + /^d/ { + recipient = $8 + xdelay[recipient] += $5 - $4 + if ($2 == "k") sbytes[recipient] += $6 + if ($2 == "k") succ[recipient] += 1 + if ($2 == "d") fail[recipient] += 1 + if ($2 == "z") temp[recipient] += 1 + } + END { + for (recipient in xdelay) { + str = sprintf("%.2f",xdelay[recipient]) + print 0 + sbytes[recipient],succ[recipient] + fail[recipient],succ[recipient] + fail[recipient] + temp[recipient],str,recipient + } + } +' diff --git a/sqmail-4.3.07/src/rhosts.sh b/sqmail-4.3.07/src/rhosts.sh new file mode 100644 index 0000000..96261e7 --- /dev/null +++ b/sqmail-4.3.07/src/rhosts.sh @@ -0,0 +1,18 @@ +awk ' + /^d/ { + host = $8 + while (num = index(host,"@")) + host = substr(host,num + 1) + xdelay[host] += $5 - $4 + if ($2 == "k") sbytes[host] += $6 + if ($2 == "k") succ[host] += 1 + if ($2 == "d") fail[host] += 1 + if ($2 == "z") temp[host] += 1 + } + END { + for (host in xdelay) { + str = sprintf("%.2f",xdelay[host]) + print 0 + sbytes[host],succ[host] + fail[host],succ[host] + fail[host] + temp[host],str,host + } + } +' diff --git a/sqmail-4.3.07/src/rxdelay.sh b/sqmail-4.3.07/src/rxdelay.sh new file mode 100644 index 0000000..643d6a4 --- /dev/null +++ b/sqmail-4.3.07/src/rxdelay.sh @@ -0,0 +1,7 @@ + +awk ' + { + str = sprintf("%.2f",$4/$3) + print str,$3,$5 + } +' | sort -n diff --git a/sqmail-4.3.07/src/select.h1 b/sqmail-4.3.07/src/select.h1 new file mode 100644 index 0000000..32d0968 --- /dev/null +++ b/sqmail-4.3.07/src/select.h1 @@ -0,0 +1,8 @@ +#ifndef SELECT_H +#define SELECT_H + +#include <sys/types.h> +#include <sys/time.h> +extern int select(); + +#endif diff --git a/sqmail-4.3.07/src/select.h2 b/sqmail-4.3.07/src/select.h2 new file mode 100644 index 0000000..c9bd274 --- /dev/null +++ b/sqmail-4.3.07/src/select.h2 @@ -0,0 +1,13 @@ +#ifndef SELECT_H +#define SELECT_H + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/select.h> +#undef FD_SETSIZE +#define FD_SETSIZE 1024U +#undef __FD_SETSIZE +#define __FD_SETSIZE 1024U +extern int select(); + +#endif diff --git a/sqmail-4.3.07/src/senders.sh b/sqmail-4.3.07/src/senders.sh new file mode 100644 index 0000000..f9e7338 --- /dev/null +++ b/sqmail-4.3.07/src/senders.sh @@ -0,0 +1,23 @@ + +awk ' + /^m/ { + sender = $10"/"$8 + messages[sender] += 1 + succ[sender] += $5 + fail[sender] += $6 + temp[sender] += $7 + mbytes[sender] += $4 + sbytes[sender] += $4 * $5 + rbytes[sender] += $4 * ($5 + $6) + } + /^d/ { + sender = $10"/"$7 + xdelay[sender] += $5 - $4 + } + END { + for (sender in messages) { + str = sprintf("%.6f",xdelay[sender]) + print messages[sender],mbytes[sender],sbytes[sender],rbytes[sender],succ[sender] + fail[sender],succ[sender] + fail[sender] + temp[sender],str,sender + } + } +' diff --git a/sqmail-4.3.07/src/sendmail.c b/sqmail-4.3.07/src/sendmail.c new file mode 100644 index 0000000..69971e3 --- /dev/null +++ b/sqmail-4.3.07/src/sendmail.c @@ -0,0 +1,161 @@ +#include <unistd.h> +#include "getoptb.h" +#include "buffer.h" +#include "alloc.h" +#include "auto_qmail.h" +#include "exit.h" +#include "env.h" +#include "str.h" +#include "logmsg.h" + +#define WHO "sendmail" + +void nomem() +{ + logmsg(WHO,111,FATAL,"out of memory\n"); +} + +void die_usage() +{ + logmsg(WHO,100,USAGE,"sendmail [ -t ] [ -fsender ] [ -Fname ] [ -bp ] [ -bs ] [ arg ... ]\n"); +} + +char *smtpdarg[] = { "bin/qmail-smtpd", 0 }; + +void smtpd() +{ + if (!env_get("PROTO")) { + if (!env_puts("RELAYCLIENT=")) nomem(); + if (!env_puts("DATABYTES=0")) nomem(); + if (!env_puts("PROTO=TCP")) nomem(); + if (!env_puts("TCP6LOCALIP=::1")) nomem(); + if (!env_puts("TCPLOCALIP=127.0.0.1")) nomem(); + if (!env_puts("TCPLOCALHOST=localhost")) nomem(); + if (!env_puts("TCPREMOTEIP=127.0.0.1")) nomem(); + if (!env_puts("TCP6REMOTEIP=::1")) nomem(); + if (!env_puts("TCPREMOTEHOST=localhost")) nomem(); + if (!env_puts("TCPREMOTEINFO=sendmail-bs")) nomem(); + } + execv(*smtpdarg,smtpdarg); + logmsg(WHO,111,FATAL,"unable to run qmail-smtpd\n"); +} + +char *qreadarg[] = { "bin/qmail-qread", 0 }; +void mailq() +{ + execv(*qreadarg,qreadarg); + logmsg(WHO,111,FATAL,"unable to run qmail-qread\n"); +} + +void do_sender(const char *s) +{ + char *x; + int n; + int a; + int i; + + env_unset("QMAILNAME"); + env_unset("MAILNAME"); + env_unset("NAME"); + env_unset("QMAILHOST"); + env_unset("MAILHOST"); + + n = str_len(s); + a = str_rchr(s,'@'); + if (a == n) + { + env_put("QMAILUSER",s); + return; + } + env_put("QMAILHOST",s + a + 1); + + x = (char *) alloc((a + 1) * sizeof(char)); + if (!x) nomem(); + for (i = 0; i < a; i++) + x[i] = s[i]; + x[i] = 0; + env_put("QMAILUSER",x); + alloc_free(x); +} + +int flagh; +char *sender; + +int main(int argc, char **argv) +{ + int opt; + char **qiargv; + char **arg; + int i; + + if (chdir(auto_qmail) == -1) { + buffer_putsflush(buffer_2,"sendmail: fatal: unable to switch to qmail home directory\n"); + _exit(111); + } + + flagh = 0; + sender = 0; + while ((opt = getopt(argc,argv,"vimte:f:p:o:B:F:EJxb:")) != opteof) { + switch (opt) { + case 'N': break; /* ignore DSN option */ + case 'B': break; + case 't': flagh = 1; break; + case 'f': sender = optarg; break; + case 'F': if (!env_put("MAILNAME",optarg)) nomem(); break; + case 'p': break; /* could generate a Received line from optarg */ + case 'v': break; + case 'i': break; /* what an absurd concept */ + case 'x': break; /* SVR4 stupidity */ + case 'm': break; /* twisted-paper-path blindness, incompetent design */ + case 'e': break; /* qmail has only one error mode */ + case 'o': + switch (optarg[0]) { + case 'd': break; /* qmail has only one delivery mode */ + case 'e': break; /* see 'e' above */ + case 'i': break; /* see 'i' above */ + case 'm': break; /* see 'm' above */ + } + break; + case 'E': case 'J': /* Sony NEWS-OS */ + while (argv[optind][optpos]) ++optpos; /* skip optional argument */ + break; + case 'b': + switch (optarg[0]) { + case 'm': break; + case 'p': mailq(); + case 's': smtpd(); + default: die_usage(); + } + break; + default: + die_usage(); + } + } + argc -= optind; + argv += optind; + + if (str_equal(optprogname,"mailq")) + mailq(); + + if (str_equal(optprogname,"newaliases")) { + logmsg(WHO,100,FATAL,"please use fastforward/newaliases instead\n"); + } + + qiargv = (char **) alloc((argc + 10) * sizeof(char *)); + if (!qiargv) nomem(); + + arg = qiargv; + *arg++ = "bin/qmail-inject"; + *arg++ = (flagh ? "-H" : "-a"); + if (sender) { + *arg++ = "-f"; + *arg++ = sender; + do_sender(sender); + } + *arg++ = "--"; + for (i = 0; i < argc; ++i) *arg++ = argv[i]; + *arg = 0; + + execv(*qiargv,qiargv); + logmsg(WHO,111,FATAL,"unable to run qmail-inject\n"); +} diff --git a/sqmail-4.3.07/src/setforward.c b/sqmail-4.3.07/src/setforward.c new file mode 100644 index 0000000..fe17f74 --- /dev/null +++ b/sqmail-4.3.07/src/setforward.c @@ -0,0 +1,173 @@ +#include <unistd.h> +#include "buffer.h" +#include "logmsg.h" +#include "stralloc.h" +#include "open.h" +#include "case.h" +#include "cdbmake.h" +#include "logmsg.h" + +#define WHO "setforward" + +int rename(const char *,const char *); // stdio.h + +void usage() +{ + logmsg(WHO,100,USAGE,"setforward data.cdb data.tmp"); +} +void nomem() +{ + logmsg(WHO,111,FATAL,"out of memory"); +} +void missingsemicolon() +{ + logmsg(WHO,100,FATAL,"final instruction must end with semicolon"); +} +void extracolon() +{ + logmsg(WHO,100,FATAL,"double colons are not permitted"); +} +void extracomma() +{ + logmsg(WHO,100,FATAL,"commas are not permitted before colons"); +} +void nulbyte() +{ + logmsg(WHO,100,FATAL,"NUL bytes are not permitted"); +} +void longaddress() +{ + logmsg(WHO,100,FATAL,"addresses over 800 bytes are not permitted"); +} + +char *fncdb; +char *fntmp; +int fd; +struct cdb_make cdb; +stralloc key = {0}; + +stralloc target = {0}; /* always initialized; no NUL */ +stralloc command = {0}; /* always initialized; no NUL */ +stralloc instr = {0}; /* always initialized */ + +int flagtarget = 0; +/* 0: reading target; command is empty; instr is empty */ +/* 1: target is complete; instr still has to be written; reading command */ + +void writeerr() +{ + logmsg(WHO,111,FATAL,B("unable to write to: ",fntmp)); +} + +void doit(prepend,data,datalen) +char *prepend; +char *data; +int datalen; +{ + if (!stralloc_copys(&key,prepend)) nomem(); + if (!stralloc_cat(&key,&target)) nomem(); + case_lowerb(key.s,key.len); + if (cdb_make_add(&cdb,key.s,key.len,data,datalen) == -1) + writeerr(); +} + +int getch(ch) +char *ch; +{ + int r; + + r = buffer_get(buffer_0small,ch,1); + if (r == -1) + logmsg(WHO,111,FATAL,"unable to read input: "); + return r; +} + +int main(int argc, char **argv) +{ + char ch; + + if (!stralloc_copys(&target,"")) nomem(); + if (!stralloc_copys(&command,"")) nomem(); + if (!stralloc_copys(&instr,"")) nomem(); + + fncdb = argv[1]; if (!fncdb) usage(); + fntmp = argv[2]; if (!fntmp) usage(); + + fd = open_trunc(fntmp); + if (fd == -1) + logmsg(WHO,111,FATAL,B("unable to create: ",fntmp)); + + if (cdb_make_start(&cdb,fd) == -1) writeerr(); + + for (;;) { + if (!getch(&ch)) goto EOF; + + if (ch == '#') { + while (ch != '\n') if (!getch(&ch)) goto EOF; + continue; + } + + if (ch == ' ') continue; + if (ch == '\n') continue; + if (ch == '\t') continue; + + if (ch == ':') { + if (flagtarget) extracolon(); + flagtarget = 1; + continue; + } + + if ((ch == ',') || (ch == ';')) { + if (!flagtarget) extracomma(); + if (command.len) { + if (command.s[0] == '?') { + doit("?",command.s + 1,command.len - 1); + } + else if ((command.s[0] == '|') || (command.s[0] == '!')) { + if (!stralloc_cat(&instr,&command)) nomem(); + if (!stralloc_0(&instr)) nomem(); + } + else if ((command.s[0] == '.') || (command.s[0] == '/')) { + if (!stralloc_cat(&instr,&command)) nomem(); + if (!stralloc_0(&instr)) nomem(); + } + else { + if (command.len > 800) longaddress(); + if (command.s[0] != '&') + if (!stralloc_cats(&instr,"&")) nomem(); + if (!stralloc_cat(&instr,&command)) nomem(); + if (!stralloc_0(&instr)) nomem(); + } + } + + if (!stralloc_copys(&command,"")) nomem(); + + if (ch == ';') { + if (instr.len) + doit(":",instr.s,instr.len); + + if (!stralloc_copys(&target,"")) nomem(); + if (!stralloc_copys(&instr,"")) nomem(); + flagtarget = 0; + } + continue; + } + + if (ch == '\\') if (!getch(&ch)) goto EOF; + if (ch == 0) nulbyte(); + if (!stralloc_append(flagtarget ? &command : &target,&ch)) nomem(); + } + + EOF: + if (flagtarget || target.len) + missingsemicolon(); + + if (cdb_make_finish(&cdb) == -1) writeerr(); + if (fsync(fd) == -1) writeerr(); + if (close(fd) == -1) writeerr(); /* NFS stupidity */ + + if (rename(fntmp,fncdb) == -1) + logmsg(WHO,111,FATAL,B("unable to move ",fntmp," to: ",fncdb)); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/setmaillist.c b/sqmail-4.3.07/src/setmaillist.c new file mode 100644 index 0000000..f7ac89b --- /dev/null +++ b/sqmail-4.3.07/src/setmaillist.c @@ -0,0 +1,93 @@ +#include <unistd.h> +#include <sys/stat.h> +#include "buffer.h" +#include "logmsg.h" +#include "stralloc.h" +#include "getln.h" +#include "open.h" +#include "byte.h" + +#define WHO "setmaillist" + +int rename(const char *,const char *); // stdio.h + +void usage() +{ + logmsg(WHO,100,USAGE,"setmaillist list.bin list.tmp"); +} + +stralloc line = {0}; +int match; + +char *fnbin; +char *fntmp; +int fd; +char buf[1024]; +buffer bo; + +void writeerr() +{ + logmsg(WHO,111,FATAL,B("unable to write to: ",fntmp)); +} + +static void out(char *s,int len) +{ + if (buffer_put(&bo,s,len) == -1) writeerr(); +} + +int main(int argc,char **argv) +{ + umask(033); + + fnbin = argv[1]; if (!fnbin) usage(); + fntmp = argv[2]; if (!fntmp) usage(); + + fd = open_trunc(fntmp); + if (fd == -1) + logmsg(WHO,111,FATAL,B("unable to create: ",fntmp)); + + buffer_init(&bo,write,fd,buf,sizeof(buf)); + + + do { + if (getln(buffer_0small,&line,&match,'\n') == -1) + logmsg(WHO,111,FATAL,"unable to read input: "); + + while (line.len) { + if (line.s[line.len - 1] != '\n') + if (line.s[line.len - 1] != ' ') + if (line.s[line.len - 1] != '\t') + break; + --line.len; + } + + if (byte_chr(line.s,line.len,'\0') != line.len) + logmsg(WHO,111,FATAL,"NUL in input"); + + if (line.len) + if (line.s[0] != '#') { + if ((line.s[0] == '.') || (line.s[0] == '/')) { + out(line.s,line.len); + out("",1); + } + else { + if (line.len > 800) + logmsg(WHO,111,FATAL,"addresses must be under 800 bytes"); + if (line.s[0] != '&') + out("&",1); + out(line.s,line.len); + out("",1); + } + } + + } while (match); + + if (buffer_flush(&bo) == -1) writeerr(); + if (fsync(fd) == -1) writeerr(); + if (close(fd) == -1) writeerr(); /* NFS stupidity */ + + if (rename(fntmp,fnbin) == -1) + logmsg(WHO,111,FATAL,B("unable to move ",fntmp," to: ",fnbin)); + + _exit(0); +} diff --git a/sqmail-4.3.07/src/sha1.c b/sqmail-4.3.07/src/sha1.c new file mode 100644 index 0000000..ee06e92 --- /dev/null +++ b/sqmail-4.3.07/src/sha1.c @@ -0,0 +1,188 @@ +/* +SHA-1 in C +By Steve Reid <sreid@sea-to-sky.net> +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown <jbrown@burgoyne.com> +Still 100% Public Domain + +----------------- +Adopted for s/qmail 2/2020 +feh +Still 100% Public Domain; though requiring fehQlibs-14 + +*/ + +#include <string.h> +#include "sha1.h" +#include "byte.h" + +// #define SHA1HANDSOFF + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#ifndef WORDS_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24) & 0xFF00FF00) \ + | (rol(block->l[i],8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void sha1_transform(uint32_t state[5],const uint8_t buffer[SHA1_BLOCKSIZE]) +{ + uint32_t a, b, c, d, e; + typedef union { + uint8_t c[SHA1_BLOCKSIZE]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 *block; + +#ifdef SHA1HANDSOFF + static uint8_t workspace[SHA1_BLOCKSIZE]; + + block = (CHAR64LONG16 *) workspace; + byte_copy(block,SHA1_BLOCKSIZE,buffer); +#else + block = (CHAR64LONG16 *) buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; + +#ifdef SHA1HANDSOFF + byte_zero(block,64); +#endif +} + +/* SHA1Init - Initialize new context */ + +void sha1_init(sha1_ctx *context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +/* Run your data through this. */ + +void sha1_update(sha1_ctx *context,const uint8_t *data,uint32_t len) +{ + uint32_t i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + byte_copy(&context->buffer[j],(i = 64 - j),data); + sha1_transform(context->state,context->buffer); + for (; i + 63 < len; i += 64) { + sha1_transform(context->state,data + i); + } + j = 0; + } else + i = 0; + byte_copy(&context->buffer[j],len - i,&data[i]); +} + +/* Add padding and return the message digest. */ + +void sha1_final(uint8_t digest[SHA1_DIGESTSIZE],sha1_ctx *context) +{ + uint32_t i; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)] + >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ + } + sha1_update(context,(uint8_t *) "\200",1); + + while ((context->count[0] & 504) != 448) + sha1_update(context,(uint8_t *) "\0",1); + + sha1_update(context,finalcount,8); /* Should cause a SHA1_Transform() */ + + for (i = 0; i < SHA1_DIGESTSIZE; i++) + digest[i] = (uint8_t) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + + /* Wipe variables */ + i = 0; + byte_zero(context->buffer,64); + byte_zero(context->state,20); + byte_zero(context->count,8); + byte_zero(finalcount,8); + +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + sha1_transform(context->state,context->buffer); +#endif +} + +void sha1_hash(char *hash,const char *str,uint32_t len) +{ + sha1_ctx context; + int i; + + sha1_init(&context); + for (i = 0; i < len; i++) + sha1_update(&context,(uint8_t *)str + i,1); + + sha1_final((uint8_t *)hash,&context); + hash[20] = '\0'; +} diff --git a/sqmail-4.3.07/src/sha256.c b/sqmail-4.3.07/src/sha256.c new file mode 100644 index 0000000..e5ba5dd --- /dev/null +++ b/sqmail-4.3.07/src/sha256.c @@ -0,0 +1,167 @@ +/* + * SHA256 + * + * The author (Brad Conte) has released this file "into the public domain free + * of any restrictions". This file is unchanged except for some style + * clean-up and argument order for sha256_hash (feh). + */ + +#include <stdint.h> +#include <string.h> +#include "sha256.h" + +// DBL_INT_ADD treats two unsigned ints a and b as one 64-bit integer and adds c to it + +#define DBL_INT_ADD(a,b,c) if (a > 0xffffffff - (c)) ++b; a += c; +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +uint32_t k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static void sha256_transform(sha256_ctx *ctx, uint8_t *data) +{ + uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j+1] << 16) | (data[j+2] << 8) | (data[j+3]); + for (; i < 64; ++i) + m[i] = SIG1(m[i-2]) + m[i-7] + SIG0(m[i-15]) + m[i-16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +static void sha256_init(sha256_ctx *ctx) +{ + ctx->datalen = 0; + ctx->bitlen[0] = 0; + ctx->bitlen[1] = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +static void sha256_update(sha256_ctx *ctx, uint8_t *data, uint32_t len) +{ + uint32_t i; + + for (i=0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx,ctx->data); + DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],512); + ctx->datalen = 0; + } + } +} + +static void sha256_final(uint8_t *hash,sha256_ctx *ctx) +{ + uint32_t i; + + i = ctx->datalen; + +// Pad whatever data is left in the buffer. + + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx,ctx->data); + memset(ctx->data,0,56); + } + +// Append to the padding the total message's length in bits and transform. + + DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],ctx->datalen * 8); + ctx->data[63] = ctx->bitlen[0]; + ctx->data[62] = ctx->bitlen[0] >> 8; + ctx->data[61] = ctx->bitlen[0] >> 16; + ctx->data[60] = ctx->bitlen[0] >> 24; + ctx->data[59] = ctx->bitlen[1]; + ctx->data[58] = ctx->bitlen[1] >> 8; + ctx->data[57] = ctx->bitlen[1] >> 16; + ctx->data[56] = ctx->bitlen[1] >> 24; + sha256_transform(ctx,ctx->data); + +// Since this implementation uses little endian byte ordering and SHA uses +// big endian, reverse all the bytes when copying the final state to the output hash. + + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24-i*8)) & 0x000000ff; + hash[i+4] = (ctx->state[1] >> (24-i*8)) & 0x000000ff; + hash[i+8] = (ctx->state[2] >> (24-i*8)) & 0x000000ff; + hash[i+12] = (ctx->state[3] >> (24-i*8)) & 0x000000ff; + hash[i+16] = (ctx->state[4] >> (24-i*8)) & 0x000000ff; + hash[i+20] = (ctx->state[5] >> (24-i*8)) & 0x000000ff; + hash[i+24] = (ctx->state[6] >> (24-i*8)) & 0x000000ff; + hash[i+28] = (ctx->state[7] >> (24-i*8)) & 0x000000ff; + } +} + +extern void sha256_hash(char *hash,const char *data,size_t len) +{ + sha256_ctx ctx; + sha256_init(&ctx); + sha256_update(&ctx,(uint8_t *)data,(int)len); + sha256_final((uint8_t *)hash,&ctx); +} diff --git a/sqmail-4.3.07/src/smtpdlog.c b/sqmail-4.3.07/src/smtpdlog.c new file mode 100755 index 0000000..1b44af1 --- /dev/null +++ b/sqmail-4.3.07/src/smtpdlog.c @@ -0,0 +1,271 @@ +#include <unistd.h> +#include "buffer.h" +#include "str.h" +#include "byte.h" +#include "env.h" +#include "fmt.h" +#include "exit.h" +#include "smtpdlog.h" +#define FDLOG 2 + +char *reply421pgl; +char *reply550hlo; +char *reply550mbx; +char *reply552siz; +char *reply553bmf; +char *reply553brt; +char *reply553ngw; +char *reply553env; +char *reply553inv; +char *reply554cnt; + +static char strnum[FMT_ULONG]; +static char logbuf[512]; +buffer bo2 = BUFFER_INIT(write,FDLOG,logbuf,sizeof(logbuf)); + +void smtpdlog_init() +{ + reply421pgl = env_get("REPLY_GREYLISTED"); + reply550hlo = env_get("REPLY_HELO"); + reply550mbx = env_get("REPLY_MAILBOX"); + reply552siz = env_get("REPLY_MAXSIZE"); + reply553bmf = env_get("REPLY_BADMAILFROM"); + reply553brt = env_get("REPLY_BADRCPTTO"); + reply553env = env_get("REPLY_SENDEREXIST"); + reply553ngw = env_get("REPLY_NOGATEWAY"); + reply553inv = env_get("REPLY_SENDERINVALID"); + reply554cnt = env_get("REPLY_CONTENT"); +} + +static void logs(char *s) { if (buffer_puts(&bo2,s) == -1) _exit(1); } /* single string */ +static void logp(char *s) { logs(" P:"); logs(s); } /* protocol */ +static void logh(char *s1,char *s2,char *s3) { logs(" S:"); logs(s1); logs(":"); logs(s2); logs(" H:"); logs(s3); } /* host */ +static void logm(char *s) { logs(" F:"); logs(s); } /* mailfrom */ +static void logt(char *s) { logs(" T:"); logs(s); } /* rcptto */ +static void logi(char *s) { logs(" '"); logs(s); logs("'"); } /* information */ +static void logn(char *s) { if (buffer_puts(&bo2,s) == -1 ) _exit(1); if (buffer_flush(&bo2) == -1) _exit(1); } /* end */ +static void logpid() { strnum[fmt_ulong(strnum,getpid())] = 0; logs("qmail-smtpd: pid "); logs(strnum); logs(" "); } + +void smtp_loga(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7,char *s8,char *s9) + { logpid(); logs(s1); logs(s9); logp(s2); logh(s3,s4,s5); logm(s6); logt(s7), logs(" ?~"); logi(s8); logn("\n"); } /* Auth info */ +void smtp_logb(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) + { logpid(); logs(s1); logs(s7); logp(s2); logh(s3,s4,s5); logs(" ?~"); logi(s6); logn("\n"); } /* Auth info */ +void smtp_logg(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) + { logpid(); logs(s1); logp(s2); logh(s3,s4,s5); logm(s6); logt(s7); logn("\n"); } /* Generic */ +void smtp_logh(char *s1,char *s2,char *s3,char *s4,char *s5) + { logpid(); logs(s1); logp(s2); logh(s3,s4,s5); logn("\n"); } /* Host */ +void smtp_logi(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7,char *s8) + { logpid(); logs(s1); logp(s2); logh(s3,s4,s5); logm(s6); logt(s7); logi(s8); logn("\n"); } /* Generic + Info */ +void smtp_logr(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7,char *s8) + { logpid(); logs(s1); logs(s2); logp(s3); logh(s4,s5,s6); logm(s7); logt(s8); logn("\n"); } /* Recipient */ + +void die_read() { _exit(1); } +void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } +void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } +void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } +void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } +void die_starttls() { out("454 TLS not available due to temporary reason (#5.7.3)\r\n"); flush(); _exit(1); } +void die_recipients() { out("421 unable to check recipients (#4.3.0)\r\n"); flush(); _exit(1); } + +void err_unimpl() { out("500 unimplemented (#5.5.1)\r\n"); } +void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); } +void err_noop() { out("250 ok\r\n"); } +void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); } +void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); } + +int err_child() { out("454 problem with child and I can't auth (#4.3.0)\r\n"); return -1; } +int err_fork() { out("454 child won't start and I can't auth (#4.3.0)\r\n"); return -1; } +int err_pipe() { out("454 unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; } +int err_write() { out("454 unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; } + +int err_postgl() { out("454 problem with child and I can't greylist (#4.3.0)\r\n"); return -1; } +int err_forkgl() { out("454 problem with child and I can't greylist (#4.3.0)\r\n"); return -1; } + +/* TLS */ + +int err_starttls() +{ + out("454 TLS not available due to temporary reason (#5.7.3)\r\n"); + _exit(1); +} +void err_tlsreq(char *s1,char *s2,char *s3,char *s4,char *s5) +{ + out("535 STARTTLS required (#5.7.1)\r\n"); + smtp_logh(s1,s2,s3,s4,s5); +} + +/* Helo */ + +void err_helo(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7,char *s8) +{ + out("550 sorry, invalid HELO/EHLO greeting "); + if (reply550hlo) out(reply550hlo); + out(" (#5.7.1)\r\n"); + smtp_logi(s1,s2,s3,s4,s5,s6,s7,s8); + } + +/* Auth */ + +void err_authsetup(char *s1,char *s2,char *s3,char *s4,char *s5) +{ + out("530 Auth not available (#5.7.1)\r\n"); + smtp_logh(s1,s2,s3,s4,s5); +} +void err_authd() +{ + out("503 you're already authenticated (#5.5.0)\r\n"); +} +void err_authmail() +{ + out("503 no auth during mail transaction (#5.5.0)\r\n"); +} +void err_authfail(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("535 authentication failed (#5.7.1)\r\n"); smtp_logb(s1,s2,s3,s4,s5,s6,s7); +} +void err_authreq(char *s1,char *s2,char *s3,char *s4,char *s5) +{ + out("535 authentication required (#5.7.1)\r\n"); smtp_logh(s1,s2,s3,s4,s5); +} +void err_submission(char *s1,char *s2,char *s3,char *s4,char *s5) +{ + out("530 Authorization required (#5.7.1) \r\n"); smtp_logh(s1,s2,s3,s4,s5); +} +int err_authabort() +{ + out("501 auth exchange canceled (#5.0.0)\r\n"); + return -1; +} +int err_authinput() +{ + out("501 malformed auth input (#5.5.4)\r\n"); + return -1; +} +void err_authinvalid(char *s1,char *s2,char *s3,char *s4,char *s5) +{ + out("504 auth type unimplemented (#5.5.1)\r\n"); + smtp_logh(s1,s2,s3,s4,s5); +} +int err_noauth() +{ + out("504 auth type unimplemented (#5.5.1)\r\n"); + return -1; +} + +/* Mail From: */ + +void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); } + +void err_mav(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("553 sorry, invalid sender address specified "); + if (reply553inv) out(reply553inv); + out(" (#5.7.1)\r\n"); + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} +void err_bmf(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7,char *s8) +{ + out("553 sorry, your envelope sender is in my badmailfrom list "); + if (reply553bmf) out(reply553bmf); + out(" (#5.7.1)\r\n"); + smtp_logi(s1,s2,s3,s4,s5,s6,s7,s8); +} +void err_mfdns(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("553 sorry, your envelope sender must exist "); + if (reply553env) out(reply553env); + out(" (#5.7.1)\r\n"); + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} + +/* SPF */ + +void err_spf(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7,char *msg) +{ + int i, j; + int len = str_len(msg); + + for (i = 0; i < len; i = j + 1) { + j = byte_chr(msg + i, len - i, '\n') + i; + if (j < len) { + out("550-"); + msg[j] = 0; + out(msg); + msg[j] = '\n'; + } else { + out("550 "); + out(msg); + } + } + out(" (#5.7.1)\r\n"); + + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} + +/* Rcpt To: */ + +void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); } + +void postgrey(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("421 greylisted"); + if (reply421pgl) out(reply421pgl); + out(" (#4.3.0)\r\n"); + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} +void err_nogateway(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("553 sorry, that domain isn't in my list of allowed rcpthosts "); + if (reply553ngw) out(reply553ngw); + out(" (#5.7.1)\r\n"); + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} +void err_brt(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("553 sorry, your envelope recipient is in my badrcptto list "); + if (reply553brt) out(reply553brt); + out(" (#5.7.1)\r\n"); + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} +void err_rcpts(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("452 sorry, too many recipients (#4.5.3)\r\n"); /* RFC 5321 */ + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} +void err_recipient(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + if (env_get("RECIPIENTS450")) + out("450 sorry, mailbox currently unavailable (#4.2.1)\r\n"); + else { + out("550 sorry, no mailbox by that name "); + if (reply550mbx) out(reply550mbx); out(" (#5.7.1)\r\n"); + } + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} + +/* Data */ + +void straynewline() +{ + out("451 Bare Line Feeds (LF) are not accepted in SMTP; CRLF is required according to RFC 2822.\r\n"); + flush(); + _exit(1); +} +void err_notorious() +{ + out("503 DATA command not accepted at this time (#5.5.1)\r\n"); + flush(); + _exit(1); +} +void err_size(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7) +{ + out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); + smtp_logg(s1,s2,s3,s4,s5,s6,s7); +} +void err_data(char *s1,char *s2,char *s3,char *s4,char *s5,char *s6,char *s7,char *s8) +{ + out("554 sorry, invalid message content "); + if (reply554cnt) out(reply554cnt); + out(" (#5.3.2)\r\n"); + smtp_logi(s1,s2,s3,s4,s5,s6,s7,s8); +} diff --git a/sqmail-4.3.07/src/spawn.c b/sqmail-4.3.07/src/spawn.c new file mode 100644 index 0000000..effcb26 --- /dev/null +++ b/sqmail-4.3.07/src/spawn.c @@ -0,0 +1,276 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "alloc.h" +#include "sig.h" +#include "wait.h" +#include "buffer.h" +#include "byte.h" +#include "str.h" +#include "stralloc.h" +#include "select.h" +#include "exit.h" +#include "fd.h" +#include "open.h" +#include "error.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "auto_spawn.h" + +extern int truncreport; +extern int spawn(); +extern void report(); +extern void initialize(); + +struct delivery + { + int used; + int fdin; /* pipe input */ + int pid; /* zero if child is dead */ + int wstat; /* if !pid: status of child */ + int fdout; /* pipe output, -1 if !pid; delays eof until after death */ + stralloc output; + } +; + +struct delivery *d; + +void sigchld() +{ + int wstat; + int pid; + int i; + + while ((pid = wait_nohang(&wstat)) > 0) + for (i = 0; i < auto_spawn; ++i) + if (d[i].used) + if (d[i].pid == pid) { + close(d[i].fdout); d[i].fdout = -1; + d[i].wstat = wstat; d[i].pid = 0; + } +} + +int flagwriting = 1; + +ssize_t okwrite(int fd,char *buf,int n) +{ + int w; + if (!flagwriting) return n; + w = write(fd,buf,n); + if (w != -1) return w; + if (errno == EINTR) return -1; + flagwriting = 0; close(fd); + return n; +} + +int flagreading = 1; +char outbuf[1024]; +buffer bo; + +int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ +int flagabort = 0; /* if 1, everything except delnum is garbage */ +int delnum; +stralloc messid = {0}; +stralloc sender = {0}; +stralloc recip = {0}; + +void err(char *s) +{ + char ch; + + ch = delnum; + buffer_put(&bo,&ch,1); + buffer_puts(&bo,s); + buffer_putflush(&bo,"",1); +} + +void docmd() +{ + int f; + int i; + int j; + int fdmess; + int pi[2]; + struct stat st; + + if (flagabort) { err("Zqmail-spawn: Out of memory. (#4.3.0)\n"); return; } + if (delnum < 0) { err("Zqmail-spawn: Internal error: delnum negative. (#4.3.5)\n"); return; } + if (delnum >= auto_spawn) { err("Zqmail-spawn: Internal error: delnum too big. (#4.3.5)\n"); return; } + if (d[delnum].used) { err("Zqmail-spawn: Internal error: delnum in use. (#4.3.5)\n"); return; } + + for (i = 0; i < messid.len; ++i) + if (messid.s[i]) + if (!i || (messid.s[i] != '/')) + if ((unsigned char) (messid.s[i] - '0') > 9) + { err("Dqmail-spawn: Internal error: messid has nonnumerics. (#5.3.5)\n"); return; } + + if (messid.len > 100) { err("Dqmail-spawn: Internal error: messid too long. (#5.3.5)\n"); return; } + if (!messid.s[0]) { err("Dqmail-spawn: Internal error: messid too short. (#5.3.5)\n"); return; } + + if (!stralloc_copys(&d[delnum].output,"")) + { err("Zqmail-spawn: Out of memory. (#4.3.0)\n"); return; } + + j = byte_rchr(recip.s,recip.len,'@'); + if (j >= recip.len) { err("DSorry, address must include host name. (#5.1.3)\n"); return; } + + fdmess = open_read(messid.s); + if (fdmess == -1) { err("Zqmail-spawn: Unable to open message. (#4.3.0)\n"); return; } + + if (fstat(fdmess,&st) == -1) + { close(fdmess); err("Zqmail-spawn: Unable to fstat message. (#4.3.0)\n"); return; } + if ((st.st_mode & S_IFMT) != S_IFREG) + { close(fdmess); err("ZSorry, message has wrong type. (#4.3.5)\n"); return; } + if (st.st_uid != auto_uidq) /* aaack! qmailq has to be trusted! */ + /* your security is already toast at this point. damage control... */ + { close(fdmess); err("ZSorry, message has wrong owner. (#4.3.5)\n"); return; } + + if (pipe(pi) == -1) { + if (errno == EFAULT) err("Zqmail-spawn: Unable to create pipe (wrong fildes). (#4.3.0)\n"); + else if (errno == EMFILE) err("Zqmail-spawn: Unable to create pipe (too many FDS). (#4.3.0)\n"); + else if (errno == ENFILE) err("Zqmail-spawn: Unable to create pipe (system file table full). (#4.3.0)\n"); + else if (errno == ENOMEM) err("Zqmail-spawn: Unable to create pipe (out of memory). (#4.3.0)\n"); + else err("Zqmail-spawn: Unable to create pipe (unkown reason). (#4.3.0)\n"); + close(fdmess); + return; + } + + fd_coe(pi[0]); + + f = spawn(fdmess,pi[1],sender.s,recip.s,j); + close(fdmess); + + if (f == -1) + { close(pi[0]); close(pi[1]); err("Zqmail-spawn: Unable to fork. (#4.3.0)\n"); return; } + + d[delnum].fdin = pi[0]; + d[delnum].fdout = pi[1]; fd_coe(pi[1]); + d[delnum].pid = f; + d[delnum].used = 1; +} + +char cmdbuf[1024]; + +void getcmd() +{ + int i; + int r; + char ch; + + r = read(0,cmdbuf,sizeof(cmdbuf)); + if (r == 0) + { flagreading = 0; return; } + if (r == -1) { + if (errno != EINTR) + flagreading = 0; + return; + } + + for (i = 0; i < r; ++i) { + ch = cmdbuf[i]; + switch (stage) { + case 0: + delnum = (unsigned int) (unsigned char) ch; + messid.len = 0; stage = 1; break; + case 1: + if (!stralloc_append(&messid,&ch)) flagabort = 1; + if (ch) break; + sender.len = 0; stage = 2; break; + case 2: + if (!stralloc_append(&sender,&ch)) flagabort = 1; + if (ch) break; + recip.len = 0; stage = 3; break; + case 3: + if (!stralloc_append(&recip,&ch)) flagabort = 1; + if (ch) break; + docmd(); + flagabort = 0; stage = 0; break; + } + } +} + +char inbuf[128]; + +int main(int argc,char **argv) +{ + char ch; + int i; + int r; + fd_set rfds; + int nfds; + + if (chdir(auto_qmail) == -1) _exit(110); + if (chdir("queue/mess") == -1) _exit(110); + if (!stralloc_copys(&messid,"")) _exit(111); + if (!stralloc_copys(&sender,"")) _exit(111); + if (!stralloc_copys(&recip,"")) _exit(111); + + d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery)); + if (!d) _exit(111); + + buffer_init(&bo,okwrite,1,outbuf,sizeof(outbuf)); + + sig_pipeignore(); + sig_childcatch(sigchld); + + initialize(argc,argv); + + ch = auto_spawn; + buffer_putflush(&bo,&ch,1); + + for (i = 0; i < auto_spawn; ++i) + { d[i].used = 0; d[i].output.s = 0; } + + for (;;) { + if (!flagreading) { + for (i = 0; i < auto_spawn; ++i) if (d[i].used) break; + if (i >= auto_spawn) _exit(0); + } + sig_childunblock(); + + FD_ZERO(&rfds); + if (flagreading) FD_SET(0,&rfds); + nfds = 1; + + for (i = 0; i < auto_spawn; ++i) + if (d[i].used) { + FD_SET(d[i].fdin,&rfds); + if (d[i].fdin >= nfds) + nfds = d[i].fdin + 1; + } + + r = select(nfds,&rfds,(fd_set *) 0,(fd_set *) 0,(struct timeval *) 0); + sig_childblock(); + + if (r != -1) { + if (flagreading) + if (FD_ISSET(0,&rfds)) getcmd(); + for (i = 0; i < auto_spawn; ++i) + if (d[i].used) + + if (FD_ISSET(d[i].fdin,&rfds)) { + r = read(d[i].fdin,inbuf,128); + if (r == -1) + continue; /* read error on a readable pipe? be serious */ + if (r == 0) { + ch = i; + buffer_put(&bo,&ch,1); + report(&bo,d[i].wstat,d[i].output.s,d[i].output.len); + buffer_put(&bo,"",1); + buffer_flush(&bo); + close(d[i].fdin); d[i].used = 0; + continue; + } + while (!stralloc_readyplus(&d[i].output,r)) + sleep(10); /*XXX*/ + byte_copy(d[i].output.s + d[i].output.len,r,inbuf); + d[i].output.len += r; + if (truncreport > 100) + if (d[i].output.len > truncreport) { + char *truncmess = "\nError report too long, sorry.\n"; + d[i].output.len = truncreport - str_len(truncmess) - 3; + stralloc_cats(&d[i].output,truncmess); + } + } + } + } +} diff --git a/sqmail-4.3.07/src/spf.c b/sqmail-4.3.07/src/spf.c new file mode 100644 index 0000000..2b61ba1 --- /dev/null +++ b/sqmail-4.3.07/src/spf.c @@ -0,0 +1,647 @@ +#include "stralloc.h" +#include "alloc.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "now.h" +#include "dns.h" +#include "case.h" +#include "spf.h" + +/* long lived SPF variables (output) */ + +stralloc spfinfo = {0}; /* SPF results - see spf.h */ +stralloc spfrecord = {0}; /* Used for diagnostics */ + +/* s/qmail control SPF variables (input) */ + +stralloc spflocalrules; /* Local rules provided here */ +stralloc spfexplain; /* Default SPF_EXPMSG in spf.h */ + +stralloc spfmf = {0}; /* aka envelopefrom = clientid */ +stralloc spfhelo = {0}; /* helo or domain part for spfmf */ +stralloc dnsname = {0}; /* FQDN of client host in DNS */ +stralloc spflocal = {0}; /* Receiving host */ + +stralloc spfexpmsg = {0}; /* additional explanation given as 5xx SMTP response */ +stralloc expdomain = {0}; /* the domain, for which explanation is given */ +int flagip6; + +stralloc domain = {0}; +stralloc identity = {0}; + +static int recursion; +char ip4remote[4] = {0, 0, 0, 0}; +char ip6remote[16] = {16 * 0}; + +/* Sample SPF TXT records: +Standard example: example.net TXT "v=spf1 mx a:pluto.example.net include:aspmx.googlemail.com -all" +Fehcom's example: fehcom.net TXT "v=spf1 ip4:85.25.149.179/32 ip6:2001:4dd0:ff00:3d4::2/64 -all" +Include example: mailing.com TXT "v=spf1 a:smtpout.mailing.com include:spf.nl2go.com ~all" +Exists+Expand: exists.com TXT "v=spf1 exists:%{ir}.%{l1r+-}._spf.%{d} -all" +*/ + +/* Entry point: -------------------------------------- Go for SPF */ + +/** + @brief spf_query + prepares the SPF TXT record query + @param input: pointer to remoteip, helo, mf, localhost, and flagIP6 + @return int r = SPF return code + */ + + +int spf_query(const char *remoteip,const char *helo,const char *mf,const char *local,const int flagip) +{ + int at; + int r = SPF_INIT; + flagip6 = flagip; + + if (!stralloc_copys(&spfinfo," ")) return SPF_NOMEM; + + switch (flagip6) { + case -1: if (!spf_info("MLocal=",remoteip)) return SPF_NOMEM; + if (!spf_info("R:","+")) return SPF_NOMEM; + break; + case 0: if (!ip4_scan(remoteip,ip4remote)) return SPF_SYNTAX; + if (ipme_is4(ip4remote) == 1) { + if (!spf_info("MLocal=",remoteip)) return SPF_NOMEM; + if (!spf_info("R:","+")) return SPF_NOMEM; + return SPF_ME; + } break; + case 1: if (!ip6_scan(remoteip,ip6remote)) return SPF_SYNTAX; + if (ipme_is6(ip6remote) == 1) { + if (!spf_info("MLocal=",remoteip)) return SPF_NOMEM; + if (!spf_info("R:","+")) return SPF_NOMEM; + return SPF_ME; + } break; + } + + if (helo && str_len(helo)) { + if (!stralloc_copys(&spfhelo,helo)) return SPF_NOMEM; + } else { + if (!stralloc_copys(&spfhelo,"unknown")) return SPF_NOMEM; + } + if (!stralloc_0(&spfhelo)) return SPF_NOMEM; + + if (mf && str_len(mf)) { + if (!stralloc_copys(&spfmf,mf)) return SPF_NOMEM; + if (!stralloc_0(&spfmf)) return SPF_NOMEM; + at = str_rchr(spfmf.s,'@'); + if (spfmf.s[at] == '@') { + if (!stralloc_copys(&domain,spfmf.s + at + 1)) return SPF_NOMEM; + } else { +// if (!stralloc_0(&spfhelo)) return SPF_NOMEM; + if (!stralloc_copys(&domain,&spfhelo)) return SPF_NOMEM; + } + if (!stralloc_copy(&identity,&domain)) return SPF_NOMEM; + } + if (!stralloc_0(&identity)) return SPF_NOMEM; + + if (local && str_len(local)) { + if (!stralloc_copys(&spflocal,local)) return SPF_NOMEM; + } else { + if (!stralloc_copys(&spflocal,"localhost")) return SPF_NOMEM; + } + if (!stralloc_0(&spflocal)) return SPF_NOMEM; + + if (!spf_info("S=",remoteip)) return SPF_NOMEM; + if (!spf_info("O=",spfmf.s)) return SPF_NOMEM; + if (!spf_info("C=",identity.s)) return SPF_NOMEM; + if (!spf_info("H=",spfhelo.s)) return SPF_NOMEM; + + if (!stralloc_copy(&spfexpmsg,&spfexplain)) return SPF_NOMEM; + if (!stralloc_0(&spfexpmsg)) return SPF_NOMEM; + + recursion = 0; + dnsname.len = 0; + + if (r == SPF_INIT) r = spf_lookup(&domain); + if (r == SPF_LOOP) { + if (!spf_info("P=","Maximum nesting level exceeded; possible loop")) return SPF_NOMEM; + if (!spf_info("R:","e")) return SPF_NOMEM; + } + if (r < 0) r = SPF_UNKNOWN; /* return 2main */ + + return r; +} + +/* SPF Lookup: -------------------------------------- Return cases */ + +static struct spf_aliases { + char *alias; + int defrc; +} spf_aliases[] = { + { "allow", SPF_OK } +, { "pass", SPF_OK } +, { "deny", SPF_FAIL } +, { "softdeny",SPF_SOFTFAIL } +, { "fail", SPF_FAIL } +, { "softfail",SPF_SOFTFAIL } +, { "unknown", SPF_NEUTRAL } +, { 0, SPF_UNKNOWN } +}; + +/** + @brief spf_lookup + calles the actual (recursive) SPF DNS query + @param input: pointer to stralloc domain (fqdn) + @input stralloc spflocalrules (if provided -- for artificial results) + @output stralloc spfdata with RDATA (+ artificial information) + @return int r = SPF return code + */ + +int spf_lookup(stralloc *domain) +{ + stralloc spfdata = {0}; + stralloc sa = {0}; + struct spf_aliases *da; + int first = !recursion; + int local_pos = -1; + int localrules = 0; + int q = -1; + int i, r; + int begin, pos; + int spfrc; + int done; + char *p; + + /* Fallthrough result */ + + REDIRECT: + if (++recursion > LOOKUP_LIMIT) return SPF_EXHAUST; + + if (!stralloc_copys(&expdomain,domain->s)) return SPF_NOMEM; // *FIXME */ + + if (!stralloc_copys(&spfdata,"")) return SPF_NOMEM; + r = spf_records(&spfdata,domain); + + if (!stralloc_0(domain)) return SPF_NOMEM; + if (first) if (!stralloc_copys(&spfrecord,"")) return SPF_NOMEM; + if (!stralloc_cats(&spfrecord,"(")) return SPF_NOMEM; + if (!stralloc_cat(&spfrecord,domain)) return SPF_NOMEM; + if (!stralloc_cats(&spfrecord,")")) return SPF_NOMEM; + if (!stralloc_cats(&spfrecord," => ")) return SPF_NOMEM; + if (!stralloc_cat(&spfrecord,&spfdata)) return SPF_NOMEM; + if (!stralloc_cats(&spfrecord,"\n")) return SPF_NOMEM; + if (!stralloc_0(&spfrecord)) return SPF_NOMEM; + + /* In spite of none-existing SPF data, use local rules as substitude */ + + if (r == SPF_NONE) { /* No SPF records published */ + if (!first) { + return r; + } else { + spfdata.len = 0; + } + if (localrules) { /* append local ruleset */ + local_pos = spfdata.len; + if (!stralloc_cats(&spfdata,spflocalrules.s)) return SPF_NOMEM; + } + if (!stralloc_0(&spfdata)) return SPF_NOMEM; + + if (!stralloc_copys(&expdomain,"")) return SPF_NOMEM; + + } else if (r == SPF_OK) { /* SPF records published */ + if (!stralloc_0(&spfdata)) return SPF_NOMEM; + r = SPF_NEUTRAL; + + if (first && localrules) { /* try to add local rules before failure of all mechs */ + pos = 0; + p = (char *) 0; + while (pos < spfdata.len) { + NXTOK(begin,pos,&spfdata); + if (!spfdata.s[begin]) continue; + + if (p && spfdata.s[begin] != *p) p = (char *) 0; + if (!p && (spfdata.s[begin] == '-' || + spfdata.s[begin] == '~' || + spfdata.s[begin] == '?')) p = &spfdata.s[begin]; + + if (p && p > spfdata.s && case_equals(spfdata.s + begin + 1,"all")) { + /* ok, we can insert the local rules at p */ + local_pos = p - spfdata.s; + + if (!stralloc_readyplus(&spfdata,spflocalrules.len)) return 0; + p = spfdata.s + local_pos; + byte_copyr(p + spflocalrules.len,spfdata.len - local_pos,p); + byte_copy(p,spflocalrules.len,spflocalrules.s); + spfdata.len += spflocalrules.len; + + pos += spflocalrules.len; + break; + } + } + + if (pos >= spfdata.len) pos = spfdata.len - 1; + for (i = 0; i < pos; i++) + if (!spfdata.s[i]) spfdata.s[i] = ' '; + } + + } else { /* Any other SPF return code */ + return r; + } + + /* (artificial) SPF data exist; work thru them */ + + pos = 0; + done = 0; + while (pos < spfdata.len) { + NXTOK(begin,pos,&spfdata); + if (!spfdata.s[begin]) continue; + + if (!done && localrules) { /* in local ruleset? */ + if (local_pos >= 0 && begin >= local_pos) { + if (begin < (local_pos + spflocalrules.len)) { + if (!stralloc_copys(&expdomain,"")) return SPF_NOMEM; + } else { + if (!stralloc_copy(&expdomain,domain)) return SPF_NOMEM; + } + } + } + + for (p = spfdata.s + begin; *p; ++p) + if (*p == ':' || *p == '/' || *p == '=') break; + + if (*p == '=') { + *p++ = 0; + + if (case_equals(spfdata.s + begin,"redirect")) { /* modifiers are simply handled here */ + if (done) continue; + +// if (!stralloc_0(domain)) return SPF_NOMEM; + if (!spf_parse(&sa,p,domain->s)) return SPF_NOMEM; + if (!stralloc_copy(domain,&sa)) return SPF_NOMEM; + if (!spf_info("D=",p)) return SPF_NOMEM; + r = SPF_UNKNOWN; + + goto REDIRECT; + } else if (case_equals(spfdata.s + begin,"default")) { /* we don't need those anymore */ + if (done) continue; + + for (da = spf_aliases; da->alias; ++da) + if (case_equals(da->alias,p)) break; + + r = da->defrc; + } else if (case_equals(spfdata.s + begin,"exp")) { /* exp= only on top level */ + stralloc out = {0}; + + if (!first) continue; + if (!stralloc_copys(&sa,p)) return SPF_NOMEM; + + switch (dns_txt(&out,&sa)) { + case -1: return SPF_NOMEM; + case 0: continue; /* nobody @home */ + } + + if (!stralloc_copys(&spfexpmsg,out.s)) return SPF_NOMEM; + if (!stralloc_append(&spfexpmsg,"\n")) return SPF_NOMEM; + if (!stralloc_0(&spfexpmsg)) return SPF_NOMEM; + } + } else if (!done) { /* and unknown modifiers are ignored */ + if (!stralloc_copys(&sa,spfdata.s + begin)) return SPF_NOMEM; + if (!stralloc_0(&sa)) return SPF_NOMEM; + + switch (spfdata.s[begin]) { + case '-': begin++; spfrc = SPF_FAIL; break; + case '~': begin++; spfrc = SPF_SOFTFAIL; break; + case '+': begin++; spfrc = SPF_OK; break; + case '?': begin++; spfrc = SPF_NEUTRAL; break; + default: spfrc = SPF_OK; + } + + if (*p == '/') { + *p++ = 0; + q = spf_mechanism(spfdata.s + begin,0,p,domain->s); + } else { + if (*p) *p++ = 0; + i = str_chr(p,'/'); + if (p[i] == '/') { + p[i++] = 0; + q = spf_mechanism(spfdata.s + begin,p,p + i,domain->s); + } else if (i > 0) { + q = spf_mechanism(spfdata.s + begin,p,0,domain->s); + } else { + q = spf_mechanism(spfdata.s + begin,0,0,domain->s); + } + } + if (q == SPF_OK) q = spfrc; + + switch (q) { + case SPF_OK: if (!spf_info("R:","+")) return SPF_NOMEM; break; + case SPF_NEUTRAL: if (!spf_info("R:","?")) return SPF_NOMEM; break; + case SPF_SYNTAX: if (!spf_info("P=","Unknown parse error")) return SPF_NOMEM; + if (!spf_info("R:","e")) return SPF_NOMEM; break; + case SPF_SOFTFAIL: if (!spf_info("R:","~")) return SPF_NOMEM; break; + case SPF_FAIL: if (!spf_info("R:","-")) return SPF_NOMEM; break; + case SPF_EXT: if (!spf_info("P=","Unknown SPF mechanism")) return SPF_NOMEM; break; + case SPF_ERROR: if (localrules) if (local_pos >= 0 && begin >= local_pos) break; + if (!spf_info("R:","o")) return SPF_NOMEM; q = SPF_NONE; break; + case SPF_NONE: continue; + } + + r = q; + done = 1; /* we're done, no more mechanisms */ + } + } + + /* we fell through, no local rule applied */ + if (!done) + if (!stralloc_copy(&expdomain,domain)) return SPF_NOMEM; + + return r; +} + +/* Mechanisms: -------------------------------------- Lookup classes */ + +static struct mechanisms { + char *mechanism; + int (*func)(char *spfspec,char *prefix); + unsigned int use_spfspec : 1; + unsigned int use_prefix : 1; + unsigned int expands : 1; + unsigned int filldomain : 1; + int defresult : 4; +} mechanisms[] = { + { "all", 0, 0,0,0,0,SPF_OK } +, { "include", spf_include,1,0,1,0,0 } +, { "a", spf_a, 1,1,1,1,0 } +, { "mx", spf_mx, 1,1,1,1,0 } +, { "ptr", spf_ptr, 1,0,1,1,0 } +, { "ip4", spf_ip4, 1,1,0,0,0 } +, { "ip6", spf_ip6, 1,1,0,0,0 } +, { "exists", spf_exists, 1,0,1,0,0 } +, { "extension",0, 1,1,0,0,SPF_EXT } +, { 0, 0, 1,1,0,0,SPF_EXT } +}; + +/** + @brief spf_mechanism + evaluates the provided mechanisms in the SPF record [RFC7208 Sec 5.] + @param input: pointer to mechanism, SPF specification from record, CIDR prefix length, domain + @input stralloc spflocalrules (if provided) + @output pointer to spfspec: data evaluated + @return int r + */ + +int spf_mechanism(char *mechanism,char *spfspec,char *prefix,char *domain) +{ + struct mechanisms *mech; + stralloc sa = {0}; + int r; + int pos; + + for (mech = mechanisms; mech->mechanism; mech++) + if (case_equals(mech->mechanism,mechanism)) break; + + if (mech->use_spfspec && !spfspec && mech->filldomain) spfspec = domain; + if (!mech->use_spfspec != !spfspec) return SPF_SYNTAX; + if (mech->use_prefix && !get_prefix(prefix)) return SPF_SYNTAX; + + if (!mech->func) return mech->defresult; + if (!stralloc_readyplus(&sa,1)) return SPF_NOMEM; + + if (mech->expands && case_diffs(spfspec,domain)) { + if (!spf_parse(&sa,spfspec,domain)) return SPF_NOMEM; + for (pos = 0; (sa.len - pos) > 255;) { + pos += byte_chr(sa.s + pos,sa.len - pos,'.'); + if (pos < sa.len) pos++; + } + sa.len -= pos; + if (pos > 0) byte_copy(sa.s,sa.len,sa.s + pos); + if (!stralloc_0(&sa)) return SPF_NOMEM; + spfspec = sa.s; + } + + r = mech->func(spfspec,prefix); + return r; +} + +/** + @brief spf_include + deals with recursive evaluation of SPF record [RFC7208 Sec. 5.2] + @param input: pointer to included SPF specification; CIDR prefix length + @return int r = 1 ok; 0 failure + */ + +int spf_include(char *spfspec,char *prefix) +{ + stralloc sa = {0}; + int r; + + if (!stralloc_copys(&sa,spfspec)) return SPF_NOMEM; + + r = spf_lookup(&sa); + switch (r) { + case SPF_NONE: r = SPF_UNKNOWN; break; + case SPF_SYNTAX: r = SPF_UNKNOWN; break; + case SPF_NEUTRAL: + case SPF_SOFTFAIL: + case SPF_FAIL: r = SPF_NONE; break; + } + if (!stralloc_0(&sa)) return SPF_NOMEM; + if (!spf_info("I=",sa.s)) return SPF_NOMEM; + + return r; +} + +/** + @brief spf_parse + parses the substructure of the SPF record and calls spf_macros + @param input: pointer to SPF specification, pointer to domain + output: stralloc sa -- + @output pointer to spfspec: with found data + @return int r = 1 ok; 0 failure + */ + +int spf_parse(stralloc *sa,char *spfspec,char *domain) +{ + char *p; + int pos; + char append; + + if (!stralloc_readyplus(sa,3)) return 0; + if (!stralloc_copys(sa,"")) return 0; + + for (p = spfspec; *p; ++p) { + append = *p; + if (byte_equal(p,1,"%")) { + p++; + switch (*p) { + case '%': break; + case '_': append = ' '; break; + case '-': if (!stralloc_cats(sa,"%20")) return 0; continue; + case '{': + pos = str_chr(p,'}'); + if (p[pos] != '}') { p--; break; } + p[pos] = '\0'; + if (!spf_macros(sa,p + 1,domain)) return 0; + p += pos; + continue; + default: p--; + } + } + if (!stralloc_append(sa,&append)) return 0; + } + + return 1; +} + +/** + @brief spf_macros + deals with macros in the SPF specificaton [RFC7208 Sec. 7ff] + @param input: pointer to SPF macro, pointer to domain + output: pointer to stralloc expand(ed information) + @return int r = 1 ok; 0 failure + */ + +int spf_macros(stralloc *expand,char *macro,char *domain) +{ + static const char hextab[] = "0123456789abcdef"; + stralloc sa = {0}; + int reverse = 0; + int ndigits = -1; + int urlencode; + unsigned long u; + char ch = {0}; + char ascii; + int pos, i, n; + int start = expand->len; + + /* URL encoding - hidden in RFC 7208 Sec. 7.3 */ + + if (*macro == 'x') { urlencode = -1; ++macro; } else urlencode = 0; + ch = *macro; + if (!ch) { return 1; } + if (ch >= 'A' && ch <= 'Z') { ch += 32; urlencode = 1; } + if (urlencode == -1) ch -= 32; + + /* No. digits determine number of printed labels */ + + i = 0; + while (*macro) { + i++; + if (*macro == '}') break; + if (*macro >= '0' && *macro <= '9') { + scan_ulong(macro,&u); ndigits = u; + } else if (i > 1 && *macro == 'r') { reverse = 1; break; } /* Reverse representation */ + macro++; + } + + switch (ch) { /* see RFC7208 sec. 7.2 */ + case 's': case 'S': + if (!stralloc_readyplus(&sa,spfmf.len)) return 0; + if (!stralloc_copys(&sa,spfmf.s)) return 0; + break; + case 'l': case 'L': + i = byte_rchr(spfmf.s,spfmf.len,'@'); + if (i < spfmf.len) { + if (!stralloc_copyb(&sa,spfmf.s,i)) return 0; + } else { + if (!stralloc_copys(&sa,"postmaster")) return 0; + } + break; + case 'o': case 'O': + i = byte_rchr(spfmf.s,spfmf.len,'@') + 1; + if (i > spfmf.len) break; + if (!stralloc_copys(&sa,spfmf.s + i)) return 0; + break; + case 'd': case 'D': + if (!stralloc_copys(&sa,domain)) return 0; /* the hack for 'Z'; Russions everywhere ;-) */ + break; + case 'i': case 'c': case 'I': case 'C': + if (!stralloc_ready(&sa,IPFMT)) return 0; + if (flagip6) { + sa.len = ip6_fmt(sa.s,ip6remote); + } else { + sa.len = ip4_fmt(sa.s,ip4remote); + } + break; + case 'p': case 'P': + if (!dnsname.len) spf_ptr(domain,0); + if (dnsname.len) { + if (!stralloc_copys(&sa,dnsname.s)) return 0; + } else { + if (!stralloc_copys(&sa,"unknown")) return 0; + } + break; + case 'h': case 'H': + if (!stralloc_copys(&sa,spfhelo.s)) return 0; /* FIXME: FQDN? */ + break; + case 't': case 'T': + if (!stralloc_ready(&sa,FMT_ULONG)) return 0; + sa.len = fmt_ulong(sa.s,(unsigned long)now()); + break; + case 'v': case 'V': + if (flagip6) { + if (!stralloc_copys(&sa,"ip6")) return 0; + } else { + if (!stralloc_copys(&sa,"in-addr")) return 0; + } + break; + case 'r': case 'R': + if (!stralloc_copy(&sa,&spflocal)) return 0; + break; + default: break; + } + if (!stralloc_0(&sa)) return 0; // XXX + + if (reverse) { + n = 0; + for (i = 1; i <= sa.len; i++) { + if ((ndigits == -1) || (n < ndigits)) { + if (!byte_diff(sa.s + sa.len - i - 1,1,".") || (i == sa.len)) { + n++; + if (!stralloc_cats(expand,sa.s + sa.len - i)) return 0; + if (i < sa.len) { + sa.s[sa.len - i - 1] = 0; + if (!stralloc_cats(expand,".")) return 0; + } + } + } + } + } else if (ndigits != -1) { + n = pos = 0; + for (i = 1; i <= sa.len; i++) { + if (n < ndigits) { + if (!byte_diff(sa.s + i,1,".")) { n++; pos = i; } + } + } + if (!stralloc_catb(expand,sa.s,pos)) return 0; + } else + if (!stralloc_cats(expand,sa.s)) return 0; + + if (urlencode) { + stralloc_copyb(&sa,expand->s + start,expand->len - start); + expand->len = start; + + for (i = 0; i < sa.len; ++i) { + ch = sa.s[i]; + if (urlchr_table[(unsigned char)ch]) { + if (!stralloc_readyplus(expand,3)) return 0; + if (!stralloc_append(expand,"%")) return 0; + ascii = hextab[(unsigned char)ch >> 4]; + if (!stralloc_append(expand,&ascii)) return 0; + ascii = hextab[(unsigned char)ch & 0x0f]; + if (!stralloc_append(expand,&ascii)) return 0; + } else { + if (!stralloc_append(expand,&ch)) return 0; + } + } + } + + return 1; +} + +int spf_info(char *s,const char *t) +{ + if (!stralloc_cats(&spfinfo,s)) return 0; + if (!stralloc_cats(&spfinfo,t)) return 0; + if (!stralloc_cats(&spfinfo," ")) return 0; + + return 1; +} diff --git a/sqmail-4.3.07/src/spfdnsip.c b/sqmail-4.3.07/src/spfdnsip.c new file mode 100755 index 0000000..e9cf9ee --- /dev/null +++ b/sqmail-4.3.07/src/spfdnsip.c @@ -0,0 +1,406 @@ +#include <unistd.h> +#include "stralloc.h" +#include "alloc.h" +#include "ip.h" +#include "ipalloc.h" +#include "ipme.h" +#include "str.h" +#include "fmt.h" +#include "scan.h" +#include "byte.h" +#include "now.h" +#include "dns.h" +#include "case.h" +#include "spf.h" + +// shared by spf.c + spfdnsip.c + +extern stralloc dnsname; +extern char ip4remote[4]; +extern char ip6remote[16]; +extern int flagip6; + +/** + @brief match_ip + compares IPv4/IPv6 addreses up to prefix length + @param input: ip_address1,prefix length, ip_address2 + @return 1 ok; 0 failure + */ + +int match_ip4(unsigned char ip1[4],int prefix,char ip2[4]) +{ + stralloc iptest1 = {0}; + stralloc iptest2 = {0}; + + if (flagip6) return 0; + + if (ip4_bytestring(&iptest1,ip1,prefix) == prefix) + if (ip4_bytestring(&iptest2,ip2,prefix) == prefix) + if (byte_diff(iptest1.s,prefix,iptest2.s)) return 0; + + return 1; +} + +int match_ip6(unsigned char ip1[16],int prefix,char ip2[16]) +{ + stralloc iptest1 = {0}; + stralloc iptest2 = {0}; + + if (!flagip6) return 0; + + if (ip6_bytestring(&iptest1,ip1,prefix) == prefix) + if (ip6_bytestring(&iptest2,ip2,prefix) == prefix) + if (byte_diff(iptest1.s,prefix,iptest2.s)) return 0; + + return 1; +} + +/** + @brief get_prefix + return integer value of prefix length + @param input: pointer to prefix + @return (int) length of prefix + */ + +int get_prefix(char *prefix) +{ + unsigned long r; + int pos; + + if (!prefix || *prefix == '0') { + if (flagip6 == 0) return 32; + if (flagip6 == 1) return 128; + } + + pos = scan_ulong(prefix,&r); + if (!pos || (prefix[pos] && !(prefix[pos] == '/'))) return SPF_SYNTAX; + if (flagip6 == 0 && r > 32) return SPF_SYNTAX; + if (flagip6 == 1 && r > 128) return SPF_SYNTAX; + + return r; +} + +/* DNS Record: -------------------------------------- Fetch multiple SPF TXT RRs */ + +/** + @brief spf_records + get TXT records for domain and extract SPF information + @param input: pointer stralloc domain + output: pointer to stralloc spf records + @return SPF_OK, SPF_NONE; SPF_MULTIRR, SPF_DNSSOFT, SPF_NOMEM + */ + +int spf_records(stralloc *spfrec,stralloc *domain) +{ + static stralloc out = {0}; + static stralloc spf = {0}; + int i, k; + int begin; + int r = 0; + + begin = -1; + + DNS_INIT + r = dns_txt(&out,(const stralloc *)domain); + switch (r) { + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: return SPF_DNSSOFT; /* return 2main */ + case DNS_NXD: return SPF_NONE; + } + r = SPF_NONE; + + for (k = 0; k < out.len; ++k) { + if (case_starts(out.s + k,"v=spf1")) { + begin = k; + break; + } + } + + if (begin >= 0) { + if (case_starts(out.s + k + 6,"v=spf1")) return SPF_MULTIRR; /* return 2main */ + + if (!stralloc_copys(&spf,"")) return SPF_NOMEM; + for (i = begin; i < out.len; ++i) { + if (out.s[i] == '\r' || out.s[i] == '\n' || out.s[i] == '\0') break; + if (!stralloc_append(&spf,out.s + i)) return SPF_NOMEM; + } + if (!stralloc_0(&spf)) return SPF_NOMEM; + if (!stralloc_copys(spfrec,spf.s)) return SPF_NOMEM; + + r = SPF_OK; + } + + return r; +} + +/* Mechanisms: -------------------------------------- Lookup functions */ + +/** + @brief spf_a (a; a:fqdns; a:fqdns/56) + compares A + AAAA records for SPF info and client host + @param input: pointer to spfspecification, pointer to prefix + @return SPF_OK, SPF_NONE; SPF_DNSSOFT, SPF_NOMEM + */ + +int spf_a(char *spfspec,char *prefix) +{ + stralloc sa = {0}; + stralloc ip = {0}; + int ipprefix, r, j; + + ipprefix = get_prefix(prefix); + if (ipprefix < 0) return SPF_SYNTAX; + + if (!stralloc_copys(&sa,spfspec)) return SPF_NOMEM; + if (!stralloc_readyplus(&ip,0)) return SPF_NOMEM; + if (!spf_info("MA/AAAA=",spfspec)) return SPF_NOMEM; + + DNS_INIT + + switch (dns_ip4(&ip,&sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: r = SPF_DNSSOFT; break; + case DNS_NXD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for (j = 0; j + 4 <= ip.len; j += 4) + if (match_ip4(ip.s + j,ipprefix,ip4remote)) + return SPF_OK; + } + + switch (dns_ip6(&ip,&sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: r = SPF_DNSSOFT; break; + case DNS_NXD: r = SPF_NONE; break; + default: + r = SPF_NONE; + for (j = 0; j + 16 <= ip.len; j += 16) + if (match_ip6(ip.s + j,ipprefix,ip6remote)) + return SPF_OK; + } + + return r; +} + +/** + @brief spf_mx (mx; mx:domain; mx:domain/24) + compares MX records for SPF info and client host + @param input: pointer to spfspecification, pointer to prefix + @return SPF_OK, SPF_NONE; SPF_DNSSOFT, SPF_NOMEM + */ + +int spf_mx(char *spfspec,char *prefix) +{ + stralloc sa = {0}; + ipalloc ia = {0}; + unsigned long random; + int ipprefix; + int j, r; + + ipprefix = get_prefix(prefix); + if (ipprefix < 0) return SPF_SYNTAX; + + random = now() + (getpid() << 16); + + if (!stralloc_copys(&sa,spfspec)) return SPF_NOMEM; + if (!spf_info("MMX=",spfspec)) return SPF_NOMEM; + + switch (dns_mxip(&ia,&sa,random)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: return SPF_DNSSOFT; + default: + r = SPF_NONE; + for (j = 0; j < ia.len; ++j) { + if (byte_diff(ip6remote,16,V6localnet) && !ip6_isv4mapped(ip6remote)) { + if (match_ip6(&ia.ix[j].addr.ip6.d,ipprefix,ip6remote)) + return SPF_OK; + } + if (byte_diff(ip4remote,4,V4localnet)) { + if (match_ip4(&ia.ix[j].addr.ip4.d,ipprefix,ip4remote)) + return SPF_OK; + } + } + } + + return r; +} + +/** + @brief spf_ptr (ptr; ptr:fqdn) + compares PTR records from SPF info and client host + @param input: pointer to spfspecification; prefix not used + @return SPF_OK, SPF_NONE; SPF_DNSSOFT, SPF_NOMEM + */ + +int spf_ptr(char *spfspec,char *prefix) +{ + stralloc fqdn = {0}; + stralloc out = {0}; + stralloc ip = {0}; + int slen = str_len(spfspec); + int rc, r; + int k = 0; + int pos; + int l = 0; + + /* we didn't find host with the matching IP before */ + if (dnsname.len == 7 && str_equal(dnsname.s,"unknown")) + return SPF_NONE; + + if (!spf_info("MPTR=",spfspec)) return SPF_NOMEM; + + /* the hostname found will probably be the same as before */ + while (dnsname.len) { + pos = dnsname.len - slen; + if (pos < 0) break; + if (pos > 0 && dnsname.s[pos - 1] != '.') break; + if (case_diffb(dnsname.s + pos,slen,spfspec)) break; + return SPF_OK; + } + + /* ok, either it's the first test or it's a very weired setup + Assumptions: + ip -> inverse DNS name (only one!) + inverse DNS name -> (same) ip (only one!) + */ + + + if (!stralloc_readyplus(&fqdn,255)) return SPF_NOMEM; + if (!stralloc_readyplus(&out,255)) return SPF_NOMEM; + if (!stralloc_readyplus(&ip,32)) return SPF_NOMEM; + + if (flagip6) { + rc = dns_name6(&out,ip6remote); // usually: 2. . .ip6.addr => only one + switch (rc) { + case DNS_MEM: return SPF_NOMEM; + case DNS_COM: r = SPF_DNSSOFT; break; + case DNS_ERR: r = SPF_NONE; break; + case DNS_NXD: r = SPF_NONE; break; + default: r = SPF_NONE; l++; + if (l > LOOKUP_LIMIT) { r = SPF_ERROR; break; } + switch (dns_ip6(&ip,&out)) { // theoretical more IPs cound be retrieved + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: r = SPF_DNSSOFT; break; + case DNS_NXD: r = SPF_NONE; break; + default: r = SPF_NONE; + for (k = 0; k + 16 <= ip.len; k += 16) { + if (k > 32 * LOOKUP_LIMIT) { r = SPF_ERROR; break; } + if (match_ip6(ip.s + k,128,ip6remote)) { + if (!dnsname.len) + if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM; + pos = out.len - slen; + if (pos < 0) continue; + if (pos > 0 && out.s[pos - 1] != '.') continue; + if (case_diffb(out.s + pos,slen,spfspec)) continue; + + if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM; + r = SPF_OK; + } + } + } + } + } else { // IP4 branch + rc = dns_name4(&out,ip4remote); // usual answer: d.c.b.e.in-arpa.addr for IP4 a.b.c.d => only one + switch (rc) { + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: r = SPF_DNSSOFT; break; + case DNS_NXD: r = SPF_NONE; break; + default: r = SPF_NONE; l++; + if (l > LOOKUP_LIMIT) { r = SPF_ERROR; break; } + switch (dns_ip4(&ip,&out)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: r = SPF_DNSSOFT; break; + case DNS_NXD: r = SPF_NONE; break; + default: r = SPF_NONE; + for (k = 0; k + 4 <= ip.len; k += 4) { + if (k > 32 * LOOKUP_LIMIT) { r = SPF_ERROR; break; } + if (match_ip4(ip.s + k,32,ip4remote)) { + if (!dnsname.len) + if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM; + pos = out.len - slen; + if (pos < 0) continue; + if (pos > 0 && out.s[pos - 1] != '.') continue; + if (case_diffb(out.s + pos,slen,spfspec)) continue; + + if (!stralloc_copy(&dnsname,&out)) return SPF_NOMEM; + r = SPF_OK; + } + } + } + } + } + if (!dnsname.len) + if (!stralloc_copys(&dnsname,"unknown")) return SPF_NOMEM; + + return r; +} + +/** + @brief spf_ip4 (ip4; ip4:fqdn; ip4:fqdn/24) + compares A records for SPF info and client host + @param input: pointer to spfspecification, pointer to prefix + @return SPF_OK, SPF_NONE; SPF_DNSSOFT, SPF_NOMEM + */ + +int spf_ip4(char *spfspec,char *prefix) +{ + char spfip[4]; + + if (flagip6) return SPF_NONE; + int ipprefix = get_prefix(prefix); + + if (ipprefix < 0) return SPF_SYNTAX; + if (!ip4_scan(spfspec,spfip)) return SPF_SYNTAX; + + if (!spf_info("MIPv4=",spfspec)) return SPF_NOMEM; + if (!match_ip4(spfip,ipprefix,ip4remote)) return SPF_NONE; + + return SPF_OK; +} + +/** + @brief spf_ip6 (ip6; ip6:fqdn; ip6:fqdn/56) + compares AAAA records for SPF info and client host + @param input: pointer to spfspecification, pointer to prefix + @return SPF_OK, SPF_NONE; SPF_DNSSOFT, SPF_NOMEM + */ + +int spf_ip6(char *spfspec,char *prefix) +{ + char spfip[16]; + + if (!flagip6) return SPF_NONE; + int ipprefix = get_prefix(prefix); + + if (ipprefix < 0) return SPF_SYNTAX; + if (!ip6_scan(spfspec,spfip)) return SPF_SYNTAX; + + if (!spf_info("MIPv6=",spfspec)) return SPF_NOMEM; + if (!match_ip6(spfip,ipprefix,ip6remote)) return SPF_NONE; + + return SPF_OK; +} + +/** + @brief spf_exists (exists; exists:fqdn) + simply looks for a A records only for SPF info and client host + @param input: pointer to spfspecification, prefix not used + @return SPF_OK, SPF_NONE; SPF_DNSSOFT, SPF_NOMEM + */ + +int spf_exists(char *spfspec,char *prefix) +{ + stralloc sa = {0}; + stralloc ip = {0}; + + if (!stralloc_copys(&sa,spfspec)) return SPF_NOMEM; + if (!spf_info("MExists=",spfspec)) return SPF_NOMEM; + + switch (dns_ip4(&ip,&sa)) { + case DNS_MEM: return SPF_NOMEM; + case DNS_ERR: return SPF_DNSSOFT; + case DNS_NXD: return SPF_NONE; + default: return SPF_OK; + } + +} diff --git a/sqmail-4.3.07/src/spfquery.c b/sqmail-4.3.07/src/spfquery.c new file mode 100644 index 0000000..8c642ee --- /dev/null +++ b/sqmail-4.3.07/src/spfquery.c @@ -0,0 +1,98 @@ +#include <string.h> +#include <unistd.h> +#include "buffer.h" +#include "stralloc.h" +#include "alloc.h" +#include "spf.h" +#include "exit.h" +#include "dns.h" +#include "str.h" +#include "byte.h" +#include "logmsg.h" + +#define WHO "spfquery" + +void die(int e,char *s) { buffer_putsflush(buffer_2,s); _exit(e); } +void die_nomem() { die(111,"fatal: out of memory\n"); } + +static stralloc heloin = {0}; +static stralloc mfin = {0}; +static stralloc spflocal = {0}; +static stralloc spfbounce = {0}; + +int main(int argc,char **argv) +{ + stralloc spfip = {0}; + int flag = 0; + int r; + int verbose = 0; + flagip6 = 1; + + if (argc < 4) + logmsg(WHO,100,USAGE,"spfquery <sender-ip> <sender-helo/ehlo> <envelope-from> [<local rules>] [-v(erbose) ]\n"); + + if (!stralloc_copys(&spfip,argv[1])) die_nomem(); + if (!stralloc_0(&spfip)) die_nomem(); + r = byte_chr(spfip.s,spfip.len,':'); + if (r < spfip.len) flag = 1; + + if (!stralloc_copys(&heloin,argv[2])) die_nomem(); + if (!stralloc_0(&heloin)) die_nomem(); + + if (!stralloc_copys(&mfin,argv[3])) die_nomem(); + if (!stralloc_0(&mfin)) die_nomem(); + + if (argc > 4) { + if (!byte_diff(argv[4],2,"-v")) verbose = 1; + else { + if (!stralloc_copys(&spflocal,argv[4])) die_nomem(); + if (spflocal.len && !stralloc_0(&spflocal)) die_nomem(); + } + } + + if (argc > 5) { + if (!byte_diff(argv[5],2,"-v")) verbose = 1; + } + + if (!stralloc_copys(&spfexplain,SPF_DEFEXP)) die_nomem(); + if (!stralloc_0(&spfexplain)) die_nomem(); + + DNS_INIT + r = spf_query(spfip.s,heloin.s,mfin.s,"localhost",flag); + if (r == SPF_NOMEM) die_nomem(); + + buffer_puts(buffer_1,"result="); + switch (r) { + case SPF_ME: buffer_puts(buffer_1,"loopback"); break; + case SPF_OK: buffer_puts(buffer_1,"pass"); break; + case SPF_NONE: buffer_puts(buffer_1,"none"); break; + case SPF_UNKNOWN: buffer_puts(buffer_1,"unknown"); break; + case SPF_NEUTRAL: buffer_puts(buffer_1,"neutral"); break; + case SPF_SOFTFAIL: buffer_puts(buffer_1,"softfail"); break; + case SPF_FAIL: buffer_puts(buffer_1,"fail"); break; + case SPF_ERROR: buffer_puts(buffer_1,"error"); break; + case SPF_SYNTAX: buffer_puts(buffer_1,"IP address syntax error"); break; + default: buffer_puts(buffer_1,"undefined"); break; + } + + buffer_putsflush(buffer_1,"\n"); + if (r == SPF_SYNTAX) _exit(1); + + if (verbose) { + buffer_puts(buffer_1,"SPF records read: \n"); + buffer_put(buffer_1,spfrecord.s,spfrecord.len); + } + + buffer_puts(buffer_1,"SPF information evaluated: "); + buffer_put(buffer_1,spfinfo.s,spfinfo.len); + buffer_putsflush(buffer_1,"\n"); + + if (r == SPF_FAIL) { + buffer_puts(buffer_1,"SPF results returned: "); + if (!spf_parse(&spfbounce,spfexpmsg.s,expdomain.s)) die_nomem(); + buffer_put(buffer_1,spfbounce.s,spfbounce.len); + buffer_putsflush(buffer_1,"\n"); + } + + _exit(0); +} diff --git a/sqmail-4.3.07/src/splogger.c b/sqmail-4.3.07/src/splogger.c new file mode 100644 index 0000000..4e64590 --- /dev/null +++ b/sqmail-4.3.07/src/splogger.c @@ -0,0 +1,70 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <syslog.h> +#include <unistd.h> +#include "error.h" +#include "buffer.h" +#include "exit.h" +#include "str.h" +#include "scan.h" +#include "fmt.h" + +char buf[800]; /* syslog truncates long lines (or crashes); GPACIC */ +int bufpos = 0; /* 0 <= bufpos < sizeof(buf) */ +int flagcont = 0; +int priority; /* defined if flagcont */ +char stamp[FMT_ULONG + FMT_ULONG + 3]; /* defined if flagcont */ + +void stamp_make() +{ + struct timeval tv; + char *s; + gettimeofday(&tv,(struct timezone *) 0); + s = stamp; + s += fmt_ulong(s,(unsigned long) tv.tv_sec); + *s++ = '.'; + s += fmt_uint0(s,(unsigned int) tv.tv_usec,6); + *s = 0; +} + +void flush() +{ + if (bufpos) { + buf[bufpos] = 0; + if (flagcont) + syslog(priority,"%s+%s",stamp,buf); /* logger folds invisibly; GPACIC */ + else { + stamp_make(); + priority = LOG_INFO; + if (str_start(buf,"warning:")) priority = LOG_WARNING; + if (str_start(buf,"alert:")) priority = LOG_ALERT; + syslog(priority,"%s %s",stamp,buf); + flagcont = 1; + } + } + bufpos = 0; +} + +int main(int argc,char **argv) +{ + char ch; + + if (argv[1]) + if (argv[2]) { + unsigned long facility; + scan_ulong(argv[2],&facility); + openlog(argv[1],0,facility << 3); + } + else + openlog(argv[1],0,LOG_MAIL); + else + openlog("splogger",0,LOG_MAIL); + + for (;;) { + if (buffer_get(buffer_0,&ch,1) < 1) _exit(0); + if (ch == '\n') { flush(); flagcont = 0; continue; } + if (bufpos == sizeof(buf) - 1) flush(); + if ((ch < 32) || (ch > 126)) ch = '?'; /* logger truncates at 0; GPACIC */ + buf[bufpos++] = ch; + } +} diff --git a/sqmail-4.3.07/src/srs2.c b/sqmail-4.3.07/src/srs2.c new file mode 100644 index 0000000..1bb431b --- /dev/null +++ b/sqmail-4.3.07/src/srs2.c @@ -0,0 +1,641 @@ +/* Copyright (c) 2004 Shevek (srs@anarres.org) + * All rights reserved. + * + * This file is a part of libsrs2 from http://www.libsrs2.org/ + * + * Redistribution and use in source and binary forms, with or without + * modification, under the terms of either the GNU General Public + * License version 2 or the BSD license, at the discretion of the + * user. Copies of these licenses have been included in the libsrs2 + * distribution. See the the file called LICENSE for more + * information. + */ + +/* This is a minimal adapted s/qmail version; it requires complete + refactoring: + + a) Use stralloc for addresses + b) Replace stdio, str*, and mem* functions + c) Use tai64 for timestamp function + d) Remove va args + e) Reduce code by 50% +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include <time.h> /* time */ +#include <sys/types.h> /* tyepdefs */ +#include <sys/time.h> /* timeval / timezone struct */ +#include <string.h> /* memcpy, strcpy, memset */ +#include "srs2.h" +#include "sha1.h" + +#ifndef HAVE_STRCASECMP +# ifdef HAVE__STRICMP +# define strcasecmp _stricmp +# endif +#endif + +#ifndef HAVE_STRNCASECMP +# ifdef HAVE__STRNICMP +# define strncasecmp _strnicmp +# endif +#endif + + /* Use this */ +#define STRINGP(s) ((s != NULL) && (*(s) != '\0')) + +static const char *srs_separators = "=-+"; + +static srs_malloc_t srs_f_malloc = malloc; +static srs_realloc_t srs_f_realloc = realloc; +static srs_free_t srs_f_free = free; + +int srs_set_malloc(srs_malloc_t m, srs_realloc_t r, srs_free_t f) +{ + srs_f_malloc = m; + srs_f_realloc = r; + srs_f_free = f; + return SRS_SUCCESS; +} + +#define X(e,s) if (code == e) return s; + +const char *srs_strerror(int code) +{ + X(0,"") + /* Simple errors */ + X(SRS_SUCCESS,"Success") + X(SRS_ENOTSRSADDRESS,"Not an SRS address.") + + /* Config errors */ + X(SRS_ENOSECRETS,"No secrets in SRS configuration.") + X(SRS_ESEPARATORINVALID,"Invalid separator suggested.") + + /* Input errors */ + X(SRS_ENOSENDERATSIGN,"No at sign in sender address") + X(SRS_EBUFTOOSMALL,"Buffer too small.") + + /* Syntax errors */ + X(SRS_ENOSRS0HOST,"No host in SRS0 address.") + X(SRS_ENOSRS0USER,"No user in SRS0 address.") + X(SRS_ENOSRS0HASH,"No hash in SRS0 address.") + X(SRS_ENOSRS0STAMP,"No timestamp in SRS0 address.") + X(SRS_ENOSRS1HOST,"No host in SRS1 address.") + X(SRS_ENOSRS1USER,"No user in SRS1 address.") + X(SRS_ENOSRS1HASH,"No hash in SRS1 address.") + X(SRS_EBADTIMESTAMPCHAR,"Bad base32 character in timestamp.") + X(SRS_EHASHTOOSHORT,"Hash too short in SRS address.") + + /* SRS errors */ + X(SRS_ETIMESTAMPOUTOFDATE,"Time stamp out of date.") + X(SRS_EHASHINVALID,"Hash invalid in SRS address.") + + return "Unknown SRS error."; +} + +srs_t *srs_new() +{ + srs_t *srs = (srs_t *)srs_f_malloc(sizeof(srs_t)); + srs_init(srs); + return srs; +} + +void srs_init(srs_t *srs) +{ + memset(srs, 0, sizeof(srs_t)); + srs->secrets = NULL; + srs->numsecrets = 0; + srs->separator = '='; + srs->maxage = 21; + srs->hashlen = 4; + srs->hashmin = srs->hashlen; + srs->alwaysrewrite = FALSE; +} + +void srs_free(srs_t *srs) +{ + int i; + for (i = 0; i < srs->numsecrets; i++) { + memset(srs->secrets[i], 0, strlen(srs->secrets[i])); + srs_f_free(srs->secrets[i]); + srs->secrets[i] = '\0'; + } + srs_f_free(srs); +} + +int srs_add_secret(srs_t *srs, const char *secret) +{ + int newlen = (srs->numsecrets + 1) * sizeof(char *); + srs->secrets = (char **)srs_f_realloc(srs->secrets, newlen); + srs->secrets[srs->numsecrets++] = strdup(secret); + return SRS_SUCCESS; +} + +const char *srs_get_secret(srs_t *srs, int idx) +{ + if (idx < srs->numsecrets) + return srs->secrets[idx]; + return NULL; +} + +#define SRS_PARAM_DEFINE(n, t) \ + int srs_set_ ## n (srs_t *srs, t value) { \ + srs->n = value; \ + return SRS_SUCCESS; \ + } \ + t srs_get_ ## n (srs_t *srs) { \ + return srs->n; \ + } + +int srs_set_separator(srs_t *srs, char value) +{ + if (strchr(srs_separators, value) == NULL) + return SRS_ESEPARATORINVALID; + srs->separator = value; + return SRS_SUCCESS; +} + +char srs_get_separator(srs_t *srs) +{ + return srs->separator; +} + +SRS_PARAM_DEFINE(maxage, int) + /* XXX Check hashlen >= hashmin */ +SRS_PARAM_DEFINE(hashlen, int) +SRS_PARAM_DEFINE(hashmin, int) +SRS_PARAM_DEFINE(alwaysrewrite, srs_bool) +SRS_PARAM_DEFINE(noforward, srs_bool) +SRS_PARAM_DEFINE(noreverse, srs_bool) + +/* Don't mess with these unless you know what you're doing well + * enough to rewrite the timestamp functions. These are based on + * a 2 character timestamp. Changing these in the wild is probably + * a bad idea. */ +#define SRS_TIME_PRECISION (60 * 60 * 24) /* One day */ +#define SRS_TIME_BASEBITS 5 /* 2^5 = 32 = strlen(CHARS) */ +/* This had better be a real variable since we do arithmethic + * with it. */ +const char *SRS_TIME_BASECHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +#define SRS_TIME_SIZE 2 +#define SRS_TIME_SLOTS (1<<(SRS_TIME_BASEBITS<<(SRS_TIME_SIZE-1))) + +int srs_timestamp_create(srs_t *srs, char *buf, time_t now) +{ + now = now / SRS_TIME_PRECISION; + buf[1] = SRS_TIME_BASECHARS[now & ((1 << SRS_TIME_BASEBITS) - 1)]; + now = now >> SRS_TIME_BASEBITS; + buf[0] = SRS_TIME_BASECHARS[now & ((1 << SRS_TIME_BASEBITS) - 1)]; + buf[2] = '\0'; + return SRS_SUCCESS; +} + +int srs_timestamp_check(srs_t *srs, const char *stamp) +{ + const char *sp; + char *bp; + int off; + time_t now; + time_t then; + + /* We had better go around this loop exactly twice! */ + then = 0; + for (sp = stamp; *sp; sp++) { + bp = strchr(SRS_TIME_BASECHARS, toupper(*sp)); + if (bp == NULL) + return SRS_EBADTIMESTAMPCHAR; + off = bp - SRS_TIME_BASECHARS; + then = (then << SRS_TIME_BASEBITS) | off; + } + + time(&now); + now = (now / SRS_TIME_PRECISION) % SRS_TIME_SLOTS; + while (now < then) + now = now + SRS_TIME_SLOTS; + + if (now <= then + srs->maxage) + return SRS_SUCCESS; + return SRS_ETIMESTAMPOUTOFDATE; +} + +const char *SRS_HASH_BASECHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static void srs_hash_create_v(srs_t *srs, int idx, char *buf, int nargs, va_list ap) +{ + sha1_ctx ctx; + char srshash[SHA1_DIGESTSIZE + 1]; + char *secret; + char *data; + int len; + char *lcdata; + unsigned char *hp; + char *bp; + int i; + int j; + + secret = srs->secrets[idx]; + sha1_init(&ctx); + sha1_update(&ctx, secret, strlen(secret)); + + for (i = 0; i < nargs; i++) { + data = va_arg(ap, char *); + len = strlen(data); + lcdata = alloca(len + 1); + for (j = 0; j < len; j++) { + if (isupper(data[j])) + lcdata[j] = tolower(data[j]); + else + lcdata[j] = data[j]; + } + sha1_update(&ctx, lcdata, len); + } + + sha1_final(srshash, &ctx); /* args inverted */ + srshash[SHA1_DIGESTSIZE] = '\0'; + + /* A little base64 encoding. Just a little. */ + hp = (unsigned char *)srshash; + bp = buf; + for (i = 0; i < srs->hashlen; i++) { + switch (i & 0x03) { + default: /* NOTREACHED */ + case 0: + j = (*hp >> 2); + break; + case 1: + j = ((*hp & 0x03) << 4) | + ((*(hp + 1) & 0xF0) >> 4); + hp++; + break; + case 2: + j = ((*hp & 0x0F) << 2) | + ((*(hp + 1) & 0xC0) >> 6); + hp++; + break; + case 3: + j = (*hp++ & 0x3F); + break; + } + *bp++ = SRS_HASH_BASECHARS[j]; + } + + *bp = '\0'; + buf[srs->hashlen] = '\0'; +} + +int srs_hash_create(srs_t *srs, char *buf, int nargs, ...) +{ + va_list ap; + + if (srs->numsecrets == 0) + return SRS_ENOSECRETS; + if (srs->secrets == NULL) + return SRS_ENOSECRETS; + if (srs->secrets[0] == NULL) + return SRS_ENOSECRETS; + + va_start(ap, nargs); + srs_hash_create_v(srs, 0, buf, nargs, ap); + va_end(ap); + + return SRS_SUCCESS; +} + +int srs_hash_check(srs_t *srs, char *hash, int nargs, ...) +{ + va_list ap; + char *srshash; + char *tmp; + int len; + int i; + + len = strlen(hash); + if (len < srs->hashmin) + return SRS_EHASHTOOSHORT; + if (len < srs->hashlen) { + tmp = alloca(srs->hashlen + 1); + strncpy(tmp, hash, srs->hashlen); + tmp[srs->hashlen] = '\0'; + hash = tmp; + len = srs->hashlen; + } + + for (i = 0; i < srs->numsecrets; i++) { + va_start(ap, nargs); + srshash = alloca(srs->hashlen + 1); + srs_hash_create_v(srs, i, srshash, nargs, ap); + va_end(ap); + if (strncasecmp(hash, srshash, len) == 0) + return SRS_SUCCESS; + } + + return SRS_EHASHINVALID; +} + +int srs_compile_shortcut(srs_t *srs, + char *buf, int buflen, + char *sendhost, char *senduser, + const char *aliashost) { + char *srshash; + char srsstamp[SRS_TIME_SIZE + 1]; + int len; + int ret; + + /* This never happens if we get called from guarded() */ + if ((strncasecmp(senduser, SRS0TAG, 4) == 0) && + (strchr(srs_separators, senduser[4]) != NULL)) { + sendhost = senduser + 5; + if (*sendhost == '\0') + return SRS_ENOSRS0HOST; + senduser = strchr(sendhost, SRSSEP); + if ((senduser == NULL) || (*senduser == '\0')) + return SRS_ENOSRS0USER; + } + + len = strlen(SRS0TAG) + 1 + + srs->hashlen + 1 + + SRS_TIME_SIZE + 1 + + strlen(sendhost) + 1 + strlen(senduser) + + 1 + strlen(aliashost); + if (len >= buflen) + return SRS_EBUFTOOSMALL; + + ret = srs_timestamp_create(srs, srsstamp, time(NULL)); + if (ret != SRS_SUCCESS) + return ret; + srshash = alloca(srs->hashlen + 1); + ret = srs_hash_create(srs, srshash,3, srsstamp, sendhost, senduser); + if (ret != SRS_SUCCESS) + return ret; + + sprintf(buf, SRS0TAG "%c%s%c%s%c%s%c%s@%s", srs->separator, + srshash, SRSSEP, srsstamp, SRSSEP, + sendhost, SRSSEP, senduser, + aliashost); + + return SRS_SUCCESS; +} + +int srs_compile_guarded(srs_t *srs, + char *buf, int buflen, + char *sendhost, char *senduser, + const char *aliashost) { + char *srshost; + char *srsuser; + char *srshash; + int len; + int ret; + + if ((strncasecmp(senduser, SRS1TAG, 4) == 0) && + (strchr(srs_separators, senduser[4]) != NULL)) { + /* Used as a temporary convenience var */ + srshash = senduser + 5; + if (*srshash == '\0') + return SRS_ENOSRS1HASH; + /* Used as a temporary convenience var */ + srshost = strchr(srshash, SRSSEP); + if (!STRINGP(srshost)) + return SRS_ENOSRS1HOST; + *srshost++ = '\0'; + srsuser = strchr(srshost, SRSSEP); + if (!STRINGP(srsuser)) + return SRS_ENOSRS1USER; + *srsuser++ = '\0'; + srshash = alloca(srs->hashlen + 1); + ret = srs_hash_create(srs, srshash, 2, srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; + len = strlen(SRS1TAG) + 1 + + srs->hashlen + 1 + + strlen(srshost) + 1 + strlen(srsuser) + + 1 + strlen(aliashost); + if (len >= buflen) + return SRS_EBUFTOOSMALL; + sprintf(buf, SRS1TAG "%c%s%c%s%c%s@%s", srs->separator, + srshash, SRSSEP, + srshost, SRSSEP, srsuser, + aliashost); + return SRS_SUCCESS; + } + else if ((strncasecmp(senduser, SRS0TAG, 4) == 0) && + (strchr(srs_separators, senduser[4]) != NULL)) { + srsuser = senduser + 4; + srshost = sendhost; + srshash = alloca(srs->hashlen + 1); + ret = srs_hash_create(srs, srshash, 2, srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; + len = strlen(SRS1TAG) + 1 + + srs->hashlen + 1 + + strlen(srshost) + 1 + strlen(srsuser) + + 1 + strlen(aliashost); + if (len >= buflen) + return SRS_EBUFTOOSMALL; + sprintf(buf, SRS1TAG "%c%s%c%s%c%s@%s", srs->separator, + srshash, SRSSEP, + srshost, SRSSEP, srsuser, + aliashost); + } + else { + return srs_compile_shortcut(srs, buf, buflen, + sendhost, senduser, aliashost); + } + + return SRS_SUCCESS; +} + +int srs_parse_shortcut(srs_t *srs, char *buf, int buflen, char *senduser) +{ + char *srshash; + char *srsstamp; + char *srshost; + char *srsuser; + int ret; + + if (strncasecmp(senduser, SRS0TAG, 4) == 0) { + srshash = senduser + 5; + if (!STRINGP(srshash)) + return SRS_ENOSRS0HASH; + srsstamp = strchr(srshash, SRSSEP); + if (!STRINGP(srsstamp)) + return SRS_ENOSRS0STAMP; + *srsstamp++ = '\0'; + srshost = strchr(srsstamp, SRSSEP); + if (!STRINGP(srshost)) + return SRS_ENOSRS0HOST; + *srshost++ = '\0'; + srsuser = strchr(srshost, SRSSEP); + if (!STRINGP(srsuser)) + return SRS_ENOSRS0USER; + *srsuser++ = '\0'; + ret = srs_timestamp_check(srs, srsstamp); + if (ret != SRS_SUCCESS) + return ret; + ret = srs_hash_check(srs, srshash, 3, srsstamp, + srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; + sprintf(buf, "%s@%s", srsuser, srshost); + return SRS_SUCCESS; + } + + return SRS_ENOTSRSADDRESS; +} + +int srs_parse_guarded(srs_t *srs, char *buf, int buflen, char *senduser) +{ + char *srshash; + char *srshost; + char *srsuser; + int ret; + + if (strncasecmp(senduser, SRS1TAG, 4) == 0) { + srshash = senduser + 5; + if (!STRINGP(srshash)) + return SRS_ENOSRS1HASH; + srshost = strchr(srshash, SRSSEP); + if (!STRINGP(srshost)) + return SRS_ENOSRS1HOST; + *srshost++ = '\0'; + srsuser = strchr(srshost, SRSSEP); + if (!STRINGP(srsuser)) + return SRS_ENOSRS1USER; + *srsuser++ = '\0'; + ret = srs_hash_check(srs, srshash, 2, srshost, srsuser); + if (ret != SRS_SUCCESS) + return ret; + sprintf(buf, SRS0TAG "%s@%s", srsuser, srshost); + return SRS_SUCCESS; + } + else { + return srs_parse_shortcut(srs, buf, buflen, senduser); + } +} + +int srs_forward(srs_t *srs, char *buf, int buflen, + const char *sender, const char *alias) +{ + char *senduser; + char *sendhost; + char *tmp; + int len; + + if (srs->noforward) + return SRS_ENOTREWRITTEN; + + /* This is allowed to be a plain domain */ + while ((tmp = strchr(alias, '@')) != NULL) + alias = tmp + 1; + + tmp = strchr(sender, '@'); + if (tmp == NULL) + return SRS_ENOSENDERATSIGN; + sendhost = tmp + 1; + + len = strlen(sender); + + if (! srs->alwaysrewrite) { + if (strcasecmp(sendhost, alias) == 0) { + if (strlen(sender) >= buflen) + return SRS_EBUFTOOSMALL; + strcpy(buf, sender); + return SRS_SUCCESS; + } + } + + /* Reconstruct the whole show into our alloca() buffer. */ + senduser = alloca(len + 1); + strcpy(senduser, sender); + tmp = (senduser + (tmp - sender)); + sendhost = tmp + 1; + *tmp = '\0'; + + return srs_compile_guarded(srs, buf, buflen, + sendhost, senduser, alias); +} + +int srs_forward_alloc(srs_t *srs, char **sptr, + const char *sender, const char *alias) +{ + char *buf; + int slen; + int alen; + int len; + int ret; + + if (srs->noforward) + return SRS_ENOTREWRITTEN; + + slen = strlen(sender); + alen = strlen(alias); + + /* strlen(SRSxTAG) + strlen("====+@") < 64 */ + len = slen + alen + srs->hashlen + SRS_TIME_SIZE + 64; + buf = (char *)srs_f_malloc(len); + + ret = srs_forward(srs, buf, len, sender, alias); + + if (ret == SRS_SUCCESS) + *sptr = buf; + else + srs_f_free(buf); + + return ret; +} + +int srs_reverse(srs_t *srs, char *buf, int buflen, const char *sender) +{ + char *senduser; + char *tmp; + int len; + + if (!SRS_IS_SRS_ADDRESS(sender)) + return SRS_ENOTSRSADDRESS; + + if (srs->noreverse) + return SRS_ENOTREWRITTEN; + + len = strlen(sender); + if (len >= buflen) + return SRS_EBUFTOOSMALL; + senduser = alloca(len + 1); + strcpy(senduser, sender); + + /* We don't really care about the host for reversal. */ + tmp = strchr(senduser, '@'); + if (tmp != NULL) + *tmp = '\0'; + return srs_parse_guarded(srs, buf, buflen, senduser); +} + +int srs_reverse_alloc(srs_t *srs, char **sptr, const char *sender) +{ + char *buf; + int len; + int ret; + + *sptr = NULL; + + if (!SRS_IS_SRS_ADDRESS(sender)) + return SRS_ENOTSRSADDRESS; + + if (srs->noreverse) + return SRS_ENOTREWRITTEN; + + len = strlen(sender) + 1; + buf = (char *)srs_f_malloc(len); + + ret = srs_reverse(srs, buf, len, sender); + + if (ret == SRS_SUCCESS) + *sptr = buf; + else + srs_f_free(buf); + + return ret; +} diff --git a/sqmail-4.3.07/src/srsforward.c b/sqmail-4.3.07/src/srsforward.c new file mode 100644 index 0000000..46176b5 --- /dev/null +++ b/sqmail-4.3.07/src/srsforward.c @@ -0,0 +1,169 @@ +#include <unistd.h> +#include <sys/types.h> +#include "control.h" +#include "sig.h" +#include "constmap.h" +#include "readwrite.h" +#include "exit.h" +#include "env.h" +#include "qmail.h" +#include "auto_qmail.h" +#include "buffer.h" +#include "str.h" +#include "fmt.h" +#include "stralloc.h" +#include "logmsg.h" +#include "srs2.h" + +#define WHO "srsforward" + +void die_nomem() { logmsg(WHO,111,FATAL,"out of memory"); } +void die_control() { logmsg(WHO,110,FATAL,"Unable to read control files"); } + +struct qmail qqt; +char *srsdomaininfo = 0; +stralloc srsdomains = {0}; +struct constmap mapsrsdomains; +stralloc srshost = {0}; +stralloc srserror = {0}; + +/** @file srsforward.c + @brief forwarding mails with SRS enhanced addresss + @return 0 on success (forwarded or not) + -3 SRS error with error output + 111 no memory / processing error + 110 control file not readable +*/ + +static int srserror_str(int code) { + if (!stralloc_copys(&srserror,"SRS: ")) die_nomem(); + if (!stralloc_cats(&srserror,srs_strerror(code))) die_nomem(); + if (!stralloc_0(&srserror)) die_nomem(); + return -3; +} + +ssize_t mywrite(int fd,char *buf,int len) +{ + qmail_put(&qqt,buf,len); + return len; +} + +char inbuf[BUFSIZE_LINE]; +char outbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); +buffer bo = BUFFER_INIT(mywrite,-1,outbuf,sizeof(outbuf)); + +char num[FMT_ULONG]; + +int main(int argc,char **argv) +{ + int i, j, r; + char *qqx; + srs_t *srs; + stralloc cookie = {0}; + char separator = '='; + char srssender[512]; + char *host = 0; + char *sender = 0; + char *dtline = 0; + char *sendhost = 0; + int alwaysrewrite = 0; + + sig_pipeignore(); + + sender = env_get("NEWSENDER"); + if (!sender) + logmsg(WHO,100,FATAL,"NEWSENDER not set"); + host = env_get("HOST"); + if (!host) + logmsg(WHO,100,FATAL,"HOST not set"); + dtline = env_get("DTLINE"); + if (!dtline) + logmsg(WHO,100,FATAL,"DTLINE not set"); + + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",auto_qmail)); + + if (!stralloc_cats(&srshost,"!")) die_nomem(); + if (!stralloc_cats(&srshost,host)) die_nomem(); + + switch (control_readfile(&srsdomains,"control/srsdomains",0)) { + case -1: die_control(); + case 0: if (!constmap_init(&mapsrsdomains,"",0,1)) die_nomem(); break; + case 1: if (!constmap_init(&mapsrsdomains,srsdomains.s,srsdomains.len,1)) die_nomem(); break; + } + if (constmap(&mapsrsdomains,srshost.s,srshost.len)) return 0; // domain blacklisted + if ((srsdomaininfo = constmap(&mapsrsdomains,host,str_len(host))) == 0) { + if ((srsdomaininfo = constmap(&mapsrsdomains,"*",1)) == 0) return 0; // '*' means always SRS + else alwaysrewrite = 1; + } + + if (*srsdomaininfo) { + i = str_chr(srsdomaininfo,'|'); // multiple cookies; separated by ' ' + if (srsdomaininfo[i] == '|') { + srsdomaininfo[i] = 0; + j = str_chr(srsdomaininfo + i + 1,'|'); + if (srsdomaininfo[i + j + 1] == '|') { + srsdomaininfo[i + j + 1] = 0; + sendhost = srsdomaininfo + i + j + 2; // separator: - + = + } + separator = srsdomaininfo[i + 1]; + } + if (!stralloc_copys(&cookie,srsdomaininfo)) die_nomem(); + if (!stralloc_0(&cookie)) die_nomem(); + if (!stralloc_copys(&srshost,"")) die_nomem(); + if (*sendhost) { + j = str_len(sendhost); + if (sendhost[j - 1] == '.') { + if (!stralloc_copys(&srshost,sendhost)) die_nomem(); + if (!stralloc_cats(&srshost,host)) die_nomem(); + } else + if (!stralloc_copys(&srshost,sendhost)) die_nomem(); + } else + if (!stralloc_copys(&srshost,host)) die_nomem(); + if (!stralloc_0(&srshost)) die_nomem(); + } else + die_control(); + + /* Let's go SRS rewrite */ + + srs = srs_new(); + + if (separator == '-' || separator == '+' || separator == '=') { // '=' is default + r = srs_set_separator(srs,separator); + if (r != SRS_SUCCESS) return srserror_str(r); + } + if (alwaysrewrite) { + r = srs_set_alwaysrewrite(srs,alwaysrewrite); + if (r != SRS_SUCCESS) return srserror_str(r); + } + + for (j = 0, i = 0; j < cookie.len; j++) { + if (cookie.s[j] == ' ' || cookie.s[j] == '\0' ) { + cookie.s[j] = '\0'; + r = srs_add_secret(srs,cookie.s + i); + if (r != SRS_SUCCESS) return srserror_str(r); + i = j + 1; + if (cookie.s[i] == ' ') { j++; continue; } + } + } + + if ((r = srs_forward(srs,srssender,sizeof(srssender),sender,srshost.s)) != SRS_SUCCESS) + logmsg(WHO,100,FATAL,B("Unable to forward: ",sender," ",srs_strerror(r))); + + if (qmail_open(&qqt) == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + qmail_puts(&qqt,dtline); + if (buffer_copy(&bo,&bi) != 0) + logmsg(WHO,111,FATAL,"unable to read message: "); + buffer_flush(&bo); + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + qmail_from(&qqt,srssender); + while (*++argv) qmail_to(&qqt,*argv); + qqx = qmail_close(&qqt); + if (*qqx) logmsg(WHO,*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + logmsg(WHO,0,LOG,B(srssender,": qp ",num)); + +} diff --git a/sqmail-4.3.07/src/srsreverse.c b/sqmail-4.3.07/src/srsreverse.c new file mode 100644 index 0000000..d9b57db --- /dev/null +++ b/sqmail-4.3.07/src/srsreverse.c @@ -0,0 +1,172 @@ +#include <unistd.h> +#include <sys/types.h> +#include "control.h" +#include "sig.h" +#include "constmap.h" +#include "readwrite.h" +#include "exit.h" +#include "env.h" +#include "qmail.h" +#include "auto_qmail.h" +#include "auto_break.h" +#include "buffer.h" +#include "case.h" +#include "str.h" +#include "fmt.h" +#include "stralloc.h" +#include "logmsg.h" +#include "srs2.h" + +#define WHO "srsreverse" + +void die_nomem() { logmsg(WHO,111,FATAL,"out of memory"); } +void die_control() { logmsg(WHO,110,FATAL,"Unable to read control files"); } + +struct qmail qqt; +char *vdomainuser = 0; +stralloc vdomains = {0}; +struct constmap mapvdomains; +char *srsdomaininfo = 0; +stralloc srsdomains = {0}; +struct constmap mapsrsdomains; +stralloc srserror = {0}; +stralloc srshost = {0}; + +/** @file srsreverse.c + @brief forwarding bounces with SRS enhanced addresss + @return 0 on success (forwarded or not) + -3 SRS error with error output + 111 no memory / processing error + 110 control file not readable +*/ + +static int srserror_str(int code) { + if (!stralloc_copys(&srserror,"SRS: ")) die_nomem(); + if (!stralloc_cats(&srserror,srs_strerror(code))) die_nomem(); + if (!stralloc_0(&srserror)) die_nomem(); + return -3; +} + +ssize_t mywrite(int fd,char *buf,int len) +{ + qmail_put(&qqt,buf,len); + return len; +} + +char inbuf[BUFSIZE_LINE]; +char outbuf[BUFSIZE_LINE]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); +buffer bo = BUFFER_INIT(mywrite,-1,outbuf,sizeof(outbuf)); + +char num[FMT_ULONG]; + +int main() +{ + int i, j, r; + char *recipient; + char *dtline; + char *qqx; + srs_t *srs; + stralloc cookie = {0}; + char separator = '='; + char srsrecipient[512]; + char *host = 0; + + sig_pipeignore(); + + recipient = env_get("RECIPIENT"); + if (!recipient) + logmsg(WHO,100,FATAL,"RECIPIENT not set"); + dtline = env_get("DTLINE"); + if (!dtline) + logmsg(WHO,100,FATAL,"DTLINE not set"); + host = env_get("HOST"); + if (!host) + logmsg(WHO,100,FATAL,"HOST not set"); + + if (chdir(auto_qmail) == -1) + logmsg(WHO,111,FATAL,B("unable to chdir to: ",auto_qmail)); + + /* Check for particular virtual SRS domain user */ + + switch (control_readfile(&vdomains,"control/virtualdomains",0)) { + case -1: die_control(); + case 0: if (!constmap_init(&mapvdomains,"",0,1)) die_nomem(); break; + case 1: if (!constmap_init(&mapvdomains,vdomains.s,vdomains.len,1)) die_nomem(); break; + } + + j = str_len(host); + for (i = 0; i <= j; ++i) + if ((i == 0) || (host[i] == '.')) { + if (!stralloc_copys(&srshost,"")) die_nomem(); + if (!stralloc_catb(&srshost,host + i,j - i)) die_nomem(); + if ((srsdomaininfo = constmap(&mapvdomains,srshost.s,srshost.len)) != 0) goto SRSDOMAINS; + } + if (!stralloc_copys(&srshost,host)) die_nomem(); + + SRSDOMAINS: + + switch (control_readfile(&srsdomains,"control/srsdomains",0)) { + case -1: die_control(); + case 0: if (!constmap_init(&mapsrsdomains,"",0,1)) die_nomem(); break; + case 1: if (!constmap_init(&mapsrsdomains,srsdomains.s,srsdomains.len,1)) die_nomem(); break; + } + if ((srsdomaininfo = constmap(&mapsrsdomains,srshost.s,srshost.len)) == 0) + if ((srsdomaininfo = constmap(&mapsrsdomains,"*",1)) == 0) return 0; + + if (*srsdomaininfo) { + i = str_chr(srsdomaininfo,'|'); // multiple cookies; separated by ' ' + if (srsdomaininfo[i] == '|') { + srsdomaininfo[i] = 0; + separator = srsdomaininfo[i+1]; + } + if (!stralloc_copys(&cookie,srsdomaininfo)) die_nomem(); + if (!stralloc_0(&cookie)) die_nomem(); + } + + /* strip virtual user from recipient */ + + if ((vdomainuser = constmap(&mapvdomains,host,j))) { + i = str_chr(recipient,*auto_break); + if (!case_diffb(recipient,i - 1,vdomainuser)) recipient += i + 1; + } + + /* Let's go SRS reverse */ + + srs = srs_new(); + + if (separator == '-' || separator == '+' || separator == '=') { // '=' is default + r = srs_set_separator(srs,separator); + if (r != SRS_SUCCESS) return srserror_str(r); + } + + for (j = 0, i = 0; j < cookie.len; j++) { + if (cookie.s[j] == ' ' || cookie.s[j] == '\0' ) { + cookie.s[j] = '\0'; + r = srs_add_secret(srs,cookie.s + i); + if (r != SRS_SUCCESS) return srserror_str(r); + i = j + 1; + if (cookie.s[i] == ' ') { j++; continue; } + } + } + + if ((r = srs_reverse(srs,srsrecipient,sizeof(srsrecipient),recipient)) != SRS_SUCCESS) { + logmsg(WHO,100,FATAL,B("unable to reverse: ",recipient," ",srs_strerror(r))); + } + + if (qmail_open(&qqt) == -1) + logmsg(WHO,111,FATAL,"unable to fork: "); + qmail_puts(&qqt,dtline); + if (buffer_copy(&bo,&bi) != 0) + logmsg(WHO,111,FATAL,"unable to read message: "); + buffer_flush(&bo); + + num[fmt_ulong(num,qmail_qp(&qqt))] = 0; + + qmail_from(&qqt,""); + qmail_to(&qqt,srsrecipient); + qqx = qmail_close(&qqt); + if (*qqx) logmsg(WHO,*qqx == 'D' ? 100 : 111,FATAL,qqx + 1); + logmsg(WHO,0,LOG,B(srsrecipient,": qp ",num)); + +} diff --git a/sqmail-4.3.07/src/strset.c b/sqmail-4.3.07/src/strset.c new file mode 100644 index 0000000..8f3ffe8 --- /dev/null +++ b/sqmail-4.3.07/src/strset.c @@ -0,0 +1,125 @@ +#include "strset.h" +#include "str.h" +#include "byte.h" +#include "alloc.h" + +uint32 strset_hash(char *s) +{ + unsigned char ch; + uint32 h; + + h = 5381LL; + + while ((ch = *s)) { + h = ((h << 5) + h) ^ ch; + ++s; + } + return h; +} + +int strset_init(strset *set) +{ + int h; + set->mask = 15; + set->n = 0; + set->a = 10; + + set->first = (int *) alloc(sizeof(int) * (set->mask + 1)); + if (!set->first) return 0; + set->p = (strset_list *) alloc(sizeof(strset_list) * set->a); + if (!set->p) { alloc_free(set->first); return 0; } + set->x = (char **) alloc(sizeof(char *) * set->a); + if (!set->x) { alloc_free(set->p); alloc_free(set->first); return 0; } + + for (h = 0; h <= set->mask; ++h) + set->first[h] = -1; + + return 1; +} + +char *strset_in(strset *set,char *s) +{ + uint32 h; + strset_list *sl; + int i; + char *xi; + + h = strset_hash(s); + i = set->first[h & set->mask]; + + while (i >= 0) { + sl = set->p + i; + if (sl->h == h) { + xi = set->x[i]; + if (!str_diff(xi,s)) return xi; + } + i = sl->next; + } + return 0; +} + +int strset_add(strset *set,char *s) +{ + uint32 h; + int n; + strset_list *sl; + + n = set->n; + + if (n == set->a) { + int newa; + strset_list *newp; + char **newx; + + newa = n + 10 + (n >> 3); + newp = (strset_list *) alloc(sizeof(strset_list) * newa); + if (!newp) return 0; + newx = (char **) alloc(sizeof(char *) * newa); + if (!newx) { alloc_free(newp); return 0; } + + byte_copy(newp,sizeof(strset_list) * n,set->p); + byte_copy(newx,sizeof(char *) * n,set->x); + alloc_free(set->p); + alloc_free(set->x); + set->p = newp; + set->x = newx; + set->a = newa; + + if (n + n + n > set->mask) { + int newmask; + int *newfirst; + int i; + uint32 h; + + newmask = set->mask + set->mask + 1; + newfirst = (int *) alloc(sizeof(int) * (newmask + 1)); + if (!newfirst) return 0; + + for (h = 0; h <= newmask; ++h) + newfirst[h] = -1; + + for (i = 0; i < n; ++i) { + sl = set->p + i; + h = sl->h & newmask; + sl->next = newfirst[h]; + newfirst[h] = i; + } + + alloc_free(set->first); + set->first = newfirst; + set->mask = newmask; + } + } + + h = strset_hash(s); + + sl = set->p + n; + sl->h = h; + h &= set->mask; + sl->next = set->first[h]; + set->first[h] = n; + set->x[n] = s; + set->n = n + 1; + + return 1; +} diff --git a/sqmail-4.3.07/src/successes.sh b/sqmail-4.3.07/src/successes.sh new file mode 100644 index 0000000..ec5efd3 --- /dev/null +++ b/sqmail-4.3.07/src/successes.sh @@ -0,0 +1,13 @@ +awk ' + /^d k/ { + reason = $11 + succ[reason] += 1 + xdelay[reason] += $5 - $4 + } + END { + for (reason in succ) { + str = sprintf("%.2f",xdelay[reason]) + print succ[reason],str,reason + } + } +' diff --git a/sqmail-4.3.07/src/suids.sh b/sqmail-4.3.07/src/suids.sh new file mode 100644 index 0000000..da2fb81 --- /dev/null +++ b/sqmail-4.3.07/src/suids.sh @@ -0,0 +1,22 @@ +awk ' + /^m/ { + uid = $10 + messages[uid] += 1 + succ[uid] += $5 + fail[uid] += $6 + temp[uid] += $7 + mbytes[uid] += $4 + sbytes[uid] += $4 * $5 + rbytes[uid] += $4 * ($5 + $6) + } + /^d/ { + uid = $10 + xdelay[uid] += $5 - $4 + } + END { + for (uid in messages) { + str = sprintf("%.6f",xdelay[uid]) + print messages[uid],mbytes[uid],sbytes[uid],rbytes[uid],succ[uid] + fail[uid],succ[uid] + fail[uid] + temp[uid],str,uid + } + } +' diff --git a/sqmail-4.3.07/src/tai64nfrac.c b/sqmail-4.3.07/src/tai64nfrac.c new file mode 100644 index 0000000..f3db977 --- /dev/null +++ b/sqmail-4.3.07/src/tai64nfrac.c @@ -0,0 +1,85 @@ +#include "buffer.h" +#include "stralloc.h" +#include "exit.h" +#include "readwrite.h" +#include "open.h" +#include "scan.h" +#include "fmt.h" +#include "getln.h" + +#define TAI64NLEN 24 + +/** @file tai64nfrac + @brief Read a TAI64N external format timestamp from stdin and + write fractional seconds since epoch (TAI, not UTC) to stdout. + Return the characters after the timestamp. + */ + +char outbuf[64]; +buffer bo = BUFFER_INIT(write,1,outbuf,sizeof(outbuf)); + +static void outs(char *s) +{ + if (buffer_puts(&bo,s) == -1) _exit(1); + if (buffer_flush(&bo) == -1) _exit(1); +} + +static void outi(int i) +{ + char num[FMT_ULONG]; + + if (buffer_put(&bo,num,fmt_ulong(num,(unsigned long) i)) == -1) _exit(1); + if (buffer_flush(&bo) == -1) _exit(1); +} + +char inbuf[1024]; +buffer bi = BUFFER_INIT(read,0,inbuf,sizeof(inbuf)); + +int main(void) +{ + int c; + int i; + int match; + unsigned long u; + unsigned long seconds; + unsigned long nanoseconds; + stralloc line = {0}; + +/* Read from stdin */ + + buffer_init(&bi,read,0,inbuf,sizeof(inbuf)); + + for (;;) { + if (getln(&bi,&line,&match,'\n') != 0) _exit(1); + if (!match) break; + if (!stralloc_0(&line)) _exit(1); + + seconds = 0; + nanoseconds = 0; + + if (line.s[0] == '@') { /* tai64 timestamp */ + for (i = 1; i <= TAI64NLEN; i++) { + c = (int)line.s[i]; + u = c - '0'; + if (u >= 10) { + u = c - 'a'; + if (u >= 6) break; + u += 10; + } + seconds <<= 4; + seconds += nanoseconds >> 28; + nanoseconds &= 0xfffffff; + nanoseconds <<= 4; + nanoseconds += u; + } + seconds -= 4611686018427387914ULL; + seconds = seconds > 0 ? seconds : 0; + outi(seconds); outs("."); outi(nanoseconds); outs(line.s + i); outs("\n"); + } else { + outs("tai64nfrac: fatal: Wrong TAI64N input format."); outs("\n"); + _exit(1); + } + } + + _exit(0); +} diff --git a/sqmail-4.3.07/src/tcpto.c b/sqmail-4.3.07/src/tcpto.c new file mode 100644 index 0000000..92c33ea --- /dev/null +++ b/sqmail-4.3.07/src/tcpto.c @@ -0,0 +1,169 @@ +#include <sys/socket.h> +#include <unistd.h> +#include "tcpto.h" +#include "open.h" +#include "lock.h" +#include "seek.h" +#include "now.h" +#include "ip.h" +#include "ipalloc.h" +#include "byte.h" +#include "datetime.h" + +char tcpto_buf[1024]; + +static int flagwasthere; +static int fdlock; + +static int getbuf() +{ + int r; + int fd; + + fdlock = open_write("queue/lock/tcpto"); + if (fdlock == -1) return 0; + fd = open_read("queue/lock/tcpto"); + if (fd == -1) { close(fdlock); return 0; } + if (lock_ex(fdlock) == -1) { close(fdlock); close(fd); return 0; } + r = read(fd,tcpto_buf,sizeof(tcpto_buf)); + close(fd); + if (r < 0) { close(fdlock); return 0; } + r >>= 5; + if (!r) close(fdlock); + return r; +} + +int tcpto(struct ip_mx *ix) +{ + int af = ix->af; + struct ip_address *ip = &ix->addr; + int n; + int i; + char *record; + datetime_sec when; + + flagwasthere = 0; + + n = getbuf(); + if (!n) return 0; + close(fdlock); + + record = tcpto_buf; + + for (i = 0; i < n; ++i) { + if (af == record[0] && byte_equal(ip->d,af == AF_INET ? 4 : 16,record + 16)) { + flagwasthere = 1; + if (record[4] >= 2) { + when = (unsigned long) (unsigned char) record[11]; + when = (when << 8) + (unsigned long) (unsigned char) record[10]; + when = (when << 8) + (unsigned long) (unsigned char) record[9]; + when = (when << 8) + (unsigned long) (unsigned char) record[8]; + + if (now() - when < ((60 + (getpid() & 31)) << 6)) return 1; + } + return 0; + } + record += 32; + } + return 0; +} + +void tcpto_err(struct ip_mx *ix,int flagerr) +{ + int af = ix->af; + struct ip_address *ip = &ix->addr; + int n; + int i; + char *record; + datetime_sec when; + datetime_sec firstwhen; + int firstpos; + datetime_sec lastwhen; + + if (!flagerr) + if (!flagwasthere) + return; /* could have been added, but not worth the effort to check */ + + n = getbuf(); + if (!n) return; + + record = tcpto_buf; + + for (i = 0; i < n; ++i) { + if (af == record[0] && byte_equal(ip->d,af == AF_INET ? 4 : 16,record + 16)) { + if (!flagerr) + record[4] = 0; + else { + lastwhen = (unsigned long) (unsigned char) record[11]; + lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[10]; + lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[9]; + lastwhen = (lastwhen << 8) + (unsigned long) (unsigned char) record[8]; + when = now(); + + if (record[4] && (when < 120 + lastwhen)) { close(fdlock); return; } + + if (++record[4] > 10) record[4] = 10; + record[8] = when; when >>= 8; + record[9] = when; when >>= 8; + record[10] = when; when >>= 8; + record[11] = when; + } + if (seek_set(fdlock,i << 5) == 0) + if (write(fdlock,record,32) < 32) + ; /*XXX*/ + close(fdlock); + return; + } + record += 32; + } + + if (!flagerr) { close(fdlock); return; } + + record = tcpto_buf; + + for (i = 0; i < n; ++i) { + if (!record[4]) break; + record += 32; + } + + if (i >= n) { + firstpos = -1; + record = tcpto_buf; + + for (i = 0; i < n; ++i) { + when = (unsigned long) (unsigned char) record[11]; + when = (when << 8) + (unsigned long) (unsigned char) record[10]; + when = (when << 8) + (unsigned long) (unsigned char) record[9]; + when = (when << 8) + (unsigned long) (unsigned char) record[8]; + when += (record[4] << 10); + if ((firstpos < 0) || (when < firstwhen)) { + firstpos = i; + firstwhen = when; + } + record += 32; + } + i = firstpos; + } + + if (i >= 0) { + record = tcpto_buf + (i << 5); + record[0] = af; + if (af == AF_INET6) + byte_copy(record + 16,16,ip->d); + else { + byte_copy(record + 16,4,ip->d); + byte_copy(record + 20,12,"............"); + } + when = now(); + record[8] = when; when >>= 8; + record[9] = when; when >>= 8; + record[10] = when; when >>= 8; + record[11] = when; + record[4] = 1; + if (seek_set(fdlock,i << 5) == 0) + if (write(fdlock,record,32) < 32) + ; /*XXX*/ + } + + close(fdlock); +} diff --git a/sqmail-4.3.07/src/tcpto_clean.c b/sqmail-4.3.07/src/tcpto_clean.c new file mode 100644 index 0000000..e0b6969 --- /dev/null +++ b/sqmail-4.3.07/src/tcpto_clean.c @@ -0,0 +1,21 @@ +#include <unistd.h> +#include "tcpto.h" +#include "open.h" +#include "buffer.h" + +char tcpto_cleanbuf[1024]; + +void tcpto_clean() /* running from queue/mess */ +{ + int fd; + int i; + buffer bo; + + fd = open_write("../lock/tcpto"); + if (fd == -1) return; + buffer_init(&bo,write,fd,tcpto_cleanbuf,sizeof(tcpto_cleanbuf)); + for (i = 0; i < sizeof(tcpto_cleanbuf); ++i) + buffer_put(&bo,"",1); + buffer_flush(&bo); /* if it fails, bummer */ + close(fd); +} diff --git a/sqmail-4.3.07/src/tls_errors.c b/sqmail-4.3.07/src/tls_errors.c new file mode 100644 index 0000000..5c30236 --- /dev/null +++ b/sqmail-4.3.07/src/tls_errors.c @@ -0,0 +1,158 @@ +#include <unistd.h> +#include "stralloc.h" +#include "tls_errors.h" +#include "error.h" + +/** @file tls_errors.c + @brief temp_tls* routines are used for error messges +*/ + +/* TLS error messages: A) Setup */ + +void temp_tlscert() +{ + out("ZCan't load X.509 certificate: "); + outsafe(&certfile); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlskey() +{ + out("ZCan't load X.509 private key: "); + outsafe(&keyfile); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlschk() +{ + out("ZKeyfile does not match X.509 certificate: "); + outsafe(&keypwd); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlsca() +{ + out("ZI wasn't able to set up CAFILE: "); + outsafe(&cafile); + out(" or CADIR: "); + outsafe(&cadir); + out(" for TLS. (#4.4.1)\n"); + zerodie(); +} + +void temp_tlscipher() +{ + out("ZI wasn't able to process the TLS ciphers: "); + outsafe(&ciphers); + out(" (#4.4.1)\n"); + zerodie(); +} + +/* TLS error messages: B) Connection related */ + +void temp_tlsctx() +{ + out("ZI wasn't able to create TLS context for: "); + outsafe(&host); out(" at "); out(remotehost.s); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlscon() +{ + errno = EPROTO; + out("ZI wasn't able to establish a TLS connection with: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlserr() +{ + errno = EPROTO; + out("ZTLS connection/protocol error with: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlshost() +{ + out("ZI wasn't able to negotiate a StartTLS connection with: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(". (#4.4.1)\n"); + zerodie(); +} + + +/* TLS error messages: C) Verification related */ + +void temp_tlspeercert() +{ + out("ZUnable to obtain X.509 certificate from: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlspeerverify() +{ + out("ZUnable to verify X.509 certificate from: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlspeervalid() +{ + out("ZUnable to validate X.509 certificate Subject for: "); + outsafe(&host); out(" at "); out(remotehost.s); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlscertfp() +{ + out("ZReceived X.509 certificate from: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(" does not match fingerprint: "); + outsafe(&cafile); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_invaliddigest() +{ + out("ZInvalid digest length provided given for: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlsamissing() +{ + out("ZTLSA X.509 cert required but missing from: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlsainvalid() +{ + out("ZTLSA fingerprint matching error for: "); + out(remotehost.s); + out(". (#4.4.1)\n"); + zerodie(); +} + +void temp_tlsdigest() +{ + out("ZReceived X.509 certificate from: "); + out(remotehost.s); out(" for "); outsafe(&host); + out(" posses an unknown digest method"); + out(". (#4.4.1)\n"); + zerodie(); +} diff --git a/sqmail-4.3.07/src/tls_remote.c b/sqmail-4.3.07/src/tls_remote.c new file mode 100644 index 0000000..1318b4e --- /dev/null +++ b/sqmail-4.3.07/src/tls_remote.c @@ -0,0 +1,387 @@ +#include <unistd.h> +#include "ucspissl.h" +#include "fmt.h" +#include "stralloc.h" +#include "str.h" +#include "byte.h" +#include "case.h" +#include "dns.h" +#include "constmap.h" +#include "tls_remote.h" +#include "tls_errors.h" + +/** @file tls_remote.c -- TLS client functions + @brief connection functions: tls_conn, tls_exit; + verification functions: tls_certkey, tls_checkpeer, tls_fingerprint, tlsa_check; + tls_destination, tls_domaincert + dummy functions: tls_crlcheck + + tls_checkpeer: r = 0 -> ADH, r = 1 -> wildcard DN, r = 2 -> DN, r = 3 -> CA; r < 0 -> error + tls_fingerprint: r = 0 -> failed, r = 1 -> ok; r < 0 -> error + tlsa_check: r = 0 -> nothing, r = usage + 1, r < 0 -> error +*/ + +/* Caution: OpenSSL's X509_pubkey_digest() does not work as expected. + I've included now: X509_pkey_digest() and X509_cert_digest() (as makro) */ + +#define X509_cert_digest X509_digest + +int tls_certkey(SSL_CTX *ctx,const char *cert,const char *key,char *ppwd) +{ + if (!cert) return 0; + + if (SSL_CTX_use_certificate_chain_file(ctx,cert) != 1) + return -1; + + if (!key) key = cert; + + if (ppwd) SSL_CTX_set_default_passwd_cb_userdata(ctx,ppwd); + + if (SSL_CTX_use_PrivateKey_file(ctx,key,SSL_FILETYPE_PEM) != 1) + return -2; + + if (SSL_CTX_check_private_key(ctx) != 1) + return -3; + + return 0; +} + +int tls_conn(SSL *ssl,int smtpfd) +{ + SSL_set_options(ssl,SSL_OP_NO_SSLv2); + SSL_set_options(ssl,SSL_OP_NO_SSLv3); + return SSL_set_fd(ssl,smtpfd); +} + +int tls_checkpeer(SSL *ssl,X509 *cert,const stralloc host,const int flag,const int verify) +{ + STACK_OF(GENERAL_NAME) *extensions; + const GENERAL_NAME *ext; + char buf[SSL_NAME_LEN]; + char *dnsname = 0; + int dname = 0; + int num; + int len; + int fflag; + int i; + int rc = 0; + + fflag = flag; + if (flag > 20) fflag = flag - 20; + if (flag > 10) fflag = flag - 10; + + /* X.509 CA DN/SAN name validation against DNS */ + + if (host.len && fflag > 4) { + extensions = (GENERAL_NAME *)X509_get_ext_d2i(cert,NID_subject_alt_name,0,0); + num = sk_GENERAL_NAME_num(extensions); /* num = 0, if no SAN extensions */ + + for (i = 0; i < num; ++i) { + ext = sk_GENERAL_NAME_value(extensions,i); + if (ext->type == GEN_DNS) { + #if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL + if (ASN1_STRING_type(ext->d.ia5) != V_ASN1_IA5STRING) continue; + dnsname = (char *)ASN1_STRING_data(ext->d.ia5); + #else + if (OBJ_sn2nid((const char*)ext->d.ia5) != V_ASN1_IA5STRING) continue; + dnsname = (char *)ASN1_STRING_get0_data(ext->d.ia5); + #endif + len = ASN1_STRING_length(ext->d.ia5); + dname = 1; + } + } + + if (!dname) { + X509_NAME_get_text_by_NID(X509_get_subject_name(cert),NID_commonName,buf,sizeof(buf)); + buf[SSL_NAME_LEN - 1] = 0; + dnsname = buf; + len = SSL_NAME_LEN - 1; + } + + switch (fflag) { + case 5: if (dnsname[0] == '*' && dnsname[1] == '.') + if (case_diffrs(dnsname + 1,host.s)) return -3; + if (case_diffrs(dnsname,host.s)) return -3; + rc = 3; break; + case 6: if (case_diffs(dnsname,host.s)) return -3; + rc = 2; break; + } + } + + /* X.509 CA Verification: root CA must be available */ + + if (fflag > 3 && verify > -2) { + if (SSL_get_verify_result(ssl) != X509_V_OK) return -2; + else rc = 1; + } + + return rc; +} + +int tls_checkcrl(SSL *ssl) // not implemented yet +{ + + return 0; +} + +int dig_ascii(char *digascii,const char *digest,const int len) +{ + static const char hextab[] = "0123456789abcdef"; + int j; + + for (j = 0; j < len; j++) { + digascii[2 * j] = hextab[(unsigned char)digest[j] >> 4]; + digascii[2 * j + 1] = hextab[(unsigned char)digest[j] & 0x0f]; + } + digascii[2 * len] = '\0'; + + return (2 * j); // 2*len +} + +/* X509_pkey_digest() takes the same args as X509_digest(); + however returning the correct hash of pubkey in md. + Subjects keys are restricted to 2048 byte in size. + Return codes: 1: sucess, 0: failed. */ + +int X509_pkey_digest(const X509 *cert,const EVP_MD *type,unsigned char *md,unsigned int *dlen) +{ + unsigned int len = 0; + unsigned int size = 2048; + unsigned char *buf; + unsigned char *buf2; + unsigned char buffer[size]; // avoid malloc + +/* Following Viktor's suggestion */ + + if (!X509_get0_pubkey_bitstr(cert)) return 0; // no Subject public key + + len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),0); + if (len > size) return 0; + buf2 = buf = buffer; + i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),(unsigned char **)&buf2); + if (buf2 - buf != len) return 0; + + if (!EVP_Digest(buf,len,md,dlen,type,0)) return 0; // OpenSSL voodoo + return 1; +} + +/* Return codes: -4: no X.509 cert (fatal), -3: matching error (deferred), + -2: unsupported type, -1: weird TLSA record + 0: No X.509 cert; seen: usage++; */ + +int tlsa_check(const STACK_OF(X509) *certs,const stralloc host,const unsigned long p) +{ + const EVP_MD *methodsha256 = EVP_sha256(); + const EVP_MD *methodsha512 = EVP_sha512(); + stralloc out = {0}; + stralloc sa = {0}; + stralloc cn = {0}; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + unsigned int n = 0; + int i = 0; + int r; + char port[FMT_ULONG]; + uint16 type; + uint16 selector; + uint16 usage; + +// construct TLSA FQDN -- simple procedure; returning Usage + + if (host.len < 2) return 0; + if (!stralloc_copyb(&sa,"_",1)) temp_nomem(); + port[fmt_ulong(port,p)] = 0; + if (!stralloc_cats(&sa,port)) temp_nomem(); + if (!stralloc_cats(&sa,"._tcp.")) temp_nomem(); + if (!stralloc_cats(&sa,host.s)) temp_nomem(); + + if (dns_cname(&cn,&sa) > 0) // query name could be a cname + { if (dns_tlsa(&out,&cn) <= 0) return 0; } + else + { if (dns_tlsa(&out,&sa) <= 0) return 0; } + if (out.len < 5) return -1; + + /* https://www.openssl.org/docs/man3.0/man3/X509_digest.html (1.1.1): + "The len parameter, if not NULL, points to a place where the digest size will be stored." + [sigh] + */ + + do { + usage = (unsigned char) out.s[i]; // Usage: PKIX-TA [0], PKIX-EE [1], DANE-TA [2], DANE-EE [3] + selector = (unsigned char) out.s[i + 1]; // Selector: 0 = Cert, 1 = SPKI + type = (unsigned char) out.s[i + 2]; // Type: 0/1/2 = [Cert|SPKI]/SHA256/SHA512 + + unsigned len = sk_X509_num(certs); + for (n = 0; n < len; n++) { + X509 *cert = sk_X509_value(certs,n); + if (type == 1) { + if (selector == 0) r = X509_cert_digest(cert,methodsha256,digest,&dlen); + if (selector == 1) r = X509_pkey_digest(cert,methodsha256,digest,&dlen); + } else if (type == 2) { + if (selector == 0) r = X509_cert_digest(cert,methodsha512,digest,&dlen); + if (selector == 1) r = X509_pkey_digest(cert,methodsha512,digest,&dlen); + } else + return -2; + + if (!byte_diff(digest,dlen,out.s + i + 3)) return ++usage; + } + + i += (dlen + 3); + } while (i < out.len - 4); + + return -3; +} + +int tls_fingerprint(X509 *cert,const char *fingerprint,int dlen) +{ + const EVP_MD *methodsha1 = EVP_sha1(); + const EVP_MD *methodsha224 = EVP_sha224(); + const EVP_MD *methodsha256 = EVP_sha256(); + const EVP_MD *methodsha512 = EVP_sha512(); + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned char digascii[257]; + unsigned int len; + + switch (dlen) { /* fetch digest from cert; len = bitlength/8 */ + case 40: if (!X509_digest(cert,methodsha1,digest,&len)) return -2; + case 56: if (!X509_digest(cert,methodsha224,digest,&len)) return -2; + case 64: if (!X509_digest(cert,methodsha256,digest,&len)) return -2; + case 128: if (!X509_digest(cert,methodsha512,digest,&len)) return -2; + default: return -3; + } + + len = dig_ascii(digascii,digest,len); + if (!str_diffn(digascii,fingerprint,len)) return 1; + + return 0; +} + +int tls_exit(SSL *ssl) +{ + if (SSL_shutdown(ssl) == 0) + SSL_shutdown(ssl); + + return 0; +} + +/** @brief tls_destination + @param stralloc hostname (maybe 0-terminated) + + Certificate Fallthru + + @return values: | ADH | Cert *DN FQDN Hash | noTLSA noTLS + ----------+-----+--------------------+------------- + optional TLS | 1 | 3 - - - | - 9 + mandatory TLS | 2 | 4 5 6 7 | 8 + + no TLS -1 + */ + +int tls_destination(const stralloc hostname) +{ + int i; + stralloc tlshost = {0}; + stralloc tlsdest = {0}; + + if (!stralloc_copy(&tlshost,&hostname)) temp_nomem(); + if (!stralloc_0(&tlshost)) temp_nomem(); + +// Host rules + + if (!stralloc_copys(&tlsdest,"!")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return -1; + + if (!stralloc_copys(&tlsdest,"?")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 9; + + if (!stralloc_copys(&tlsdest,"/")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 8; + + if (!stralloc_copys(&tlsdest,"%")) temp_nomem(); // CERT + hash + if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 7; + + if (!stralloc_copys(&tlsdest,"=")) temp_nomem(); // CERT + FQDN + if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 6; + + if (!stralloc_copys(&tlsdest,"~")) temp_nomem(); // CERT + Wild + if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 5; + +// Domain rules + + for (i = 0; i < tlshost.len; ++i) // TLS fallthru + if ((i == 0) || (tlshost.s[i] == '.')) { + if (!stralloc_copys(&tlsdest,"?")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 9; + } + + for (i = 0; i < tlshost.len; ++i) // no TLSA + if ((i == 0) || (tlshost.s[i] == '.')) { + if (!stralloc_copys(&tlsdest,"/")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 8; + } + + for (i = 0; i < tlshost.len; ++i) // CERT + Wild + if ((i == 0) || (tlshost.s[i] == '.')) { + if (!stralloc_copys(&tlsdest,"~")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 5; + } + + for (i = 0; i < tlshost.len; ++i) // CERT - generic + if ((i == 0) || (tlshost.s[i] == '.')) { + if (!stralloc_copys(&tlsdest,"")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 4; + } + + for (i = 0; i < tlshost.len; ++i) // ADH per host/domain + if ((i == 0) || (tlshost.s[i] == '.')) { + if (!stralloc_copys(&tlsdest,"-")) temp_nomem(); + if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 2; + } + +// General rules (mandatory TLS) + + tlsdestinfo = 0; + if (constmap(&maptlsdestinations,"/*",2)) return 8; // no TLSA + if (constmap(&maptlsdestinations,"=*",2)) return 6; // CERT + FQDN + if (constmap(&maptlsdestinations,"~*",2)) return 5; // CERT + Wild + if (constmap(&maptlsdestinations,"+*",2)) return 4; // CERT + if (constmap(&maptlsdestinations,"-*",2)) return 2; // ADH + +// Fall thru rules (optional TLS) + + if (constmap(&maptlsdestinations,"?",1)) return 9; // fallback to no TLS + if (constmap(&maptlsdestinations,"*",1)) return 3; // CERT + if (constmap(&maptlsdestinations,"-",1)) return 1; // ADH + + return 0; +} + +int tls_domaincerts(const stralloc domainname) +{ + int i; + tlsdomaininfo = 0; // extern + +/* Our Certs - per domain */ + + if (domainname.len) + for (i = 0; i < domainname.len; ++i) + if ((i == 0) || (domainname.s[i] == '.')) + if ((tlsdomaininfo = constmap(&mapdomaincerts,domainname.s + i,domainname.len - i))) return 2; + +/* Standard Cert (if any) */ + + if ((tlsdomaininfo = constmap(&mapdomaincerts,"*",1))) return 1; + + return 0; +} diff --git a/sqmail-4.3.07/src/tls_start.c b/sqmail-4.3.07/src/tls_start.c new file mode 100644 index 0000000..a632f94 --- /dev/null +++ b/sqmail-4.3.07/src/tls_start.c @@ -0,0 +1,82 @@ +#include <unistd.h> +#include "scan.h" +#include "env.h" +#include "open.h" +#include "stralloc.h" +#include "fd.h" +#include "logmsg.h" +#include "qmail.h" + +#define WHO "tls_start" + +static void die_nomem() { logmsg(WHO,111,FATAL,"out of memory"); } + +int starttls_init(void) +{ + unsigned long fd; + char *fdstr; + + if (!(fdstr = env_get("SSLCTLFD"))) return 0; + if (!scan_ulong(fdstr,&fd)) return 0; + if (write((int)fd,"Y",1) < 1) return 0; + + if (!(fdstr = env_get("SSLREADFD"))) return 0; + if (!scan_ulong(fdstr,&fd)) return 0; + if (fd_move(0,(int)fd) == -1) return 0; + + if (!(fdstr = env_get("SSLWRITEFD"))) return 0; + if (!scan_ulong(fdstr,&fd)) return 0; + if (fd_move(1,(int)fd) == -1) return 0; + + return 1; +} + +int starttls_info(void) +{ + unsigned long fd; + char *fdstr; + char envbuf[BUFSIZE_MESS]; + char *x; + int j; + + stralloc ssl_env = {0}; + stralloc ssl_parm = {0}; + stralloc ssl_value = {0}; + + if (!(fdstr = env_get("SSLCTLFD"))) return 0; + if (!scan_ulong(fdstr,&fd)) return 0; + + while ((j = read(fd,envbuf,BUFSIZE_MESS)) > 0 ) { + if (!stralloc_catb(&ssl_env,envbuf,j)) die_nomem(); + if (ssl_env.len >= 2 && ssl_env.s[ssl_env.len - 2] == 0 && ssl_env.s[ssl_env.len - 1] == 0) + break; + } + if (j <= 0) return 0; // nothing to read; really nothing + + x = ssl_env.s; + + for (j = 0; j < ssl_env.len - 1; ++j) { + if ( *x != '=' ) { + if (!stralloc_catb(&ssl_parm,x,1)) die_nomem(); + x++; + } else { + if (!stralloc_0(&ssl_parm)) die_nomem(); + x++; + + for (; j < ssl_env.len - j - 1; ++j) { + if ( *x != '\0' ) { + if (!stralloc_catb(&ssl_value,x,1)) die_nomem(); + x++; + } else { + if (!stralloc_0(&ssl_value)) die_nomem(); + x++; + if (!env_put(ssl_parm.s,ssl_value.s)) die_nomem(); + ssl_parm.len = 0; + ssl_value.len = 0; + break; + } + } + } + } + return j; +} diff --git a/sqmail-4.3.07/src/tls_timeoutio.c b/sqmail-4.3.07/src/tls_timeoutio.c new file mode 100644 index 0000000..c5f40a2 --- /dev/null +++ b/sqmail-4.3.07/src/tls_timeoutio.c @@ -0,0 +1,99 @@ +/* This is essentially taken from Eric Vermeulen's TLS patch */ +#include "select.h" +#include "error.h" +#include "ndelay.h" +#include "now.h" +#include "logmsg.h" +#include "ucspissl.h" +#include "tls_timeoutio.h" + +int tls_timeoutio(int (*fun)(), + int t, int rfd, int wfd, SSL *ssl, char *buf, int len) +{ + int n; + const datetime_sec end = (datetime_sec)t + now(); + + do { + fd_set fds; + struct timeval tv; + + const int r = buf ? fun(ssl,buf,len) : fun(ssl); + if (r > 0) return r; + + t = end - now(); + if (t < 0) break; + tv.tv_sec = (time_t)t; tv.tv_usec = 0; + + FD_ZERO(&fds); + switch (SSL_get_error(ssl,r)) { + default: return r; /* some other error */ + case SSL_ERROR_WANT_READ: + FD_SET(rfd,&fds); n = select(rfd + 1,&fds,NULL,NULL,&tv); + break; + case SSL_ERROR_WANT_WRITE: + FD_SET(wfd,&fds); n = select(wfd + 1,NULL,&fds,NULL,&tv); + break; + } + + /* n is the number of descriptors that changed status */ + } while (n > 0); + + if (n != -1) errno = ETIMEDOUT; + return -1; +} + +int tls_timeoutaccept(int t,int rfd,int wfd,SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = tls_timeoutio(SSL_accept,t,rfd,wfd,ssl,NULL,0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl,SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int tls_timeoutconn(int t,int rfd,int wfd,SSL *ssl) +{ + int r; + + /* if connection is established, keep NDELAY */ + if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1; + r = tls_timeoutio(SSL_connect,t,rfd,wfd,ssl,NULL,0); + + if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); } + else SSL_set_mode(ssl,SSL_MODE_ENABLE_PARTIAL_WRITE); + + return r; +} + +int tls_timeoutrehandshake(int t,int rfd,int wfd,SSL *ssl) +{ + int r; + + SSL_renegotiate(ssl); + r = tls_timeoutio(SSL_do_handshake,t,rfd,wfd,ssl,NULL,0); + if (r <= 0) return r; + if (SSL_get_state(ssl) & SSL_ST_CONNECT) return -2; /* now a macro in ssl.h */ + + /* this is for the client only */ + SSL_set_connect_state(ssl); + + return tls_timeoutio(SSL_do_handshake,t,rfd,wfd,ssl,NULL,0); +} + +int tls_timeoutread(int t,int rfd,int wfd,SSL *ssl,char *buf,int len) +{ + if (!buf) return 0; + if (SSL_pending(ssl)) return SSL_read(ssl,buf,len); + return tls_timeoutio(SSL_read,t,rfd,wfd,ssl,buf,len); +} + +int tls_timeoutwrite(int t,int rfd,int wfd,SSL *ssl,char *buf,int len) +{ + if (!buf) return 0; + return tls_timeoutio(SSL_write,t,rfd,wfd,ssl,buf,len); +} diff --git a/sqmail-4.3.07/src/token822.c b/sqmail-4.3.07/src/token822.c new file mode 100644 index 0000000..239887c --- /dev/null +++ b/sqmail-4.3.07/src/token822.c @@ -0,0 +1,461 @@ +#include "stralloc.h" +#include "alloc.h" +#include "genalloc.h" +#include "str.h" +#include "token822.h" + +static struct token822 comma = { TOKEN822_COMMA }; + +void token822_reverse(token822_alloc *ta) +{ + int i; + int n; + struct token822 temp; + + n = ta->len - 1; + for (i = 0; i + i < n; ++i) { + temp = ta->t[i]; + ta->t[i] = ta->t[n - i]; + ta->t[n - i] = temp; + } +} + +GEN_ALLOC_ready(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_ready) +GEN_ALLOC_readyplus(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus) +GEN_ALLOC_append(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus,token822_append) + +static int needspace(int t1,int t2) +{ + if (!t1) return 0; + if (t1 == TOKEN822_COLON) return 1; + if (t1 == TOKEN822_COMMA) return 1; + if (t2 == TOKEN822_LEFT) return 1; + + switch (t1) { + case TOKEN822_ATOM: case TOKEN822_LITERAL: + case TOKEN822_QUOTE: case TOKEN822_COMMENT: + switch (t2) { + case TOKEN822_ATOM: case TOKEN822_LITERAL: + case TOKEN822_QUOTE: case TOKEN822_COMMENT: + return 1; + } + } + return 0; +} + +static int atomok(char ch) +{ + switch (ch) { + case ' ': case '\t': case '\r': case '\n': + case '(': case '[': case '"': + case '<': case '>': case ';': case ':': + case '@': case ',': case '.': + return 0; + } + return 1; +} + +static void atomcheck(struct token822 *t) +{ + int i; + char ch; + + for (i = 0; i < t->slen; ++i) { + ch = t->s[i]; + if ((ch < 32) || (ch > 126) || (ch == ')') || (ch == ']') || (ch == '\\')) { + t->type = TOKEN822_QUOTE; + return; + } + } +} + +int token822_unparse(stralloc *sa,token822_alloc *ta,unsigned int linelen) +{ + struct token822 *t; + int len; + int ch; + int i; + int j; + int lasttype; + int newtype; + char *s; + char *lineb; + char *linee; + + len = 0; + lasttype = 0; + + for (i = 0; i < ta->len; ++i) { + t = ta->t + i; + newtype = t->type; + if (needspace(lasttype,newtype)) ++len; + lasttype = newtype; + + switch (newtype) { + case TOKEN822_COMMA: + len += 3; break; + case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: case TOKEN822_RIGHT: + case TOKEN822_SEMI: case TOKEN822_COLON: + ++len; break; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT: + if (t->type != TOKEN822_ATOM) len += 2; + for (j = 0; j < t->slen; ++j) + switch (ch = t->s[j]) { + case '"': case '[': case ']': case '(': case ')': + case '\\': case '\r': case '\n': ++len; + default: ++len; + } + break; + } + } + len += 2; + + if (!stralloc_ready(sa,len)) return -1; + + s = sa->s; + lineb = s; + linee = 0; + + lasttype = 0; + + for (i = 0; i < ta->len; ++i) { + t = ta->t + i; + newtype = t->type; + if (needspace(lasttype,newtype)) *s++ = ' '; + lasttype = newtype; + + switch (newtype) { + case TOKEN822_COMMA: + *s++ = ','; +#define NSUW \ + s[0] = '\n'; s[1] = ' '; \ + if (linee && (!linelen || (s - lineb <= linelen))) \ + { while (linee < s) { linee[0] = linee[2]; ++linee; } linee -= 2; } \ + else { if (linee) lineb = linee + 1; linee = s; s += 2; } + NSUW + break; + case TOKEN822_AT: *s++ = '@'; break; + case TOKEN822_DOT: *s++ = '.'; break; + case TOKEN822_LEFT: *s++ = '<'; break; + case TOKEN822_RIGHT: *s++ = '>'; break; + case TOKEN822_SEMI: *s++ = ';'; break; + case TOKEN822_COLON: *s++ = ':'; break; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT: + if (t->type == TOKEN822_QUOTE) *s++ = '"'; + if (t->type == TOKEN822_LITERAL) *s++ = '['; + if (t->type == TOKEN822_COMMENT) *s++ = '('; + + for (j = 0; j < t->slen; ++j) + switch (ch = t->s[j]) { + case '"': case '[': case ']': case '(': case ')': + case '\\': case '\r': case '\n': *s++ = '\\'; + default: *s++ = ch; + } + if (t->type == TOKEN822_QUOTE) *s++ = '"'; + if (t->type == TOKEN822_LITERAL) *s++ = ']'; + if (t->type == TOKEN822_COMMENT) *s++ = ')'; + break; + } + } + NSUW + --s; + sa->len = s - sa->s; + return 1; +} + +int token822_unquote(stralloc *sa,token822_alloc *ta) +{ + struct token822 *t; + int len; + int i; + int j; + char *s; + + len = 0; + + for (i = 0; i < ta->len; ++i) { + t = ta->t + i; + switch (t->type) { + case TOKEN822_COMMA: case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: + case TOKEN822_RIGHT: case TOKEN822_SEMI: case TOKEN822_COLON: + ++len; break; + case TOKEN822_LITERAL: + len += 2; + case TOKEN822_ATOM: case TOKEN822_QUOTE: + len += t->slen; + } + } + + if (!stralloc_ready(sa,len)) return -1; + + s = sa->s; + + for (i = 0; i < ta->len; ++i) { + t = ta->t + i; + switch (t->type) { + case TOKEN822_COMMA: *s++ = ','; break; + case TOKEN822_AT: *s++ = '@'; break; + case TOKEN822_DOT: *s++ = '.'; break; + case TOKEN822_LEFT: *s++ = '<'; break; + case TOKEN822_RIGHT: *s++ = '>'; break; + case TOKEN822_SEMI: *s++ = ';'; break; + case TOKEN822_COLON: *s++ = ':'; break; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: + if (t->type == TOKEN822_LITERAL) *s++ = '['; + for (j = 0; j < t->slen; ++j) + *s++ = t->s[j]; + if (t->type == TOKEN822_LITERAL) *s++ = ']'; + break; + case TOKEN822_COMMENT: break; + } + } + sa->len = s - sa->s; + return 1; +} + +int token822_parse(token822_alloc *ta,stralloc *sa,stralloc *buf) +{ + int i; + int salen; + int level; + struct token822 *t; + int numtoks; + int numchars; + char *cbuf; + + salen = sa->len; + + numchars = 0; + numtoks = 0; + + for (i = 0; i < salen; ++i) + switch (sa->s[i]) { + case '.': case ',': case '@': case '<': case '>': case ':': case ';': + ++numtoks; break; + case ' ': case '\t': case '\r': case '\n': break; + case ')': case ']': return 0; + /* other control chars and non-ASCII chars are also bad, in theory */ + case '(': + level = 1; + while (level) { + if (++i >= salen) return 0; + switch (sa->s[i]) { + case '(': ++level; break; + case ')': --level; break; + case '\\': if (++i >= salen) return 0; + default: ++numchars; + } + } + ++numtoks; + break; + case '"': + level = 1; + while (level) { + if (++i >= salen) return 0; + switch (sa->s[i]) { + case '"': --level; break; + case '\\': if (++i >= salen) return 0; + default: ++numchars; + } + } + ++numtoks; + break; + case '[': + level = 1; + while (level) { + if (++i >= salen) return 0; + switch (sa->s[i]) { + case ']': --level; break; + case '\\': if (++i >= salen) return 0; + default: ++numchars; + } + } + ++numtoks; + break; + default: + do { + if (sa->s[i] == '\\') if (++i >= salen) break; + ++numchars; + if (++i >= salen) break; + } while (atomok(sa->s[i])); + --i; + ++numtoks; + } + + if (!token822_ready(ta,numtoks)) return -1; + if (!stralloc_ready(buf,numchars)) return -1; + cbuf = buf->s; + ta->len = numtoks; + + t = ta->t; + + for (i = 0; i < salen; ++i) + switch (sa->s[i]) { + case '.': t->type = TOKEN822_DOT; ++t; break; + case ',': t->type = TOKEN822_COMMA; ++t; break; + case '@': t->type = TOKEN822_AT; ++t; break; + case '<': t->type = TOKEN822_LEFT; ++t; break; + case '>': t->type = TOKEN822_RIGHT; ++t; break; + case ':': t->type = TOKEN822_COLON; ++t; break; + case ';': t->type = TOKEN822_SEMI; ++t; break; + case ' ': case '\t': case '\r': case '\n': break; + case '(': + t->type = TOKEN822_COMMENT; t->s = cbuf; t->slen = 0; + level = 1; + while (level) { + ++i; /* assert: < salen */ + switch (sa->s[i]) { + case '(': ++level; break; + case ')': --level; break; + case '\\': ++i; /* assert: < salen */ + default: *cbuf++ = sa->s[i]; ++t->slen; + } + } + ++t; + break; + case '"': + t->type = TOKEN822_QUOTE; t->s = cbuf; t->slen = 0; + level = 1; + while (level) { + ++i; /* assert: < salen */ + switch (sa->s[i]) { + case '"': --level; break; + case '\\': ++i; /* assert: < salen */ + default: *cbuf++ = sa->s[i]; ++t->slen; + } + } + ++t; + break; + case '[': + t->type = TOKEN822_LITERAL; t->s = cbuf; t->slen = 0; + level = 1; + while (level) { + ++i; /* assert: < salen */ + switch (sa->s[i]) { + case ']': --level; break; + case '\\': ++i; /* assert: < salen */ + default: *cbuf++ = sa->s[i]; ++t->slen; + } + } + ++t; + break; + default: + t->type = TOKEN822_ATOM; t->s = cbuf; t->slen = 0; + do { + if (sa->s[i] == '\\') if (++i >= salen) break; + *cbuf++ = sa->s[i]; ++t->slen; + if (++i >= salen) break; + } while (atomok(sa->s[i])); + atomcheck(t); + --i; + ++t; + } + return 1; +} + +static int gotaddr(token822_alloc *taout,token822_alloc *taaddr,int (*callback)()) +{ + int i; + + if (callback(taaddr) != 1) + return 0; + + if (!token822_readyplus(taout,taaddr->len)) + return 0; + + for (i = 0; i < taaddr->len; ++i) + taout->t[taout->len++] = taaddr->t[i]; + + taaddr->len = 0; + return 1; +} + +int token822_addrlist(token822_alloc *taout,token822_alloc *taaddr,token822_alloc *ta,int (*callback)()) +{ + struct token822 *t; + struct token822 *beginning; + int ingroup; + int wordok; + + taout->len = 0; + taaddr->len = 0; + + if (!token822_readyplus(taout,1)) return -1; + if (!token822_readyplus(taaddr,1)) return -1; + + ingroup = 0; + wordok = 1; + + beginning = ta->t + 2; + t = ta->t + ta->len - 1; + + /* rfc 822 address lists are easy to parse from right to left */ + +#define FLUSH if (taaddr->len) if (!gotaddr(taout,taaddr,callback)) return -1; +#define FLUSHCOMMA if (taaddr->len) { \ +if (!gotaddr(taout,taaddr,callback)) return -1; \ +if (!token822_append(taout,&comma)) return -1; } +#define ADDRLEFT if (!token822_append(taaddr,t--)) return -1; +#define OUTLEFT if (!token822_append(taout,t--)) return -1; + + while (t >= beginning) { + switch (t->type) { + case TOKEN822_SEMI: + FLUSHCOMMA + if (ingroup) return 0; + ingroup = 1; + wordok = 1; + break; + case TOKEN822_COLON: + FLUSH + if (!ingroup) return 0; + ingroup = 0; + while ((t >= beginning) && (t->type != TOKEN822_COMMA)) + OUTLEFT + if (t >= beginning) + OUTLEFT + wordok = 1; + continue; + case TOKEN822_RIGHT: + FLUSHCOMMA + OUTLEFT + while ((t >= beginning) && (t->type != TOKEN822_LEFT)) + ADDRLEFT + /* important to use address here even if it's empty: <> */ + if (!gotaddr(taout,taaddr,callback)) return -1; + if (t < beginning) return 0; + OUTLEFT + while ((t >= beginning) && ((t->type == TOKEN822_COMMENT) || + (t->type == TOKEN822_ATOM) || (t->type == TOKEN822_QUOTE) || + (t->type == TOKEN822_AT) || (t->type == TOKEN822_DOT))) + OUTLEFT + wordok = 0; + continue; + case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: + if (!wordok) + FLUSHCOMMA + wordok = 0; + ADDRLEFT + continue; + case TOKEN822_COMMENT: + /* comment is lexically a space; shouldn't affect wordok */ + break; + case TOKEN822_COMMA: + FLUSH + wordok = 1; + break; + default: + wordok = 1; + ADDRLEFT + continue; + } + OUTLEFT + } + FLUSH + ++t; + while (t > ta->t) + if (!token822_append(taout,--t)) return -1; + + token822_reverse(taout); + return 1; +} diff --git a/sqmail-4.3.07/src/trigger.c b/sqmail-4.3.07/src/trigger.c new file mode 100644 index 0000000..ec22e11 --- /dev/null +++ b/sqmail-4.3.07/src/trigger.c @@ -0,0 +1,41 @@ +#include "select.h" +#include "ndelay.h" +#include "open.h" +#include "trigger.h" +#include "close.h" + +static int fd = -1; + +void trigger_set() +{ + if (fd != -1) close(fd); + fd = open_read("lock/trigger"); +} + +void trigger_selprep(int *nfds,fd_set *rfds) +{ + if (fd != -1) { + FD_SET(fd,rfds); + if (*nfds < fd + 1) *nfds = fd + 1; + } +} + +int trigger_pulled(fd_set *rfds) +{ + if (fd != -1) if (FD_ISSET(fd,rfds)) return 1; + return 0; +} + +void write(); /* compiler warning (temp) */ + +void triggerpull() +{ + int fd; + + fd = open_write("lock/trigger"); + if (fd >= 0) { + ndelay_on(fd); + write(fd,"",1); /* if it fails, bummer */ + close(fd); + } +} diff --git a/sqmail-4.3.07/src/triggerpull.c b/sqmail-4.3.07/src/triggerpull.c new file mode 100644 index 0000000..3691c5a --- /dev/null +++ b/sqmail-4.3.07/src/triggerpull.c @@ -0,0 +1,16 @@ +#include <unistd.h> +#include "ndelay.h" +#include "open.h" +#include "triggerpull.h" + +void triggerpull(void) +{ + int fd; + + fd = open_write("lock/trigger"); + if (fd >= 0) { + ndelay_on(fd); + write(fd,"",1); /* if it fails, bummer */ + close(fd); + } +} diff --git a/sqmail-4.3.07/src/trycpp.c b/sqmail-4.3.07/src/trycpp.c new file mode 100644 index 0000000..690f2f3 --- /dev/null +++ b/sqmail-4.3.07/src/trycpp.c @@ -0,0 +1,7 @@ +int main() +{ +#ifdef NeXT + printf("nextstep\n"); exit(0); +#endif + printf("unknown\n"); exit(0); +} diff --git a/sqmail-4.3.07/src/trycrypt.c b/sqmail-4.3.07/src/trycrypt.c new file mode 100644 index 0000000..c32bd40 --- /dev/null +++ b/sqmail-4.3.07/src/trycrypt.c @@ -0,0 +1,4 @@ +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/trydnsresolv.c b/sqmail-4.3.07/src/trydnsresolv.c new file mode 100644 index 0000000..c32bd40 --- /dev/null +++ b/sqmail-4.3.07/src/trydnsresolv.c @@ -0,0 +1,4 @@ +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/trydrent.c b/sqmail-4.3.07/src/trydrent.c new file mode 100644 index 0000000..c778176 --- /dev/null +++ b/sqmail-4.3.07/src/trydrent.c @@ -0,0 +1,8 @@ +#include <sys/types.h> +#include <dirent.h> + +void foo() +{ + DIR *dir; + struct dirent *d; +} diff --git a/sqmail-4.3.07/src/tryflock.c b/sqmail-4.3.07/src/tryflock.c new file mode 100644 index 0000000..b18743a --- /dev/null +++ b/sqmail-4.3.07/src/tryflock.c @@ -0,0 +1,8 @@ +#include <sys/types.h> +#include <sys/file.h> +#include <fcntl.h> + +int main() +{ + flock(0,LOCK_EX | LOCK_UN | LOCK_NB); +} diff --git a/sqmail-4.3.07/src/tryidn2.c b/sqmail-4.3.07/src/tryidn2.c new file mode 100644 index 0000000..f35850c --- /dev/null +++ b/sqmail-4.3.07/src/tryidn2.c @@ -0,0 +1,6 @@ +#include <idn2.h> + +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/tryldap.c b/sqmail-4.3.07/src/tryldap.c new file mode 100644 index 0000000..715a7ba --- /dev/null +++ b/sqmail-4.3.07/src/tryldap.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include <ldap.h> + +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/trylsock.c b/sqmail-4.3.07/src/trylsock.c new file mode 100644 index 0000000..c32bd40 --- /dev/null +++ b/sqmail-4.3.07/src/trylsock.c @@ -0,0 +1,4 @@ +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/trymkffo.c b/sqmail-4.3.07/src/trymkffo.c new file mode 100644 index 0000000..e832a31 --- /dev/null +++ b/sqmail-4.3.07/src/trymkffo.c @@ -0,0 +1,7 @@ +#include <sys/types.h> +#include <sys/stat.h> + +int main() +{ + mkfifo("temp-trymkffo",0); +} diff --git a/sqmail-4.3.07/src/trynpbg1.c b/sqmail-4.3.07/src/trynpbg1.c new file mode 100644 index 0000000..01a152e --- /dev/null +++ b/sqmail-4.3.07/src/trynpbg1.c @@ -0,0 +1,26 @@ +#include "select.h" +#include "open.h" +#include "fifo.h" + +#define FN "temp-trynpbg1.fifo" + +int main() +{ + int flagbug; + struct timeval instant; + fd_set rfds; + + flagbug = 0; + if (fifo_make(FN,0600) != -1) { + close(0); + if (open_read(FN) == 0) { + FD_ZERO(&rfds); + FD_SET(0,&rfds); + instant.tv_sec = instant.tv_usec = 0; + if (select(1,&rfds,(fd_set *) 0,(fd_set *) 0,&instant) > 0) + flagbug = 1; + } + unlink(FN); + } + _exit(!flagbug); +} diff --git a/sqmail-4.3.07/src/tryqlibs.c b/sqmail-4.3.07/src/tryqlibs.c new file mode 100644 index 0000000..8cc108d --- /dev/null +++ b/sqmail-4.3.07/src/tryqlibs.c @@ -0,0 +1,6 @@ +#include "stralloc.h" + +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/tryrsolv.c b/sqmail-4.3.07/src/tryrsolv.c new file mode 100644 index 0000000..4b7857d --- /dev/null +++ b/sqmail-4.3.07/src/tryrsolv.c @@ -0,0 +1,6 @@ +#include "dnsresolv.h" + +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/trysalen.c b/sqmail-4.3.07/src/trysalen.c new file mode 100644 index 0000000..731a109 --- /dev/null +++ b/sqmail-4.3.07/src/trysalen.c @@ -0,0 +1,11 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +void foo() +{ + struct sockaddr sa; + sa.sa_len = 0; +} diff --git a/sqmail-4.3.07/src/trysgact.c b/sqmail-4.3.07/src/trysgact.c new file mode 100644 index 0000000..1471ecc --- /dev/null +++ b/sqmail-4.3.07/src/trysgact.c @@ -0,0 +1,10 @@ +#include <signal.h> + +int main() +{ + struct sigaction sa; + sa.sa_handler = 0; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(0,&sa,(struct sigaction *) 0); +} diff --git a/sqmail-4.3.07/src/trysgprm.c b/sqmail-4.3.07/src/trysgprm.c new file mode 100644 index 0000000..3d73f86 --- /dev/null +++ b/sqmail-4.3.07/src/trysgprm.c @@ -0,0 +1,10 @@ +#include <signal.h> + +int main() +{ + sigset_t ss; + + sigemptyset(&ss); + sigaddset(&ss,SIGCHLD); + sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0); +} diff --git a/sqmail-4.3.07/src/tryshadow.c b/sqmail-4.3.07/src/tryshadow.c new file mode 100644 index 0000000..fbce408 --- /dev/null +++ b/sqmail-4.3.07/src/tryshadow.c @@ -0,0 +1,4 @@ +main() +{ + ; +} diff --git a/sqmail-4.3.07/src/tryshsgr.c b/sqmail-4.3.07/src/tryshsgr.c new file mode 100644 index 0000000..81b395c --- /dev/null +++ b/sqmail-4.3.07/src/tryshsgr.c @@ -0,0 +1,14 @@ +int main() +{ + short x[4]; + + x[0] = x[1] = 1; + if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1); + + if (getgroups(1,x) == -1) _exit(1); + if (x[1] != 1) _exit(1); + x[1] = 2; + if (getgroups(1,x) == -1) _exit(1); + if (x[1] != 2) _exit(1); + _exit(0); +} diff --git a/sqmail-4.3.07/src/tryslib.c b/sqmail-4.3.07/src/tryslib.c new file mode 100644 index 0000000..c32bd40 --- /dev/null +++ b/sqmail-4.3.07/src/tryslib.c @@ -0,0 +1,4 @@ +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/tryspnam.c b/sqmail-4.3.07/src/tryspnam.c new file mode 100644 index 0000000..00060cf --- /dev/null +++ b/sqmail-4.3.07/src/tryspnam.c @@ -0,0 +1,9 @@ +#include <shadow.h> + +int main() +{ + struct spwd *spw; + + spw = getspnam(""); + puts(spw->sp_pwdp); +} diff --git a/sqmail-4.3.07/src/trysysel.c b/sqmail-4.3.07/src/trysysel.c new file mode 100644 index 0000000..abebad5 --- /dev/null +++ b/sqmail-4.3.07/src/trysysel.c @@ -0,0 +1,11 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/select.h> /* SVR4 silliness */ +#include <stdio.h> +#include "select.h" + +int main() +{ + printf("FD_SETSIZE:%d\n",FD_SETSIZE); + return 0; +} diff --git a/sqmail-4.3.07/src/trysyslog.c b/sqmail-4.3.07/src/trysyslog.c new file mode 100644 index 0000000..4b99afc --- /dev/null +++ b/sqmail-4.3.07/src/trysyslog.c @@ -0,0 +1,9 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <syslog.h> + +main() +{ + openlog("foo",0,LOG_MAIL); + syslog(0,"foo"); +} diff --git a/sqmail-4.3.07/src/tryulong32.c b/sqmail-4.3.07/src/tryulong32.c new file mode 100644 index 0000000..20683d6 --- /dev/null +++ b/sqmail-4.3.07/src/tryulong32.c @@ -0,0 +1,11 @@ +int main() +{ + unsigned long u; + u = 1; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u; + if (!u) _exit(0); + _exit(1); +} diff --git a/sqmail-4.3.07/src/tryuserpw.c b/sqmail-4.3.07/src/tryuserpw.c new file mode 100644 index 0000000..a359d27 --- /dev/null +++ b/sqmail-4.3.07/src/tryuserpw.c @@ -0,0 +1,9 @@ +#include <userpw.h> + +int main() +{ + struct userpw *upw; + + upw = getuserpw(""); + puts(upw->upw_passwd); +} diff --git a/sqmail-4.3.07/src/tryutmp.c b/sqmail-4.3.07/src/tryutmp.c new file mode 100644 index 0000000..2a25e5d --- /dev/null +++ b/sqmail-4.3.07/src/tryutmp.c @@ -0,0 +1,7 @@ +#include <sys/types.h> +#include <utmp.h> + +int main() +{ + ; +} diff --git a/sqmail-4.3.07/src/tryvfork.c b/sqmail-4.3.07/src/tryvfork.c new file mode 100644 index 0000000..b01d2f8 --- /dev/null +++ b/sqmail-4.3.07/src/tryvfork.c @@ -0,0 +1,4 @@ +int main() +{ + vfork(); +} diff --git a/sqmail-4.3.07/src/trywaitp.c b/sqmail-4.3.07/src/trywaitp.c new file mode 100644 index 0000000..0380358 --- /dev/null +++ b/sqmail-4.3.07/src/trywaitp.c @@ -0,0 +1,7 @@ +#include <sys/types.h> +#include <sys/wait.h> + +int main() +{ + waitpid(0,0,0); +} diff --git a/sqmail-4.3.07/src/warn-auto.sh b/sqmail-4.3.07/src/warn-auto.sh new file mode 100644 index 0000000..64131e6 --- /dev/null +++ b/sqmail-4.3.07/src/warn-auto.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# WARNING: This file was auto-generated. Do not edit! +# POSIX compliant usage of options. diff --git a/sqmail-4.3.07/src/warn-shsgr b/sqmail-4.3.07/src/warn-shsgr new file mode 100644 index 0000000..37c351e --- /dev/null +++ b/sqmail-4.3.07/src/warn-shsgr @@ -0,0 +1,3 @@ +Oops. Your getgroups() returned 0, and setgroups() failed; this means +that I can't reliably do my shsgr test. Please either ``make'' as root +or ``make'' while you're in one or more supplementary groups. diff --git a/sqmail-4.3.07/src/wildmat.c b/sqmail-4.3.07/src/wildmat.c new file mode 100644 index 0000000..739f943 --- /dev/null +++ b/sqmail-4.3.07/src/wildmat.c @@ -0,0 +1,109 @@ +/*** wildmat.c.orig Wed Dec 3 11:46:31 1997 */ +/* $Revision: 1.1 $ +** +** Do shell-style pattern matching for ?, \, [], and * characters. +** Might not be robust in face of malformed patterns; e.g., "foo[a-" +** could cause a segmentation violation. It is 8bit clean. +** +** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. +** Rich $alz is now <rsalz@osf.org>. +** April, 1991: Replaced mutually-recursive calls with in-line code +** for the star character. +** +** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code. +** This can greatly speed up failing wildcard patterns. For example: +** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* +** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 +** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1 +** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without +** the ABORT code, it takes 22310 calls to fail. Ugh. The following +** explanation is from Lars: +** The precondition that must be fulfilled is that DoMatch will consume +** at least one character in text. This is true if *p is neither '*' nor +** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic +** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With +** FALSE, each star-loop has to run to the end of the text; with ABORT +** only the last one does. +** +** Once the control of one instance of DoMatch enters the star-loop, that +** instance will return either TRUE or ABORT, and any calling instance +** will therefore return immediately after (without calling recursively +** again). In effect, only one star-loop is ever active. It would be +** possible to modify the code to maintain this context explicitly, +** eliminating all recursive calls at the cost of some complication and +** loss of clarity (and the ABORT stuff seems to be unclear enough by +** itself). I think it would be unwise to try to get this into a +** released version unless you have a good test data base to try it out +** on. +*/ + +#define TRUE 1 +#define FALSE 0 +#define ABORT -1 + +/* What character marks an inverted character class? */ +#define NEGATE_CLASS '^' +/* Is "*" a common pattern? */ +#define OPTIMIZE_JUST_STAR +/* Do tar(1) matching rules, which ignore a trailing slash? */ +#undef MATCH_TAR_PATTERN + +/* +** Match text and p, return TRUE, FALSE, or ABORT. +*/ +static int DoMatch(register char *text, register char *p) +{ + register int last; + register int matched; + register int reverse; + + for (; *p; text++, p++) { + if (*text == '\0' && *p != '*') + return ABORT; + switch (*p) { + case '\\': /* Literal match with following character. */ + p++; + case '?': /* Match anything. */ + continue; + case '*': /* Consecutive stars act just like one. */ + while (*++p == '*') + continue; + if (*p == '\0') return TRUE; /* Trailing star matches everything. */ + while (*text) + if ((matched = DoMatch(text++, p)) != FALSE) return matched; + return ABORT; + case '[': + reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE; + if (reverse) p++; /* Inverted character class. */ + matched = FALSE; + if (p[1] == ']' || p[1] == '-') + if (*++p == *text) matched = TRUE; + for (last = *p; *++p && *p != ']'; last = *p) /* This next line requires a good C compiler. */ + if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p) + matched = TRUE; + if (matched == reverse) return FALSE; + continue; + default: /* FALLTHROUGH */ + if (*text != *p) return FALSE; + continue; + } + } + +#ifdef MATCH_TAR_PATTERN + if (*text == '/') + return TRUE; +#endif /* MATCH_TAR_ATTERN */ + return *text == '\0'; +} + +/* +** User-level routine. Returns TRUE or FALSE. +*/ +int wildmat(char *text,char *p) +{ +#ifdef OPTIMIZE_JUST_STAR + if (p[0] == '*' && p[1] == '\0') + return TRUE; +#endif /* OPTIMIZE_JUST_STAR */ + return DoMatch(text, p) == TRUE; +} diff --git a/sqmail-4.3.07/src/xqp.sh b/sqmail-4.3.07/src/xqp.sh new file mode 100644 index 0000000..16b3fc6 --- /dev/null +++ b/sqmail-4.3.07/src/xqp.sh @@ -0,0 +1,9 @@ + +awk ' + /^d/ { + if ($9 == x) print + } + /^m/ { + if ($9 == x) print + } +' x="$1" diff --git a/sqmail-4.3.07/src/xrecipient.sh b/sqmail-4.3.07/src/xrecipient.sh new file mode 100644 index 0000000..e65f74c --- /dev/null +++ b/sqmail-4.3.07/src/xrecipient.sh @@ -0,0 +1,6 @@ + +awk ' + /^d/ { + if ($8 == x) print + } +' x="$1" diff --git a/sqmail-4.3.07/src/xsender.sh b/sqmail-4.3.07/src/xsender.sh new file mode 100644 index 0000000..333a5c0 --- /dev/null +++ b/sqmail-4.3.07/src/xsender.sh @@ -0,0 +1,9 @@ + +awk ' + /^d/ { + if ($7 == x) print + } + /^m/ { + if ($8 == x) print + } +' x="<$1>" diff --git a/sqmail-4.3.07/src/zddist.sh b/sqmail-4.3.07/src/zddist.sh new file mode 100644 index 0000000..f147549 --- /dev/null +++ b/sqmail-4.3.07/src/zddist.sh @@ -0,0 +1,7 @@ +echo 'Distribution of ddelays for successful deliveries + +Meaning of each line: The first pct% of successful deliveries +all happened within doneby seconds. The average ddelay was avg. +' +( echo doneby avg pct +HOME/bin/ddist ) | HOME/bin/columnt diff --git a/sqmail-4.3.07/src/zdeferrals.sh b/sqmail-4.3.07/src/zdeferrals.sh new file mode 100644 index 0000000..affe4b1 --- /dev/null +++ b/sqmail-4.3.07/src/zdeferrals.sh @@ -0,0 +1,8 @@ +echo 'Reasons for deferral + +One line per reason for deferral. Information on each line: +* del is the number of deliveries that ended for this reason. +* xdelay is the total xdelay on those deliveries. +' +( echo del xdelay reason +HOME/bin/deferrals | sort -k2 ) | HOME/bin/columnt | tr _ ' ' diff --git a/sqmail-4.3.07/src/zfailures.sh b/sqmail-4.3.07/src/zfailures.sh new file mode 100644 index 0000000..91f72ab --- /dev/null +++ b/sqmail-4.3.07/src/zfailures.sh @@ -0,0 +1,8 @@ +echo 'Reasons for failure + +One line per reason for delivery failure. Information on each line: +* del is the number of deliveries that ended for this reason. +* xdelay is the total xdelay on those deliveries. +' +( echo del xdelay reason +HOME/bin/failures | sort -k2 ) | HOME/bin/columnt | tr _ ' ' diff --git a/sqmail-4.3.07/src/zoverall.sh b/sqmail-4.3.07/src/zoverall.sh new file mode 100644 index 0000000..d19ec33 --- /dev/null +++ b/sqmail-4.3.07/src/zoverall.sh @@ -0,0 +1,77 @@ +echo 'Basic statistics + +qtime is the time spent by a message in the queue. + +ddelay is the latency for a successful delivery to one recipient---the +end of successful delivery, minus the time when the message was queued. + +xdelay is the latency for a delivery attempt---the time when the attempt +finished, minus the time when it started. The average concurrency is the +total xdelay for all deliveries divided by the time span; this is a good +measure of how busy the mailer is. +' + +awk ' + BEGIN { + messages = 0 + recips = 0 + tries = 0 + deliveries = 0 + succ = 0 + fail = 0 + mbytes = 0 + rbytes = 0 + } + /^m/ { + ++messages + mbytes += $4 + rbytes += $4 * $5 + qtime += $3 - $2 + recips += $5 + $6 + tries += $5 + $6 + $7 + if (!seen || ($2 < first)) first = $2 + if (!seen || ($3 > last)) last = $3 + seen = 1 + } + /^d k/ { ++succ; ddelay += $5 - $3 } + /^d d/ { ++fail } + /^d/ { + ++deliveries + xdelay += $5 - $4 + if (!seen || ($3 < first)) first = $3 + if (!seen || ($5 > last)) last = $5 + seen = 1 + } + END { + print "Completed messages:", messages + if (messages) { + print "Recipients for completed messages:", recips + print "Total delivery attempts for completed messages:", tries + print "Average delivery attempts per completed message:", tries / messages + print "Bytes in completed messages:", mbytes + print "Bytes weighted by success:", rbytes + print "Average message qtime (s):", qtime / messages + } + print "" + print "Total delivery attempts:", deliveries + if (deliveries) { + print " success:", succ + print " failure:", fail + print " deferral:", deliveries - succ - fail + str = sprintf("%.6f",ddelay) + print "Total ddelay (s):", str + if (succ) { + str = sprintf("%.6f",ddelay / succ) + print "Average ddelay per success (s):", str + } + str = sprintf("%.6f",xdelay) + print "Total xdelay (s):", str + str = sprintf("%.6f",xdelay / deliveries) + print "Average xdelay per delivery attempt (s):", str + if (last > first) { + print "Time span (days):", (last - first) / 86400 + print "Average concurrency:", xdelay / (last - first) + } + } + } +' diff --git a/sqmail-4.3.07/src/zrecipients.sh b/sqmail-4.3.07/src/zrecipients.sh new file mode 100644 index 0000000..37f3078 --- /dev/null +++ b/sqmail-4.3.07/src/zrecipients.sh @@ -0,0 +1,10 @@ +echo 'Recipients + +One line per recipient. Information on each line: +* sbytes is the number of bytes successfully delivered to this recipient. +* mess is the number of messages sent to this recipient (success plus failure). +* tries is the number of delivery attempts (success, failure, deferral). +* xdelay is the total xdelay incurred by this recipient. +' +( echo sbytes mess tries xdelay recipient +HOME/bin/recipients | sort -k4 ) | HOME/bin/columnt diff --git a/sqmail-4.3.07/src/zrhosts.sh b/sqmail-4.3.07/src/zrhosts.sh new file mode 100644 index 0000000..4cd1802 --- /dev/null +++ b/sqmail-4.3.07/src/zrhosts.sh @@ -0,0 +1,10 @@ +echo 'Recipient hosts + +One line per recipient host. Information on each line: +* sbytes is the number of bytes successfully delivered to this host. +* mess is the number of messages sent to this host (success plus failure). +* tries is the number of delivery attempts (success, failure, deferral). +* xdelay is the total xdelay incurred by this host. +' +( echo sbytes mess tries xdelay host +HOME/bin/rhosts | sort -k4 ) | HOME/bin/columnt diff --git a/sqmail-4.3.07/src/zrxdelay.sh b/sqmail-4.3.07/src/zrxdelay.sh new file mode 100644 index 0000000..cf50e8d --- /dev/null +++ b/sqmail-4.3.07/src/zrxdelay.sh @@ -0,0 +1,8 @@ +echo 'Recipients in the best order for mailing lists + +One line per recipient, sorted by avg. Information on each line: +* avg is the _average_ xdelay for the recipient. +* tries is the number of deliveries that avg is based on. +' +( echo avg tries recipient +HOME/bin/recipients | HOME/bin/rxdelay ) | HOME/bin/columnt diff --git a/sqmail-4.3.07/src/zsenders.sh b/sqmail-4.3.07/src/zsenders.sh new file mode 100644 index 0000000..9f52ce8 --- /dev/null +++ b/sqmail-4.3.07/src/zsenders.sh @@ -0,0 +1,13 @@ +echo 'Senders + +One line per sender. Information on each line: +* mess is the number of messages sent by this sender. +* bytes is the number of bytes sent by this sender. +* sbytes is the number of bytes successfully received from this sender. +* rbytes is the number of bytes from this sender, weighted by recipient. +* recips is the number of recipients (success plus failure). +* tries is the number of delivery attempts (success, failure, deferral). +* xdelay is the total xdelay incurred by this sender. +' +( echo mess bytes sbytes rbytes recips tries xdelay sender +HOME/bin/senders | sort -n -k7 ) | HOME/bin/columnt diff --git a/sqmail-4.3.07/src/zsendmail.sh b/sqmail-4.3.07/src/zsendmail.sh new file mode 100644 index 0000000..e87715b --- /dev/null +++ b/sqmail-4.3.07/src/zsendmail.sh @@ -0,0 +1,18 @@ + +awk ' + /^d/ { + if ($2 == "k") stat="stat=Sent" + else if ($2 == "d") stat="stat=Failed" + else stat="stat=Deferred" + str1 = sprintf("%.6f",$5-$3) + str2 = sprintf("%.6f",$5-$4) + print $5" qp "$9": to="$8", uid="$10", ddelay="str1", xdelay="str2", "stat" ("$11")" + next + } + /^m/ { + str1 = sprintf("%.6f",$3-$2) + print $3" qp "$9": from="$8", uid="$10", size="$4", nrcpts="$5+$6", deferrals="$7", qtime="str1 + next + } + { print } +' diff --git a/sqmail-4.3.07/src/zsuccesses.sh b/sqmail-4.3.07/src/zsuccesses.sh new file mode 100644 index 0000000..93ca179 --- /dev/null +++ b/sqmail-4.3.07/src/zsuccesses.sh @@ -0,0 +1,8 @@ +echo 'Reasons for success + +One line per reason for successful delivery. Information on each line: +* del is the number of deliveries that ended for this reason. +* xdelay is the total xdelay on those deliveries. +' +( echo del xdelay reason +HOME/bin/successes | sort -k2 ) | HOME/bin/columnt | tr _ ' ' diff --git a/sqmail-4.3.07/src/zsuids.sh b/sqmail-4.3.07/src/zsuids.sh new file mode 100644 index 0000000..ba515f3 --- /dev/null +++ b/sqmail-4.3.07/src/zsuids.sh @@ -0,0 +1,13 @@ +echo 'Sender uids + +One line per sender uid. Information on each line: +* mess is the number of messages sent by this uid. +* bytes is the number of bytes sent by this uid. +* sbytes is the number of bytes successfully received from this uid. +* rbytes is the number of bytes from this uid, weighted by recipient. +* recips is the number of recipients (success plus failure). +* tries is the number of delivery attempts (success, failure, deferral). +* xdelay is the total xdelay incurred by this uid. +' +( echo mess bytes sbytes rbytes recips tries xdelay uid +HOME/bin/suids | sort -n -k7 ) | HOME/bin/columnt |