source: npl/mailserver/netqmail/roberto-netqmail-1.06.patch-latest @ 6e9c61f

Last change on this file since 6e9c61f was 4f7cda5, checked in by Edwin Eefting <edwin@datux.nl>, 6 years ago

upgrade to netqmail 1.06 with Roberto Puzzanghera's patch (http://notes.sagredo.eu/en/qmail-notes-185/patching-qmail-82.html)

  • Property mode set to 100644
File size: 928.4 KB
Line 
1v. 2018.04.03
2Combined patch for netqmail-1.06 by Roberto Puzzanghera [roberto dot puzzanghera at sagredo dot eu]
3More info at http://notes.sagredo.eu/node/82
4==========================================================================================================
5
6= This patch puts together
7* Erwin Hoffmann's qmail-authentication patch v. 0.8.3 (23.08.2015), which updates the patches provided by
8  Krysztof Dabrowski and Bjoern Kalkbrenner.
9  It provides cram-md5, login, plain authentication support for qmail-smtpd and qmail-remote.
10  http://www.fehcom.de/qmail/smtpauth.html##PATCHES
11* Frederik Vermeulen's qmail-tls patch v. 20160908
12  implements SSL or TLS encrypted and authenticated SMTP.
13  http://inoa.net/qmail-tls/
14  The file update_tmprsadh was modified to chown all .pem files to vpopmail.
15* Marcel Telka's force-tls patch v. 2016.05.15
16  optionally gets qmail to require TLS before authentication to improve security.
17  You have to declare FORCETLS=0 if you want to allow the auth without TLS
18  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06_force-tls.patch-2012.10.28
19* Antonio Nati's chkuser patch v. 2.0.9
20  performs, among the other things, a check for the existence of recipients during the SMTP conversation,
21  bouncing emails of fake senders.
22  http://www.interazioni.it/opensource/chkuser/
23* Flavio Curti's qmail-queue-custom-error patch
24  enables simscan and qmail-dkim to return the appropriate message for each e-mail it refuses to deliver.
25  https://no-way.org/uploads/qmail-error/
26* Christophe Saout's qmail-SPF rc5 patch
27  Modified by Manvendra Bhangui to make it IPv4-mapped IPv6 addresses compliant.
28  checks incoming mails inside the SMTP daemon, add Received-SPF lines and optionally block undesired transfers.
29  http://www.saout.de/misc/spf/
30* Marcelo Coelho's qmail-SRS patch
31  implements Sender Rewriting Scheme fixing SPF break upon email forwarding.
32  http://www.mco2.com.br/opensource/qmail/srs/
33* Christopher K. Davis' oversize dns patch
34  enables qmail to handle large DNS packets.
35  http://www.ckdhr.com/ckd/qmail-103.patch
36* Jul's reread-concurrency v.2 patch
37  rereads control/concurrencylocal and control/concurrencyremote files when qmail-send receives a HUP signal.
38  http://js.hu/package/qmail/index.html
39* Johannes Erdfelt's Big Concurrency patch
40  sets the spawn limit above 255
41  http://qmail.org/big-concurrency.patch
42* Mihai Secasiu's Big Concurrency fix v.1.0 patch
43  fixes a compiler error if you set concurrency higher than 509 in conf-spawn.
44  http://patchlog.com/linux/qmail-big-concurrency/
45* Bill Shupp's netqmail-maildir++.patch
46  adds maildirquota support to qmail-pop3d and qmail-local.
47  Fixed a bug where the filesize part of the S=<filesize> component of the Maildir++ compatible filename is wrong (tx MG).
48  More info here: http://notes.sagredo.eu/en/qmail-notes-185/installing-dovecot-and-sieve-on-a-vpopmail-qmail-server-28.html#comment995
49  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/netqmail-maildir.patch
50* Kyle B. Wheeler's "Better qmail-smtpd Logging" v.4 (05 Jan 2010) patch
51  facilitates diagnostics of qmail-smtpd logging its actions and decisions (search for a line with qmail-smtp:)
52  http://www.memoryhole.net/qmail/#logging
53* John Simpson's (?) Greeting delay patch
54  adds a user-definable delay after SMTP clients have initiated SMTP sessions, prior to qmail-smtpd responding
55  with "220 ESMTP". It can reject connections from clients which tried to send commands before greeting.
56  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-greetdelay.patch
57* Manvendra Bhangui's DKIM and SURBL filter v.1.22 patch
58  adds DKIM signing & verification and SURBL filtering support to qmail.
59  qmail-dk is based on Russ Nelson's patch: http//:www.qmail.org/qmail-1.03-dk-0.54.patch
60  qmail-dkim uses hacked libdkim libraries from libdkim project at http://libdkim.sourceforge.net/
61  surbfilter is built on djb functions and some functions have been ruthlessly borrowed from qmail surbl
62  interface by Pieter Droogendijk and the surblhost program at http://surblhost.sourceforge.net/
63  (file hier.c modified to chown /var/qmail/control/cache and subdirs to vpopmail)
64  http://sourceforge.net/projects/indimail/files/netqmail-addons/qmail-dkim-1.0/
65  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/ANNOUNCE.surblfilter
66* Claudio Jeker and Andre Oppermann's EXTTODO patch (release 5. Jan. 2003)
67  addresses a problem known as the silly qmail (queue)  problem
68  http://www.nrg4u.com/qmail/ext_todo-20030105.patch
69* Russell Nelson's big-todo patch
70  makes qmail use a hashing mechanism in the todo folder similar to that used in the rest of the queue
71  http://www.qmail.org/big-todo.103.patch
72* Stephane Cottin's qmail-inject-null-sender patch (let's call it in this way)
73  prevents qmail-inject from rewriting the null sender, fixing an issue with sieve vacation/reject messages.
74  More info here: http://www.dovecot.org/list/dovecot/2009-June/040811.html
75  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-inject-null-sender.patch
76* Russell Nelson's (modified by Charles Cazabon) doublebounce-trim patch, which updates the original
77  version by Russel Nelson
78  prevents double bounces from hitting your queue a second time provided that you delete the first line
79  from /var/qmail/control/doublebounceto
80  http://qmail.org/doublebounce-trim.patch
81* Will Harris' esmtp-size patch
82  enables qmail-smtpd to reject messages if they're larger than the maximum number of bytes allowed
83  according to the /var/qmail/control/databytes control file.
84  http://will.harris.ch/qmail-smtpd.c.diff
85* Inter7's qmail-taps-extended patch
86  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-tap.diff
87  Extended by Michai Secasiu (http://patchlog.com/patches/qmail-taps-extended/)
88  Provides the ability to archive each email that flows through the system.
89  Archiving only messages from or to certain email addresses is possible as well.
90* Rolf Eike Beer's qmail-remote CRLF patch
91  enables qmail-remote to handle CR properly, always sending the line breaks as CRLF and avoiding to
92  double the CR (like qmail-remote normally does)
93  http://opensource.sf-tec.de/qmail/
94* Andy Repton's outgoingip patch (adjusted by Sergio Gelato)
95  by default all outgoing emails are sent through the first IP address on the interface. In case of a multiple
96  IP server this patch makes qmail send outgoing emails with the IP eventually stored in control/outgoingip.
97  The ehlo domain is NOT modified by this patch.
98  http://www.qmail.org/outgoingip.patch
99  Robbie Walker provided a patch to correct qmail-qmqpc.c's call to timeoutconn(), because the function
100  signature was modified by the original outgoingip patch
101  http://notes.sagredo.eu/node/82#comment-373
102* Iain Patterson's qmail-smtpd pid, qp log patch
103  makes qmail-smtpd log a line similar to the following:
104  @4000000039b89c95026a89b4 mail recv: pid 8155 from <name@domain.xy> qp 8157
105  The pid allows you to match the message up with a given tcpserver process and the qp lets you find a
106  particular delivery.
107  http://iain.cx/qmail/patches.html#smtpd_pidqp
108* Jonathan de Boyne Pollard's any-to-cname patch
109  avoids qmail getting large amounts of DNS data we have no interest in and that may overflow our response
110  buffer.
111  http://www.memoryhole.net/qmail/#any-to-cname
112* Matthias Andree's qmail-rfc2821 patch
113  makes qmail rfc2821 compliant
114  http://www-dt.e-technik.uni-dortmund.de/~ma/qmail/patch-qmail-1.03-rfc2821.diff
115* Jonathan de Boyne Pollard's smtpd-502-to-500 patch
116  makes qmail rfc2821 compliant
117  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/smtpd-502-to-500.patch
118* Fabio Busatto's qmail-dnsbl patch
119  allows you to reject spam and virus looking at the sender's ip address.
120  Modified by Luca Franceschini to add support for whitelists, TXT and A queries, configurable return codes
121  451 or 553 with custom messages
122  http://qmail-dnsbl.sourceforge.net/
123* Scott Gifford's qmail-moreipme patch v. 0.6
124  prevents a problem caused by an MX or other mail routing directive instructing qmail to connect to
125  itself without realizing it's connecting to itself, saving CPU time.
126  http://www.suspectclass.com/sgifford/qmail/qmail-1.03-moreipme.README
127  http://www.suspectclass.com/sgifford/qmail/qmail-1.03-moreipme-0.6.patch
128* Alex Nee's qmail-hide-ip-headers patch
129  It will hide your Private or Public IP in the email Headers when you are sending Mail as a Relay Client.
130  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-hide-ip-headers.patch
131* John Saunders' qmail-date-localtime patch
132  causes the various qmail programs to generate date stamps in the local timezone.
133  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/qmail-date-localtime.patch
134* Dean Gaudet's qmail-liberal-lf patch v. 0.95
135  allow qmail-smtpd to accept messages that are terminated with a single \n instead of the required \r\n
136  sequence.
137  http://www.arctic.org/~dean/patches/qmail-0.95-liberal-lf.patch
138* Michael Samuel's maxrcpt patch
139  allows you to set a limit on how many recipients are specified for any one email message by setting
140  control/maxrcpt. RFC 2821 section 4.5.3.1 says that an MTA MUST allow at least 100 recipients for each
141  message, since this is one of the favourite tricks of the spammer.
142  http://copilotco.com/mail-archives/qmail.1997/msg03066.html
143* Inter7's qmail-eMPF patch
144  More info: http://www.qmailwiki.org/EMPF
145  eMPF follows a set of administrator-defined rules describing who can message whom.  With this, companies can segregate
146  various parts of their organizations email activities, as well as provide a variety of security-enhancing services.
147* qregex (by  Andrew St. Jean http://www.arda.homeunix.net/downloads-qmail/, contributors: Jeremy Kitchen, Alex Pleiner,
148  Thanos Massias. Original patch by Evan Borgstrom)
149  adds the ability to match address evelopes via Regular Expressions (REs) in the qmail-smtpd process.
150  Added new control file 'badhelonorelay', control/badmailto renamed control/badrcptto (Tx Luca Franceschini).
151* brtlimit
152  Luca Franceschini derived this patch from http://netdevice.com/qmail/patch/goodrcptto-12.patch
153  added control/brtlimit and BRTLIMIT variable to limit max invalid recipient errors before closing the connection (man qmail-control)
154* validrcptto
155  http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/validrcptto.README
156  Luca Franceschini grabbed the code from several patches with additional features: http://qmail.jms1.net/patches/validrcptto cdb.shtml,
157  http://netdevice.com/qmail/patch/goodrcptto-ms-12.patch, http://patch.be/qmail/badrcptto.html
158  It works in conjunction with chkuser with both cdb and mysql accounts.
159* reject-relay-test by Russell Nelson
160  http://qmail.org/qmail-smtpd-relay-reject
161  It gets qmail to reject relay probes generated by so-called anti-spammers. These relay probes have '!', '%' and '@'
162  in the local (username) part of the address.
163* Luca Franceschini
164  added DISABLETLS environment variable, useful if you want to disable TLS on a desired port
165  added FORCEAUTHMAILFROM environment variable to REQUIRE that authenticated user and 'mail from' are identical
166  added SMTPAUTHMETHOD, SMTPAUTHUSER and SMTP_AUTH_USER env variables for external plugins (see http://qmail-spp.sourceforge.net/doc/)
167* fixed little bug in 'mail from' address handling
168  patch by Andre Opperman at http://qmail.cr.yp.narkive.com/kBry6GJl/bug-in-qmail-smtpd-c-addrparse-function
169* Luca Franceschini's qlog patch
170  smtpd logging with fixed format. An entry 'qlogenvelope' is generated after accepting or rejecting every recipients in the envelope phase.
171* Luca Franceschini's reject null senders patch
172  useful in special cases if you temporarily need to reject the null sender (although breaks RFC compatibility).
173  You just need to put 1 (actually any number different from 0) in your control/rejectnullsenders to reject the null sender with 421 error message.
174* dnscname patch
175  Removes CNAME check in order to avoid getting large amounts of data of no interest in and that may overflow the response buffer.
176  https://lists.gt.net/qmail/users/138190
177* Luca Franceschini's rcptcheck patch
178  (based on original patch from Jay Soffian (http://www.soffian.org/downloads/qmail/qmail-smtpd-doc.html)
179  Originally designed for the purpose of receipt validation, it can also be used to limit the numbr of email a given IP and/or auth-user and/or domain che send
180  in a given time interval. It has to be used in conjuction with the rcptcheck-overlimit.sh LF's script http://notes.sagredo.eu/files/qmail/rcptcheck-overlimit.sh
181  http://notes.sagredo.eu/files/qmail/patches/rcptcheck.patch
182
183= Disclaimer
184This patch comes with the usual warranty: it works for me, it may not work for you,
185use at your own risk etc. etc. :)
186Comments, suggestions, criticisms are always welcome!
187
188= Usage
189
190* Install libdomainkeys
191wget http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/tar/libdomainkeys-0.69.tar.gz
192tar xzf libdomainkeys-0.69.tar.gz
193wget http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/libdomainkeys-0.69.diff
194cd libdomainkeys-0.69
195chown -R root.root .
196patch < ../libdomainkeys-0.69.diff
197make
198cp libdomainkeys.a /usr/lib
199
200* Install libsrs2
201wget http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/tar/libsrs2-1.0.18.tar.gz
202tar xzf libsrs2-1.0.18.tar.gz
203cd libsrs2-1.0.18
204./configure
205make
206make install
207ldconfig
208cd ../
209
210* Apply the patch and compile
211wget http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/roberto-netqmail-1.06.patch-latest.gz
212wget http://qmail.org/netqmail-1.06.tar.gz
213tar xzf netqmail-1.06.tar.gz
214cd netqmail-1.06
215chown -R root.root .
216gunzip -c ../roberto-netqmail-1.06.patch-latest.gz | patch
217make
218make setup check
219
220* You have to export SMTPAUTH in your run file if you want to do the auth
221
222* You have to export SURBL=1 in your run file if you want to enable SURBL
223
224* /var/qmail/control/cache must be owned by the user who runs qmail-smtpd, vpopmail.vchkpwd in my case.
225  Change the permissions according to your qmail configuration.
226
227=================================================================================================================
228
229= Changelog
230
2312018.04.03
232-DKIM patch updated to v. 1.22
233 * openssl 1.1.0 port
234 * various improvements, bug fixes
235
2362018.01.10
237-maildir++
238 * fixed a bug where the filesize part of the S=<filesize> component of the Maildir++ compatible filename
239   is wrong (tx MG). More info here: http://notes.sagredo.eu/en/qmail-notes-185/installing-dovecot-and-sieve-on-a-vpopmail-qmail-server-28.html#comment995
240-qmail-queue-extra
241 * removed, because it was causing more problems than advantages, as the domain of the log@yourdomain.tld
242   had to match the system domain inside control/me and shouldn't be a virtual domain as well.
243
2442017.10.11 (tx Luca Franceschini)
245-qlogfix
246 * log strings should terminate with \n to avoid trailing ^M using splogger
247 * bug reporting custom errors from qmail-queue in qlog
248-added dnscname patch
249-added rcptcheck patch
250
2512017.08.18
252-qmail-smtpd now retains authentication upon rset
253 (tx to Andreas http://notes.sagredo.eu/qmail-notes-185/smtp-auth-qmail-tls-forcetls-patch-for-qmail-84.html#comment750)
254
2552017-05-14
256-DKIM patch updated to v. 1.20
257 It now manages long TXT records, avoiding the rejection of some hotmail.com messages.
258
2592016-12-19
260-Several new patches and improvements added (thanks to Luca Franceschini)
261More info here http://notes.sagredo.eu/node/178
262 -qregex patch
263 -brtlimit patch
264 -validrcptto patch
265 -rbl patch (updates qmail-dnsbl patch)
266 -reject-relay-test patch
267 -added DISABLETLS environment variable, useful if you want to disable TLS on a desired port
268 -added FORCEAUTHMAILFROM environment variable to REQUIRE that authenticated user and 'mail from' are identical
269 -fixed little bug in 'mail from' address handling (patch by Andre Opperman at http://qmail.cr.yp.narkive.com/kBry6GJl/bug-in-qmail-smtpd-c-addrparse-function)
270 -added SMTPAUTHMETHOD, SMTPAUTHUSER and SMTP_AUTH_USER env variables for external plugins
271 -qlog patch
272 -reject null senders patch
273 -bouncecontrolmime patch
274 -qmail-taps-extended (updates qmail-tap)
275
2762016-12-02
277-fixed BUG in qmail-remote.c: in case of remote server who doesn't allow EHLO the response for an alternative
278HELO was checked twice, making the connection to die. (Thanks to Luca Franceschini)
279Patch applied: http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/fix_sagredo_remotehelo.patch
280
2812016-09-19
282-qmail-tls patch updated to v. 20160918
283  * bug: qmail-remote accepting any dNSName, without checking that is matches (E. Surovegin)
284  * bug: documentation regarding RSA and DH keys (K. Peter, G. A. Bofill)
285
2862016-05-15
287-force-tls patch improved (a big thanks to Marcel Telka). Now qmail-smtpd avoids to write the auth verb if the
288 the STARTTLS command was not sent by the client
289
2902016-03-09
291-DKIM patch upgraded to v. 1.19
292 * verification will not fail when a dkim signature does not include the subject provided that the
293   UNSIGNED_SUBJECT environment variable is declared.
294
2952015-12-26
296-qmail-tls patch updated to v. 20151215
297 * typo in #if OPENSSL_VERSION_NUMBER for 2015-12-08 patch release (V. Smith)
298 * add ECDH to qmail-smtpd
299 * increase size of RSA and DH pregenerated keys to 2048 bits
300 * qmail-smtpd sets RELAYCLIENT if relaying allowed by cert
301 more info at http://inoa.net/qmail-tls/
302
3032015-12-15
304-DKIM patch by Manvendra Bhangui updated to v. 1.18
305
3062015-10-03
307-qmail-authentication: updated to v. 0.8.3
308
3092015-08-08
310-fixed a bug on qmail-remote.c that was causing the sending of an additionale ehlo greeting (thanks to Cristoph Grover)
311
3122015-04-11
313-qmail-authentication: updated to v. 0.8.2
314-qmail-tls: upgraded to v. 20141216 (POODLE vulnerability fixed)
315
3162015-03-28
317-added qmail-eMPF patch
318
3192014-11-19
320-security fix: the SSLv3 connection is now switched off
321
3222014-11-15
323-modified the QUEUE_EXTRA variable in extra.h to improve the qmail-send's log
324
3252014-04-14
326-added maxrcpt patch
327
3282014-03-10
329-added qmail-0.95-liberal-lf patch
330
3312013-12-30
332-added qmail-srs
333-the character "=" is now considered valid in the sender address by chkuser in order to accept SRS
334
3352013-12-18
336-added qmail-date-localtime patch
337
3382013-12-14
339-added qmail-hide-ip patch
340
3412013-12-10
342-the original greetdelay by e.h. has been replaced with the improved patch by John Simpson. Now
343communications trying to send commands before the greeting will be closed. Premature disconnections will be
344logged as well.
345-CHKUSER_SENDER_FORMAT enabled to reject fake senders without any domain declared (like <foo>)
346-chkuser logging: I slightly modified the log line adding the variables' name just to facilitate its interpretation
347-added qmail-moreipme patch
348
3492013-12-07
350-added qmail-dnsbl patch
351
3522013-12-05
353-added two patches to make qmail rfc2821 compliant
354
3552013-11-23
356-added any-to-cname patch
357
3582013-09-27
359-DKIM patch upgraded to v. 1.17. Defined -DHAVE_SHA_256 while compiling dkimverify.cpp in the Makefile.
360This solved an issue while verifying signatures using sha256.
361
3622013-09-16
363-Minor fixes to the DKIM patch.
364
3652013-09-13
366-DKIM patch upgraded to v. 1.16. The signing at qmail-remote level has been revised by its author.
367
3682013-08-25
369-qmail-qmqpc.c call to timeoutconn() needed a correction because the function signature was modified by the
370 outgoingip patch. Thanks to Robbie Walker (diff here http://notes.sagredo.eu/node/82#comment-373)
371
3722013-08-21
373-fixed a bug in hier.c which caused the installation not to build properly the queue/todo dir structure (thanks to
374 Scott Ramshaw)
375
3762013-08-18
377-DKIM-SURBL patch by Manvendra Bhangui updated to v. 1.14
378
3792013-08-12
380-DKIM patch upgraded to v. 1.12. The new patch adds surblfilter functionality.
381-added qmail-smtpd pid, qp log patch
382
3832013-08-08
384-qmail-SPF modified by Manvendra Bhangui to make it IPv6-mapped IPv4 addresses compliant. In order to have it
385working with such addresses you have to patch tcpserver.c accordingly. You can use a patch fot ucspi-tcp6-0.98
386by Manvendra Bhangui at http://notes.sagredo.eu/sites/notes.sagredo.eu/files/qmail/patches/tcpserver-ipv6mapped_ipv4.patch
387or wait for v. 0.99 relase of ucspi-tcp6
388-added outgoingip patch
389-added qmail-bounce patch
390
3912013-03-31
392qmail-auth updated to latest v. 0.8.1 Added authentication by recipient domain for qmail-remote.
393Look at README.auth for further details
394
3952013-02-11
396some code adjustments in qmail-smtpd.c smtpd_ehlo() to restore total compatibility with esmtp-size patch
397
3982013-02-08
399qmail-auth updated to latest v. 0.7.6. Look at README.auth for further details
400
4012013-01-28
402fixed an issue on qmail-pop3d which was causing a double +OK after the pass command (thanks to Rakesh, Orbit
403and Simplex for helping in testing and troubleshooting)
404
4052013-01-06
406environment variable GREETDELAY renamed to SMTPD_GREETDELAY
407
4082012-10-31
409qmail-auth updated to latest v. 0.7.5. Look at README.auth for further details
410The qmail-forcetls patch was simplyfied accordingly.
411You MUST export SMTPAUTH="" in your run file now.
412
4132012-04-25
414-added qmail-remote CRLF (thanks to Pierre Lauriente for the help on testing and troubleshooting)
415The qmail-remote CRLF patch solved a problem of broken headers after sieve forwarding that was
416caused by a bad handling of the CR (carriage return) by qmail-remote.
417The issue is also reported here http://www.dt.e-technik.uni-dortmund.de/~ma/qmail-bugs.html
418
4192012.04.16
420-added qmail-tap
421
4222012.02.08
423-added smtp-size patch
424
4252012.01.29
426-added doublebounce-trim patch
427
4282011.12.12
429-file update_tmprsadh modified to chown the .pem files to vpopmail to avoid hang-ups during the smtp
430conversation on port 587 caused by permission problems.
431
4322011.10.06
433-qmail-remote.c: fixed. It was not going into tls on authentication (thanks to Krzysztof Gajdemski)
434-force-tls now quits if the starttls command is not provided when required (thanks to Jacekalex)
435
436=================================================================================================================
437
438diff -ruN ../netqmail-1.06-original/CHKUSER.automatic_patching netqmail-1.06/CHKUSER.automatic_patching
439--- ../netqmail-1.06-original/CHKUSER.automatic_patching        1970-01-01 01:00:00.000000000 +0100
440+++ netqmail-1.06/CHKUSER.automatic_patching    2016-11-22 21:04:38.804137924 +0100
441@@ -0,0 +1,94 @@
442+Chkuser 2.0.9 automatic patching
443+
444+When to use automatic patching
445+==============================
446+
447+The release.tar package contains some .patch files, ready for installation,
448+trying to semplify the most frequent situations.
449+
450+You may use one of these patches if you have these sources:
451+
452+       - a clean qmail 1.03 or netqmail 1.05
453+
454+You may also consider using one of these patches if you have additional compatible
455+patches installed. This means that these additional patches should not have changed
456+the same sources and lines which are going to be used by chkuser.
457+
458+If you have any doubt, backup your sources and try the automatic installation,
459+otherwise execute the manual installation (that's very easy).
460+
461+Backup
462+======
463+
464+Save you qmail working sources before making any change.
465+
466+Basic installation
467+==================
468+
469+Download the newest release.tar package and untar it. It will create a directory
470+containing all release chkuser files and patches.
471+
472+Chose the most appropriate .patch file to be applied, according to your qmail
473+installation: .patch files names are self-describing.
474+
475+Position in the qmail/netqmail source directory:
476+
477+       $ cd /usr/.../netqmail-1.05
478+
479+Apply selected patch:
480+
481+       $ patch < /path_to_chkuser_release_dir/netqmail-1.05_chkuser-2.x.x.patch
482+
483+No errors should be displayed. If you see any error, better you restore your
484+sources and go to manual editing.
485+
486+editing vpopmail home path
487+
488+       If your production home path for vpopmail (or whatever you call him) user
489+       is NOT /home/vpopmail, you must perform the following additional actions.
490+
491+       Edit Makefile, changing the line referring to vpopmail's home path and
492+       putting the right home path:
493+
494+               VPOPMAIL_HOME=/home/vpopmail
495+
496+       Edit conf-cc, changing the string referring to vpopmail's home path and
497+       putting the right home path:
498+
499+               cc -O2 -I/home/vpopmail/include
500+
501+chkuser settings
502+================
503+
504+Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the
505+ones you don't want. Default settings should cover the most of situations.
506+
507+See the related settings pages for more informations.
508+
509+Make
510+====
511+Now, make (or gmake on *BSD) as your usual. No errors (just warnings) should
512+come out. If you see any error, better you restore your sources
513+and go to manual editing.
514+
515+Checking
516+========
517+Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run:
518+
519+       $ ./qmail-smtpd
520+       mail from <wrong_sender>
521+       mail from <right_sender>
522+       rcpt to: <fake_user@your_domain>
523+       rcpt to: <real_user@your_domain>
524+
525+You should see error and ok messages, depending on the addresses you typed.
526+
527+Install
528+=======
529+Copy the new executable in the /var/qmail/bin directory (or make install).
530+
531+Running
532+=======
533+This patched qmail-smtpd must be executed in a different way than the normal one.
534+See the running pages for detailed instructions.
535+
536diff -ruN ../netqmail-1.06-original/CHKUSER.changelog netqmail-1.06/CHKUSER.changelog
537--- ../netqmail-1.06-original/CHKUSER.changelog 1970-01-01 01:00:00.000000000 +0100
538+++ netqmail-1.06/CHKUSER.changelog     2016-11-22 21:04:38.804137924 +0100
539@@ -0,0 +1,183 @@
540+
541+CHKUSER 2.0 change log
542+
543+2.0.9 - 21 march 2007
544+   New features
545+        - New variable for accepting only authorized senders:
546+                #define CHKUSER_EXTRA_MUSTAUTH_VARIABLE "CHKUSER_MUSTAUTH"
547+                if the variable exists, then messages are accepted only if RELAYCLIENT is set
548+                If defined, it works always despite CHKUSER being ON/OFF
549+                This feature can be used for a "submission port" (see RFC 2476)
550+        - Improved checking of MySQL server availability (if MySQL is used as vpopmail user's DB)
551+        - Introduction of a new variable for disabling chkuser on the fly: CHKUSER_DISABLE_VARIABLE
552+                (can be used for single IP or defined as "RELAYCLIENT" for all authorized e-mail clients)
553+        - Improved starting variables checking sequence
554+                CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE cannot be defined together
555+                        and in such a case a fatal error is displayed; (in previous versions
556+                        CHKUSER_ALWAYS_ON would automatically disable CHKUSER_STARTING_VARIABLE definition).
557+                CHKUSER_DISABLE_VARIABLE is always evaluated after CHKUSER_ALWAYS_ON is set or
558+                        CHKUSER_STARTING_VARIABLE is evaluated, so CHKUSER_ALWAYS_ON or
559+                        CHKUSER_STARTING_VARIABLE can set the general behaviour, while
560+                        CHKUSER_DISABLE_VARIABLE should be invoked to handle exceptions.
561+        - New variable for accepting qmail doublebounces: CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
562+                Sender "#@[]" will be accepted. It is used by qmail for doublebounces, and should be enabled for
563+                selected IP senders.
564+       - define CHKUSER_ENABLE_VAUTH_OPEN has been substituted by CHKUSER_VAUTH_OPEN_CALL: this new
565+               define must contain the name of the call used to open the auth DB
566+
567+   Updated features
568+        - checking for ezmlm mailing list is now done looking for file "editor"
569+                within mailing-list directory
570+        - defines for allowed character within sender and rcpt addresses increased
571+                up to CHKUSER_ALLOW_SENDER_CHAR_10 and CHKUSER_ALLOW_RCPT_CHAR_10
572+        - updated SMTP error strings; more exact and detailed error codes
573+                (thanks to Olivier Dony and Dane Thorsen)
574+        - logging of valid rcpt. If CHKUSER_LOG_VALID_RCPT is defined then all valid
575+                recipients are logged, even if domain does not want bouncing or chkuser
576+                is disabled
577+
578+   Bugs corrected
579+        - negative checking of backend DB connection did not report
580+                DB unavailability in some situations
581+                (thanks to Matt Brookings of Inter7)
582+        - in check_rcpt_address_format format checking was done using defines
583+                reserved for senders
584+
585+V 2.0.8 - 7 december 2004
586+   Features
587+       Freeze of new features of 2.0.7, except null senders behaviour.
588+       CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST is no more available
589+       CHKUSER_ENABLE_NULL_SENDER is no more available
590+       NULL SENDERS are now always accepted. No option is available to disable
591+               this behaviour. Previous chkuser versions broke RFC compatibility on
592+               null senders, and complicated real life e-mailing.
593+       Logging of null senders <> is now available.
594+       
595+   Bugs corrected
596+       Sender controls were not executed if CHKUSER_STARTING_VARIABLE was defined
597+               (thanks to Charles Sprickman)
598+       Domains not in control/virtualdomains are now explicitely excluded from
599+               following cascade checks; in previous versions following cascade
600+               checks were done using fake domains paths.
601+       vget_assign is now handled correctly (a domain in rcpthosts but not
602+               in virtualdomains could have an incorrect path in previous versions
603+               (this bug is also in all chkusr versions)
604+
605+       Defaults changed
606+       CHKUSER_RCPT_FORMAT is now undefined as default
607+       CHKUSER_RCPT_MX is now undefined as default.
608+       CHKUSER_SENDER_FORMAT is now undefined as default
609+       CHKUSER_SENDER_MX is now undefined as default.
610+       CHKUSER_ERROR_DELAY_INCREASE new default is 300 milliseconds
611+       
612+V 2.0.7 - 25 october 2004
613+   Features
614+       added vclose() of DB auth connection, overriding
615+               qmail-smtpd _exit call
616+       improved MX checking; now SOFT failure is handled as
617+               temporary error.
618+       added #define CHKUSER_RCPTMX_TMP_STRING
619+       added #define CHKUSER_SENDERMX_TMP_STRING
620+       added handling of mailman mailing lists
621+               (and related #define CHKUSER_ENABLE_MAILMAN_LISTS)
622+       changed order of checking for recipients:
623+               1 - valias
624+               2 - alias
625+               3 - alias extensions
626+               4 - users
627+               5 - users extensions
628+               6 - lists
629+       added #define CHKUSER_ACCEPT_NULL_SENDER (default defined)
630+       added #define CHKUSER_ENABLE_ALIAS_DEFAULT (default not defined)
631+               enables checking of .qmail-alias-default
632+       added #define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY"
633+               in order to allow a easy identification of remote IP
634+               (substitutes RELAYCLIENT in chkuser logging)
635+       added #define CHKUSER_ALLOW_RCPT_SRS
636+               enable usage of "#" and "+" characters within rcpt address
637+       added #define CHKUSER_ALLOW_RCPT_CHAR_1 '$'
638+       added #define CHKUSER_ALLOW_RCPT_CHAR_2 '%'
639+       added #define CHKUSER_ALLOW_RCPT_CHAR_3 '£'
640+       added #define CHKUSER_ALLOW_RCPT_CHAR_4 '?'
641+       added #define CHKUSER_ALLOW_RCPT_CHAR_5 '*'
642+       #define CHKUSER_ENABLE_USERS_EXTENSIONS
643+               substitutes #define CHKUSER_ENABLE_EXTENSIONS
644+       #define CHKUSER_ENABLE_EZMLM_LISTS
645+               substitutes #define CHKUSER_ENABLE_LISTS
646+       #define CHKUSER_USERS_DASH
647+               substitutes #define CHKUSER_EXTENSION_DASH
648+
649+   Bugs corrected
650+       sender address "name@" could cause a crash. Corrected
651+               (Thanks to Dmitry Petukhov)
652+       Corrected Makefile: now qmail-smtpd.c recompiles if chkuser.h
653+               changes
654+       Corrected a bug in #endif sequence related to
655+               #define CHKUSER_RCPT_FORMAT (thanks to Alex Plainer)
656+       Corrected a bug in chkuser_sender; now is not executed when
657+               chkuser is disabled
658+       Corrected check of format for domains:
659+               "xn--" admitted as leading string
660+       Deleted correction over usage of RELAYCLIENT variable
661+               Previous correction could affect a special
662+               feature of RELAYCLIENT (thanks to Alex Pleiner)
663+
664+   Defaults changed
665+       #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST (default undefined)
666+
667+
668+V 2.0.6 - 25 september 2004
669+       No bugs, just doc updates and an empty patch file corrected
670+
671+       #define CHKUSER_ENABLE_VGET_REAL_DOMAIN was existing and working in code,
672+               but not reported both in docs and inside chkuser_settings.h
673+                       (default is commented, but this #define is important)   
674+       patch for toaster-0.6-1 was empty. Now the correct one is provided
675+
676+V 2.0.5 - 23 september 2004
677+       This is the first public release.
678+
679+       added #define CHKUSER_ALLOW_SENDER_CHAR_1 (default not defined)
680+       added #define CHKUSER_ALLOW_SENDER_CHAR_2 (default not defined)
681+       added #define CHKUSER_ALLOW_SENDER_CHAR_3 (default not defined)
682+       added #define CHKUSER_ALLOW_SENDER_CHAR_4 (default not defined)
683+       added #define CHKUSER_ALLOW_SENDER_CHAR_5 (default not defined)
684+       added #define CHKUSER_MIN_DOMAIN_LEN (default defined 4) -
685+               Previously it was hard coded as 5. Juergen Kendzorra
686+               showed me some existing names long only 4 chars.
687+       added #define CHKUSER_LOG_VALID_SENDER (default defined)
688+
689+V 2.0.4 - 15 september 2004
690+
691+       added #define CHKUSER_SENDER_NOCHECK_VARIABLE (default not defined)
692+       added #define CHKUSER_DEBUG_STDERR (default not defined)
693+       added #define CHKUSER_ALLOW_SENDER_SRS (default not defined)
694+       cleaned some typos in code and documentation (thanks to Juergen
695+               Kendzorra - http://www.kendzorra.de)
696+
697+
698+V 2.0.3 - 8 september 2004
699+       This is the first version released outside, for wider testing.
700+
701+       Tested Makefile for netqmail 1.05
702+       Added Makefiles for applying over other patches
703+
704+V 2.0.0 - july 2004
705+       chkuser 2.0.0 starts here, and is a private internal release.
706+       Version 2.0 is much more modular than previous one (named chkusr),
707+       and has been designed with the goal of enabling more features and
708+       semplifying installations and upgrades of the patch himself.
709+
710+       chkusr changes his name, to reflect a deep change of the patch.
711+
712+       Chkusr 1.0 received a lot of feedbacks and suggestions.
713+       The most of these suggestions are now inside version 2.0.
714+
715+               - Marcelo Coelho (marcelo at tpn.com.br), segnaled me some
716+               unseen minor bugs of chkusr 1.0 (minor but very annoying to
717+               my pride) and suggested some very interesting features
718+               (some of them are now in chkuser 2.0).
719+               - Iulian Margarintescu (http:://www.erata.net) suggested a
720+               workable way of introducing quota check on recipients
721+               (now in chkuser 2.0).
722+
723diff -ruN ../netqmail-1.06-original/CHKUSER.copyright netqmail-1.06/CHKUSER.copyright
724--- ../netqmail-1.06-original/CHKUSER.copyright 1970-01-01 01:00:00.000000000 +0100
725+++ netqmail-1.06/CHKUSER.copyright     2016-11-22 21:04:38.804137924 +0100
726@@ -0,0 +1,15 @@
727+
728+chkuser for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
729+
730+Author: Antonio Nati tonix@interazioni.it
731+
732+All rights on this software and
733+the identifying words chkusr and chkuser kept by the author
734+
735+This software may be freely used, modified and distributed,
736+but this lines must be kept in every original or derived version.
737+
738+Original author "Antonio Nati" and the web URL
739+"http://www.interazioni.it/opensource"
740+must be indicated in every related work or web page
741+
742diff -ruN ../netqmail-1.06-original/CHKUSER.log_format netqmail-1.06/CHKUSER.log_format
743--- ../netqmail-1.06-original/CHKUSER.log_format        1970-01-01 01:00:00.000000000 +0100
744+++ netqmail-1.06/CHKUSER.log_format    2016-11-22 21:04:38.804137924 +0100
745@@ -0,0 +1,69 @@
746+
747+chkuser 2.0.9 logging format
748+
749+When #defines for logging are enabled, chkuser patch emits log informations
750+on the same qmail-smtpd log destination
751+
752+This is the log format:
753+
754+    CHKUSER "brief message": \
755+           from <sender:remoteinfo:relayclient> \
756+           remote <helo:remotehostname:remotehostip> \
757+           rcpt <recipient> : "extended message"
758+
759+where
760+       brief message   
761+                   * accepted rcpt
762+                   * relaying rcpt
763+                   * rejected relaying
764+                   * rejected rcpt
765+                   * no auth resource
766+                   * mbx overquota
767+                   * rejected intrusion
768+                   * intrusion threshold
769+                   * accepted sender
770+                   * rejected sender
771+                   * must auth
772+
773+       sender  sender declared within "mail from"
774+
775+       remoteinfo      the value of "TCPREMOTEINFO" or the autenticated user
776+
777+       relayclient     the value of CHKUSER_IDENTIFY env variable (this name
778+                        is defined by #define CHKUSER_IDENTIFY_REMOTE_VARIABLE)
779+
780+       helo            helo declared from remote system
781+
782+       hostname        the value of "TCPREMOTEHOST"
783+
784+       remotehostip    the value of "TCPREMOTEIP"
785+
786+       recipient       recipient address
787+
788+       extended message        this field has more wide description for
789+                               some generic "brief message":
790+                       accepted rcpt   found existing recipient
791+                       accepted rcpt   accepted any recipient for any rcpt doman (from 2.0.9)
792+                       accepted rcpt   accepted any recipient for this domain (from 2.0.9)
793+                       relaying rcpt   client allowed to relay
794+                       rejected relaying       client not allowed to relay
795+                       rejected rcpt   not existing recipient
796+                       rejected rcpt   max number of recipients
797+                       rejected rcpt   max number of invalid recipients
798+                       rejected rcpt   invalid rcpt address format
799+                       rejected rcpt   invalid rcpt MX domain
800+                       rejected rcpt   temporary DNS problem (from 2.0.9)
801+                       intrusion threshold     max number of allowed rcpt
802+                       intrusion threshold     max number of allowed invalid rcpt
803+                       rejected intrusion      rcpt ignored, session over intrusion threshold
804+                       no auth resource        no auth resource available
805+                       must auth       sender not authenticated/authorized (from 2.0.9)
806+                       mbx overquota   rcpt mailbox is overquota
807+                       accepted sender sender accepted (from 2.0.9)
808+                       accepted sender accepted any sender always (from 2.0.9)
809+                       accepted sender accepted null sender always (from 2.0.9)
810+                       accepted doublebounce   accepted qmail doublebounce #@[] (from 2.0.9)
811+                       rejected sender         invalid sender address format
812+                       rejected sender         invalid sender MX domain
813+                       rejected sender         temporary DNS problem (from 2.0.9)
814+       
815diff -ruN ../netqmail-1.06-original/CHKUSER.manual_patching netqmail-1.06/CHKUSER.manual_patching
816--- ../netqmail-1.06-original/CHKUSER.manual_patching   1970-01-01 01:00:00.000000000 +0100
817+++ netqmail-1.06/CHKUSER.manual_patching       2016-11-22 21:04:38.804137924 +0100
818@@ -0,0 +1,182 @@
819+Chkuser 2.0 manual editing
820+
821+Manual editing is a very simple operation.
822+
823+Watching the patch design, shown in the patch design page, you may see that
824+only some simple changes must be done to qmail-smtpd.c and Makefile.
825+
826+Backup
827+======
828+
829+Save you qmail working sources before making any change.
830+
831+Basic installation
832+==================
833+
834+Download the newest release.tar package and untar it. It will create a directory
835+containing all chkuser files and patches.
836+
837+Position in the qmail/netqmail source directory:
838+
839+       $ cd /usr/.../netqmail-1.05
840+
841+Copy all the chkuser sources:
842+
843+       $ cp /path_to_release_tar/chkuser* .
844+
845+edit qmail-smtpd.c
846+       within qmail-smtpd.c, change the following lines:
847+
848+       At the end of initial #include declarations, add the following (+) lines:
849+
850+       #include "timeoutwrite.h"
851+       #include "commands.h"
852+       
853++      /* start chkuser code */
854++      #include "chkuser.h"
855++      /* end chkuser code */
856+
857+       #define MAXHOPS 100
858+
859+Within smtp_mail routine, add the following (+) lines
860+
861+       void smtp_mail(arg) char *arg;
862+       {
863+       if (!addrparse(arg)) { err_syntax(); return; }
864++      /* start chkuser code */
865++      if (chkuser_sender (&addr) != CHKUSER_OK) { return; }
866++      /* end chkuser code */
867+       flagbarf = bmfcheck();
868+
869+       Within smtp_rcpt routine, delete the following (-) lines and substitute
870+       them with the (+) ones:
871+
872+-        if (relayclient) {
873+-          --addr.len;
874+-          if (!stralloc_cats(&addr,relayclient)) die_nomem();
875+-        }
876+-        else
877+-          if (!addrallowed()) { err_nogateway(); return; }
878+
879++      /* start chkuser code */
880++        switch (chkuser_realrcpt (&mailfrom, &addr)) {
881++          case CHKUSER_KO:
882++            return;
883++            break;
884++          case CHKUSER_RELAYING:
885++            --addr.len;
886++            if (!stralloc_cats(&addr,relayclient)) die_nomem();
887++            if (!stralloc_0(&addr)) die_nomem();
888++            break;
889++      }
890++      /* end chkuser code */
891+
892+       if (!stralloc_cats(&rcptto,"T")) die_nomem();
893+       if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
894+       if (!stralloc_0(&rcptto)) die_nomem();
895+
896+edit Makefile
897+       Within Makefile, change or add the following lines.
898+
899+       At the begininng of the file:
900+
901+       # Don't edit Makefile! Use conf-* for configuration.
902+
903++      VPOPMAIL_HOME=/home/vpopmail
904++      SMTPD_CHKUSER_OBJ=chkuser.o dns.o
905++      VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib`
906+
907+       SHELL=/bin/sh
908+
909+       Be carefule to use the right path, if your vpopmail production home
910+       path is NOT "/home/vpopmail".
911+
912+       dns.lib is added to qmail-smtpd building instructions, so, if you
913+       have previously patched qmail-smtpd in order to include dns.lib, take
914+       care to delete the duplication from the previous lines.
915+
916+       Before "clean:" insert the chkuser.o definition:
917+
918+       exit.h auto_spawn.h
919+               ./compile chkspawn.c
920++       chkuser.o: \
921++       compile chkuser.c chkuser.h chkuser_settings.h
922++              ./compile chkuser.c
923+
924+       clean: \
925+
926+       Beware: the "./compile chkuser.c" line has an heading TAB.
927+
928+       Change the qmail-smtpd compiling and linking instructions,
929+       deleting the (-) lines and adding the (+) ones.
930+
931+
932+       qmail-smtpd: \
933+       load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
934+       timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
935+       date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
936+       open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
937+-      fs.a auto_qmail.o socket.lib
938++      fs.a auto_qmail.o socket.lib $(SMTPD_CHKUSER_OBJ)
939+-            ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
940++            ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o commands.o timeoutread.o \
941+             timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
942+             received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
943+             datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
944+-            alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \
945+-            socket.lib`
946++            alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
947++            $(VPOPMAIL_LIBS) \
948++            `cat socket.lib`
949+
950+       Beware: all the lines starting from and following "./load" have an heading TAB.
951+
952+edit TARGETS
953+       Append the following blue line at the end of TARGETS file:
954+
955+       man
956+       setup
957+       check
958++      chkuser.o
959+
960+edit conf-cc
961+       Edit conf-cc, adding the include path of production vpopmail:
962+
963+       cc -O2 -I/home/vpopmail/include
964+
965+       Be carefule to use the right path, if your vpopmail production home path
966+       is NOT "/home/vpopmail".
967+
968+chkuser settings
969+================
970+Edit chkuser_settings.h, uncommenting the options you prefer, and commenting the
971+ones you don't want. Default settings should cover the most of situations.
972+
973+See the related settings pages for more informations.
974+
975+Make
976+====
977+Now, make (or gmake on *BSD) as your usual. No errors (just warnings)
978+should come out. If you see any error, check carefully edited lines.
979+
980+Checking
981+========
982+Select a domain, contained in your rcpthosts, for which bouncing is enabled, and run:
983+
984+       $ ./qmail-smtpd
985+       mail from <wrong_sender>
986+       mail from <right_sender>
987+       rcpt to: <fake_user@your_domain>
988+       rcpt to: <real_user@your_domain>
989+
990+You should see error and ok messages, depending on the addresses you typed.
991+
992+Install
993+=======
994+Copy the new executable in the /var/qmail/bin directory (or make install).
995+
996+Running
997+=======
998+This patched qmail-smtpd must be executed in a different way than the normal one.
999+See the running pages for detailed instructions.
1000+
1001diff -ruN ../netqmail-1.06-original/CHKUSER.readme netqmail-1.06/CHKUSER.readme
1002--- ../netqmail-1.06-original/CHKUSER.readme    1970-01-01 01:00:00.000000000 +0100
1003+++ netqmail-1.06/CHKUSER.readme        2016-11-22 21:04:38.804137924 +0100
1004@@ -0,0 +1,54 @@
1005+chkuser 2.0 - README
1006+
1007+Description
1008+===========
1009+The original qmail-smtpd accepts by default all messages, checking later for
1010+the existence of the recipient. So, if the message is delivered to not existing
1011+recipients a lot of additional system work and network traffic are generated,
1012+with multiple expensive bouncing if the sender is a fake one.
1013+
1014+chkuser has been developed with the goal to improve the acceptance SMTP phase
1015+of qmail-smtpd. qmail-smtpd patched with chkuser may check the existance of
1016+e-mail recipients immediately in the SMTP acceptance phase of a message and
1017+rejects istantly all messages not directed to existing users, avoiding
1018+additional traffic, work and messages bounced more times.
1019+
1020+These goals are achieved enquirying the existing vpopmail archives (each
1021+format is supported: cdb, MySQL, LDAP, etc.) by using standard vpopmail calls,
1022+or using customized chkuser routines.
1023+
1024+Version 2.0 - From chkusr to chkuser
1025+====================================
1026+Version 2.0 is a lot different from previous versions, so it deserves a more
1027+evident change in the name.
1028+
1029+Version 2.0 has been designed with the goal to be modular, and to make more easy
1030+both adding new features to chkuser code and semplifing code update.
1031+
1032+Patching over original qmail files is done over a few points, while the most of
1033+chkuser code remains ouside, in dedicated chkuser's files.
1034+
1035+Same for settings, that are inside a dedicated chkuser_settings.h file.
1036+
1037+The intention is to semplify upgrading: for future chkuser releases, upgrading
1038+will require only to update chkuser specific files, leaving all the rest
1039+untouched, and changing chkuser_settings.h only if new features must be enabled.
1040+
1041+Logging and SPAM
1042+================
1043+chkuser 2.0 has detailed logging of accepted and refused recipients and senders,
1044+allowing a deep analysis of "who's sending to who". This can lead to more
1045+sophisticated future enhancements of anti-SPAM features.
1046+
1047+Intrusion rejection
1048+===================
1049+chkuser 2.0 can be tuned to reject sessions exceeding some recipients limits
1050+(limits can be set for max recipients and max not existing recipients).
1051+
1052+URL Location
1053+============
1054+For any new release, support, FAQ, mailing lists, or other information, see:
1055+
1056+       http://www.interazioni.it/opensource
1057+
1058+
1059diff -ruN ../netqmail-1.06-original/CHKUSER.running netqmail-1.06/CHKUSER.running
1060--- ../netqmail-1.06-original/CHKUSER.running   1970-01-01 01:00:00.000000000 +0100
1061+++ netqmail-1.06/CHKUSER.running       2016-11-22 21:04:38.804137924 +0100
1062@@ -0,0 +1,103 @@
1063+
1064+CHKUSER 2.0.9 - Running instructions
1065+
1066+Chkuser may run using the most of security, following very strictly the sacurity
1067+model used By Dan Berstein. To achieve this goal, chkuser may switch between
1068+differents UID/GID, for differente purposes.
1069+
1070+However this is incompatible with TLS patches (like toaster-0.6-1), as these patches
1071+want to run under a unique UID/GID. Luckily, qmail is enought robust to let us
1072+run this way.
1073+
1074+To achieve both these goals, chkuser uses a #define (CHKUSER_ENABLE_UIDGID)
1075+that indicates if UID/GID switching is wanted, and running instructions must
1076+adapt to this way.
1077+
1078+Instead, when this define is not used, another way of running must be used.
1079+(Just for precision, even if the CHKUSER_ENABLE_UIDGID define is used, chkuser
1080+may be run without switching UID/GID).
1081+
1082+Running with UID/GID switch
1083+===========================
1084+
1085+If you want the most security when using chkuser, and you have enabled
1086+CHKUSER_ENABLE_UIDGID within chkuser_settings.h (it's enabled by default), use
1087+these instructions.
1088+
1089+Description.
1090+       qmail-smtpd-chkusr must be installed (by default in /var/qmail/bin) with
1091+       setuid (user qmaild) and setgid (group qnofiles), and executed by tcpserver
1092+       with -u vpopmail-user and -g vchkpw-group  parameters.
1093+
1094+       qmail-smtpd-chkusr starts running with the original qmail-smtpd uid and gid,
1095+       switching to needed uid and gid only for vpopmail checks on user existance,
1096+       turning back to the starting uid and gid.
1097+
1098+Instructions.
1099+       You have to set SUID (set-user-ID-on-execution) and SGID
1100+       (set-group-ID-on-execution) bits on qmail-smtpd-chkusr:
1101+               chown qmaild qmail-smtpd
1102+               chgrp nofiles qmail-smtpd
1103+               chmod 6555 qmail-smtpd
1104+
1105+       and the result you see should be like (different size and date, of course):
1106+               -r-sr-sr-x 1 qmaild nofiles 57056 Feb 14 18:18 qmail-smtpd-chkusr
1107+
1108+       Integrate qmail-smtpd in your start files:
1109+
1110+       As example, a real start command for qmail-smtpd-chkusr may be
1111+
1112+       #!/bin/sh -e
1113+       #
1114+       # Using splogger to send the log through syslog.
1115+
1116+       exec env - PATH="/var/qmail/bin:/usr/local/bin" \
1117+       tcpserver -t 5 -v -p -x <your.tcp.smtp.cdb> \
1118+       -u <vpopmail-user> -g <vchkpw-group> -l <your-host.domain> 0 smtp \
1119+       qmail-smtpd-chkusr splogger smtpd &
1120+
1121+       where
1122+               <vpopmail-user> = vpopmail uid
1123+               <vchkpw-group> = vchkpw gid
1124+               <your-host.domain> = your host.domain (!)
1125+               <your.tcp.smtp.cdb> = your tcp.permission.to.relay cdb
1126+
1127+       NOTE: if you are using more system users for your domains, the execution
1128+       uid (which I indicated as vpopmail) should be set to root.
1129+
1130+
1131+Running with fixed UID/GID
1132+==========================
1133+You may use these instructions if you've not defined CHKUSER_ENABLE_UIDGID, or if
1134+you want to run qmail-smtpd as unique user, despite of CHKUSER_ENABLE_UIDGID define.
1135+qmail-smtpd is well safe and robust, and there is no risk running it directly as
1136+vpopmail user, unless you use untrusted software layered down.
1137+
1138+Description.
1139+       qmail-smtpd must be installed normally (-r-xr-xr-x) and executed by tcpserver
1140+       with -u vpopmail-user and -g vchkpw-group parameters.
1141+
1142+Instructions.
1143+       Integrate qmail-smtpd-chkusr in your start files:
1144+
1145+       As example, a real start command for qmail-smtpd-chkusr may be
1146+
1147+       #!/bin/sh -e
1148+       #
1149+       # Using splogger to send the log through syslog.
1150+
1151+       exec env - PATH="/var/qmail/bin:/usr/local/bin" \
1152+       tcpserver -t 5 -v -p -x <your.tcp.smtp.cdb> \
1153+       -u <vpopmail-user> -g <vchkpw-group> -l <your-host.domain> 0 smtp \
1154+       qmail-smtpd-chkusr splogger smtpd &
1155+
1156+       where
1157+               <vpopmail-user> = vpopmail uid
1158+               <vchkpw-group> = vchkpw gid
1159+               <your-host.domain> = your host.domain (!)
1160+               <your.tcp.smtp.cdb> = your tcp.permission.to.relay cdb
1161+
1162+       NOTE: if you are using more system users for your domains, the execution user
1163+       (which I indicated as vpopmail) should be set to root.
1164+
1165+
1166diff -ruN ../netqmail-1.06-original/FILES netqmail-1.06/FILES
1167--- ../netqmail-1.06-original/FILES     2007-11-30 21:22:54.000000000 +0100
1168+++ netqmail-1.06/FILES 2016-11-22 21:03:57.061529799 +0100
1169@@ -136,6 +136,8 @@
1170 dnsip.c
1171 dnsmxip.c
1172 dnsptr.c
1173+dnstxt.c
1174+spfquery.c
1175 hostname.c
1176 ipmeprint.c
1177 tcp-env.c
1178@@ -336,13 +338,16 @@
1179 byte.h
1180 byte_chr.c
1181 byte_copy.c
1182+byte_cspn.c
1183 byte_cr.c
1184 byte_diff.c
1185 byte_rchr.c
1186+byte_rcspn.c
1187 byte_zero.c
1188 str.h
1189 str_chr.c
1190 str_cpy.c
1191+str_cpyb.c
1192 str_diff.c
1193 str_diffn.c
1194 str_len.c
1195@@ -402,6 +407,8 @@
1196 date822fmt.c
1197 dns.h
1198 dns.c
1199+spf.h
1200+spf.c
1201 trylsock.c
1202 tryrsolv.c
1203 ip.h
1204@@ -432,3 +439,4 @@
1205 tcp-environ.5
1206 constmap.h
1207 constmap.c
1208+qmail-todo.c
1209diff -ruN ../netqmail-1.06-original/LICENSE.authentication netqmail-1.06/LICENSE.authentication
1210--- ../netqmail-1.06-original/LICENSE.authentication    1970-01-01 01:00:00.000000000 +0100
1211+++ netqmail-1.06/LICENSE.authentication        2016-11-22 21:03:57.102528432 +0100
1212@@ -0,0 +1,43 @@
1213+AUTHOR
1214+======
1215+
1216+Author:
1217+       Dr. Erwin Hoffmann - FEHCom Germany
1218+Web-Site:     
1219+       http://www.fehcom.de/qmail.html
1220+E-Mail:       
1221+       feh@fehcom.de
1222+
1223+
1224+LICENSE
1225+=======
1226+
1227+qmail AUTHENTICATION is free software.
1228+This includes:
1229+       You can download and use qmail AUTHENTICATION (and parts of it) as you like.
1230+       You can modify the source code without notification to or permission by the author.
1231+Please check:
1232+       http://www.cr.yp.to/softwarelaw.html
1233+
1234+
1235+DEPENDENCIES
1236+============
1237+
1238+qmail AUTHENTICATION patches (modifies) parts of the qmail-1.03 source files.
1239+It should only be applied against the source as supplied by D.J. Bernstein.
1240+
1241+
1242+FITNESS
1243+=======
1244+
1245+The Author does not guarantee a specific fitness of qmail AUTHENTICATION.
1246+If you use qmail AUTHENTICATION, it's on your own risk.
1247+
1248+
1249+DISTRIBUTION
1250+============
1251+
1252+qmail AUTHENTICATION may be included in ports and packages under the following conditions:
1253+       The port/package has to show the current version number of qmail AUTHENTICATION.
1254+       All files (namely this) have to be included.
1255+
1256diff -ruN ../netqmail-1.06-original/MakeArgs.c netqmail-1.06/MakeArgs.c
1257--- ../netqmail-1.06-original/MakeArgs.c        1970-01-01 01:00:00.000000000 +0100
1258+++ netqmail-1.06/MakeArgs.c    2016-11-22 21:03:57.102528432 +0100
1259@@ -0,0 +1,144 @@
1260+/*
1261+ * $Log: MakeArgs.c,v $
1262+ * Revision 2.8  2007-12-21 14:35:42+05:30  Cprogrammer
1263+ * included env.h to prevent compiler warning
1264+ *
1265+ * Revision 2.7  2007-12-20 12:45:28+05:30  Cprogrammer
1266+ * expand environment variables with '$' sign
1267+ *
1268+ * Revision 2.6  2005-08-23 17:31:28+05:30  Cprogrammer
1269+ * removed sccsid variable
1270+ *
1271+ * Revision 2.5  2005-04-02 19:06:02+05:30  Cprogrammer
1272+ * djb version
1273+ *
1274+ * Revision 2.4  2005-03-30 22:52:47+05:30  Cprogrammer
1275+ * BUG - Incorrect free
1276+ *
1277+ * Revision 2.3  2004-07-12 22:47:58+05:30  Cprogrammer
1278+ * bug fix. Free all allocated members
1279+ *
1280+ * Revision 2.2  2002-12-21 18:21:09+05:30  Cprogrammer
1281+ * added functionality of escaping text via quotes
1282+ *
1283+ * Revision 2.1  2002-08-13 20:35:44+05:30  Cprogrammer
1284+ * addition spaces were not getting skipped
1285+ *
1286+ * Revision 1.2  2002-03-03 17:23:05+05:30  Cprogrammer
1287+ * replaced strcpy with scopy
1288+ *
1289+ * Revision 1.1  2001-12-13 01:46:09+05:30  Cprogrammer
1290+ * Initial revision
1291+ *
1292+ */
1293+#include "alloc.h"
1294+#include "str.h"
1295+#include "stralloc.h"
1296+#include "env.h"
1297+#include <ctype.h>
1298+
1299+#define isEscape(ch) ((ch) == '"' || (ch) == '\'')
1300+
1301+/*
1302+ * function to expand a string into command line
1303+ * arguments. To free memory allocated by this
1304+ * function the following should be done
1305+ *
1306+ * free(argv);
1307+ *
1308+ */
1309+char          **
1310+MakeArgs(char *cmmd)
1311+{
1312+       char           *ptr, *marker;
1313+       char          **argv;
1314+       int             argc, idx;
1315+       static stralloc sptr = { 0 };
1316+
1317+       for (ptr = cmmd;*ptr && isspace((int) *ptr);ptr++);
1318+       idx = str_len(ptr);
1319+       if (!stralloc_copys(&sptr, ptr))
1320+               return((char **) 0);
1321+       if (!stralloc_0(&sptr))
1322+               return((char **) 0);
1323+       /*-
1324+        * Get the number of arguments by counting
1325+        * white spaces. Allow escape via the double
1326+        * quotes character at the first word
1327+        */
1328+       for (argc = 0, ptr = sptr.s;*ptr;)
1329+       {
1330+               for (;*ptr && isspace((int) *ptr);ptr++);
1331+               if (!*ptr)
1332+                       break;
1333+               argc++;
1334+               marker = ptr;
1335+               /*- Move till you hit the next white space */
1336+               for (;*ptr && !isspace((int) *ptr);ptr++)
1337+               {
1338+                       /*-
1339+                        * 1. If escape char is encounted skip till you
1340+                        *    hit the terminating escape char
1341+                        * 2. If terminating escape char is missing, come
1342+                        *    back to the start escape char
1343+                        */
1344+                       if (ptr == marker && isEscape(*ptr))
1345+                       {
1346+                               for (ptr++;*ptr && !isEscape(*ptr);ptr++);
1347+                               if (!*ptr)
1348+                                       ptr = marker;
1349+                       }
1350+               } /*- for(;*ptr && !isspace((int) *ptr);ptr++) */
1351+       } /*- for (argc = 0, ptr = sptr.s;*ptr;) */
1352+       /*
1353+        * Allocate memory to store the arguments
1354+        * Do not bother extra bytes occupied by
1355+        * white space characters.
1356+        */
1357+       if (!(argv = (char **) alloc((argc + 1) * sizeof(char *))))
1358+               return ((char **) 0);
1359+       for (idx = 0, ptr = sptr.s;*ptr;)
1360+       {
1361+               for (;*ptr && isspace((int) *ptr);ptr++)
1362+                       *ptr = 0;
1363+               if (!*ptr)
1364+                       break;
1365+               if (*ptr == '$')
1366+                       argv[idx++] = env_get(ptr + 1);
1367+               else
1368+                       argv[idx++] = ptr;
1369+               marker = ptr;
1370+               for (;*ptr && !isspace((int) *ptr);ptr++)
1371+               {
1372+                       if (ptr == marker && isEscape(*ptr))
1373+                       {
1374+                               for (ptr++;*ptr && !isEscape(*ptr);ptr++);
1375+                               if (!*ptr)
1376+                                       ptr = marker;
1377+                               else /*- Remove the quotes */
1378+                               {
1379+                                       argv[idx - 1] += 1;
1380+                                       *ptr = 0;
1381+                               }
1382+                       }
1383+               }
1384+       } /*- for (idx = 0, ptr = sptr.s;*ptr;) */
1385+       argv[idx++] = (char *) 0;
1386+       return (argv);
1387+}
1388+
1389+void
1390+FreeMakeArgs(char **argv)
1391+{
1392+       alloc_free(argv);
1393+       return;
1394+}
1395+
1396+void
1397+getversion_MakeArgs__c()
1398+{
1399+       static char     *x = "$Id: MakeArgs.c,v 2.8 2007-12-21 14:35:42+05:30 Cprogrammer Stab mbhangui $";
1400+       x++;
1401+       x--;
1402+       return;
1403+}
1404diff -ruN ../netqmail-1.06-original/Makefile netqmail-1.06/Makefile
1405--- ../netqmail-1.06-original/Makefile  2007-11-30 21:22:54.000000000 +0100
1406+++ netqmail-1.06/Makefile      2018-04-03 15:05:13.390470191 +0200
1407@@ -1,5 +1,14 @@
1408 # Don't edit Makefile! Use conf-* for configuration.
1409 
1410+VPOPMAIL_HOME=/home/vpopmail
1411+VPOPMAIL_LIBS=`head -1 $(VPOPMAIL_HOME)/etc/lib_deps` `cat dns.lib`
1412+
1413+SMTPD_CHKUSER_OBJ=chkuser.o dns.o
1414+
1415+LIBDOMAINKEYS=/usr/lib
1416+
1417+DEFINES=-DEXTERNAL_TODO # use to enable external todo
1418+
1419 SHELL=/bin/sh
1420 
1421 default: it
1422@@ -136,6 +145,18 @@
1423 compile auto_usera.c
1424        ./compile auto_usera.c
1425 
1426+base64.o: \
1427+compile base64.c base64.h stralloc.h substdio.h str.h
1428+       ./compile base64.c
1429+
1430+md5c.o : \
1431+compile md5c.c md5.h
1432+       ./compile md5c.c
1433+
1434+hmac_md5.o : \
1435+compile hmac_md5.c hmac_md5.h global.h
1436+       ./compile hmac_md5.c
1437+
1438 binm1: \
1439 binm1.sh conf-qmail
1440        cat binm1.sh \
1441@@ -203,6 +224,10 @@
1442 compile byte_cr.c byte.h
1443        ./compile byte_cr.c
1444 
1445+byte_cspn.o: \
1446+compile byte_cspn.c byte.h
1447+       ./compile byte_cspn.c
1448+
1449 byte_diff.o: \
1450 compile byte_diff.c byte.h
1451        ./compile byte_diff.c
1452@@ -211,6 +236,10 @@
1453 compile byte_rchr.c byte.h
1454        ./compile byte_rchr.c
1455 
1456+byte_rcspn.o: \
1457+compile byte_rcspn.c byte.h
1458+       ./compile byte_rcspn.c
1459+
1460 byte_zero.o: \
1461 compile byte_zero.c byte.h
1462        ./compile byte_zero.c
1463@@ -300,6 +329,10 @@
1464 exit.h auto_spawn.h
1465        ./compile chkspawn.c
1466 
1467+chkuser.o: \
1468+compile chkuser.c chkuser.h chkuser_settings.h
1469+       ./compile chkuser.c
1470+
1471 clean: \
1472 TARGETS
1473        rm -f `cat TARGETS`
1474@@ -320,10 +353,13 @@
1475        chmod 755 compile
1476 
1477 condredirect: \
1478-load condredirect.o qmail.o strerr.a fd.a sig.a wait.a seek.a env.a \
1479-substdio.a error.a str.a fs.a auto_qmail.o
1480-       ./load condredirect qmail.o strerr.a fd.a sig.a wait.a \
1481-       seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o
1482+load condredirect.o srs.o rcpthosts.o cdb.a control.o constmap.o case.a \
1483+       getln.a stralloc.a alloc.a open.a qmail.o strerr.a fd.a sig.a \
1484+       wait.a seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o
1485+       ./load condredirect srs.o rcpthosts.o cdb.a control.o constmap.o \
1486+       case.a getln.a stralloc.a alloc.a open.a qmail.o strerr.a fd.a sig.a \
1487+       wait.a seek.a env.a substdio.a error.a str.a fs.a auto_qmail.o \
1488+       -I/usr/local/include -L/usr/local/lib -lsrs2
1489 
1490 condredirect.0: \
1491 condredirect.1
1492@@ -331,7 +367,7 @@
1493 
1494 condredirect.o: \
1495 compile condredirect.c sig.h readwrite.h exit.h env.h error.h fork.h \
1496-wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h
1497+wait.h seek.h qmail.h substdio.h strerr.h substdio.h fmt.h stralloc.h srs.h
1498        ./compile condredirect.c
1499 
1500 config: \
1501@@ -393,84 +429,96 @@
1502        rm -f trydrent.o
1503 
1504 dns.lib: \
1505-tryrsolv.c compile load socket.lib dns.o ipalloc.o ip.o stralloc.a \
1506-alloc.a error.a fs.a str.a
1507+tryrsolv.c compile load socket.lib dns.o ipalloc.o strsalloc.o ip.o \
1508+stralloc.a alloc.a error.a fs.a str.a
1509        ( ( ./compile tryrsolv.c && ./load tryrsolv dns.o \
1510-       ipalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
1511+       ipalloc.o strsalloc.o ip.o stralloc.a alloc.a error.a fs.a str.a \
1512        -lresolv `cat socket.lib` ) >/dev/null 2>&1 \
1513        && echo -lresolv || exit 0 ) > dns.lib
1514        rm -f tryrsolv.o tryrsolv
1515 
1516 dns.o: \
1517-compile dns.c ip.h ipalloc.h ip.h gen_alloc.h fmt.h alloc.h str.h \
1518-stralloc.h gen_alloc.h dns.h case.h
1519+compile dns.c ip.h ipalloc.h strsalloc.h gen_alloc.h fmt.h alloc.h \
1520+str.h stralloc.h dns.h case.h
1521        ./compile dns.c
1522 
1523 dnscname: \
1524-load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1525+load dnscname.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1526 substdio.a error.a str.a fs.a dns.lib socket.lib
1527-       ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1528+       ./load dnscname dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1529        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1530        socket.lib`
1531 
1532 dnscname.o: \
1533-compile dnscname.c substdio.h subfd.h substdio.h stralloc.h \
1534+compile dnscname.c substdio.h subfd.h stralloc.h \
1535 gen_alloc.h dns.h dnsdoe.h readwrite.h exit.h
1536        ./compile dnscname.c
1537 
1538 dnsdoe.o: \
1539-compile dnsdoe.c substdio.h subfd.h substdio.h exit.h dns.h dnsdoe.h
1540+compile dnsdoe.c substdio.h subfd.h exit.h dns.h dnsdoe.h
1541        ./compile dnsdoe.c
1542 
1543 dnsfq: \
1544-load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1545+load dnsfq.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1546 substdio.a error.a str.a fs.a dns.lib socket.lib
1547-       ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1548+       ./load dnsfq dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1549        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1550        socket.lib`
1551 
1552 dnsfq.o: \
1553-compile dnsfq.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
1554-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
1555+compile dnsfq.c substdio.h subfd.h stralloc.h gen_alloc.h \
1556+dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
1557        ./compile dnsfq.c
1558 
1559 dnsip: \
1560-load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1561+load dnsip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1562 substdio.a error.a str.a fs.a dns.lib socket.lib
1563-       ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1564+       ./load dnsip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1565        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1566        socket.lib`
1567 
1568 dnsip.o: \
1569-compile dnsip.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
1570-dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h exit.h
1571+compile dnsip.c substdio.h subfd.h stralloc.h gen_alloc.h \
1572+dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h exit.h
1573        ./compile dnsip.c
1574 
1575 dnsmxip: \
1576-load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o now.o stralloc.a alloc.a \
1577-substdio.a error.a str.a fs.a dns.lib socket.lib
1578-       ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o now.o \
1579+load dnsmxip.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o stralloc.a \
1580+alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
1581+       ./load dnsmxip dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o now.o \
1582        stralloc.a alloc.a substdio.a error.a str.a fs.a  `cat \
1583        dns.lib` `cat socket.lib`
1584 
1585 dnsmxip.o: \
1586-compile dnsmxip.c substdio.h subfd.h substdio.h stralloc.h \
1587-gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h ip.h gen_alloc.h \
1588+compile dnsmxip.c substdio.h subfd.h stralloc.h \
1589+gen_alloc.h fmt.h dns.h dnsdoe.h ip.h ipalloc.h strsalloc.h \
1590 now.h datetime.h exit.h
1591        ./compile dnsmxip.c
1592 
1593 dnsptr: \
1594-load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o stralloc.a alloc.a \
1595+load dnsptr.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1596 substdio.a error.a str.a fs.a dns.lib socket.lib
1597-       ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o stralloc.a \
1598+       ./load dnsptr dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1599        alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1600        socket.lib`
1601 
1602 dnsptr.o: \
1603-compile dnsptr.c substdio.h subfd.h substdio.h stralloc.h gen_alloc.h \
1604+compile dnsptr.c substdio.h subfd.h stralloc.h gen_alloc.h \
1605 str.h scan.h dns.h dnsdoe.h ip.h exit.h
1606        ./compile dnsptr.c
1607 
1608+dnstxt: \
1609+load dnstxt.o dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a alloc.a \
1610+substdio.a error.a str.a fs.a dns.lib socket.lib
1611+       ./load dnstxt dns.o dnsdoe.o ip.o ipalloc.o strsalloc.o stralloc.a \
1612+       alloc.a substdio.a error.a str.a fs.a  `cat dns.lib` `cat \
1613+       socket.lib`
1614+
1615+dnstxt.o: \
1616+compile dnstxt.c substdio.h subfd.h stralloc.h gen_alloc.h \
1617+str.h scan.h dns.h dnsdoe.h ip.h exit.h
1618+       ./compile dnstxt.c
1619+
1620 dot-qmail.0: \
1621 dot-qmail.5
1622        nroff -man dot-qmail.5 > dot-qmail.0
1623@@ -593,10 +641,13 @@
1624        rm -f tryvfork.o tryvfork
1625 
1626 forward: \
1627-load forward.o qmail.o strerr.a alloc.a fd.a wait.a sig.a env.a \
1628-substdio.a error.a str.a fs.a auto_qmail.o
1629-       ./load forward qmail.o strerr.a alloc.a fd.a wait.a sig.a \
1630-       env.a substdio.a error.a str.a fs.a auto_qmail.o
1631+load forward.o srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
1632+       cdb.a case.a open.a stralloc.a alloc.a getln.a \
1633+       fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o
1634+       ./load forward srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
1635+       cdb.a case.a open.a stralloc.a alloc.a getln.a \
1636+       fd.a wait.a sig.a env.a substdio.a error.a str.a fs.a auto_qmail.o \
1637+       -I/usr/local/include -L/usr/local/lib -lsrs2
1638 
1639 forward.0: \
1640 forward.1
1641@@ -604,7 +655,7 @@
1642 
1643 forward.o: \
1644 compile forward.c sig.h readwrite.h exit.h env.h qmail.h substdio.h \
1645-strerr.h substdio.h fmt.h
1646+strerr.h substdio.h fmt.h stralloc.h srs.h
1647        ./compile forward.c
1648 
1649 fs.a: \
1650@@ -703,7 +754,7 @@
1651 
1652 hier.o: \
1653 compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h
1654-       ./compile hier.c
1655+       ./compile $(DEFINES) hier.c
1656 
1657 home: \
1658 home.sh conf-qmail
1659@@ -755,7 +806,7 @@
1660 install-big.o: \
1661 compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \
1662 fifo.h
1663-       ./compile install-big.c
1664+       ./compile $(DEFINES) install-big.c
1665 
1666 install.o: \
1667 compile install.c substdio.h strerr.h error.h open.h readwrite.h \
1668@@ -777,38 +828,52 @@
1669        ./compile ip.c
1670 
1671 ipalloc.o: \
1672-compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h ip.h \
1673+compile ipalloc.c alloc.h gen_allocdefs.h ip.h ipalloc.h \
1674 gen_alloc.h
1675        ./compile ipalloc.c
1676 
1677 ipme.o: \
1678-compile ipme.c hassalen.h byte.h ip.h ipalloc.h ip.h gen_alloc.h \
1679-stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h
1680+compile ipme.c hassalen.h byte.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h \
1681+stralloc.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h readwrite.h
1682        ./compile ipme.c
1683 
1684 ipmeprint: \
1685-load ipmeprint.o ipme.o ip.o ipalloc.o stralloc.a alloc.a substdio.a \
1686-error.a str.a fs.a socket.lib
1687-       ./load ipmeprint ipme.o ip.o ipalloc.o stralloc.a alloc.a \
1688-       substdio.a error.a str.a fs.a  `cat socket.lib`
1689+load ipmeprint.o ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a \
1690+strsalloc.o stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
1691+       ./load ipmeprint ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a \
1692+       strsalloc.o stralloc.a alloc.a substdio.a error.a str.a fs.a \
1693+       `cat socket.lib`
1694 
1695 ipmeprint.o: \
1696 compile ipmeprint.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
1697-ipalloc.h ip.h gen_alloc.h exit.h
1698+ipalloc.h strsalloc.h ip.h gen_alloc.h exit.h auto_qmail.h
1699        ./compile ipmeprint.c
1700 
1701+ipmetest: \
1702+load ipmetest.o ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a stralloc.a alloc.a substdio.a \
1703+error.a str.a fs.a env.a socket.lib
1704+       ./load ipmetest ipme.o ip.o ipalloc.o auto_qmail.o open.a getln.a stralloc.a alloc.a \
1705+       substdio.a error.a env.a str.a fs.a `cat socket.lib`
1706+
1707+ipmetest.o: \
1708+compile ipmetest.c subfd.h substdio.h substdio.h ip.h ipme.h ip.h \
1709+ipalloc.h ip.h gen_alloc.h exit.h auto_qmail.h
1710+       ./compile ipmetest.c
1711+
1712 it: \
1713 qmail-local qmail-lspawn qmail-getpw qmail-remote qmail-rspawn \
1714+qmail-newmvrt \
1715 qmail-clean qmail-send qmail-start splogger qmail-queue qmail-inject \
1716 predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
1717 qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
1718 qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
1719 qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
1720-dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
1721+dnsptr dnsip dnsmxip dnsfq dnstxt hostname ipmeprint ipmetest qreceipt qreceipt qsmhook qbiff \
1722 forward preline condredirect bouncesaying except maildirmake \
1723 maildir2mbox maildirwatch qail elq pinq idedit install-big install \
1724+dktest qmail-dk qmail-dkim dkim spawn-filter dk-filter surblfilter \
1725 instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
1726-binm3 binm3+df
1727+binm3 binm3+df srsfilter surblqueue dknewkey qmail-todo spfquery update_tmprsadh
1728 
1729 load: \
1730 make-load warn-auto.sh systype
1731@@ -890,6 +955,38 @@
1732 readwrite.h open.h headerbody.h maildir.h strerr.h
1733        ./compile maildirwatch.c
1734 
1735+maildirgetquota.o: \
1736+compile maildirgetquota.c maildirgetquota.h maildirmisc.h
1737+       ./compile maildirgetquota.c
1738+
1739+maildirflags.o: \
1740+compile maildirflags.c
1741+       ./compile maildirflags.c
1742+
1743+maildiropen.o: \
1744+compile maildiropen.c maildirmisc.h
1745+       ./compile maildiropen.c
1746+
1747+maildirparsequota.o: \
1748+compile maildirparsequota.c
1749+       ./compile maildirparsequota.c
1750+
1751+maildirquota.o: \
1752+compile maildirquota.c maildirquota.h maildirmisc.h numlib.h
1753+       ./compile maildirquota.c
1754+
1755+overmaildirquota.o: \
1756+compile overmaildirquota.c
1757+       ./compile overmaildirquota.c
1758+
1759+strtimet.o: \
1760+compile strtimet.c
1761+       ./compile strtimet.c
1762+
1763+strpidt.o: \
1764+compile strpidt.c
1765+       ./compile strpidt.c
1766+
1767 mailsubj: \
1768 warn-auto.sh mailsubj.sh conf-qmail conf-break conf-split
1769        cat warn-auto.sh mailsubj.sh \
1770@@ -934,8 +1031,9 @@
1771 preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \
1772 maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \
1773 qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \
1774-qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \
1775-envelopes.0 forgeries.0
1776+qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 dkim.8 dktest.8 \
1777+envelopes.0 forgeries.0 qmail-dk.0 qmail-dkim.0 dk-filter.0 spawn-filter.0 \
1778+surblfilter.0
1779 
1780 mbox.0: \
1781 mbox.5
1782@@ -1107,11 +1205,80 @@
1783        | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1784        > qmail-control.5
1785 
1786+qmail-dk: \
1787+load qmail-dk.o triggerpull.o fmtqfn.o now.o date822fmt.o mess822_ok.o \
1788+subgetopt.o MakeArgs.o datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
1789+str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \
1790+$(LIBDOMAINKEYS)/libdomainkeys.a env.a getln.a control.o stralloc.a dns.lib
1791+       ./load qmail-dk triggerpull.o fmtqfn.o now.o mess822_ok.o \
1792+       date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
1793+       subgetopt.o MakeArgs.o substdio.a error.a fs.a auto_qmail.o \
1794+       auto_split.o auto_uids.o \
1795+       fd.a wait.a $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto env.a control.o open.a getln.a \
1796+       stralloc.a alloc.a substdio.a str.a case.a `cat dns.lib`
1797+
1798+qmail-dk.0: \
1799+qmail-dk.8
1800+       nroff -man qmail-dk.8 > qmail-dk.0
1801+qmail-dk.8: qmail-dk.9
1802+       cat qmail-dk.9 \
1803+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1804+       > qmail-dk.8
1805+
1806+qmail-dk.o: \
1807+compile qmail-dk.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
1808+qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
1809+env.h wait.h fd.h fork.h str.h uint64.h \
1810+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
1811+       ./compile qmail-dk.c
1812+
1813+dktest: load dktest.o scan_ulong.o dktrace.o \
1814+dns.o strsalloc.o ip.o error.o ipalloc.o fmt_ulong.o \
1815+scan_xlong.o socket_v4mappedprefix.o socket_v6any.o \
1816+case_diffs.o case_diffb.o fmt_str.o stralloc.a alloc.a str.a \
1817+$(LIBDOMAINKEYS)/libdomainkeys.a dns.lib
1818+       ./load dktest scan_ulong.o dktrace.o \
1819+       dns.o strsalloc.o ip.o error.o ipalloc.o fmt_ulong.o \
1820+       scan_xlong.o socket_v4mappedprefix.o socket_v6any.o \
1821+       case_diffs.o case_diffb.o fmt_str.o stralloc.a alloc.a str.a \
1822+       $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto `cat dns.lib`
1823+
1824+dktest.o: compile dktest.c domainkeys.h conf-domainkeys
1825+       ./compile `grep -h -v "^#" conf-domainkeys` dktest.c
1826+
1827+dktest.8: dktest.9
1828+       cat dktest.9 \
1829+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1830+       > dktest.8
1831+
1832+dktrace.o: compile dktrace.c dktrace.h str.h case.h conf-domainkeys
1833+       ./compile `grep -h -v "^#" conf-domainkeys` dktrace.c
1834+
1835+qmail-dkim: \
1836+load qmail-dkim.o triggerpull.o fmtqfn.o now.o date822fmt.o \
1837+subgetopt.o MakeArgs.o dkimdns.o datetime.a seek.a ndelay.a \
1838+open.a sig.a alloc.a substdio.a error.a \
1839+str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \
1840+$(LIBDOMAINKEYS)/libdomainkeys.a env.a getln.a control.o stralloc.a dns.lib libdkim.a
1841+       g++ -o qmail-dkim qmail-dkim.o triggerpull.o dkimdns.o fmtqfn.o now.o \
1842+       subgetopt.o MakeArgs.o date822fmt.o datetime.a seek.a ndelay.a \
1843+       open.a sig.a substdio.a error.a fs.a auto_qmail.o \
1844+       auto_split.o auto_uids.o fd.a wait.a \
1845+       $(LIBDOMAINKEYS)/libdomainkeys.a -lcrypto env.a control.o open.a getln.a \
1846+       stralloc.a alloc.a substdio.a str.a case.a libdkim.a `cat dns.lib`
1847+
1848+qmail-dkim.o: \
1849+compile qmail-dkim.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
1850+qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
1851+sgetopt.h env.h wait.h fd.h fork.h str.h dkim.h \
1852+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
1853+       ./compile -DHAVE_CONFIG_H qmail-dkim.c
1854+
1855 qmail-getpw: \
1856 load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \
1857 auto_usera.o
1858        ./load qmail-getpw case.a substdio.a error.a str.a fs.a \
1859-       auto_break.o auto_usera.o
1860+       auto_break.o auto_usera.o
1861 
1862 qmail-getpw.0: \
1863 qmail-getpw.8
1864@@ -1125,6 +1292,28 @@
1865        | sed s}SPAWN}"`head -1 conf-spawn`"}g \
1866        > qmail-getpw.8
1867 
1868+qmail-dkim.0: qmail-dkim.8
1869+       nroff -man qmail-dkim.8 > qmail-dkim.0
1870+qmail-dkim.8: qmail-dkim.9
1871+       cat qmail-dkim.9 \
1872+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1873+       > qmail-dkim.8
1874+
1875+dkim.8: dkim.9
1876+       cat dkim.9 | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1877+       > dkim.8
1878+
1879+dk-filter.0: dk-filter.8
1880+       nroff -man dk-filter.8 > dk-filter.0
1881+dk-filter.8: dk-filter.9
1882+       cat dk-filter.9 \
1883+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
1884+       > dk-filter.8
1885+
1886+dknewkey: dknewkey.sh warn-auto.sh
1887+       rm -f dknewkey
1888+       cat warn-auto.sh dknewkey.sh > dknewkey
1889+
1890 qmail-getpw.o: \
1891 compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \
1892 error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \
1893@@ -1136,15 +1325,16 @@
1894        nroff -man qmail-header.5 > qmail-header.0
1895 
1896 qmail-inject: \
1897-load qmail-inject.o headerbody.o hfield.o newfield.o quote.o now.o \
1898+load qmail-inject.o rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o quote.o now.o \
1899 control.o date822fmt.o constmap.o qmail.o case.a fd.a wait.a open.a \
1900 getln.a sig.a getopt.a datetime.a token822.o env.a stralloc.a alloc.a \
1901 substdio.a error.a str.a fs.a auto_qmail.o
1902-       ./load qmail-inject headerbody.o hfield.o newfield.o \
1903+       ./load qmail-inject rcpthosts.o cdb.a srs.o headerbody.o hfield.o newfield.o \
1904        quote.o now.o control.o date822fmt.o constmap.o qmail.o \
1905        case.a fd.a wait.a open.a getln.a sig.a getopt.a datetime.a \
1906        token822.o env.a stralloc.a alloc.a substdio.a error.a \
1907-       str.a fs.a auto_qmail.o
1908+       str.a fs.a auto_qmail.o \
1909+       -I/usr/local/include -L/usr/local/lib -lsrs2
1910 
1911 qmail-inject.0: \
1912 qmail-inject.8
1913@@ -1171,15 +1361,20 @@
1914        > qmail-limits.7
1915 
1916 qmail-local: \
1917-load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \
1918+load qmail-local.o constmap.o control.o rcpthosts.o srs.o cdb.a qmail.o quote.o now.o gfrom.o myctime.o \
1919 slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \
1920 wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \
1921-fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib
1922-       ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \
1923+fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib maildirquota.o \
1924+maildirgetquota.o maildiropen.o maildirparsequota.o overmaildirquota.o \
1925+strtimet.o strpidt.o
1926+       ./load qmail-local constmap.o control.o rcpthosts.o srs.o cdb.a \
1927+       qmail.o quote.o now.o gfrom.o myctime.o \
1928        slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \
1929        lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \
1930        substdio.a error.a str.a fs.a datetime.a auto_qmail.o \
1931-       auto_patrn.o  `cat socket.lib`
1932+       auto_patrn.o  `cat socket.lib` maildirquota.o maildirgetquota.o \
1933+       maildiropen.o maildirparsequota.o overmaildirquota.o strtimet.o strpidt.o \
1934+       -I/usr/local/include -L/usr/local/lib -lsrs2
1935 
1936 qmail-local.0: \
1937 qmail-local.8
1938@@ -1200,11 +1395,11 @@
1939 qmail-lspawn: \
1940 load qmail-lspawn.o spawn.o prot.o slurpclose.o coe.o sig.a wait.a \
1941 case.a cdb.a fd.a open.a stralloc.a alloc.a substdio.a error.a str.a \
1942-fs.a auto_qmail.o auto_uids.o auto_spawn.o
1943+fs.a auto_qmail.o auto_uids.o auto_spawn.o envread.o str_diffn.o
1944        ./load qmail-lspawn spawn.o prot.o slurpclose.o coe.o \
1945        sig.a wait.a case.a cdb.a fd.a open.a stralloc.a alloc.a \
1946        substdio.a error.a str.a fs.a auto_qmail.o auto_uids.o \
1947-       auto_spawn.o
1948+       auto_spawn.o envread.o str_diffn.o
1949 
1950 qmail-lspawn.0: \
1951 qmail-lspawn.8
1952@@ -1213,9 +1408,22 @@
1953 qmail-lspawn.o: \
1954 compile qmail-lspawn.c fd.h wait.h prot.h substdio.h stralloc.h \
1955 gen_alloc.h scan.h exit.h fork.h error.h cdb.h uint32.h case.h \
1956-slurpclose.h auto_qmail.h auto_uids.h qlx.h
1957+slurpclose.h auto_qmail.h auto_uids.h qlx.h env.h
1958        ./compile qmail-lspawn.c
1959 
1960+qmail-newmvrt: \
1961+load qmail-newmvrt.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \
1962+stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o
1963+       ./load qmail-newmvrt cdbmss.o getln.a open.a cdbmake.a \
1964+       seek.a case.a stralloc.a alloc.a strerr.a substdio.a \
1965+       error.a str.a auto_qmail.o
1966+
1967+qmail-newmvrt.o: \
1968+compile qmail-newmvrt.c strerr.h stralloc.h gen_alloc.h substdio.h \
1969+getln.h exit.h readwrite.h open.h auto_qmail.h cdbmss.h cdbmake.h \
1970+uint32.h substdio.h
1971+       ./compile qmail-newmvrt.c
1972+
1973 qmail-newmrh: \
1974 load qmail-newmrh.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \
1975 stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o
1976@@ -1269,11 +1477,13 @@
1977 qmail-pop3d: \
1978 load qmail-pop3d.o commands.o case.a timeoutread.o timeoutwrite.o \
1979 maildir.o prioq.o now.o env.a strerr.a sig.a open.a getln.a \
1980-stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib
1981+stralloc.a alloc.a substdio.a error.a str.a fs.a socket.lib maildirquota.o \
1982+maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
1983        ./load qmail-pop3d commands.o case.a timeoutread.o \
1984        timeoutwrite.o maildir.o prioq.o now.o env.a strerr.a sig.a \
1985        open.a getln.a stralloc.a alloc.a substdio.a error.a str.a \
1986-       fs.a  `cat socket.lib`
1987+       fs.a  `cat socket.lib` maildirquota.o maildirgetquota.o \
1988+    maildirparsequota.o maildirflags.o maildiropen.o strtimet.o strpidt.o
1989 
1990 qmail-pop3d.0: \
1991 qmail-pop3d.8
1992@@ -1419,13 +1629,13 @@
1993        nroff -man qmail-qstat.8 > qmail-qstat.0
1994 
1995 qmail-queue: \
1996-load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o \
1997-datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
1998-str.a fs.a auto_qmail.o auto_split.o auto_uids.o
1999+load qmail-queue.o triggerpull.o fmtqfn.o now.o date822fmt.o wildmat.o qregex.o \
2000+datetime.a seek.a case.a ndelay.a open.a sig.a getln.a stralloc.a alloc.a substdio.a error.a \
2001+env.a control.o constmap.o str.a fs.a auto_qmail.o auto_split.o auto_uids.o
2002        ./load qmail-queue triggerpull.o fmtqfn.o now.o \
2003-       date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
2004-       alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
2005-       auto_split.o auto_uids.o
2006+        date822fmt.o wildmat.o qregex.o env.a control.o constmap.o datetime.a case.a seek.a ndelay.a open.a sig.a \
2007+        getln.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o auto_uids.o
2008+
2009 
2010 qmail-queue.0: \
2011 qmail-queue.8
2012@@ -1439,14 +1649,18 @@
2013 
2014 qmail-remote: \
2015 load qmail-remote.o control.o constmap.o timeoutread.o timeoutwrite.o \
2016-timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o ipme.o quote.o \
2017+timeoutconn.o tcpto.o now.o dns.o ip.o ipalloc.o strsalloc.o ipme.o quote.o \
2018 ndelay.a case.a sig.a open.a lock.a seek.a getln.a stralloc.a alloc.a \
2019-substdio.a error.a str.a fs.a auto_qmail.o dns.lib socket.lib
2020+substdio.a error.a str.a fs.a auto_qmail.o \
2021+base64.o md5c.o hmac_md5.o \
2022+dns.lib socket.lib
2023        ./load qmail-remote control.o constmap.o timeoutread.o \
2024        timeoutwrite.o timeoutconn.o tcpto.o now.o dns.o ip.o \
2025-       ipalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
2026+        tls.o ssl_timeoutio.o -L/usr/local/ssl/lib -lssl -lcrypto \
2027+       ipalloc.o strsalloc.o ipme.o quote.o ndelay.a case.a sig.a open.a \
2028        lock.a seek.a getln.a stralloc.a alloc.a substdio.a error.a \
2029-       str.a fs.a auto_qmail.o  `cat dns.lib` `cat socket.lib`
2030+        base64.o md5c.o hmac_md5.o \
2031+        str.a fs.a auto_qmail.o `cat dns.lib` `cat socket.lib`
2032 
2033 qmail-remote.0: \
2034 qmail-remote.8
2035@@ -1455,7 +1669,7 @@
2036 qmail-remote.o: \
2037 compile qmail-remote.c sig.h stralloc.h gen_alloc.h substdio.h \
2038 subfd.h substdio.h scan.h case.h error.h auto_qmail.h control.h dns.h \
2039-alloc.h quote.h ip.h ipalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h \
2040+alloc.h quote.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ipme.h ip.h ipalloc.h strsalloc.h \
2041 gen_alloc.h gen_allocdefs.h str.h now.h datetime.h exit.h constmap.h \
2042 tcpto.h readwrite.h timeoutconn.h timeoutread.h timeoutwrite.h
2043        ./compile qmail-remote.c
2044@@ -1463,11 +1677,11 @@
2045 qmail-rspawn: \
2046 load qmail-rspawn.o spawn.o tcpto_clean.o now.o coe.o sig.a open.a \
2047 seek.a lock.a wait.a fd.a stralloc.a alloc.a substdio.a error.a str.a \
2048-auto_qmail.o auto_uids.o auto_spawn.o
2049+auto_qmail.o auto_uids.o auto_spawn.o envread.o str_diffn.o
2050        ./load qmail-rspawn spawn.o tcpto_clean.o now.o coe.o \
2051        sig.a open.a seek.a lock.a wait.a fd.a stralloc.a alloc.a \
2052        substdio.a error.a str.a auto_qmail.o auto_uids.o \
2053-       auto_spawn.o
2054+       auto_spawn.o  envread.o str_diffn.o
2055 
2056 qmail-rspawn.0: \
2057 qmail-rspawn.8
2058@@ -1475,20 +1689,21 @@
2059 
2060 qmail-rspawn.o: \
2061 compile qmail-rspawn.c fd.h wait.h substdio.h exit.h fork.h error.h \
2062-tcpto.h
2063+tcpto.h env.h
2064        ./compile qmail-rspawn.c
2065 
2066 qmail-send: \
2067-load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \
2068+load qmail-send.o rcpthosts.o cdb.a srs.o qsutil.o control.o constmap.o newfield.o prioq.o \
2069 trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \
2070 datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \
2071 lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \
2072 auto_split.o env.a
2073-       ./load qmail-send qsutil.o control.o constmap.o newfield.o \
2074+       ./load qmail-send rcpthosts.o cdb.a srs.o qsutil.o control.o constmap.o newfield.o \
2075        prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \
2076        qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \
2077        wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \
2078-       substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a
2079+       substdio.a error.a str.a fs.a auto_qmail.o auto_split.o env.a \
2080+       -I/usr/local/include -L/usr/local/lib -lsrs2
2081 
2082 qmail-send.0: \
2083 qmail-send.8
2084@@ -1509,7 +1724,7 @@
2085 scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \
2086 qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \
2087 fmtqfn.h readsubdir.h direntry.h
2088-       ./compile qmail-send.c
2089+       ./compile $(DEFINES) qmail-send.c
2090 
2091 qmail-showctl: \
2092 load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \
2093@@ -1528,21 +1743,26 @@
2094 compile qmail-showctl.c substdio.h subfd.h substdio.h exit.h fmt.h \
2095 str.h control.h constmap.h stralloc.h gen_alloc.h direntry.h \
2096 auto_uids.h auto_qmail.h auto_break.h auto_patrn.h auto_spawn.h \
2097-auto_split.h
2098+auto_split.h spf.h
2099        ./compile qmail-showctl.c
2100 
2101 qmail-smtpd: \
2102 load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
2103-timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
2104-date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
2105-open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
2106-fs.a auto_qmail.o socket.lib
2107-       ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
2108-       timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
2109-       received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
2110-       datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
2111-       alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
2112-       socket.lib`
2113+strerr.a wildmat.o qregex.o \
2114+timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o constmap.o \
2115+received.o date822fmt.o now.o qmail.o spf.o dns.o cdb.a fd.a wait.a \
2116+datetime.a getln.a open.a sig.a case.a env.a stralloc.a alloc.a substdio.a \
2117+error.a str.a fs.a auto_qmail.o base64.o socket.lib dns.lib lock.a policy.o \
2118+$(SMTPD_CHKUSER_OBJ)
2119+       ./load qmail-smtpd $(SMTPD_CHKUSER_OBJ) rcpthosts.o commands.o timeoutread.o \
2120+       strerr.a wildmat.o qregex.o \
2121+       timeoutwrite.o ip.o ipme.o ipalloc.o strsalloc.o control.o \
2122+       tls.o ssl_timeoutio.o ndelay.a -L/usr/local/ssl/lib -lssl -lcrypto \
2123+       constmap.o received.o date822fmt.o now.o qmail.o spf.o cdb.a \
2124+       fd.a wait.a datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
2125+       alloc.a substdio.a error.a strerr.a str.a fs.a auto_qmail.o base64.o policy.o \
2126+       $(VPOPMAIL_LIBS) \
2127+       `cat socket.lib`
2128 
2129 qmail-smtpd.0: \
2130 qmail-smtpd.8
2131@@ -1551,9 +1771,10 @@
2132 qmail-smtpd.o: \
2133 compile qmail-smtpd.c sig.h readwrite.h stralloc.h gen_alloc.h \
2134 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
2135-error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
2136-substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
2137-exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
2138+error.h ipme.h ip.h ipalloc.h strsalloc.h ip.h gen_alloc.h ip.h qmail.h \
2139+substdio.h strerr.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
2140+exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h spf.h dns.h base64.h \
2141+cdb.h chkuser.h
2142        ./compile qmail-smtpd.c
2143 
2144 qmail-start: \
2145@@ -1574,7 +1795,7 @@
2146 
2147 qmail-start.o: \
2148 compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h
2149-       ./compile qmail-start.c
2150+       ./compile $(DEFINES) qmail-start.c
2151 
2152 qmail-tcpok: \
2153 load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \
2154@@ -1606,6 +1827,20 @@
2155 fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h
2156        ./compile qmail-tcpto.c
2157 
2158+qmail-todo: \
2159+load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o now.o \
2160+readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a alloc.a \
2161+substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
2162+       ./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \
2163+       readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \
2164+       alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o
2165+
2166+qmail-todo.o: \
2167+compile alloc.h auto_qmail.h byte.h constmap.h control.h direntry.h error.h \
2168+exit.h fmt.h fmtqfn.h getln.h open.h ndelay.h now.h readsubdir.h readwrite.h \
2169+scan.h select.h str.h stralloc.h substdio.h trigger.h
2170+       ./compile $(DEFINES) qmail-todo.c
2171+
2172 qmail-upq: \
2173 warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split
2174        cat warn-auto.sh qmail-upq.sh \
2175@@ -1639,10 +1874,10 @@
2176 qreceipt: \
2177 load qreceipt.o headerbody.o hfield.o quote.o token822.o qmail.o \
2178 getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a substdio.a error.a \
2179-str.a auto_qmail.o
2180+str.a auto_qmail.o scan_ulong.o
2181        ./load qreceipt headerbody.o hfield.o quote.o token822.o \
2182        qmail.o getln.a fd.a wait.a sig.a env.a stralloc.a alloc.a \
2183-       substdio.a error.a str.a auto_qmail.o
2184+       substdio.a error.a str.a auto_qmail.o scan_ulong.o
2185 
2186 qreceipt.0: \
2187 qreceipt.1
2188@@ -1779,7 +2014,7 @@
2189 qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
2190 qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
2191 qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
2192-dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
2193+dnsip.c dnsmxip.c dnsptr.c dnstxt.c hostname.c ipmeprint.c ipmetest.c tcp-env.c \
2194 sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
2195 except.c bouncesaying.c condredirect.c maildirmake.c maildir2mbox.c \
2196 maildirwatch.c splogger.c qail.sh elq.sh pinq.sh qmail-upq.sh \
2197@@ -1813,8 +2048,9 @@
2198 trywaitp.c sig.h sig_alarm.c sig_block.c sig_catch.c sig_pause.c \
2199 sig_pipe.c sig_child.c sig_term.c sig_hup.c sig_misc.c sig_bug.c \
2200 trysgact.c trysgprm.c env.3 env.h env.c envread.c byte.h byte_chr.c \
2201-byte_copy.c byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h \
2202-str_chr.c str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c \
2203+byte_copy.c byte_cr.c byte_cspn.c byte_diff.c byte_rchr.c byte_rcspn.c \
2204+byte_zero.c str.h spf.c spf.h spfquery.c \
2205+str_chr.c str_cpy.c str_cpyb.c str_diff.c str_diffn.c str_len.c str_rchr.c \
2206 str_start.c lock.h lock_ex.c lock_exnb.c lock_un.c tryflock.c getln.3 \
2207 getln.h getln.c getln2.3 getln2.c sgetopt.3 sgetopt.h sgetopt.c \
2208 subgetopt.3 subgetopt.h subgetopt.c error.3 error_str.3 error_temp.3 \
2209@@ -1824,10 +2060,11 @@
2210 headerbody.h headerbody.c token822.h token822.c control.h control.c \
2211 datetime.3 datetime.h datetime.c datetime_un.c prioq.h prioq.c \
2212 date822fmt.h date822fmt.c dns.h dns.c trylsock.c tryrsolv.c ip.h ip.c \
2213-ipalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
2214+ipalloc.h strsalloc.h ipalloc.c select.h1 select.h2 trysysel.c ndelay.h ndelay.c \
2215 ndelay_off.c direntry.3 direntry.h1 direntry.h2 trydrent.c prot.h \
2216 prot.c chkshsgr.c warn-shsgr tryshsgr.c ipme.h ipme.c trysalen.c \
2217-maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c
2218+maildir.5 maildir.h maildir.c tcp-environ.5 constmap.h constmap.c \
2219+update_tmprsadh
2220        shar -m `cat FILES` > shar
2221        chmod 400 shar
2222 
2223@@ -1897,6 +2134,23 @@
2224        ./chkspawn
2225        ./compile spawn.c
2226 
2227+spf.o: \
2228+compile spf.c stralloc.h gen_alloc.h alloc.h ipme.h ip.h ipalloc.h \
2229+strsalloc.h str.h fmt.h scan.h byte.h now.h case.h
2230+       ./compile spf.c
2231+
2232+spfquery: \
2233+load spfquery.o spf.o ip.o getln.o getln2.o open_read.o ipme.o ipalloc.o strsalloc.o \
2234+now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a error.a fs.a case.a dns.lib
2235+       ./load spfquery spf.o ip.o getln.o getln2.o open_read.o ipme.o ipalloc.o strsalloc.o \
2236+       now.o dns.o datetime.a stralloc.a alloc.a str.a substdio.a \
2237+       case.a error.a fs.a `cat dns.lib` `cat socket.lib`
2238+
2239+spfquery.o: \
2240+compile spfquery.c substdio.h subfd.h stralloc.h gen_alloc.h alloc.h \
2241+spf.h exit.h
2242+       ./compile spfquery.c
2243+
2244 splogger: \
2245 load splogger.o substdio.a error.a str.a fs.a syslog.lib socket.lib
2246        ./load splogger substdio.a error.a str.a fs.a  `cat \
2247@@ -1911,13 +2165,33 @@
2248 scan.h fmt.h
2249        ./compile splogger.c
2250 
2251+srs.o: \
2252+compile srs.c srs.h auto_qmail.h stralloc.h
2253+       ./compile srs.c
2254+
2255+
2256+srsfilter: \
2257+load srsfilter.o srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
2258+       cdb.a case.a open.a stralloc.a alloc.a getln.a fd.a wait.a sig.a \
2259+       env.a substdio.a error.a str.a fs.a auto_qmail.o
2260+       ./load srsfilter srs.o qmail.o strerr.a control.o rcpthosts.o constmap.o \
2261+       cdb.a case.a open.a stralloc.a alloc.a getln.a fd.a wait.a sig.a \
2262+       env.a substdio.a error.a str.a fs.a auto_qmail.o \
2263+       -I/usr/local/include -L/usr/local/lib -lsrs2
2264+
2265+srsfilter.o: \
2266+compile srsfilter.c sig.h readwrite.h exit.h env.h qmail.h substdio.h strerr.h \
2267+substdio.h fmt.h stralloc.h srs.h
2268+       ./compile srsfilter.c
2269+
2270+
2271 str.a: \
2272-makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \
2273-str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \
2274-byte_cr.o byte_zero.o
2275-       ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \
2276-       str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \
2277-       byte_diff.o byte_copy.o byte_cr.o byte_zero.o
2278+makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o str_chr.o \
2279+str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o byte_rcspn.o \
2280+byte_diff.o byte_copy.o byte_cr.o byte_zero.o
2281+       ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o \
2282+       str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_cspn.o \
2283+       byte_rcspn.o byte_diff.o byte_copy.o byte_cr.o byte_zero.o
2284 
2285 str_chr.o: \
2286 compile str_chr.c str.h
2287@@ -1927,6 +2201,10 @@
2288 compile str_cpy.c str.h
2289        ./compile str_cpy.c
2290 
2291+str_cpyb.o: \
2292+compile str_cpyb.c str.h
2293+       ./compile str_cpyb.c
2294+
2295 str_diff.o: \
2296 compile str_diff.c str.h
2297        ./compile str_diff.c
2298@@ -2006,6 +2284,11 @@
2299 compile strerr_sys.c error.h strerr.h
2300        ./compile strerr_sys.c
2301 
2302+strsalloc.o: \
2303+compile strsalloc.c alloc.h gen_allocdefs.h stralloc.h strsalloc.h \
2304+gen_alloc.h
2305+       ./compile strsalloc.c
2306+
2307 subfderr.o: \
2308 compile subfderr.c readwrite.h substdio.h subfd.h substdio.h
2309        ./compile subfderr.c
2310@@ -2066,11 +2349,11 @@
2311 
2312 tcp-env: \
2313 load tcp-env.o dns.o remoteinfo.o timeoutread.o timeoutwrite.o \
2314-timeoutconn.o ip.o ipalloc.o case.a ndelay.a sig.a env.a getopt.a \
2315-stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
2316+timeoutconn.o ip.o ipalloc.o strsalloc.o case.a ndelay.a sig.a env.a \
2317+getopt.a stralloc.a alloc.a substdio.a error.a str.a fs.a dns.lib socket.lib
2318        ./load tcp-env dns.o remoteinfo.o timeoutread.o \
2319-       timeoutwrite.o timeoutconn.o ip.o ipalloc.o case.a ndelay.a \
2320-       sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
2321+       timeoutwrite.o timeoutconn.o ip.o ipalloc.o strsalloc.o case.a \
2322+       ndelay.a sig.a env.a getopt.a stralloc.a alloc.a substdio.a error.a \
2323        str.a fs.a  `cat dns.lib` `cat socket.lib`
2324 
2325 tcp-env.0: \
2326@@ -2108,6 +2391,19 @@
2327 compile timeoutwrite.c timeoutwrite.h select.h error.h readwrite.h
2328        ./compile timeoutwrite.c
2329 
2330+qmail-smtpd: tls.o ssl_timeoutio.o ndelay.a
2331+qmail-remote: tls.o ssl_timeoutio.o
2332+qmail-smtpd.o: tls.h ssl_timeoutio.h
2333+qmail-remote.o: tls.h ssl_timeoutio.h
2334+
2335+tls.o: \
2336+compile tls.c exit.h error.h
2337+       ./compile tls.c
2338+
2339+ssl_timeoutio.o: \
2340+compile ssl_timeoutio.c ssl_timeoutio.h select.h error.h ndelay.h
2341+       ./compile ssl_timeoutio.c
2342+
2343 token822.o: \
2344 compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
2345 gen_alloc.h gen_allocdefs.h
2346@@ -2139,3 +2435,165 @@
2347 wait_pid.o: \
2348 compile wait_pid.c error.h haswaitp.h
2349        ./compile wait_pid.c
2350+MakeArgs.o: compile MakeArgs.c alloc.h str.h alloc.h stralloc.h
2351+       ./compile MakeArgs.c
2352+
2353+spawn-filter: \
2354+load spawn-filter.o auto_qmail.o \
2355+fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \
2356+case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o \
2357+error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a
2358+       ./load spawn-filter \
2359+       fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \
2360+       case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o auto_qmail.o \
2361+       error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a
2362+
2363+spawn-filter.o: \
2364+compile spawn-filter.c fmt.h str.h strerr.h env.h substdio.h stralloc.h error.h \
2365+wait.h qregex.h
2366+       ./compile spawn-filter.c
2367+
2368+qregex.o: \
2369+compile qregex.c qregex.h case.h stralloc.h constmap.h substdio.h byte.h env.h
2370+       ./compile qregex.c
2371+
2372+wildmat.o: \
2373+compile wildmat.c
2374+       ./compile wildmat.c
2375+
2376+spawn-filter.0: \
2377+spawn-filter.8
2378+       nroff -man spawn-filter.8 > spawn-filter.0
2379+
2380+spawn-filter.8: \
2381+spawn-filter.9
2382+       cat spawn-filter.9 \
2383+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
2384+       > spawn-filter.8
2385+
2386+str_cspn.o: \
2387+compile str.h str_cspn.c
2388+       ./compile str_cspn.c
2389+
2390+mess822_ok.o: \
2391+compile mess822_ok.c uint64.h
2392+
2393+surblfilter.0: surblfilter.8
2394+       nroff -man surblfilter.8 > surblfilter.0
2395+
2396+surblfilter.8: surblfilter.9
2397+       cat surblfilter.9 \
2398+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
2399+       > surblfilter.8
2400+
2401+surblfilter: \
2402+load surblfilter.o envread.o strerr_die.o strerr_sys.o \
2403+control.o alloc.o alloc_re.o error.o \
2404+error_str.o auto_qmail.o \
2405+case_startb.o byte_diff.o str_cspn.o \
2406+byte_copy.o byte_chr.o byte_rchr.o byte_cr.o \
2407+getln.o getln2.o open_read.o str_len.o str_diffn.o \
2408+str_cpy.o str_chr.o scan_xlong.o \
2409+now.o scan_ulong.o mess822_ok.o constmap.o \
2410+ip.o strsalloc.o dns.o ipalloc.o fmt_str.o fmt_ulong.o \
2411+socket_v6any.o socket_v4mappedprefix.o \
2412+sgetopt.o subgetopt.o base64sub.o \
2413+case_diffb.o stralloc.a substdio.a
2414+       ./load surblfilter envread.o strerr_die.o strerr_sys.o \
2415+       control.o alloc.o alloc_re.o error.o \
2416+       error_str.o auto_qmail.o \
2417+       case_startb.o byte_diff.o str_cspn.o \
2418+       byte_copy.o byte_chr.o byte_rchr.o byte_cr.o \
2419+       getln.o getln2.o open_read.o str_len.o str_diffn.o \
2420+       str_cpy.o str_chr.o scan_xlong.o \
2421+       now.o scan_ulong.o mess822_ok.o constmap.o \
2422+       ip.o strsalloc.o dns.o ipalloc.o fmt_str.o fmt_ulong.o \
2423+       socket_v6any.o socket_v4mappedprefix.o \
2424+       sgetopt.o subgetopt.o base64sub.o \
2425+       case_diffb.o stralloc.a substdio.a -lresolv
2426+
2427+surblfilter.o: \
2428+compile surblfilter.c alloc.h error.h str.h case.h \
2429+constmap.h auto_qmail.h stralloc.h env.h control.h \
2430+strerr.h substdio.h getln.h byte.h dns.h ip.h \
2431+ipalloc.h mess822.h scan.h subgetopt.h uint64.h \
2432+base64.h
2433+       ./compile surblfilter.c
2434+
2435+surblqueue: \
2436+surblqueue.sh conf-qmail
2437+       cat surblqueue.sh \
2438+       | sed s}QMAIL}"`head -1 conf-qmail`"}g \
2439+       > surblqueue
2440+       chmod 755 surblqueue
2441+
2442+hastai.h: \
2443+trytai.c compile load
2444+       (( ./compile trytai.c && ./load trytai -ltai) >/dev/null \
2445+       2>&1 \
2446+       && echo \#define HASTAI 1 || exit 0 ) > hastai.h
2447+       rm -f trytai.o trytai
2448+
2449+uint64.h: \
2450+tryulong64.c compile load uint64.h1 uint64.h2
2451+       ( ( ./compile tryulong64.c && ./load tryulong64 && \
2452+       ./tryulong64 ) >/dev/null 2>&1 \
2453+       && cat uint64.h1 || cat uint64.h2 ) > uint64.h
2454+       rm -f tryulong64.o tryulong64
2455+
2456+base64sub.o: \
2457+compile base64sub.c base64.h stralloc.h substdio.h str.h
2458+       ./compile base64sub.c
2459+
2460+dk-filter: \
2461+warn-auto.sh dk-filter.sh conf-qmail
2462+       cat warn-auto.sh dk-filter.sh \
2463+       | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
2464+       > dk-filter
2465+
2466+DKIMHDRS = dkim.h dkimdns.h dkimbase.h dkimsign.h dkimverify.h
2467+DKIMSRCS = dkimfuncs.cpp dkimbase.cpp
2468+DKIMOBJS = $(DKIMSRCS:.cpp=.o)
2469+dkim : libdkim.a dkim.o dkimdns.o
2470+       g++ -o dkim $(LFLAGS) -L. dkim.o dkimdns.o libdkim.a `cat dns.lib` -lcrypto
2471+
2472+dkimverify.o: dkim.h dkimdns.h dkimverify.h dkimverify.cpp
2473+       g++ -DHAVE_CONFIG_H -c dkimverify.cpp
2474+
2475+dkimsign.o: dkim.h dkimsign.h dkimsign.cpp
2476+       g++ -DHAVE_CONFIG_H -c dkimsign.cpp
2477+
2478+dkim.o: dkim.c $(DKIMHDRS)
2479+       g++ -DHAVE_CONFIG_H -I. -DHAVE_EVP_SHA256 -c dkim.c
2480+
2481+libdkim.a: $(DKIMOBJS) dkimverify.o dkimsign.o makelib
2482+       rm -f libdkim.a
2483+       ./makelib libdkim.a $(DKIMOBJS) dkimsign.o dkimverify.o
2484+.cpp.o:
2485+       g++ -I. -DHAVE_CONFIG_H $(CFLAGS) $(INCL) -c $<
2486+
2487+cert cert-req: \
2488+Makefile-cert
2489+       @$(MAKE) -sf $< $@
2490+
2491+Makefile-cert: \
2492+conf-qmail conf-users conf-groups Makefile-cert.mk
2493+       @cat Makefile-cert.mk \
2494+       | sed s}QMAIL}"`head -1 conf-qmail`"}g \
2495+       > $@
2496+
2497+update_tmprsadh: \
2498+conf-qmail conf-users conf-groups update_tmprsadh.sh
2499+       @cat update_tmprsadh.sh\
2500+       | sed s}UGQMAILD}"`head -2 conf-users|tail -1`:`head -1 conf-groups`"}g \
2501+       | sed s}QMAIL}"`head -1 conf-qmail`"}g \
2502+       > $@
2503+       chmod 755 update_tmprsadh
2504+
2505+tmprsadh: \
2506+update_tmprsadh
2507+       echo "Creating new temporary RSA and DH parameters"
2508+       ./update_tmprsadh
2509+
2510+policy.o: policy.c policy.h conf-policy
2511+       ./compile policy.c `head -1 conf-policy`
2512diff -ruN ../netqmail-1.06-original/Makefile-cert.mk netqmail-1.06/Makefile-cert.mk
2513--- ../netqmail-1.06-original/Makefile-cert.mk  1970-01-01 01:00:00.000000000 +0100
2514+++ netqmail-1.06/Makefile-cert.mk      2016-11-22 21:03:57.103528399 +0100
2515@@ -0,0 +1,21 @@
2516+cert-req: req.pem
2517+cert cert-req: QMAIL/control/clientcert.pem
2518+       @:
2519+
2520+QMAIL/control/clientcert.pem: QMAIL/control/servercert.pem
2521+       ln -s $< $@
2522+
2523+QMAIL/control/servercert.pem:
2524+       PATH=$$PATH:/usr/local/ssl/bin \
2525+               openssl req -new -x509 -nodes -days 366 -out $@ -keyout $@
2526+       chmod 640 $@
2527+       chown `head -2 conf-users | tail -1`:`head -1 conf-groups` $@
2528+
2529+req.pem:
2530+       PATH=$$PATH:/usr/local/ssl/bin openssl req \
2531+               -new -nodes -out $@ -keyout QMAIL/control/servercert.pem
2532+       chmod 640 QMAIL/control/servercert.pem
2533+       chown `head -2 conf-users | tail -1`:`head -1 conf-groups` QMAIL/control/servercert.pem
2534+       @echo
2535+       @echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
2536+       @echo "cat signed_req.pem >> QMAIL/control/servercert.pem"
2537diff -ruN ../netqmail-1.06-original/README.auth netqmail-1.06/README.auth
2538--- ../netqmail-1.06-original/README.auth       1970-01-01 01:00:00.000000000 +0100
2539+++ netqmail-1.06/README.auth   2016-11-22 21:03:57.103528399 +0100
2540@@ -0,0 +1,154 @@
2541+README qmail SMTP Authentication
2542+================================
2543+
2544+Scope:
2545+------
2546+
2547+This patch supports RFC 2554 "SMTP Service Extension for Authentication" and
2548+RFC 4409 "Message Submission for Mail" for
2549+
2550+* qmail-smtpd and
2551+* qmail-remote
2552+
2553+and supports commonly the AUTH methods
2554+
2555+- CRAM-MD5
2556+- LOGIN (unsecure)
2557+- PLAIN (unsecure)
2558+
2559+Additionally, RFC 1870 is honoured ("SMTP Service Extension for Message Size Declaration").
2560+For more technical details see: http://www.fehcom.de/qmail/docu/smtpauth.html.
2561+
2562+
2563+Installation:
2564+-------------
2565+
2566+* Untar the source in the qmail-1.03 home direcotry.
2567+* Run ./install_auth.
2568+* Re-make qmail.
2569+
2570+
2571+Setup for qmail-smtpd:
2572+----------------------
2573+
2574+1. Prereqs:
2575+
2576+In order to use SMTP Authentication you have to use a 'Pluggable Authentication Module'
2577+PAM to be called by qmail-smtpd; typically
2578+
2579+       /var/qmail/bin/qmail-smtpd /bin/checkpassword true 2>&1
2580+
2581+Since qmail-smtpd does not run as root, checkpassword has to be made sticky.
2582+There is no need to include additionally the hostname in the call.
2583+In order to compute the CRAM-MD5 challenge, qmail-smtpd uses the 'tcplocalhost' information.
2584+
2585+2. Invocation:
2586+
2587+In order activate SMTP authentication, you need to provide the environment
2588+variable 'SMTPAUTH' to qmail-smtpd.
2589+
2590+Possible choices:
2591+       
2592+       a) SMTPAUTH="";         qmail-smtpd supports auth of type PLAIN and/or LOGIN.
2593+       b) SMTPAUTH="+cram";    qmail-smtpd will additionally annonce CRAM-MD5,
2594+                               this requires a CRAM-MD5 supporting PAM.
2595+       c) SMTPAUTH="cram";     qmail-smtpd will only annonce CRAM-MD5.
2596+       d) SMTPAUTH="!";        this instructs qmail-smtpd to require (any type) authentication for this connection.
2597+                               This behavior is equivalent to the Submission feaure.
2598+       e) SMTPAUTH="!cram";    same as d) but now CRAM-MD5 is the only method instead.
2599+
2600+
2601+Setup for qmail-remote:
2602+-----------------------
2603+
2604+SMTP Authentication with qmail-remote is faclitated by two means:
2605+
2606+a) SMTP Authentication by sender/sending domain as provided in the 'Mail From:'
2607+
2608+The control file 'authsenders' which works similar to 'control/smtproutes'
2609+but with additional authentication information (username + password):
2610+
2611+    @example.com:relay.example.com|user|passwd
2612+    info@example.com:relay.example.com:26|infouser|infopasswd
2613+    :mailrelay.example.com:587|e=mc2|testpass
2614+
2615+Note: The choice of the AUTH method depends on the capabilities of the server.
2616+
2617+b) SMTP Authentication by recipient domain:
2618+
2619+The control file 'smtproutes' is enhanced with the authentication information:
2620+
2621+    authdomain.com:mx.authdomain.com:125|myuserid|mypassword
2622+    :mailrelay.example.com:587|e=mc2|testpass
2623+
2624+
2625+Historical Notes:
2626+-----------------
2627+
2628+SMTP authentication for qmail-smtpd was initially provided by Krysztof Dabrowski (version 0.31):
2629+
2630+
2631+Changes wrt. Krysztof Dabrowski's patch:
2632+
2633+* Avoid the 'hostname' in the call of the PAM.
2634+* Confirm to Dan Bernstein's checkpassword interface even for CRAM-MD5.
2635+* Doesn't close FD 2; thus not inhibiting logging to STDERR.
2636+* Fixed bugs in base64.c.
2637+* Modified unconditional close of FD 3 in order to sustain reading of 'control/morecpthosts.cdb'.
2638+* Evaluation of the (informational) Mail From: < > Auth=username.
2639+* Additional support for the advertised "Size" via 'Mail From: <return-path> SIZE=123456780' (RFC 1870).
2640+* RFC 3848 conformance for Received header in case of SMTP Auth (keyword ESMTPA).
2641+* Added SMTPAUTH environment variable.
2642+* The particular Submission feature has been removed; obsolete.
2643+
2644+
2645+SMTP authentication for qmail-remote is taken from Bjoern Kalkbrenner.
2646+
2647+Changes wrt. Bjoern Kalkbrenner's patch (version 0.0.1 / 20020715):
2648+
2649+* Uniform, modular support for LOGIN and PLAIN.
2650+* Added 'Mail From: < > Auth=username' information in provisionally XTEXT format.
2651+* Added CRAM-MD5 support.
2652+* Added authentication by recipient domain.
2653+
2654+
2655+Release Notes:
2656+--------------
2657+
2658+Version:       Notes:                                  Date:
2659+-------------------------------------------------------------------
2660+0.5.9          qmail-smtpd AUTH (only)                 25.3.2008
2661+0.6.9          qmail-authentication                    1.2.2010
2662+0.7.0          Based on qmail-authentication 0.69
2663+               including now CRAM-MD5 support
2664+               for qmail-remote                        31.7.2010
2665+0.7.1          cosmetics for qmail-remote              5.8.2010
2666+0.7.2          authorization-id = authentication-id
2667+               for qmail-remote;
2668+               added SMTPAUTH environment variable
2669+               for qmail-smtpd; backport from SC 2.7   29.4.2012
2670+0.7.3          Fixed missing AUTH for qmai-smtpd announcement.
2671+               Improved SUBMISSION port handling.      2.5.2012
2672+0.7.4          Fixed missing 250 statements for AUTH.
2673+               Changed SMTPAUTH settings for cram-md5  18.5.2012
2674+0.7.5          Fixed bug in qmail-remote not respecting
2675+               announced AUTH types. Tx. Callum Gibson.
2676+               Added '432' server code evaluation for
2677+               AUTH password expired in qmail-remote.  23.10.2012
2678+0.7.6          Fixed order of SMTP commands (tx roberto).
2679+                                                       02.02.2013
2680+0.8.0          Added authentication by recipient domain
2681+               for qmail-remote.
2682+               Removed SUBMISSION port feature for
2683+               qmail-smtpd.                            22.03.2013
2684+0.8.1          Added SMTPAUTH="!+crom" feature.        24.03.2013
2685+0.8.2          Bug fix: qmail-smtpd ACCEPTS auth commands
2686+               even if SMTPAUTH="-". (tx chris).       21.01.2015
2687+0.8.3          Fixed bug in MD5 calculation for AMD64.
2688+               Fixed 'die_nomem' bug in qmail-smtpd.   23.08.2015
2689+
2690+
2691+
2692+Erwin Hoffmann - Hoehn 2015-08-23 (www.fehcom.de)
2693+
2694+
2695diff -ruN ../netqmail-1.06-original/README.dnsbl netqmail-1.06/README.dnsbl
2696--- ../netqmail-1.06-original/README.dnsbl      1970-01-01 01:00:00.000000000 +0100
2697+++ netqmail-1.06/README.dnsbl  2016-11-30 21:50:01.459033083 +0100
2698@@ -0,0 +1,18 @@
2699+Code and logic from rblsmtpd and qmail-dnsbl patch http://qmail-dnsbl.sourceforge.net/
2700+
2701+-added support for whitelists, TXT and A queries, configurable return codes 451 or 553 with custom messages
2702+
2703+  # - default file control/dnsbllist can be overridden with env variable DNSBLLIST
2704+  # - if DNSBLSKIP env variable is set, qmail-smtpd skips the rbl check
2705+  # - if control/dnsblfailclosed or DNSBLFAILCLOSED are defined, qmail-smtpd considers the source ip as blacklisted even in case of lookup failures (check rblsmtpd man page for more details)
2706+  # - support for environment variable RBLSMTPD (check rblsmtpd man page for more details)
2707+  # - dnsbllist can contain empty lines and comments with '#' at start or end of lines; leading and trailing spaces are automatically removed
2708+
2709+CONTROL FILES
2710+
2711+dnsbllist
2712+            A list of dnsbl providers that qmail-smtpd checks to identify blacklisted ip addresses.
2713+
2714+            Exception:  If  the  environment  variable DNSBLSKIP is set, qmail-smtpd ignores dnsbllist, and the dnsbl check is not performed.  The check is skipped even if
2715+            some other authentication method succedeed and authorized the client to relay (smtp-auth or tls client certificate), or if RELAYCLIENT enviromnent variable  is
2716+            set.
2717diff -ruN ../netqmail-1.06-original/README.empf netqmail-1.06/README.empf
2718--- ../netqmail-1.06-original/README.empf       1970-01-01 01:00:00.000000000 +0100
2719+++ netqmail-1.06/README.empf   2016-11-22 21:03:57.103528399 +0100
2720@@ -0,0 +1,106 @@
2721+config file is: /var/qmail/control/policy
2722+more info: http://www.qmailwiki.org/EMPF
2723+
2724+eMail Messaging Policy Framework (eMPF)
2725+
2726+What it does
2727+------------
2728+
2729+   eMPF follows a set of administrator-defined rules describing
2730+who can message whom.  With this, companies can segregate various
2731+parts of their organizations email activities, as well as provide
2732+a variety of security-enhancing services.
2733+
2734+How it does it
2735+--------------
2736+
2737+   During an SMTP session, when a sender identifies themselves, either
2738+via SMTP_AUTH, or via the message envelope, as well as a recipient,
2739+eMPF loads applicable message policies to determine if the sender is
2740+allowed to message the sender, and if the recipient is allowed to
2741+receive mail from the sender.
2742+
2743+What it doesn't do
2744+------------------
2745+
2746+   Because mail from outside your mail server cannot be authenticated,
2747+the policy framework cannot be entirely sure about the identities of
2748+senders messaging local users.  However, if SMTP_AUTH is enabled, and
2749+required by your local users, it can prevent local users from transmitting
2750+information to parties who should not view it.  It is HIGHLY recommended
2751+that a mail server implementing eMPF also require SMTP_AUTH by it's local
2752+users as well as use the POLICY_ENFORCE_AUTHENTICATION configuration so that
2753+senders from the outside cannot masquerade as local users.
2754+
2755+Policy configuration
2756+--------------------
2757+
2758+   A large, complicated policy may be rather intimidating at first, however,
2759+if the rules are documented well, and a basic knowledge of the format of a
2760+policy is known, they are rather simple to set up.
2761+
2762+   comment:
2763+         # text
2764+         ; text
2765+
2766+   policy:
2767+         <domain>:<delivery policy>,[<user policy>,][<etc>,]
2768+
2769+   user policy:
2770+         <username>:<delivery policy>[<delivery policy>]
2771+
2772+   delivery policy:
2773+         <delivery type>[(<address>[,<address>])]
2774+
2775+   delivery types:
2776+         L - Local
2777+         R - Remote
2778+         E - External
2779+         I - Internal
2780+
2781+Delivery types specify what types of messaging can take place.  An uppercase
2782+delivery type allows a type of delivery, and a lowercase delivery type,
2783+disallows a type of delivery.  Delivery types may take a list of addresses.
2784+When a list of addresses is provided after a delivery type, those addresses
2785+are the only addresses covered by that delivery type.
2786+
2787+Certian delivery types apply only to either the sender of the recipient.
2788+Here is a more clear definition of each of the delivery types.
2789+
2790+   Local    - (Sender only) When sending a message to a user on the same domain
2791+   Internal - (Recipient only) When recieving a messages from a user of the
2792+              same domain
2793+   Remote   - (Sender only) When sending a message to a user on another
2794+              domain (even if that domain resides on the same mail server).
2795+   External - (Recipient only) When receiving a message from a user on
2796+              another domain (even if that domain resides on the same server).
2797+
2798+Samples
2799+-------
2800+
2801+In this example, example.com allows all messaging.  In this case,
2802+simply not defining a policy would be more efficient.
2803+
2804+   example.com:LREI,
2805+
2806+Now, example.com wishes all mail to stay internal.  As stated above,
2807+there are particular cases in which eMPF cannot authenticate a sender.
2808+This only occurs when a remote mail server is transmitting mail to a local
2809+user on your system.  In this case, a remote user could pretend to be a local
2810+user, and succesfully deliver mail to another local user.  However, the
2811+recipient would be unable to message back.
2812+
2813+   example.com:LIre,
2814+
2815+As in the above example, example.com wants all mail to stay internal, however,
2816+a few of their users are allowed to communicate with the outside world.  Sales
2817+can communicate with everybody, and Tasks can send messages only to their
2818+sister-site, example.org.
2819+
2820+   example.com:LIre,sales:RE,tasks:R(*@example.org)E(*@example.org),
2821+
2822+Something to keep in mind in this scenario, is that if example.org is hosted
2823+on the same system, and has similar policies to example.com, a policy must be
2824+established for example.org which allows messages from example.com.
2825+
2826+   example.org:LIre,sales:RE,tasks:E(*@example.com)R(*@example.com),
2827diff -ruN ../netqmail-1.06-original/README.exttodo netqmail-1.06/README.exttodo
2828--- ../netqmail-1.06-original/README.exttodo    1970-01-01 01:00:00.000000000 +0100
2829+++ netqmail-1.06/README.exttodo        2016-11-22 21:03:57.103528399 +0100
2830@@ -0,0 +1,114 @@
2831+EXTTODO by Claudio Jeker <jeker@n-r-g.com> and
2832+Andre Oppermann <opi@nrg4u.com>
2833+(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd.
2834+
2835+The EXTTODO patch is a part of the qmail-ldap patch.
2836+This patches for qmail come with NO WARRANTY.
2837+
2838+These patches are under the BSD license.
2839+
2840+RELEASE: 5. Jan. 2003
2841+
2842+EXTTODO:
2843+======================
2844+
2845+TOC:
2846+ WHAT DOES IT DO
2847+ INSTALL
2848+ CONFIG FILES
2849+ SETUP
2850+ BIG PICTURE
2851+
2852+NEWS:
2853+
2854+ This is the first release of the EXTTODO patch.
2855+
2856+================================================================================
2857+
2858+WHAT DOES IT DO
2859+
2860+ The exttodo patch addresses a problem known as the silly qmail (queue)
2861+ problem. This problem is found only on system with high injection rates.
2862+
2863+ qmail with a big local and remote concurrency could deliver a tremendous
2864+ amount of messages but normally this can not be achieved because qmail-send
2865+ becomes a bottleneck on those high volumes servers.
2866+ qmail-send preprocesses all new messages before distributing them for local
2867+ or remote delivering. In one run qmail-send does one todo run but has the
2868+ ability to close multiple jobs. Because of this layout qmail-send can not
2869+ feed all the new available (local/remote) delivery slots and therefor it is
2870+ not possible to achieve the maximum throughput.
2871+ This would be a minor problem if one qmail-send run could be done in extreme
2872+ short time but because of many file system calls (fsync and (un)link) a todo
2873+ run is expensive and throttles the throughput.
2874+
2875+ The exttodo patch tries to solve the problem by moving the todo routine into
2876+ an external program. This reduces the run time in qmail-send.
2877+
2878+ exttodo adds a new program to qmail called qmail-todo. qmail-todo prepares
2879+ incoming messages for local and remote delivering (by creating info/<messid>
2880+ local/<messid> and remote/<messid> and removing todo/<messid>). See also
2881+ INTERNALS. As next qmail-todo transmits the <messid> to qmail-send which will
2882+ add this message into the priority queue which schedules the message for
2883+ delivery.
2884+
2885+INSTALL
2886+
2887+ To enable the exttodo patch you need to define EXTERNAL_TODO while compiling
2888+ qmail(-ldap) this can be done with the -D flag of cc (e.g. cc -DEXTERNAL_TODO).
2889+
2890+ NOTE: the exttodo patch can also be used on qmail systems without the
2891+ qmail-ldap patch.
2892+
2893+================================================================================
2894+
2895+CONFIG FILES
2896+
2897+ No additional control files are used or needed.
2898+
2899+================================================================================
2900+
2901+SETUP
2902+
2903+ qmail-todo will be started by qmail-start and therefor no additional setup
2904+ is needed.
2905+
2906+ To verify that exttodo is running just check if qmail-todo is running.
2907+
2908+================================================================================
2909+
2910+BIG PICTURE
2911+
2912+               +-------+   +-------+
2913+               | clean |   | clean |
2914+               +--0-1--+   +--0-1--+       +-----------+
2915+         trigger  ^ |         ^ |        +->0,1 lspawn |
2916+            |     | v         | v       /  +-----------+
2917+ +-------+  v  +--2-3--+   +--5-6--+   /
2918+ |       |  |  |       0<--7     1,2<-+
2919+ | queue |--+--| todo  |   | send  |
2920+ |       |  |  |       1-->8     3,4<-+
2921+ +-------+     +-------+   +---0---+   \
2922+                               |        \  +-----------+
2923+                               v         +->0,1 rspwan |
2924+                           +---0---+       +-----------+
2925+                           | logger|
2926+                           +-------+
2927+
2928+Communication between qmail-send and qmail-todo
2929+
2930+todo -> send:
2931+   D[LRB]<mesgid>\0
2932+          Start delivery for new message with id <messid>.
2933+          the character L, R or B defines the type
2934+          of delivery, local, remote or both respectively.
2935+   L<string>\0
2936+          Dump string to the logger without adding additional \n or similar.
2937+send -> todo:
2938+   H      Got a SIGHUP reread ~/control/locals and ~/control/virtualdomains
2939+   X      Quit ASAP.
2940+
2941+qmail-todo sends "\0" terminated messages whereas qmail-send just send one
2942+character to qmail-todo.
2943+
2944+
2945diff -ruN ../netqmail-1.06-original/README.liberal-lf netqmail-1.06/README.liberal-lf
2946--- ../netqmail-1.06-original/README.liberal-lf 1970-01-01 01:00:00.000000000 +0100
2947+++ netqmail-1.06/README.liberal-lf     2016-11-22 21:03:57.103528399 +0100
2948@@ -0,0 +1,39 @@
2949+From dgaudet-list-qmail@arctic.org Fri Jan 17 17:50:19 1997
2950+Date: Fri, 17 Jan 1997 17:38:33 -0800 (PST)
2951+From: Dean Gaudet <dgaudet-list-qmail@arctic.org>
2952+To: Julie Baumler <a4jb@psulib.cc.pdx.edu>
2953+Cc: qmail list <djb-qmail@koobera.math.uic.edu>
2954+Subject: [PATCH] qmail and \r\n (was Re: host unable to deliver to host using qmail (summary))
2955+
2956+On Thu, 16 Jan 1997, Julie Baumler wrote:
2957+>
2958+> The admin on the machine in question added "E=\r\n" this morning and low
2959+> and behold, suddenly they can send us mail.
2960+>
2961+> >From a quick look at the source code, it appears that the remote machine
2962+> is notified of the problem, but sendmail in its infinite wisdom apparently
2963+> doesn't capture the error message.  (SIGH)
2964+
2965+I don't know what the sendmail config had wrong with it, but I know that
2966+systems I've set up bounce the error just fine.  Check also in the mail
2967+logs.
2968+
2969+qmail by default violates the "be liberal in what you accept" rule when
2970+receiving mail via smtp.  It enforces strict \r\n behaviour... which is
2971+exactly what the standard dictates.  But I'm not interested in my mailer
2972+telling other admins to fix their setup, because I know they won't.  (The
2973+straynewline() function tells people that if they're under Solaris they
2974+need E=\r\n in their Mether mailer.)
2975+
2976+There is also bug in sendmail pre 8.8 that in some instances will emit
2977+\rx\n for some printable x.  The case where it occured to me, the message
2978+had been generated by a lame PC mail program.  In any event, this causes
2979+qmail to indefinitely defer the incoming message.
2980+
2981+So instead I looked at the sendmail-8.8.4 code and tried to duplicate its
2982+behaviour w.r.t. seeing \n when it wasn't expecting it.  That is what this
2983+patch implements.
2984+
2985+Go ahead, shoot me.
2986+
2987+Dean
2988diff -ruN ../netqmail-1.06-original/README.maxrcpt netqmail-1.06/README.maxrcpt
2989--- ../netqmail-1.06-original/README.maxrcpt    1970-01-01 01:00:00.000000000 +0100
2990+++ netqmail-1.06/README.maxrcpt        2016-11-22 21:03:57.104528366 +0100
2991@@ -0,0 +1,60 @@
2992+maxrcpt patch for qmail-smtpd
2993+
2994+    To: djb-qmail@xxxxxxxxxxxxxxxxxxxx
2995+    Subject: maxrcpt patch for qmail-smtpd
2996+    From: Michael Samuel <michael@xxxxxxxxxxxxxxxxxx>
2997+    Date: Wed, 12 Nov 1997 15:18:38 +1100 (EST)
2998+    Mailing-list: contact djb-qmail-help@xxxxxxxxxxxxxxxxxxxx; run by ezmlm
2999+
3000+-----BEGIN PGP SIGNED MESSAGE-----
3001+
3002+Here is a patch for qmail-smtpd to read a control file to limit the number
3003+of rcpt to commands.
3004+
3005+In the patch I have a thank-you to Sam, this is referring to
3006+mrsam@xxxxxxxxxxxxx who gave me the ideas on how to start off.
3007+
3008+Anyway, it is a "Do what you like with it, but don't blame me" license,
3009+but if it doesn't work, tell me what went wrong if you like and I will
3010+try to figure it out.
3011+
3012+
3013+
3014+Michael Samuel,
3015+
3016+Surf-Net City - Internet Cafe and Internet Service Providers
3017+
3018+                                Phone: +61 3 9593-9977
3019+                                E-Mail: michael@xxxxxxxxxxxxxxxxxx
3020+
3021+-----BEGIN PGP SIGNATURE-----
3022+Version: 2.6.3ia
3023+Charset: noconv
3024+
3025+iQCVAwUBNGkuIUqgdYLWa7qBAQHEiwP+JqNDMZDLwLY7CUdmkuY0OUHwSaFCJJiS
3026+T853fUkupG2kQz6WU8m0RXWd4Rhr+BT8+hqjDDPQYfWzK6QcEf563D0Mp7nA0ZuQ
3027+s+XHKflwb8PAZBp+lpzkMsgDg/B8mlw9dnJ4pGeP1keWR/5cgBFM78XsthW2rLXd
3028+EIXiZJ7AEhc=
3029+=5RMp
3030+-----END PGP SIGNATURE-----
3031+
3032+Here is a patch I rigged up limit the number of RCPT TO: commands per E-Mail
3033+messages.
3034+
3035+It reads the file control/maxrcpt relative to your qmail directory stucture
3036+(usually /var/qmail/control/maxrcpt).
3037+
3038+In that file you should have a integer, which represents the maximum number
3039+of recipients per E-Mail Messages.
3040+
3041+Apparently one of the SMTP rfcs recommends a minimum of 100 recipients per
3042+message be allowed.  Just something to keep in mind anyway.
3043+
3044+If /var/qmail/control/maxrcpt doesn't exist, it does not impose a limit and
3045+skips the rcpt part of the code, so unless I missed something in the source,
3046+you could even have more than MAXINT.
3047+
3048+I would like to thank Sam from the qmail list for giving me a good start to
3049+this patch, and anyone else who offered me suggestions from the qmail list.
3050+(When I refer to qmail list, I'm referring to djb-qmail@xxxxxxxxxxxxxxxxxxxx)
3051+
3052diff -ruN ../netqmail-1.06-original/README.moreipme netqmail-1.06/README.moreipme
3053--- ../netqmail-1.06-original/README.moreipme   1970-01-01 01:00:00.000000000 +0100
3054+++ netqmail-1.06/README.moreipme       2016-11-22 21:03:57.104528366 +0100
3055@@ -0,0 +1,154 @@
3056+###########
3057+### WHO ###
3058+###########
3059+
3060+This patch was written by Scott Gifford <sgifford@suspectclass.com>.
3061+The design and much of the code for supporting "notipme" was
3062+contributed by Charles Cazabon <charlesc@discworld.dyndns.org>.
3063+
3064+
3065+############
3066+### WHAT ###
3067+############
3068+
3069+This patch may be necessary in some configurations that involve network
3070+address translation or port forwarding.  It prevents a problem caused
3071+by an MX or other mail routing directive instructing qmail to connect to
3072+itself without realizing it's connecting to itself.  When this happens,
3073+it accepts the message, finds out where to deliver it to (itself), and
3074+promptly reconnects to itself to deliver the message.  Eventually, when
3075+it has done this 20 or 30 times, it will give up and bounce the message,
3076+but not before sucking up all of your CPU while it's happening.
3077+
3078+It may also be useful in some configurations that have multiple qmail
3079+servers configured on different interfaces of the same system.  qmail
3080+will normally refuse to deliver mail by SMTP to the machine it's
3081+running on, but with multiple copies of qmail, you may want to prevent
3082+this behavior.
3083+
3084+Normally, qmail can detect what IP addresses refer to itself by getting
3085+a list of all network interfaces with IP addresses from the operating
3086+system.  It uses this list to determine whether connecting to an address
3087+will cause it to connect to itself, and avoid the situation (it calls
3088+the perm_ambigmx() function, which prints the message:
3089+
3090+   Sorry. Although I'm listed as a best-preference MX or A for that host,
3091+   it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)
3092+
3093+But in situations where the OS is not aware of all IP addresses that
3094+connect back to itself, this detection fails, causing the CPU-sucking
3095+phenomenon described above.  This can happen if there is a network
3096+address translation device in front of the qmail server, such as a
3097+load-balancer or a router which allows you to share one IP address among
3098+several machines; if there is a port forwarder forwarding connections
3099+from some other machine to the SMTP server on the qmail server; or in
3100+configurations where a "dumb" mailserver is configured to use your qmail
3101+server as a "smarthost", delivering all mail to it without inspection.
3102+
3103+To solve this, other IP addresses which will ultimately connect back to
3104+your machine can be added to the file "control/moreipme", one per line.
3105+qmail will treat all addresses in this file exactly as if they were
3106+local, and if it finds an MX record or other mail routing information
3107+which would cause it to connect to any of these addresses, it will call
3108+perm_ambigmx(), and print the above error message.
3109+
3110+Additionally, IP addresses which the system detects but which should
3111+*not* be treated as local can be removed from qmail's ipme list by
3112+adding them to the file "control/notipme".
3113+
3114+IP addresses can be specified as individual addresses in the usual
3115+dotted-quad format, or as entire networks using a slash followed by
3116+the full dotted-quad netmask:
3117+
3118+    127.0.0.1
3119+    127.0.0.1/255.255.255.255
3120+    127.0.0.0/255.0.0.0
3121+    10.0.0.0/255.255.255.0
3122+
3123+An individual address is treated exactly like a network with a mask of
3124+255.255.255.255.  Addresses of interfaces found on the system are
3125+added with their individual addresses.  In addition, these addresses
3126+are implicitly added:
3127+
3128+    0.0.0.0
3129+    127.0.0.0/255.0.0.0
3130+
3131+So the list of system addresses (the "ipme" list) is, in order,
3132+127.0.0.0/255.0.0.0, 0.0.0.0, then all actual interfaces on the system
3133+in the order they are reported, then the contents of the "moreipme"
3134+file.  The list of excluded addresses (the "notipme" list) is just the
3135+contents of the "notipme" file.
3136+
3137+If an address appears in both the ipme list and the notipme list, the
3138+entry with the longest netmask wins.  If the netmask lengths are the
3139+same, notipme wins.
3140+
3141+For example, if the ipme list has 127.0.0.0/255.0.0.0 and notipme has
3142+127.0.0.2, then 127.0.0.2 will not be considered me because the entry
3143+in notipme has a 32-bit mask.  If the notipme list has
3144+127.0.0.0/255.0.0.0, all of 127.* will not be considered me.
3145+
3146+You can run the program "ipmeprint" from the source directory to see
3147+what interfaces qmail is detecting or finds in moreipme.
3148+
3149+You can run the program "ipmetest" from the source directory to test
3150+your configuration.  It takes as its first and only parameter an IP
3151+address to test, and prints either "me" or "not me".
3152+
3153+###########
3154+### HOW ###
3155+###########
3156+
3157+To apply the patch, download and save it somewhere, then cd into your
3158+qmail source directory.
3159+
3160+For stock qmail, download qmail-1.03-moreipme-0.6.patch then run:
3161+
3162+    cd qmail-1.03
3163+    patch -p1 </path/to/qmail-1.03-moreipme-0.6.patch
3164+
3165+For netqmail, first download netqmail-1.05, and run the included
3166+collate.sh script.  Then download netqmail-1.05-moreipme-0.6.patch and
3167+apply it to the netqmail base directory, after runn:
3168+
3169+    cd netqmail-1.05
3170+    patch -p1 </path/to/netqmail-1.05-moreipme-0.6.patch
3171+
3172+
3173+###################
3174+### OTHER NOTES ###
3175+###################
3176+
3177+This patch also incorporates the "0.0.0.0" patch, which causes qmail
3178+to recognize the IP address 0.0.0.0 as a local address.  See:
3179+
3180+    http://www.suspectclass.com/~sgifford/qmail/qmail-0.0.0.0.README
3181+
3182+for more information, and
3183+
3184+    http://www.suspectclass.com/~sgifford/qmail/qmail-0.0.0.0.patch
3185+
3186+for a copy of the patch.
3187+
3188+
3189+###############
3190+### HISTORY ###
3191+###############
3192+
3193+2004 May 22 - Patch version 0.6 released.  Fix from Richard Dawe where
3194+              masks weren't handled properly, removed some dead code,
3195+              updated comments and docs.  Produce a copy of the patch
3196+              for netqmail-1.05.
3197+
3198+2003 Apr 29 - Patch version 0.5 released.  Added support for netmasks
3199+              in moreipme and notipme, ipmetest utility, 127/8 as
3200+              implicit ipme.
3201+
3202+2002 Apr 26 - Patch version 0.4 released.  Includes support for "notipme" file.
3203+              Many other small fixes and cleanups.  Fixes ipmeprint to
3204+              chdir(/var/qmail) before running.
3205+
3206+2001 Oct  8 - Original release of patch.
3207+
3208+2001 Jan 22 - (roughly) Original release of 0.0.0.0 patch.
3209+
3210diff -ruN ../netqmail-1.06-original/README.qregex netqmail-1.06/README.qregex
3211--- ../netqmail-1.06-original/README.qregex     1970-01-01 01:00:00.000000000 +0100
3212+++ netqmail-1.06/README.qregex 2016-11-22 21:04:38.822137319 +0100
3213@@ -0,0 +1,203 @@
3214+QREGEX (v2) 20060423 - README April 23, 2006
3215+A Regular Expression matching patch for qmail 1.03 and netqmail
3216+
3217+
3218+OVERVIEW:
3219+
3220+qregex adds the ability to match address evelopes via Regular Expressions (REs)
3221+in the qmail-smtpd process. It has the abiltiy to match `helo/ehlo` (host name),
3222+`mail from` (envelope sender), and `rcpt to` (envelope recipient) commands.
3223+It follows all the base rules that are set out with qmail (ie using control
3224+files) so it makes for easy integretion into an existing setup (see the
3225+install instructions for more info). The v2 is specified because qregex was
3226+re-written to better conform to the security guarantee set forth by the author
3227+of qmail. The original version used stdio.h and stdlib.h for reading the
3228+control files whereas v2 now uses all stralloc functions which are much more
3229+regulated against buffer overruns and the like.
3230+See: http://cr.yp.to/qmail/guarantee.html
3231+
3232+
3233+FEATURES:
3234+
3235+Features of qregex include:
3236+
3237+1. Performs pattern matching on envelope senders and envelope
3238+   recipients against REs in the badmailfrom and badmailto control
3239+   files. Two additional control files, badmailfromnorelay and
3240+   badmailtonorelay, are used for pattern matching when the
3241+   RELAYCLIENT environment variable is not set.
3242+
3243+2. Performs pattern matching on the helo/ehlo host name. Setting the
3244+   NOBADHELO environment variable prevents the host name from being
3245+   compared to the patterns in the badhelo control file.
3246+
3247+3. Matches to patterns are logged. Setting the LOGREGEX environment
3248+   variable causes the matched regex pattern to be included in the log.
3249+
3250+4. Matching is case insensitive.
3251+
3252+5. qregex ignores empty envelope senders. An empty envelope sender is not
3253+   compared to the patterns in the badmailfrom and badmailfromnorelay
3254+   control files and is always accepted.
3255+
3256+
3257+PLATFORMS:
3258+
3259+qregex has been built and tested on the following platforms. I'm sure it won't
3260+have any problems on any platform that qmail will run on (providing they have
3261+a regex interface) but if you run into problems let me know.
3262+
3263+       - OpenBSD 3.x
3264+       - FreeBSD 4.x, 5.x
3265+       - Mandrake Linux 9.x
3266+       - SuSE Linux 8.x
3267+
3268+
3269+
3270+INSTALLATION INSTRUCTIONS:
3271+
3272+Installation is very simple, there is only one requirement. You need to use the
3273+GNU version of the patch utility (http://www.gnu.org/software/patch/patch.html).
3274+(For Solaris 8 users it is installed as 'gpatch')
3275+
3276+- If this is a new setup.
3277+Unpack the qmail archive, cd into the qmail-1.03 directory and run
3278+"patch < /path/to/qregex-<version>.patch". Follow the instructions as per the
3279+included qmail INSTALL file. Once you are done come back to this file and read
3280+the section on the control files.
3281+
3282+If you are using netqmail, then unpack the netqmail archive. Run the collate.sh
3283+script and cd into the resulting netqmail-<version> directory. From there, run
3284+"patch < /path/to/qregex-<version>.patch". Complete the netqmail installation
3285+normally. Once you are done, come back to this file and read the section on the
3286+control files.
3287+
3288+- If this is an existing setup.
3289+FIRST: create your control files (see below).
3290+cd into your existing qmail or netqmail source directory. Run
3291+"patch < /path/to/qregex-<version>.patch" then "make qmail-smtpd". Now run
3292+./qmail-smtpd and test your new rules to make sure they work as expected.
3293+
3294+Install the new binary by cd'ing to /var/qmail/bin and as root (in one command)
3295+copy the existing binary to 'qmail-smtpd.old' and copy the new binary from the
3296+source directory to 'qmail-smtpd'.
3297+(ex. cp qmail-smtpd qmail-smtpd.old && cp ~/qmail-1.03/qmail-smtpd qmail-smtpd)
3298+
3299+You can also optionally just run "make setup check" as it will install the
3300+updated documentation and man pages provided with this patch. Stopping qmail
3301+before doing the "make setup check" is always a good idea.
3302+
3303+
3304+LOGGING:
3305+
3306+qregex will log matches to the patterns in the various control files. Log
3307+messages will take these three forms depending on which control file was
3308+matched:
3309+
3310+badhelo
3311+qmail-smtpd: badhelo: <host> at <remote IP>
3312+
3313+badmailfrom and badmailfromnorelay
3314+qmail-smtpd: badmailfrom: <sender address> at <remote IP>
3315+
3316+badmailto and badmailtonorelay
3317+qmail-smtpd: badmailto: <rcpt address> at <remote IP>
3318+
3319+When the LOGREGEX environment variable is set, the matched pattern will
3320+be included in the log. Log messages will have the regex pattern appended
3321+to them. For example, a badhelo log message will look like this:
3322+
3323+qmail-smtpd: badhelo: <host> at <remote IP> matches pattern: <regex>
3324+
3325+
3326+CONTROL FILES:
3327+
3328+qregex provides you with five control files. None of these control files
3329+is mandatory and you can use them in any combination you choose in your setup.
3330+
3331+The "control/badmailfrom" and "control/badmailto" files contain your REs for
3332+matching against the 'mail from' (envelope sender) and 'rcpt to' (envelope
3333+recipient) smtp commands respectively.
3334+The "control/badmailfromnorelay" and "control/badmailtonorelay" match against
3335+the same commands but are read only when the RELAYCLIENT environment variable
3336+is not set.
3337+The "control/badhelo" file matches against the 'helo/ehlo' smtp command.
3338+
3339+If you prefer you can symlink the badmailfrom and badmailto control files
3340+(ln -s badmailfrom badmailto) and maintain fewer sets of rules. Beware
3341+this might cause problems in certain setups.
3342+       
3343+       Here's an example "badhelo" file.
3344+       -----------------------------------
3345+       # block host strings with no dot (not a FQDN)
3346+       !\.
3347+       -----------------------------------
3348+       
3349+       An example "badmailfrom" file.
3350+       -----------------------------------
3351+       # this will drop everything containing the string
3352+       # bad.domain.com or Bad.Domain.Com or BAD.domain.COM
3353+       bad\.domain\.com
3354+       # force users to fully qualify themselves
3355+       # (i.e. deny "user", accept "user@domain")
3356+       !@
3357+       -----------------------------------
3358+
3359+       And "badmailto" (a little more interesting)
3360+       -----------------------------------
3361+       # must not contain invalid characters, brakets or multiple @'s
3362+       [!%#:*^(){}]
3363+       @.*@
3364+       -----------------------------------
3365+
3366+You can use the non-RE character '!' to start an RE as a signal to qregex to
3367+negate the action. As used above in the badmailfrom file, by negating the '@'
3368+symbol qregex will signal qmail-smtpd to deny the 'mail from' command whenever
3369+the address doesn't contain an @ symbol. When used inside a bracket expression,
3370+the '!' character looses this special meaning. This is shown in the badmailto
3371+example.
3372+
3373+The norelay control files follow the same rules as the other control files but
3374+are intended to address two specific scenarios.
3375+The badmailfromnorelay file can be used to block mail trying to spoof a domain
3376+hosted on your mail server. It prevents a mail client that is not allowed to
3377+relay email through your server from using one of your hosted domains as its
3378+envelope sender.
3379+The badmailtonorelay file can be used to create email addresses that cannot
3380+receive mail from any source not allowed to relay email through your server.
3381+This is handy for creating email addresses for use only within your own
3382+domain(s) that can't receive spam from the world at large.
3383+
3384+
3385+INTERNALS:
3386+
3387+qregex (or regexmatch as the function is called) will be called during the
3388+`helo/ehlo`, `rcpt to` and `mail from` handling routines in "qmail-smtpd.c".
3389+When called, it will read the proper control file then one by one compile and
3390+execute the regex on the string passed into qmail-smtpd. If the regex matches
3391+it returns TRUE (1) and the qmail-smtpd process will deny the user the ability
3392+to continue. If you change anything and think it betters this patch please
3393+send me a new diff file so I can take a peek.
3394+
3395+
3396+CONTACT:
3397+qregex is maintained by:
3398+       Andrew St. Jean
3399+       andrew@arda.homeunix.net
3400+       www.arda.homeunix.net/store/qmail/
3401+
3402+Contributers to qregex:
3403+       Jeremy Kitchen 
3404+       kitchen at scriptkitchen dot com
3405+       http://www.scriptkitchen.com/qmail
3406+
3407+       Alex Pleiner
3408+       alex@zeitform.de
3409+       zeitform Internet Dienste
3410+       http://www.zeitform.de/
3411+
3412+       Thanos Massias
3413+
3414+Original qregex patch written by:
3415+       Evan Borgstrom
3416+       evan at unixpimps dot org
3417diff -ruN ../netqmail-1.06-original/README.rfc2821 netqmail-1.06/README.rfc2821
3418--- ../netqmail-1.06-original/README.rfc2821    1970-01-01 01:00:00.000000000 +0100
3419+++ netqmail-1.06/README.rfc2821        2016-11-22 21:03:57.104528366 +0100
3420@@ -0,0 +1,39 @@
3421+This patch is Copyright (C) 2002 - 2003 by Matthias Andree. License below.
3422+
3423+(this is the 2nd edition of this patch and the 4th edition of the
3424+introductory text)
3425+
3426+This patch changes qmail-remote to skip over MX servers that greet with
3427+codes 400 to 499 and to bounce mail when any MX server that qmail tries
3428+greets with a code 500 to 599.
3429+
3430+If you want qmail-remote to skip over hosts greeting with 5XX-codes and
3431+try the next MX for real RFC-2821 compliance (Sendmail and Postfix do
3432+that), change the
3433+
3434+  if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed");
3435+
3436+to
3437+
3438+  if (code >= 500 && code < 600) return;
3439+
3440+License:
3441+
3442+Permission is hereby granted, free of charge, to any person obtaining a
3443+copy of this software and associated documentation files (the
3444+"Software"), to deal in the Software without restriction, including
3445+without limitation the rights to use, copy, modify, merge, publish,
3446+distribute, sublicense, and/or sell copies of the Software, and to
3447+permit persons to whom the Software is furnished to do so, subject to
3448+the following conditions:
3449+
3450+The above copyright notice and this permission notice shall be included
3451+in all copies or substantial portions of the Software.
3452+
3453+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
3454+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
3455+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
3456+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
3457+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
3458+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
3459+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3460diff -ruN ../netqmail-1.06-original/README.srs netqmail-1.06/README.srs
3461--- ../netqmail-1.06-original/README.srs        1970-01-01 01:00:00.000000000 +0100
3462+++ netqmail-1.06/README.srs    2016-11-22 21:03:57.104528366 +0100
3463@@ -0,0 +1,93 @@
3464+qmail SRS patch
3465+
3466+This is a SRS (Sender Rewriting Scheme) implementation for qmail using libsrs2.
3467+
3468+Current version: qmail-srs-0.8.patch
3469+Changes
3470+
3471+    2011-03-30 (0.8):
3472+        Fixed bug reading configuration files.
3473+    2007-06-05 (0.7):
3474+        New QMAILINJECT_FORCE_SRS and QMAILINJECT_SKIP_SRS environment variables can force or skip envelope rewriting in qmail-inject.
3475+    2007-05-31 (0.6):
3476+        qmail-inject only will rewrite envelope if EXT and HOST variables are set.
3477+        Fixed bug in qmail-send handling chdir() calls (Special Thanks to Werner Fleck).
3478+    2007-01-11 (0.5):
3479+        Added parameters srs_separator and srs_alwaysrewrite from libsrs2.
3480+    2007-01-10 (0.4):
3481+        If srs_domain is empty or not set, SRS is disabled.
3482+    2006-12-18 (0.3):
3483+        forward and condredirect: modified to work with SRS.
3484+    2006-12-17 (0.2):
3485+        srsfilter: now rewrites To header with the SRS decoded address.
3486+        srsfilter: only accepts messages from null-sender, Return-Path: <>.
3487+        srsfilter: modified to reject messages without body.
3488+        qmail-inject: error message detailed.
3489+        If optional parameters are not set, will use libsrs2 defaults.
3490+        Install instructions revised.
3491+    2006-12-15 (0.1):
3492+        First release.
3493+
3494+Install instructions
3495+
3496+    Download and install libsrs2 from http://www.libsrs2.org/download.html.
3497+    Download qmail-srs-0.8.patch.
3498+    Apply this patch:
3499+
3500+    tar -xzf /path/to/qmail-1.03.tar.gz
3501+    cd qmail-1.03
3502+    patch -p1 < qmail-srs-0.8.patch
3503+    And follow your qmail instalation (config, make, make setup check, ...)
3504+    Configure some parameters in /var/qmail/control.
3505+
3506+    Required parameters:
3507+
3508+    echo srs.YOURDOMAIN > /var/qmail/control/srs_domain
3509+    echo SECRET > /var/qmail/control/srs_secrets
3510+
3511+    YOURDOMAIN: Replace with your domain name, e.g. srs.foo-bar.com
3512+    SECRET: Replace with a random string
3513+
3514+    Important! You MUST create a MX record for srs.YOURDOMAIN pointing to your server.
3515+
3516+    Optional parameters:
3517+
3518+    echo 7 > /var/qmail/control/srs_maxage
3519+    echo 4 > /var/qmail/control/srs_hashlength
3520+    echo 4 > /var/qmail/control/srs_hashmin
3521+    echo = > /var/qmail/control/srs_separator
3522+    echo 0 > /var/qmail/control/srs_alwaysrewrite
3523+    Configure your SRS domain.
3524+
3525+    echo srs.YOURDOMAIN >> /var/qmail/control/rcpthosts
3526+    echo srs.YOURDOMAIN:srs >> /var/qmail/control/virtualdomains
3527+    echo "| /var/qmail/bin/srsfilter" > /var/qmail/alias/.qmail-srs-default
3528+
3529+    YOURDOMAIN: Replace with your domain name, e.g. srs.foo-bar.com.
3530+
3531+Configuration Parameters
3532+
3533+Parameters in bold are required.
3534+Parameter      Description     Example
3535+srs_domain     A domain to use in rewritten addresses. If not set, SRS is disabled.    srs.foo-bar.com
3536+srs_secrets    A random string to generate and check SRS addresses. You can specify a list of secrets (one per line). The first secret in the list is used for generating new SRS addresses. All secrets on the list may be used to verify SRS addresses.      foobar123
3537+srs_maxage     The maximum permitted age of a rewritten address. SRS rewritten addresses expire after a specified number of days. libsrs2 default is 21, but I believe that a week is enougth to get all bounces, so I recommend you to use 7.         7
3538+srs_hashlength         The hash length to generate in a rewritten address. The hash length is a measure of security in the SRS system; longer is more secure.  4
3539+srs_hashmin    The hash length to require when checking an address. If the hash length is increased, there may be SRS addresses from your MTA in the wild which use a shorter hash length. This parameter may be set to permit checking of hashes shorter than srs_hashlength. This parameter must be at most srs_hashlength.  4
3540+srs_separator  The separator to appear immediately after SRS[01] in rewritten addresses. This must be -, + or =. Default value is =.   =
3541+srs_alwaysrewrite      Skip rcpthosts check and perform SRS rewriting for all forwarding, even when not required. This must be 0 (disabled) or 1 (enabled). Default value is 0 (disabled).     0
3542+Environment Variables (qmail-inject only)
3543+
3544+By default, this patch modifies qmail-inject to rewrite the envelope sender only if EXT and HOST variables are set.
3545+
3546+You can change this behavior using the following environment variables:
3547+
3548+    QMAILINJECT_FORCE_SRS: qmail-inject will call srsforward() even if EXT and HOST are not set.
3549+    QMAILINJECT_SKIP_SRS: qmail-inject will not call srsforward() even if EXT and HOST are set.
3550+
3551+More about SRS
3552+
3553+    http://www.openspf.org/SRS
3554+    http://www.libsrs2.org/
3555+    http://wooledge.org/~greg/qmail-srs.html
3556+
3557diff -ruN ../netqmail-1.06-original/README.surbl netqmail-1.06/README.surbl
3558--- ../netqmail-1.06-original/README.surbl      1970-01-01 01:00:00.000000000 +0100
3559+++ netqmail-1.06/README.surbl  2016-11-22 21:03:57.104528366 +0100
3560@@ -0,0 +1,34 @@
3561+SURBL filter for netqmail
3562+=========================
3563+surblfilter reads an rfc822 email on stdin and prints it back on stdout.
3564+It extracts URL and checks them against SURBL. surblfilter can be used
3565+as a filter using qmail-qfilter(1) or as a qmail-queue(8) frontend by
3566+setting QMAILQUEUE environment variable to a wrapper
3567+/var/qmail/bin/surblqueue
3568+
3569+surbfilter caches the result in /var/qmail/control/cache. qmail-smtpd
3570+needs to have write permission for this directory.
3571+
3572+surbfilter does base64 decoding for base64 encoded text/html, text/plain
3573+to extract urls.
3574+
3575+surblfilter requires two control files level2-tlds and level3-tlds in
3576+/var/qmail/control. The same can be obtained from surbl.org website
3577+
3578+http://www.surbl.org/tld/three-level-tlds
3579+http://www.surbl.org/tld/two-level-tlds
3580+
3581+surbfilter is a standone program and does not need you to patch qmail-smtpd
3582+or existing programs. It exits 88 in case it finds a SURBL listed URL. It
3583+exits 111 for temporary errors. It exits 0 if everyting is fine.
3584+
3585+surblfilter has been integrated with my qmail-dkim patch and is avaiable at
3586+
3587+https://sourceforge.net/projects/indimail/files/netqmail-addons/qmail-dkim-1.0/
3588+
3589+It has been integrated in dkim-netqmail-1.06.patch-1.9.gz. The older patches
3590+will not have surblfilter
3591+
3592+surbfilter is built on djb functions and some functions have been ruthlessly
3593+borrowed from qmail surbl interface by Pieter Droogendijk and the surblhost
3594+program at http://surblhost.sourceforge.net/
3595diff -ruN ../netqmail-1.06-original/README.tap netqmail-1.06/README.tap
3596--- ../netqmail-1.06-original/README.tap        1970-01-01 01:00:00.000000000 +0100
3597+++ netqmail-1.06/README.tap    2016-11-22 21:04:38.822137319 +0100
3598@@ -0,0 +1,34 @@
3599+qmail provides the ability to make a copy of each email that flows through the system.
3600+This is done using the QUEUE_EXTRA code. See qmail FAQ #8.2
3601+
3602+The qmail tap patch adds additional functionality:
3603+1) Specify which email addresses to tap using a regex style control file. With the
3604+   regex function, you can specify full domains or individual email addresses.
3605+
3606+2) Specify which email address to send the emails to.
3607+
3608+3) Qmail does not need to be restated to when the taps control file is changed.
3609+
3610+The regex match is applied to both the to and from email addresses. So email
3611+sent to or from the addresses will be copied. Matching is case insensitive.
3612+If there are multiple matches, the first match is used.
3613+
3614+The queue tap patch adds a new control file:
3615+
3616+/var/qmail/control/taps
3617+Contains a regex style list of addresses to tap and the email
3618+address of where you want the copy sent to.
3619+
3620+Examples:
3621+a) To tap a whole domain add a line like:
3622+A:.*@domain.com:joe@example.com
3623+
3624+
3625+b) To tap an individual email address add a line like:
3626+A:user@domain.com:other@example.com
3627+
3628+c) To tap messages going to a domain add a line like:
3629+T:.*@domain.com:joe@example.com
3630+
3631+d) To tap messages comming from a domain add a line like:
3632+F:.*@domain.com:joe@example.com
3633diff -ruN ../netqmail-1.06-original/README.tls netqmail-1.06/README.tls
3634--- ../netqmail-1.06-original/README.tls        1970-01-01 01:00:00.000000000 +0100
3635+++ netqmail-1.06/README.tls    2016-11-22 21:03:57.104528366 +0100
3636@@ -0,0 +1,100 @@
3637+Frederik Vermeulen <qmail-tls akrul inoa.net> 20160918
3638+http://inoa.net/qmail-tls/
3639+
3640+This patch implements RFC 3207 (was RFC 2487) in qmail.
3641+This means you can get SSL or TLS encrypted and
3642+authenticated SMTP between the MTAs and from MUA to MTA.
3643+The code is considered experimental (but has worked for
3644+many since its first release on 1999-03-21).
3645+
3646+Usage: - install OpenSSL-1.0.2 http://www.openssl.org/ or later
3647+         (any version since 0.9.6 is presumed to work)
3648+       - apply patch to netqmail-1.06 http://qmail.org/netqmail
3649+         The patches to qmail-remote.c and qmail-smtpd.c can be applied
3650+         separately.
3651+       - provide a server certificate in /var/qmail/control/servercert.pem.
3652+         "make cert" makes a self-signed certificate.
3653+         "make cert-req" makes a certificate request.
3654+         Note: you can add the CA certificate and intermediate
3655+         certs to the end of servercert.pem.
3656+       - replace qmail-smtpd and/or qmail-remote binary
3657+       - verify operation (header information should show
3658+         something like
3659+         "Received [..] with (DHE-RSA-AES256-SHA encrypted) SMTP;")
3660+
3661+Optional: - when DEBUG is defined, some extra TLS info will be logged
3662+          - qmail-remote will authenticate with the certificate in
3663+            /var/qmail/control/clientcert.pem. By preference this is
3664+            the same as servercert.pem, where nsCertType should be
3665+            == server,client or be a generic certificate (no usage specified).
3666+          - when a 2048 bit RSA key is provided in /var/qmail/control/rsa2048.pem,
3667+            this key will be used instead of (slow) on-the-fly generation by
3668+            qmail-smtpd. Idem for 2048 DH param in control/dh2048.pem.
3669+            `make tmprsadh` does this.
3670+            Periodical replacement can be done by crontab:
3671+            01 01 * * * /var/qmail/bin/update_tmprsadh > /dev/null 2>&1
3672+          - server authentication:
3673+            qmail-remote requires authentication from servers for which
3674+            /var/qmail/control/tlshosts/host.dom.ain.pem exists.
3675+            The .pem file contains the validating CA certificates.
3676+            One of the dNSName or the CommonName attributes have to match.
3677+            WARNING: this option may cause mail to be delayed, bounced,
3678+            doublebounced, and lost.
3679+            If /var/qmail/control/tlshosts/exhaustivelist is present,
3680+            the lists of hosts in /var/qmail/control/tlshosts is
3681+            an exhaustive list of hosts TLS is tried on.
3682+            If /var/qmail/control/notlshosts/host.dom.ain is present,
3683+            no TLS is tried on this host.
3684+          - client authentication:
3685+            when relay rules would reject an incoming mail,
3686+            qmail-smtpd can allow the mail based on a presented cert.
3687+            Certs are verified against a CA list in
3688+            /var/qmail/control/clientca.pem (eg. from
3689+            http://curl.haxx.se/ca/cacert.pem)
3690+            and the cert email-address has to match a line in
3691+            /var/qmail/control/tlsclients. This email-address is logged
3692+            in the headers. CRLs can be provided through
3693+            /var/qmail/control/clientcrl.pem.
3694+          - cipher selection:
3695+            qmail-remote:
3696+              openssl cipher string (`man ciphers`) read from
3697+              /var/qmail/control/tlsclientciphers
3698+            qmail-smtpd:
3699+              openssl cipher string read from TLSCIPHERS environment variable
3700+              (can vary based on client IP address e.g.)
3701+              or if that is not available /var/qmail/control/tlsserverciphers
3702+          - smtps (deprecated SMTP over TLS via port 465):
3703+            qmail-remote: when connecting to port 465
3704+            qmail-smtpd: when SMTPS environment variable is not empty
3705+
3706+Caveats: - do a `make clean` after patching
3707+         - binaries dynamically linked with current openssl versions need
3708+           recompilation when the shared openssl libs are upgraded.
3709+         - this patch could conflict with other patches (notably those
3710+           replacing \n with \r\n, which is a bad idea on encrypted links).
3711+         - some broken servers have a problem with TLSv1 compatibility.
3712+           Uncomment the line where we set the SSL_OP_NO_TLSv1 option.
3713+         - needs working /dev/urandom (or EGD for openssl versions >0.9.7)
3714+           for seeding random number generator.
3715+         - packagers should make sure that installing without a valid
3716+           servercert is impossible
3717+         - when applied in combination with AUTH patch, AUTH patch
3718+           should be applied first and first part of this patch
3719+           will fail. This error can be ignored. Packagers should
3720+           cut the first 12 lines of this patch to make a happy
3721+           patch
3722+         - `make tmprsadh` is recommended (or should I say required),
3723+           otherwise DH generation can be unpredictably slow
3724+         - some need "-I/usr/kerberos/include" to be added in conf-cc
3725+
3726+Copyright: GPL
3727+           Links with OpenSSL
3728+           Inspiration and code from examples in SSLeay (E. Young
3729+           <eay@cryptsoft.com> and T. Hudson <tjh@cryptsoft.com>),
3730+           stunnel (M. Trojnara <mtrojnar@ddc.daewoo.com.pl>),
3731+           Postfix/TLS (L. Jaenicke <Lutz.Jaenicke@aet.tu-cottbus.de>),
3732+           modssl (R. Engelschall <rse@engelschall.com>),
3733+           openssl examples of E. Rescorla <ekr@rtfm.com>.
3734+
3735+Bug reports: mailto:<qmail-tls akrul inoa.net>
3736+
3737diff -ruN ../netqmail-1.06-original/TARGETS netqmail-1.06/TARGETS
3738--- ../netqmail-1.06-original/TARGETS   1998-06-15 12:53:16.000000000 +0200
3739+++ netqmail-1.06/TARGETS       2016-11-22 21:04:20.043763470 +0100
3740@@ -1,3 +1,4 @@
3741+dktest
3742 auto-ccld.sh
3743 make-load
3744 find-systype
3745@@ -10,11 +11,20 @@
3746 qmail.o
3747 quote.o
3748 now.o
3749+base64.o
3750 gfrom.o
3751 myctime.o
3752 slurpclose.o
3753 make-makelib
3754 makelib
3755+maildirflags.o
3756+maildirparsequota.o
3757+maildiropen.o
3758+maildirgetquota.o
3759+maildirquota.o
3760+overmaildirquota.o
3761+strtimet.o
3762+strpidt.o
3763 case_diffb.o
3764 case_diffs.o
3765 case_lowerb.o
3766@@ -100,11 +110,14 @@
3767 str_diff.o
3768 str_diffn.o
3769 str_cpy.o
3770+str_cpyb.o
3771 str_chr.o
3772 str_rchr.o
3773 str_start.o
3774 byte_chr.o
3775 byte_rchr.o
3776+byte_cspn.o
3777+byte_rcspn.o
3778 byte_diff.o
3779 byte_copy.o
3780 byte_cr.o
3781@@ -158,6 +171,8 @@
3782 auto_uids.o
3783 qmail-lspawn
3784 qmail-getpw.o
3785+qmail-newmvrt.o
3786+qmail-newmvrt
3787 auto_break.c
3788 auto_break.o
3789 auto_usera.c
3790@@ -168,11 +183,19 @@
3791 constmap.o
3792 timeoutread.o
3793 timeoutwrite.o
3794+tls.o
3795+ssl_timeoutio.o
3796 timeoutconn.o
3797 tcpto.o
3798 dns.o
3799+srsfilter
3800+srsfilter.o
3801+srs
3802+srs.o
3803+spf.o
3804 ip.o
3805 ipalloc.o
3806+strsalloc.o
3807 hassalen.h
3808 ipme.o
3809 ndelay.o
3810@@ -182,6 +205,8 @@
3811 qmail-remote
3812 qmail-rspawn.o
3813 tcpto_clean.o
3814+md5c.o
3815+hmac_md5.o
3816 qmail-rspawn
3817 direntry.h
3818 qmail-clean.o
3819@@ -212,6 +237,9 @@
3820 headerbody.o
3821 hfield.o
3822 token822.o
3823+spf.o
3824+spfquery.o
3825+spfquery
3826 qmail-inject
3827 predate.o
3828 predate
3829@@ -270,12 +298,16 @@
3830 dnsip
3831 dnsmxip.o
3832 dnsmxip
3833+dnstxt.o
3834+dnstxt
3835 dnsfq.o
3836 dnsfq
3837 hostname.o
3838 hostname
3839 ipmeprint.o
3840 ipmeprint
3841+ipmetest.o
3842+ipmetest
3843 qreceipt.o
3844 qreceipt
3845 qsmhook.o
3846@@ -320,6 +352,7 @@
3847 binm2+df
3848 binm3
3849 binm3+df
3850+Makefile-cert
3851 it
3852 qmail-local.0
3853 qmail-lspawn.0
3854@@ -382,6 +415,51 @@
3855 addresses.0
3856 envelopes.0
3857 forgeries.0
3858+policy.o
3859 man
3860 setup
3861 check
3862+qmail-todo.o
3863+qmail-todo
3864+update_tmprsadh
3865+chkuser.o
3866+qmail-dk
3867+qmail-dkim
3868+qmail-dkim.o
3869+qmail-dk.o
3870+libdkim.a
3871+dkimbase.o
3872+dkimdns.o
3873+dkim.o
3874+dkimsign.o
3875+dkimverify.o
3876+dkim
3877+dkim.o
3878+dkim.8
3879+qmail-dkim.8
3880+qmail-dkim.0
3881+str_cpyb.o
3882+dkimfuncs.o
3883+MakeArgs.o
3884+spawn-filter spawn-filter.o qregex.o wildmat.o
3885+spawn-filter.8
3886+qmail-dk.0 spawn-filter.0
3887+dk-filter echo.o echo dk-filter.0 dk-filter.8
3888+case_startb.o
3889+mess822_ok.o
3890+scan_xlong.o
3891+socket_v4mappedprefix.o
3892+socket_v6any.o
3893+str_cspn.o
3894+surblfilter.o
3895+surblfilter
3896+surblfilter.8
3897+uint64.h
3898+surblqueue
3899+surblfilter.0
3900+base64sub.o
3901+qmail-dk.8
3902+dktest.o
3903+dktrace.o
3904+dknewkey
3905+dktest.8
3906diff -ruN ../netqmail-1.06-original/alloc.c netqmail-1.06/alloc.c
3907--- ../netqmail-1.06-original/alloc.c   1998-06-15 12:53:16.000000000 +0200
3908+++ netqmail-1.06/alloc.c       2016-11-22 21:03:57.105528332 +0100
3909@@ -1,6 +1,6 @@
3910 #include "alloc.h"
3911 #include "error.h"
3912-extern char *malloc();
3913+extern void *malloc();
3914 extern void free();
3915 
3916 #define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
3917diff -ruN ../netqmail-1.06-original/base64.c netqmail-1.06/base64.c
3918--- ../netqmail-1.06-original/base64.c  1970-01-01 01:00:00.000000000 +0100
3919+++ netqmail-1.06/base64.c      2016-11-22 21:03:57.105528332 +0100
3920@@ -0,0 +1,124 @@
3921+#include "base64.h"
3922+#include "stralloc.h"
3923+#include "substdio.h"
3924+#include "str.h"
3925+
3926+static char *b64alpha =
3927+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3928+#define B64PAD '='
3929+
3930+/* returns 0 ok, 1 illegal, -1 problem */
3931+
3932+int b64decode(in,l,out)
3933+const unsigned char *in;
3934+int l;
3935+stralloc *out; /* not null terminated */
3936+{
3937+  int p = 0;
3938+  int n;
3939+  unsigned int x;
3940+  int i, j;
3941+  char *s;
3942+  unsigned char b[3];
3943+
3944+  if (l == 0)
3945+  {
3946+    if (!stralloc_copys(out,"")) return -1;
3947+    return 0;
3948+  }
3949+
3950+  while(in[l-1] == B64PAD) {
3951+    p ++;
3952+    l--;
3953+  }
3954+
3955+  n = (l + p) / 4;
3956+  i = (n * 3) - p;
3957+  if (!stralloc_ready(out,i)) return -1;
3958+  out->len = i;
3959+  s = out->s;
3960+
3961+  for(i = 0; i < n - 1 ; i++) {
3962+    x = 0;
3963+    for(j = 0; j < 4; j++) {
3964+      if(in[j] >= 'A' && in[j] <= 'Z')
3965+        x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
3966+      else if(in[j] >= 'a' && in[j] <= 'z')
3967+        x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
3968+      else if(in[j] >= '0' && in[j] <= '9')
3969+        x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
3970+      else if(in[j] == '+')
3971+        x = (x << 6) + 62;
3972+      else if(in[j] == '/')
3973+        x = (x << 6) + 63;
3974+      else if(in[j] == '=')
3975+        x = (x << 6);
3976+    }
3977+
3978+    s[2] = (unsigned char)(x & 255); x >>= 8;
3979+    s[1] = (unsigned char)(x & 255); x >>= 8;
3980+    s[0] = (unsigned char)(x & 255); x >>= 8;
3981+    s += 3; in += 4;
3982+  }
3983+
3984+  x = 0;
3985+  for(j = 0; j < 4; j++) {
3986+    if(in[j] >= 'A' && in[j] <= 'Z')
3987+      x = (x << 6) + (unsigned int)(in[j] - 'A' + 0);
3988+    else if(in[j] >= 'a' && in[j] <= 'z')
3989+      x = (x << 6) + (unsigned int)(in[j] - 'a' + 26);
3990+    else if(in[j] >= '0' && in[j] <= '9')
3991+      x = (x << 6) + (unsigned int)(in[j] - '0' + 52);
3992+    else if(in[j] == '+')
3993+      x = (x << 6) + 62;
3994+    else if(in[j] == '/')
3995+      x = (x << 6) + 63;
3996+    else if(in[j] == '=')
3997+      x = (x << 6);
3998+  }
3999+
4000+  b[2] = (unsigned char)(x & 255); x >>= 8;
4001+  b[1] = (unsigned char)(x & 255); x >>= 8;
4002+  b[0] = (unsigned char)(x & 255); x >>= 8;
4003+
4004+  for(i = 0; i < 3 - p; i++)
4005+    s[i] = b[i];
4006+
4007+  return 0;
4008+}
4009+
4010+int b64encode(in,out)
4011+stralloc *in;
4012+stralloc *out; /* not null terminated */
4013+{
4014+  unsigned char a, b, c;
4015+  int i;
4016+  char *s;
4017+
4018+  if (in->len == 0)
4019+  {
4020+    if (!stralloc_copys(out,"")) return -1;
4021+    return 0;
4022+  }
4023+
4024+  i = in->len / 3 * 4 + 4;   
4025+  if (!stralloc_ready(out,i)) return -1;
4026+  s = out->s;
4027+
4028+  for (i = 0;i < in->len;i += 3) {
4029+    a = in->s[i];
4030+    b = i + 1 < in->len ? in->s[i + 1] : 0;
4031+    c = i + 2 < in->len ? in->s[i + 2] : 0;
4032+
4033+    *s++ = b64alpha[a >> 2];
4034+    *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
4035+
4036+    if (i + 1 >= in->len) *s++ = B64PAD;
4037+    else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
4038+
4039+    if (i + 2 >= in->len) *s++ = B64PAD;
4040+    else *s++ = b64alpha[c & 63];
4041+  }
4042+  out->len = s - out->s;
4043+  return 0;
4044+}
4045diff -ruN ../netqmail-1.06-original/base64.h netqmail-1.06/base64.h
4046--- ../netqmail-1.06-original/base64.h  1970-01-01 01:00:00.000000000 +0100
4047+++ netqmail-1.06/base64.h      2016-11-22 21:03:57.105528332 +0100
4048@@ -0,0 +1,13 @@
4049+#ifndef BASE64_H
4050+#define BASE64_H
4051+
4052+/* DKIM-1.10 */
4053+#include "stralloc.h"
4054+extern int b64decode(const unsigned char *, int, stralloc *);
4055+extern int b64encode(stralloc *, stralloc *);
4056+/* end DKIM-1.10 */
4057+
4058+extern int b64decode();
4059+extern int b64encode();
4060+
4061+#endif
4062diff -ruN ../netqmail-1.06-original/base64sub.c netqmail-1.06/base64sub.c
4063--- ../netqmail-1.06-original/base64sub.c       1970-01-01 01:00:00.000000000 +0100
4064+++ netqmail-1.06/base64sub.c   2016-11-22 21:03:57.105528332 +0100
4065@@ -0,0 +1,170 @@
4066+/*
4067+ * $Log: base64sub.c,v $
4068+ * Revision 1.6  2010-03-03 09:33:16+05:30  Cprogrammer
4069+ * renamed base64 to base64sub
4070+ *
4071+ * Revision 1.5  2004-10-22 20:18:37+05:30  Cprogrammer
4072+ * added RCS id
4073+ *
4074+ * Revision 1.4  2004-09-19 14:36:11+05:30  Cprogrammer
4075+ * corrected number of bytes in stralloc variable 'out'
4076+ *
4077+ * Revision 1.3  2004-07-30 17:36:47+05:30  Cprogrammer
4078+ * fixed bugs in b64decode()
4079+ *
4080+ * Revision 1.2  2004-07-17 21:16:27+05:30  Cprogrammer
4081+ * added RCS log
4082+ *
4083+ */
4084+#include "base64.h"
4085+#include "stralloc.h"
4086+#include "substdio.h"
4087+#include "str.h"
4088+
4089+static char    *b64alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4090+#define B64PAD '='
4091+
4092+/*
4093+ * returns 0 ok, 1 illegal, -1 problem
4094+ */
4095+
4096+int
4097+b64decode(in, l, out)
4098+       const unsigned char *in;
4099+       int             l;
4100+       stralloc       *out;            /*- not null terminated */
4101+{
4102+       int             p = 0;
4103+       int             n;
4104+       unsigned int    x;
4105+       int             i, j;
4106+       char           *s;
4107+       unsigned char   b[3];
4108+
4109+       if (l == 0)
4110+       {
4111+               if (!stralloc_copys(out, ""))
4112+                       return -1;
4113+               return 0;
4114+       }
4115+       while (in[l - 1] == B64PAD)
4116+       {
4117+               p++;
4118+               l--;
4119+       }
4120+       n = (l + p) / 4;
4121+       out->len = (n * 3) - p;
4122+       if (!stralloc_ready(out, out->len))
4123+               return -1;
4124+       s = out->s;
4125+       for (i = 0; i < n - 1; i++)
4126+       {
4127+               x = 0;
4128+               for (j = 0; j < 4; j++)
4129+               {
4130+                       if (in[j] >= 'A' && in[j] <= 'Z')
4131+                               x = (x << 6) + (unsigned int) (in[j] - 'A' + 0);
4132+                       else
4133+                       if (in[j] >= 'a' && in[j] <= 'z')
4134+                               x = (x << 6) + (unsigned int) (in[j] - 'a' + 26);
4135+                       else
4136+                       if (in[j] >= '0' && in[j] <= '9')
4137+                               x = (x << 6) + (unsigned int) (in[j] - '0' + 52);
4138+                       else
4139+                       if (in[j] == '+')
4140+                               x = (x << 6) + 62;
4141+                       else
4142+                       if (in[j] == '/')
4143+                               x = (x << 6) + 63;
4144+                       else
4145+                       if (in[j] == '=')
4146+                               x = (x << 6);
4147+               }
4148+               s[2] = (unsigned char) (x & 255);
4149+               x >>= 8;
4150+               s[1] = (unsigned char) (x & 255);
4151+               x >>= 8;
4152+               s[0] = (unsigned char) (x & 255);
4153+               x >>= 8;
4154+               s += 3;
4155+               in += 4;
4156+       }
4157+       x = 0;
4158+       for (j = 0; j < 4; j++)
4159+       {
4160+               if (in[j] >= 'A' && in[j] <= 'Z')
4161+                       x = (x << 6) + (unsigned int) (in[j] - 'A' + 0);
4162+               else
4163+               if (in[j] >= 'a' && in[j] <= 'z')
4164+                       x = (x << 6) + (unsigned int) (in[j] - 'a' + 26);
4165+               else
4166+               if (in[j] >= '0' && in[j] <= '9')
4167+                       x = (x << 6) + (unsigned int) (in[j] - '0' + 52);
4168+               else
4169+               if (in[j] == '+')
4170+                       x = (x << 6) + 62;
4171+               else
4172+               if (in[j] == '/')
4173+                       x = (x << 6) + 63;
4174+               else
4175+               if (in[j] == '=')
4176+                       x = (x << 6);
4177+       }
4178+       b[2] = (unsigned char) (x & 255);
4179+       x >>= 8;
4180+       b[1] = (unsigned char) (x & 255);
4181+       x >>= 8;
4182+       b[0] = (unsigned char) (x & 255);
4183+       x >>= 8;
4184+       for (i = 0; i < 3 - p; i++)
4185+               s[i] = b[i];
4186+       out->len = (n * 3) - p;
4187+       return 0;
4188+}
4189+
4190+int
4191+b64encode(in, out)
4192+       stralloc       *in;
4193+       stralloc       *out;            /*- not null terminated */
4194+{
4195+       unsigned char   a, b, c;
4196+       int             i;
4197+       char           *s;
4198+
4199+       if (in->len == 0)
4200+       {
4201+               if (!stralloc_copys(out, ""))
4202+                       return -1;
4203+               return 0;
4204+       }
4205+       if (!stralloc_ready(out, in->len / 3 * 4 + 4))
4206+               return -1;
4207+       s = out->s;
4208+       for (i = 0; i < in->len; i += 3)
4209+       {
4210+               a = in->s[i];
4211+               b = i + 1 < in->len ? in->s[i + 1] : 0;
4212+               c = i + 2 < in->len ? in->s[i + 2] : 0;
4213+               *s++ = b64alpha[a >> 2];
4214+               *s++ = b64alpha[((a & 3) << 4) | (b >> 4)];
4215+               if (i + 1 >= in->len)
4216+                       *s++ = B64PAD;
4217+               else
4218+                       *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
4219+
4220+               if (i + 2 >= in->len)
4221+                       *s++ = B64PAD;
4222+               else
4223+                       *s++ = b64alpha[c & 63];
4224+       }
4225+       out->len = s - out->s;
4226+       return 0;
4227+}
4228+
4229+void
4230+getversion_base64sub_c()
4231+{
4232+       static char    *x = "$Id: base64sub.c,v 1.6 2010-03-03 09:33:16+05:30 Cprogrammer Stab mbhangui $";
4233+
4234+       x++;
4235+}
4236diff -ruN ../netqmail-1.06-original/byte.h netqmail-1.06/byte.h
4237--- ../netqmail-1.06-original/byte.h    1998-06-15 12:53:16.000000000 +0200
4238+++ netqmail-1.06/byte.h        2016-11-22 21:03:57.105528332 +0100
4239@@ -3,6 +3,8 @@
4240 
4241 extern unsigned int byte_chr();
4242 extern unsigned int byte_rchr();
4243+extern unsigned int byte_cspn();
4244+extern unsigned int byte_rcspn();
4245 extern void byte_copy();
4246 extern void byte_copyr();
4247 extern int byte_diff();
4248diff -ruN ../netqmail-1.06-original/byte_cspn.c netqmail-1.06/byte_cspn.c
4249--- ../netqmail-1.06-original/byte_cspn.c       1970-01-01 01:00:00.000000000 +0100
4250+++ netqmail-1.06/byte_cspn.c   2016-11-22 21:03:57.105528332 +0100
4251@@ -0,0 +1,11 @@
4252+#include "byte.h"
4253+
4254+unsigned int byte_cspn(s,n,c)
4255+register char *s;
4256+register unsigned int n;
4257+register char *c;
4258+{
4259+  while(*c)
4260+    n = byte_chr(s,n,*c++);
4261+  return n;
4262+}
4263diff -ruN ../netqmail-1.06-original/byte_rcspn.c netqmail-1.06/byte_rcspn.c
4264--- ../netqmail-1.06-original/byte_rcspn.c      1970-01-01 01:00:00.000000000 +0100
4265+++ netqmail-1.06/byte_rcspn.c  2016-11-22 21:03:57.105528332 +0100
4266@@ -0,0 +1,17 @@
4267+#include "byte.h"
4268+
4269+unsigned int byte_rcspn(s,n,c)
4270+register char *s;
4271+register unsigned int n;
4272+register char *c;
4273+{
4274+  unsigned int ret,pos,i;
4275+
4276+  for(ret = n,pos = 0;*c;++c) {
4277+    i = byte_rchr(s + pos,n - pos,*c) + pos;
4278+    if (i < n) ret = pos = i;
4279+  }
4280+
4281+  return ret;
4282+}
4283+
4284diff -ruN ../netqmail-1.06-original/caldate.h netqmail-1.06/caldate.h
4285--- ../netqmail-1.06-original/caldate.h 1970-01-01 01:00:00.000000000 +0100
4286+++ netqmail-1.06/caldate.h     2016-11-22 21:03:57.105528332 +0100
4287@@ -0,0 +1,24 @@
4288+/*
4289+ * $Log: caldate.h,v $
4290+ * Revision 1.3  2004-10-09 23:20:20+05:30  Cprogrammer
4291+ * added function prototypes
4292+ *
4293+ * Revision 1.2  2004-06-18 22:57:44+05:30  Cprogrammer
4294+ * added RCS log
4295+ *
4296+ */
4297+#ifndef CALDATE_H
4298+#define CALDATE_H
4299+
4300+struct caldate
4301+{
4302+       long            year;
4303+       int             month;
4304+       int             day;
4305+};
4306+
4307+void            caldate_frommjd(struct caldate *, long, int *, int *);
4308+long            caldate_mjd(struct caldate *);
4309+unsigned int    caldate_fmt(char *, struct caldate *);
4310+
4311+#endif
4312diff -ruN ../netqmail-1.06-original/caltime.h netqmail-1.06/caltime.h
4313--- ../netqmail-1.06-original/caltime.h 1970-01-01 01:00:00.000000000 +0100
4314+++ netqmail-1.06/caltime.h     2016-11-22 21:03:57.105528332 +0100
4315@@ -0,0 +1,30 @@
4316+/*
4317+ * $Log: caltime.h,v $
4318+ * Revision 1.3  2004-10-09 23:20:26+05:30  Cprogrammer
4319+ * added function prototypes
4320+ *
4321+ * Revision 1.2  2004-06-18 22:57:47+05:30  Cprogrammer
4322+ * added RCS log
4323+ *
4324+ */
4325+#ifndef CALTIME_H
4326+#define CALTIME_H
4327+
4328+#include "caldate.h"
4329+#include "tai.h"
4330+
4331+struct caltime
4332+{
4333+       struct caldate  date;
4334+       int             hour;
4335+       int             minute;
4336+       int             second;
4337+       long            offset;
4338+};
4339+
4340+void            caltime_tai(struct caltime *, struct tai *);
4341+void            caltime_utc(struct caltime *, struct tai *, int *, int *);
4342+
4343+unsigned int    caltime_fmt(char *, struct caltime *);
4344+
4345+#endif
4346diff -ruN ../netqmail-1.06-original/case_startb.c netqmail-1.06/case_startb.c
4347--- ../netqmail-1.06-original/case_startb.c     1970-01-01 01:00:00.000000000 +0100
4348+++ netqmail-1.06/case_startb.c 2016-11-22 21:03:57.106528299 +0100
4349@@ -0,0 +1,31 @@
4350+#include "case.h"
4351+
4352+int case_startb(s,len,t)
4353+register char *s;
4354+unsigned int len;
4355+register char *t;
4356+{
4357+  register unsigned char x;
4358+  register unsigned char y;
4359+
4360+  for (;;) {
4361+    y = *t++ - 'A';
4362+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
4363+    if (!y) return 1;
4364+    if (!len) return 0;
4365+    --len;
4366+    x = *s++ - 'A';
4367+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
4368+    if (x != y) return 0;
4369+  }
4370+}
4371+
4372+/* DKIM 1.10 */
4373+void
4374+getversion_case_startb_c()
4375+{
4376+        static char    *x = "$Id: case_startb.c,v 1.3 2004-10-22 20:23:18+05:30 Cprogrammer Stab mbhangui $";
4377+
4378+        x++;
4379+}
4380+/* end DKIM 1.10 */
4381diff -ruN ../netqmail-1.06-original/chkspawn.c netqmail-1.06/chkspawn.c
4382--- ../netqmail-1.06-original/chkspawn.c        1998-06-15 12:53:16.000000000 +0200
4383+++ netqmail-1.06/chkspawn.c    2016-11-22 21:03:57.106528299 +0100
4384@@ -22,8 +22,8 @@
4385     _exit(1);
4386   }
4387 
4388-  if (auto_spawn > 255) {
4389-    substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 255.\n");
4390+  if (auto_spawn > 65000) {
4391+    substdio_puts(subfderr,"Oops. You have set conf-spawn higher than 65000.\n");
4392     substdio_flush(subfderr);
4393     _exit(1);
4394   }
4395diff -ruN ../netqmail-1.06-original/chkuser.c netqmail-1.06/chkuser.c
4396--- ../netqmail-1.06-original/chkuser.c 1970-01-01 01:00:00.000000000 +0100
4397+++ netqmail-1.06/chkuser.c     2016-11-22 21:04:19.987765257 +0100
4398@@ -0,0 +1,1258 @@
4399+
4400+/*
4401+ *
4402+ * 'chkuser.c' v.2.0.9
4403+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
4404+ *
4405+ * Author: Antonio Nati tonix@interazioni.it
4406+ * All rights on this software and
4407+ * the identifying words chkusr and chkuser reserved by the author
4408+ *
4409+ * This software may be freely used, modified and distributed,
4410+ * but this lines must be kept in every original or derived version.
4411+ * Original author "Antonio Nati" and the web URL
4412+ * "http://www.interazioni.it/opensource"
4413+ * must be indicated in every related work or web page
4414+ *
4415+ */
4416+
4417+#include <pwd.h>
4418+
4419+/* required by vpopmail */
4420+#include <stdio.h>
4421+
4422+#include <stdlib.h>
4423+#include <string.h>
4424+#include <unistd.h>
4425+
4426+#include "dns.h"
4427+#include "env.h"
4428+#include "ipme.h"
4429+#include "now.h"
4430+#include "str.h"
4431+#include "open.h"
4432+#include "subfd.h"
4433+#include "substdio.h"
4434+#include "stralloc.h"
4435+
4436+#include "vpopmail.h"
4437+#include "vauth.h"
4438+#include "vpopmail_config.h"
4439+
4440+#include "chkuser.h"
4441+#include "chkuser_settings.h"
4442+
4443+#if defined _exit
4444+#undef _exit
4445+#endif
4446+
4447+extern void flush();
4448+extern void out (char *s);
4449+
4450+extern char *remotehost;
4451+extern char *remoteip;
4452+extern char *remoteinfo;
4453+extern char *relayclient;
4454+extern char *fakehelo;
4455+
4456+extern void die_nomem();
4457+
4458+#define DIE_NOMEM() die_nomem()
4459+
4460+#if defined CHKUSER_DEBUG
4461+
4462+#if defined CHKUSER_DEBUG_STDERR
4463+
4464+#define CHKUSER_DBG(a) write (STDERR_FILENO, a, strlen (a))
4465+#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDERR_FILENO, str, strlen (str));}
4466+
4467+#else
4468+
4469+#define CHKUSER_DBG(a) write (STDOUT_FILENO, a, strlen (a))
4470+#define CHKUSER_DBG_INT(a) { int x; char str[30]; sprintf (str, "%d", a); write (STDOUT_FILENO, str, strlen (str));}
4471+
4472+#endif
4473+#else
4474+
4475+#define CHKUSER_DBG(a) /* DBG dummy */
4476+#define CHKUSER_DBG_INT(a) /* DBG dummy */
4477+
4478+#endif
4479+
4480+static int intrusion_threshold_reached = 0;
4481+static int first_time_init_flag = 1;
4482+
4483+static int recipients = 0;
4484+static int wrong_recipients = 0;
4485+
4486+static stralloc user = {0};
4487+static stralloc domain = {0};
4488+static stralloc domain_path = {0};
4489+static stralloc tmp_path = {0};
4490+static stralloc alias_path = {0};
4491+
4492+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
4493+ static char *identify_remote;
4494+#endif
4495+
4496+#if defined CHKUSER_ENABLE_EXTENSIONS
4497+#define CHKUSER_ENABLE_USERS_EXTENSIONS
4498+#endif
4499+
4500+#if defined CHKUSER_ENABLE_LISTS
4501+#define CHKUSER_ENABLE_EZMLM_LISTS
4502+#endif
4503+
4504+#if defined CHKUSER_EXTENSION_DASH
4505+#define CHKUSER_USERS_DASH CHKUSER_EXTENSION_DASH
4506+#endif
4507+
4508+
4509+#if defined CHKUSER_ENABLE_VALIAS
4510+#error  "chkuser setting error: CHKUSER_ENABLE_VALIAS has been substituted by VALIAS (within vpopmail includes); you don't need anymore this define"
4511+#endif
4512+
4513+#if defined CHKUSER_ENABLE_VAUTH_OPEN
4514+#error  "chkuser setting error: CHKUSER_ENABLE_VAUTH_OPEN has been substituted by CHKUSER_ENABLE_VAUTH_OPEN_CALL; edit chkuser_settings.h and change your settings"
4515+#endif
4516+
4517+#if defined CHKUSER_ENABLE_VAUTH_OPEN_CALL
4518+ static int db_already_open = 0;
4519+#endif
4520+
4521+#if defined CHKUSER_ALWAYS_ON && defined CHKUSER_STARTING_VARIABLE
4522+#error "chkuser setting error: CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE are mutually esclusive. Edit your chkuser_settings.h and disable one of them"
4523+#endif
4524+
4525+  static int starting_value = 0;
4526+
4527+#if defined CHKUSER_STARTING_VARIABLE
4528+  static char *starting_string = 0;
4529+#endif
4530+
4531+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
4532+  static int mustauth_value = 0;
4533+#endif
4534+
4535+
4536+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
4537+  static char *maxrcpt_string = 0;
4538+  static int maxrcpt_limit = 0;
4539+  static int maxrcpt_limit_reached = 0;
4540+#endif
4541+
4542+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
4543+  static char *maxwrongrcpt_string = 0;
4544+  static int maxwrongrcpt_limit = 0;
4545+  static int maxwrongrcpt_limit_reached = 0;
4546+#endif
4547+
4548+#if defined CHKUSER_MBXQUOTA_VARIABLE
4549+  static char *maxmbxquota_string = 0;
4550+  static int maxmbxquota_limit = 0;
4551+#endif
4552+
4553+  static unsigned int sender_nocheck = 0;
4554+
4555+#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX
4556+static stralloc sender_user = {0};
4557+static stralloc sender_domain = {0};
4558+#endif
4559+
4560+#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
4561+static unsigned int enable_doublebounce = 0;
4562+#endif
4563+
4564+#if defined CHKUSER_ERROR_DELAY
4565+
4566+  static int chkuser_delay_interval = CHKUSER_ERROR_DELAY * 1000;
4567+
4568+#define CHKUSER_DELAY()        chkuser_delay()
4569+
4570+void chkuser_delay (void) {
4571+
4572+        usleep (chkuser_delay_interval);
4573+
4574+#if defined CHKUSER_ERROR_DELAY_INCREASE
4575+        chkuser_delay_interval += CHKUSER_ERROR_DELAY_INCREASE * 1000;
4576+#endif
4577+}
4578+
4579+#if defined CHKUSER_RCPT_DELAY_ANYERROR
4580+#define CHKUSER_RCPT_DELAY_ANY() chkuser_delay()
4581+#else
4582+#define CHKUSER_RCPT_DELAY_ANY() /* no delay for any error */
4583+#endif
4584+
4585+#if defined CHKUSER_SENDER_DELAY_ANYERROR
4586+#define CHKUSER_SENDER_DELAY_ANY() chkuser_delay()
4587+#else
4588+#define CHKUSER_SENDER_DELAY_ANY() /* no delay for any error */
4589+#endif
4590+
4591+
4592+#else
4593+#define CHKUSER_DELAY() /* no delay */
4594+#define CHKUSER_RCPT_DELAY_ANY() /* no delay */
4595+#define CHKUSER_SENDER_DELAY_ANY() /* no delay */
4596+#endif
4597+
4598+#if defined CHKUSER_ENABLE_LOGGING
4599+
4600+static stralloc logstr = { 0 };
4601+
4602+static void chkuser_commonlog (char *sender, char *rcpt, char *title, char *description) {
4603+
4604+  substdio_puts (subfderr, "CHKUSER ");
4605+  substdio_puts (subfderr, title);
4606+  substdio_puts (subfderr, ": from <");
4607+  substdio_puts (subfderr, sender);
4608+  substdio_puts (subfderr, "|remoteinfo/auth:" );
4609+  if (remoteinfo) {
4610+       substdio_puts (subfderr, remoteinfo);
4611+  }
4612+  substdio_puts (subfderr, "|chkuser-identify:" );
4613+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
4614+  if (identify_remote) substdio_puts (subfderr, identify_remote);
4615+#endif
4616+  substdio_puts (subfderr, "> remote <helo:");
4617+  if (fakehelo) substdio_puts (subfderr, fakehelo);
4618+  substdio_puts (subfderr, "|remotehostname:" );
4619+  if (remotehost) substdio_puts (subfderr, remotehost);
4620+  substdio_puts (subfderr, "|remotehostip:" );
4621+  if (remoteip) substdio_puts (subfderr, remoteip);
4622+  substdio_puts (subfderr, "> rcpt <");
4623+  substdio_puts (subfderr, rcpt);
4624+  substdio_puts (subfderr, "> : ");
4625+  substdio_puts (subfderr, description);
4626+  substdio_puts (subfderr, "\n");
4627+  substdio_flush (subfderr);
4628+}
4629+
4630+#else
4631+#define chkuser_commonlog(a,b,c,d) /* no log */
4632+#endif
4633+
4634+#if defined CHKUSER_SENDER_FORMAT
4635+
4636+static int check_sender_address_format (stralloc *user, stralloc *domain) {
4637+
4638+        int x;
4639+
4640+        for (x = 0; x < (user->len -1); ++x) {
4641+                if ((!isalnum (user->s[x]))
4642+
4643+#if defined CHKUSER_ALLOW_SENDER_SRS
4644+               && (user->s[x] != '#')
4645+               && (user->s[x] != '+')
4646+#endif
4647+#if defined CHKUSER_ALLOW_SENDER_CHAR_1
4648+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_1)
4649+#endif
4650+#if defined CHKUSER_ALLOW_SENDER_CHAR_2
4651+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_2)
4652+#endif
4653+#if defined CHKUSER_ALLOW_SENDER_CHAR_3
4654+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_3)
4655+#endif
4656+#if defined CHKUSER_ALLOW_SENDER_CHAR_4
4657+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_4)
4658+#endif
4659+#if defined CHKUSER_ALLOW_SENDER_CHAR_5
4660+               && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_5)
4661+#endif
4662+#if defined CHKUSER_ALLOW_SENDER_CHAR_6
4663+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_6)
4664+#endif
4665+#if defined CHKUSER_ALLOW_SENDER_CHAR_7
4666+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_7)
4667+#endif
4668+#if defined CHKUSER_ALLOW_SENDER_CHAR_8
4669+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_8)
4670+#endif
4671+#if defined CHKUSER_ALLOW_SENDER_CHAR_9
4672+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_9)
4673+#endif
4674+#if defined CHKUSER_ALLOW_SENDER_CHAR_10
4675+                && (user->s[x] != CHKUSER_ALLOW_SENDER_CHAR_10)
4676+#endif
4677+               && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) {
4678+                        return 0;
4679+                }
4680+        }
4681+
4682+/*
4683+ * Be careful, this is a base check
4684+ *      Minimum is x.xx + ending \0
4685+ *      Minimum characters needed are 5
4686+ */
4687+#if defined CHKUSER_MIN_DOMAIN_LEN
4688+        if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) {
4689+                return 0;
4690+        }
4691+#endif
4692+
4693+/*
4694+ *      This is a safety check
4695+ */
4696+#if defined CHKUSER_MIN_DOMAIN_LEN
4697+        if (domain->len < 2) {
4698+                return 0;
4699+        }
4700+#endif
4701+
4702+        for (x = 0; x < (domain->len -1); ++x) {
4703+                if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) {
4704+                        return 0;
4705+                }
4706+        }
4707+
4708+        if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) {
4709+                return 0;
4710+        }
4711+        if (strstr (domain->s, "..") != NULL) {
4712+                return 0;
4713+        }
4714+       if (strncmp (domain->s, "xn--", 4) == 0) {
4715+               if (strstr (&domain->s[4], "--") != NULL)
4716+                       return 0;
4717+       } else {
4718+               if (strstr (domain->s, "--") != NULL)
4719+                       return 0;
4720+       }
4721+        if (strstr (domain->s, ".-") != NULL) {
4722+                return 0;
4723+        }
4724+        if (strstr (domain->s, "-.") != NULL) {
4725+                return 0;
4726+        }
4727+        if (strchr (domain->s, '.') == NULL) {
4728+                return 0;
4729+        }
4730+
4731+        return 1;
4732+}
4733+
4734+#endif
4735+
4736+#if defined CHKUSER_RCPT_FORMAT
4737+
4738+static int check_rcpt_address_format (stralloc *user, stralloc *domain) {
4739+
4740+        int x;
4741+
4742+        for (x = 0; x < (user->len -1); ++x) {
4743+                if ((!isalnum (user->s[x]))
4744+#if defined CHKUSER_ALLOW_RCPT_SRS
4745+                && (user->s[x] != '#')
4746+                && (user->s[x] != '+')
4747+#endif
4748+#if defined CHKUSER_ALLOW_RCPT_CHAR_1
4749+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_1)
4750+#endif
4751+#if defined CHKUSER_ALLOW_RCPT_CHAR_2
4752+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_2)
4753+#endif
4754+#if defined CHKUSER_ALLOW_RCPT_CHAR_3
4755+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_3)
4756+#endif
4757+#if defined CHKUSER_ALLOW_RCPT_CHAR_4
4758+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_4)
4759+#endif
4760+#if defined CHKUSER_ALLOW_RCPT_CHAR_5
4761+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_5)
4762+#endif
4763+#if defined CHKUSER_ALLOW_RCPT_CHAR_6
4764+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_6)
4765+#endif
4766+#if defined CHKUSER_ALLOW_RCPT_CHAR_7
4767+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_7)
4768+#endif
4769+#if defined CHKUSER_ALLOW_RCPT_CHAR_8
4770+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_8)
4771+#endif
4772+#if defined CHKUSER_ALLOW_RCPT_CHAR_9
4773+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_9)
4774+#endif
4775+#if defined CHKUSER_ALLOW_RCPT_CHAR_10
4776+                && (user->s[x] != CHKUSER_ALLOW_RCPT_CHAR_10)
4777+#endif
4778+               && (user->s[x] != '_') && (user->s[x] != '-') && (user->s[x] != '.') && (user->s[x] != '=')) {
4779+                        return 0;
4780+                }
4781+        }
4782+
4783+/*
4784+ * Be careful, this is a base check
4785+ *      Minimum is x.xx + ending \0
4786+ *      Minimum characters needed are 5
4787+ */
4788+#if defined CHKUSER_MIN_DOMAIN_LEN
4789+        if (domain->len < (CHKUSER_MIN_DOMAIN_LEN +1)) {
4790+                return 0;
4791+        }
4792+#endif
4793+
4794+/*
4795+ *      This is a safety check
4796+ */
4797+#if defined CHKUSER_MIN_DOMAIN_LEN
4798+        if (domain->len < 2) {
4799+                return 0;
4800+        }
4801+#endif
4802+        for (x = 0; x < (domain->len -1); ++x) {
4803+                if ((!isalnum (domain->s[x])) && (domain->s[x] != '-') && (domain->s[x] != '.')) {
4804+                        return 0;
4805+                }
4806+        }
4807+
4808+        if ((domain->s[0] == '-') || (domain->s[domain->len -2] == '-') || (domain->s[0] == '.') || (domain->s[domain->len -2] == '.')) {
4809+                return 0;
4810+        }
4811+        if (strstr (domain->s, "..") != NULL) {
4812+                return 0;
4813+        }
4814+       if (strncmp (domain->s, "xn--", 4) == 0) {
4815+               if (strstr (&domain->s[4], "--") != NULL)
4816+                       return 0;
4817+       } else {
4818+               if (strstr (domain->s, "--") != NULL)
4819+                       return 0;
4820+       }
4821+        if (strstr (domain->s, ".-") != NULL) {
4822+                return 0;
4823+        }
4824+        if (strstr (domain->s, "-.") != NULL) {
4825+                return 0;
4826+        }
4827+        if (strchr (domain->s, '.') == NULL) {
4828+                return 0;
4829+        }
4830+
4831+        return 1;
4832+}
4833+
4834+#endif
4835+
4836+#if defined CHKUSER_SENDER_MX || defined CHKUSER_RCPT_MX
4837+
4838+static   unsigned long mx_random;
4839+static  ipalloc mx_ip = {0};
4840+
4841+static int chkuser_mx_lookup (stralloc *domain) {
4842+
4843+  int status;
4844+
4845+       mx_random = now() + getpid();
4846+       dns_init(0);
4847+       status = dns_mxip (&mx_ip, domain, mx_random);
4848+
4849+       if (status == DNS_MEM) DIE_NOMEM();
4850+
4851+       return status;
4852+}
4853+
4854+#endif
4855+
4856+
4857+void chkuser_cleanup (int exit_value) {
4858+
4859+#if defined CHKUSER_DB_CLEANUP
4860+       vclose ();
4861+#endif
4862+       _exit (exit_value);
4863+}
4864+
4865+static void first_time_init (void) {
4866+
4867+  starting_value = 0;
4868+
4869+#if defined CHKUSER_ALWAYS_ON
4870+       starting_value = 1;
4871+#endif
4872+
4873+#if defined CHKUSER_STARTING_VARIABLE
4874+        starting_string = env_get (CHKUSER_STARTING_VARIABLE);
4875+        if (starting_string) {
4876+                if (strcasecmp(starting_string, "ALWAYS") == 0) {
4877+                        starting_value = 1;
4878+                } else if (strcasecmp(starting_string, "DOMAIN") == 0) {
4879+                        starting_value = 0;
4880+                }
4881+        } else {
4882+                starting_string = "";
4883+               starting_value = -1;
4884+        }
4885+#endif
4886+
4887+#if defined CHKUSER_DISABLE_VARIABLE
4888+       if (env_get (CHKUSER_DISABLE_VARIABLE)) {
4889+               starting_value = -1;
4890+       }
4891+#endif
4892+
4893+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
4894+        if (env_get (CHKUSER_EXTRA_MUSTAUTH_VARIABLE)) {
4895+               if (relayclient) {
4896+                       mustauth_value = 0;
4897+               } else {
4898+                       mustauth_value = 1;
4899+               }
4900+        }
4901+#endif
4902+
4903+
4904+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
4905+        maxrcpt_string = env_get (CHKUSER_RCPT_LIMIT_VARIABLE);
4906+        if (maxrcpt_string) {
4907+                maxrcpt_limit = atoi (maxrcpt_string);
4908+                if (maxrcpt_limit < 1) {
4909+                        maxrcpt_limit = 0;
4910+                }
4911+        } else {
4912+                maxrcpt_string = "";;
4913+        }
4914+#endif
4915+
4916+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
4917+        maxwrongrcpt_string = env_get (CHKUSER_WRONGRCPT_LIMIT_VARIABLE);
4918+        if (maxwrongrcpt_string) {
4919+                maxwrongrcpt_limit = atoi (maxwrongrcpt_string);
4920+                if (maxwrongrcpt_limit < 1) {
4921+                        maxwrongrcpt_limit = 0;
4922+                }
4923+        } else {
4924+                maxwrongrcpt_string = "";
4925+        }
4926+#endif
4927+
4928+#if defined CHKUSER_MBXQUOTA_VARIABLE
4929+        maxmbxquota_string = env_get (CHKUSER_MBXQUOTA_VARIABLE);
4930+        if (maxmbxquota_string) {
4931+                maxmbxquota_limit = atoi (maxmbxquota_string);
4932+                if (maxmbxquota_limit < 1) {
4933+                       maxmbxquota_limit = 0;
4934+                }
4935+       } else {
4936+                       maxmbxquota_string = "";
4937+       }
4938+#endif
4939+
4940+#if defined CHKUSER_SENDER_NOCHECK_VARIABLE
4941+
4942+        if (env_get (CHKUSER_SENDER_NOCHECK_VARIABLE)) {
4943+               sender_nocheck = 1;
4944+        } else {
4945+               sender_nocheck = 0;
4946+        }
4947+#endif
4948+
4949+#if defined CHKUSER_IDENTIFY_REMOTE_VARIABLE
4950+
4951+        identify_remote = env_get (CHKUSER_IDENTIFY_REMOTE_VARIABLE);
4952+#endif
4953+
4954+
4955+#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
4956+
4957+        if (env_get (CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE)) {
4958+                enable_doublebounce = 1;
4959+        } else {
4960+                enable_doublebounce = 0;
4961+        }
4962+#endif
4963+
4964+        if (!stralloc_ready (&user, 300)) DIE_NOMEM();
4965+        if (!stralloc_ready (&domain, 500)) DIE_NOMEM();
4966+        if (!stralloc_ready (&domain_path, 1000)) DIE_NOMEM();
4967+        if (!stralloc_ready (&tmp_path, 1000)) DIE_NOMEM();
4968+        if (!stralloc_ready (&alias_path, 1000)) DIE_NOMEM();
4969+
4970+       first_time_init_flag = 0;
4971+
4972+}
4973+
4974+/*
4975+ * realrcpt ()
4976+ *
4977+ * Returns:
4978+ *
4979+ *     CHKUSER_OK = 1 = Ok, recipients does exists
4980+ *
4981+ *     0 = Not in rcpthosts
4982+ *
4983+ *     < 0 various errors
4984+ *
4985+ *
4986+ * Parameters:
4987+ *     stralloc *sender = sender address
4988+ *     stralloc *rcpt = rcpt address to check
4989+ *
4990+ *
4991+*/
4992+
4993+static int realrcpt (stralloc *sender, stralloc *rcpt)
4994+{
4995+  int count;
4996+  int retstat = CHKUSER_KO;
4997+  struct vqpasswd *user_passwd = NULL;
4998+  int fd_file = -1;
4999+  int read_char;
5000+  int offset;
5001+  char read_buf[1024];
5002+
5003+#if defined CHKUSER_ENABLE_UIDGID
5004+  uid_t eff_uid;
5005+  gid_t eff_gid;
5006+#endif
5007+
5008+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
5009+  if (mustauth_value == 1) {
5010+       return CHKUSER_ERR_MUSTAUTH;
5011+  }
5012+#endif
5013+
5014+
5015+  if (starting_value == -1) {
5016+       if (addrallowed()) {
5017+               return CHKUSER_OK_NOCHECKALL;
5018+       } else {
5019+               if (relayclient) {
5020+                       return CHKUSER_RELAYING;
5021+               }
5022+               return CHKUSER_NORCPTHOSTS;
5023+       }
5024+  }
5025+
5026+  if (intrusion_threshold_reached == 1) {
5027+       return CHKUSER_ERR_INTRUSION_THRESHOLD;
5028+  }
5029+
5030+#if defined CHKUSER_RCPT_LIMIT_VARIABLE
5031+
5032+  ++recipients;
5033+  if ((maxrcpt_limit > 0) && (recipients >= maxrcpt_limit)) {
5034+       chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed rcpt");
5035+       intrusion_threshold_reached = 1;
5036+        return CHKUSER_ERR_MAXRCPT;
5037+  }
5038+#endif
5039+
5040+/* Search the '@' character */
5041+  count = byte_rchr(rcpt->s,rcpt->len,'@');
5042+
5043+  if (count < rcpt->len) {
5044+    if (!stralloc_copyb (&user, rcpt->s, count)) DIE_NOMEM();
5045+    if (!stralloc_copys (&domain, rcpt->s + count + 1)) DIE_NOMEM();
5046+  }
5047+  else {
5048+    if (!stralloc_copys (&user, rcpt->s)) DIE_NOMEM();
5049+    domain.len = 0;
5050+  }
5051+  if (!stralloc_0 (&user)) DIE_NOMEM();
5052+  if (!stralloc_0 (&domain)) DIE_NOMEM();
5053+
5054+#if defined CHKUSER_ENABLE_UIDGID
5055+
5056+/* qmail-smtpd is running now as (effective) qmaild:nofiles */
5057+/* Save the effective UID & GID (qmaild:nofiles) */
5058+  eff_uid = geteuid ();
5059+  eff_gid = getegid ();
5060+
5061+/* Now set new effective UID & GID, getting it from real UID & GID (vpopmail:vchkpw) */
5062+  setegid (getgid());
5063+  seteuid (getuid());
5064+
5065+/* qmail-smtpd is running now as effective vpopmail:vchkpw */
5066+#endif
5067+
5068+
5069+/*
5070+ *
5071+ * Now let's start the test/setting suite
5072+ *
5073+ **/
5074+
5075+       switch (0) {
5076+
5077+       case 0:
5078+/* These are some preliminary settings */
5079+               case_lowers (user.s);
5080+               case_lowers (domain.s);
5081+
5082+       case 1:
5083+
5084+                if (domain.len == 1) {
5085+#if defined CHKUSER_DOMAIN_WANTED
5086+                        retstat = CHKUSER_ERR_DOMAIN_MISSING;
5087+                       break;
5088+#else
5089+                        if (!stralloc_copys (&domain, DEFAULT_DOMAIN)) DIE_NOMEM();
5090+                       if (!stralloc_0 (&domain)) DIE_NOMEM();
5091+#endif
5092+                }
5093+
5094+       case 2:
5095+
5096+#if defined CHKUSER_RCPT_FORMAT
5097+                if (check_rcpt_address_format (&user, &domain) == 0) {
5098+                        retstat = CHKUSER_ERR_RCPT_FORMAT;
5099+                        break;
5100+                }
5101+#endif
5102+
5103+       case 3:
5104+
5105+                if (!addrallowed()) {
5106+
5107+#if defined CHKUSER_RCPT_MX
5108+                       switch (chkuser_mx_lookup(&domain)) {
5109+
5110+                               case DNS_HARD:
5111+                                       retstat = CHKUSER_ERR_RCPT_MX;
5112+                                       break;
5113+
5114+                               case DNS_SOFT:
5115+                                       retstat = CHKUSER_ERR_RCPT_MX_TMP;
5116+                                       break;
5117+                       }
5118+
5119+                       if (retstat != CHKUSER_KO) {
5120+                               break;
5121+                       }
5122+#endif
5123+                       if (relayclient) {
5124+                               retstat = CHKUSER_RELAYING;
5125+                               break;
5126+                       }
5127+
5128+                        retstat = CHKUSER_NORCPTHOSTS;
5129+                        break;
5130+                }
5131+
5132+       case 4:
5133+
5134+#if defined CHKUSER_ENABLE_VGET_REAL_DOMAIN
5135+/* Check if domain is a real domain */
5136+
5137+                vget_real_domain(domain.s, domain.a);
5138+
5139+                domain.len = strlen (domain.s) +1;
5140+                if (domain.len > (domain.a - 1)) DIE_NOMEM();
5141+#endif
5142+
5143+/* Let's get domain's real path */
5144+                if (vget_assign(domain.s, domain_path.s, domain_path.a -1, NULL, NULL) == NULL) {
5145+                       retstat = CHKUSER_OK;
5146+                       break;
5147+               }
5148+       
5149+               domain_path.len = strlen (domain_path.s);
5150+
5151+       case 5:
5152+
5153+/* Check if domain has bouncing enabled */
5154+
5155+               if (starting_value == 0) {
5156+
5157+                       if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
5158+
5159+#if defined CHKUSER_SPECIFIC_BOUNCING
5160+                       if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
5161+                       if (!stralloc_cats (&tmp_path, CHKUSER_SPECIFIC_BOUNCING)) DIE_NOMEM();
5162+                       if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5163+                       fd_file = open_read (tmp_path.s);       
5164+                       if (fd_file != -1) {
5165+                               close (fd_file);
5166+                       } else {
5167+                               retstat = CHKUSER_OK_NOCHECKDOMAIN;
5168+                               break;
5169+                       }
5170+#else
5171+                       if (!stralloc_cats (&tmp_path, "/.qmail-default")) DIE_NOMEM();
5172+                       if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5173+
5174+                       read_char = 0;
5175+                       fd_file = open_read (tmp_path.s);       
5176+                       if (fd_file != -1) {
5177+                               read_char = read (fd_file, read_buf, sizeof(read_buf) - 1);
5178+                               close (fd_file);
5179+                               if (read_char < 0) read_char = 0;
5180+                               }
5181+                       read_buf[read_char] = 0;
5182+
5183+                       if ( strstr(read_buf, CHKUSER_BOUNCE_STRING) == NULL ) {
5184+                               retstat = CHKUSER_OK_NOCHECKDOMAIN;
5185+                               break;
5186+                       }
5187+#endif
5188+               }
5189+
5190+
5191+        case 6:
5192+
5193+#if defined CHKUSER_ENABLE_VAUTH_OPEN_CALL
5194+                if (db_already_open != 1) {
5195+                        if (CHKUSER_VAUTH_OPEN_CALL () == 0) {
5196+                                db_already_open == 1;
5197+                        } else {
5198+                                retstat = CHKUSER_ERR_AUTH_RESOURCE;
5199+                               break;
5200+                        }
5201+                }
5202+#endif
5203+
5204+
5205+       case 7:
5206+#if defined VALIAS
5207+/* Check for aliases/forwards - valias*/
5208+
5209+               if (valias_select (user.s, domain.s) != NULL) {
5210+                       retstat = CHKUSER_OK;
5211+                       break;
5212+               }
5213+#endif
5214+
5215+       case 8:
5216+#if defined CHKUSER_ENABLE_ALIAS
5217+/* Check for aliases/forwards - .qmail.x files */
5218+
5219+               if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM();
5220+                /* Change all '.' in ':' before continuing on aliases */
5221+                for (count = 0; count < tmp_path.len; ++count)
5222+                       if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':';
5223+
5224+                if (!stralloc_copy (&alias_path, &domain_path)) DIE_NOMEM();
5225+                if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM();
5226+                if (!stralloc_cats (&alias_path, tmp_path.s)) DIE_NOMEM();
5227+                if (!stralloc_0 (&alias_path)) DIE_NOMEM();
5228+
5229+               fd_file = open_read (alias_path.s);
5230+               if (fd_file != -1) {
5231+                       close (fd_file);
5232+                       retstat = CHKUSER_OK;
5233+                       break;
5234+               }
5235+#endif
5236+
5237+       case 9:
5238+
5239+#if defined CHKUSER_ENABLE_ALIAS_DEFAULT
5240+
5241+               if (!stralloc_copy (&tmp_path, &user)) DIE_NOMEM();
5242+                /* Change all '.' in ':' before continuing on aliases */
5243+                for (count = 0; count < tmp_path.len; ++count)
5244+                       if (*(tmp_path.s + count) == '.') *(tmp_path.s + count) = ':';
5245+
5246+                /* Search for the outer '-' character */
5247+                for (offset = user.len - 1; offset > 0; --offset) {
5248+                        if (*(user.s + offset) == CHKUSER_USERS_DASH)  {
5249+                                if (!stralloc_copy (&alias_path, &domain_path)) die_nomem();
5250+                                if (!stralloc_cats (&alias_path, "/.qmail-")) die_nomem();
5251+                                if (!stralloc_catb (&alias_path, user.s, offset)) die_nomem();
5252+                                if (!stralloc_cats (&alias_path, "-default")) die_nomem();
5253+                                if (!stralloc_0 (&alias_path)) die_nomem();
5254+
5255+                                fd_file = open_read (alias_path.s);
5256+                                if (fd_file != -1) {
5257+                                        close (fd_file);
5258+                                        retstat = CHKUSER_OK;
5259+                                        break;
5260+                                }
5261+                        }
5262+               }
5263+               if (retstat != CHKUSER_KO) {
5264+                       break;
5265+                }
5266+
5267+#endif
5268+
5269+        case 10:
5270+#if defined CHKUSER_ENABLE_USERS
5271+/* User control: check the existance of a real user */
5272+
5273+                user_passwd = vauth_getpw (user.s, domain.s);
5274+
5275+#if defined CHKUSER_ENABLE_USERS_EXTENSIONS
5276+                if (user_passwd == NULL) {
5277+                       count = 0;
5278+                       while ((count < (user.len -1)) && (user_passwd == NULL)) {
5279+                               count += byte_chr(&user.s[count], user.len - count, CHKUSER_USERS_DASH);
5280+                               if (count < user.len) {
5281+                                       if (!stralloc_copyb (&tmp_path, user.s, count)) DIE_NOMEM();
5282+                                       if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5283+                                       user_passwd = vauth_getpw (tmp_path.s, domain.s);
5284+                                         ++count;
5285+                               }
5286+                        }
5287+                }
5288+
5289+#endif
5290+                if (user_passwd != NULL) {
5291+
5292+                /* If user exists check if he has BOUNCE_MAIL flag set */
5293+
5294+                        if (user_passwd->pw_gid & BOUNCE_MAIL)
5295+                                retstat = CHKUSER_KO;
5296+                        else {
5297+                                retstat = CHKUSER_OK;
5298+#if defined CHKUSER_MBXQUOTA_VARIABLE
5299+                                if ((maxmbxquota_limit > 0) && (strcasecmp(user_passwd->pw_shell, "NOQUOTA") != 0)) {
5300+                                        if (!stralloc_copys (&tmp_path, user_passwd->pw_dir)) DIE_NOMEM();
5301+                                        if (!stralloc_cats (&tmp_path, "/Maildir")) DIE_NOMEM();
5302+                                        if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5303+
5304+                                        if (vmaildir_readquota(tmp_path.s,format_maildirquota(user_passwd->pw_shell))
5305+                                                >= maxmbxquota_limit) {
5306+                                                retstat = CHKUSER_ERR_MBXFULL;
5307+                                        }
5308+                                }
5309+#endif
5310+                        }
5311+                        break;
5312+                }
5313+#endif
5314+
5315+       case 11:
5316+#if defined CHKUSER_ENABLE_EZMLM_LISTS
5317+/* Let's check for mailing lists */
5318+
5319+               /* Search for the outer CHKUSER_EZMLM_DASH character */
5320+               for (offset = user.len - 2; offset > 0; --offset) {
5321+                       if (*(user.s + offset) == CHKUSER_EZMLM_DASH)  {
5322+                               if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
5323+                               if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
5324+                               if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM();
5325+                               if (!stralloc_cats (&tmp_path, "/editor")) DIE_NOMEM();
5326+                               if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5327+                               fd_file = open_read (tmp_path.s);
5328+                               if (fd_file != -1) {
5329+                                       close (fd_file);
5330+                                       retstat = CHKUSER_OK;
5331+                                       break;
5332+                               }
5333+                       }
5334+               }
5335+               if (retstat != CHKUSER_KO) {
5336+                       break;
5337+               }
5338+#endif
5339+
5340+        case 12:
5341+#if defined CHKUSER_ENABLE_MAILMAN_LISTS
5342+/* Let's check for mailing lists */
5343+
5344+                /* Search for the outer CHKUSER_MAILMAN_DASH character */
5345+                for (offset = user.len - 2; offset > 0; --offset) {
5346+                        if (*(user.s + offset) == CHKUSER_MAILMAN_DASH)  {
5347+                                if (!stralloc_copy (&tmp_path, &domain_path)) DIE_NOMEM();
5348+                                if (!stralloc_cats (&tmp_path, "/")) DIE_NOMEM();
5349+                               if (!stralloc_cats (&alias_path, "/.qmail-")) DIE_NOMEM();
5350+                                if (!stralloc_catb (&tmp_path, user.s, offset)) DIE_NOMEM();
5351+                                if (!stralloc_0 (&tmp_path)) DIE_NOMEM();
5352+                                fd_file = open_read (tmp_path.s);
5353+                               read_char = 0;
5354+                               if (fd_file != -1) {
5355+                                       read_char = read (fd_file, read_buf, sizeof(read_buf) - 1);
5356+                                       close (fd_file);
5357+                                       if (read_char < 0) read_char = 0;
5358+                                }
5359+                               read_buf[read_char] = 0;
5360+
5361+                               if ( strstr(read_buf, CHKUSER_MAILMAN_STRING) == NULL ) {
5362+                                       retstat = CHKUSER_OK;
5363+                                       break;
5364+                               }
5365+
5366+                        }
5367+                }
5368+                if (retstat != CHKUSER_KO) {
5369+                        break;
5370+                }
5371+#endif
5372+
5373+/*
5374+ * Add this code if another case is following
5375+       case xx:
5376+               code ....
5377+               code ....
5378+               code ....
5379+               code ....
5380+
5381+               if (xxxxxxxx) {
5382+                       retstat != CHKUSER_KO)
5383+                       break;
5384+               }
5385+*/
5386+           
5387+        default:
5388+                retstat = CHKUSER_KO;
5389+
5390+       } /* end switch */
5391+
5392+#if defined CHKUSER_ENABLE_UIDGID
5393+/* Now switch back effective to saved UID & GID (qmaild:nofiles) */
5394+
5395+  setegid (eff_gid);
5396+  seteuid (eff_uid);
5397+
5398+/* qmail-smtpd is running again as (effective) qmaild:nofiles */
5399+#endif
5400+
5401+  return retstat;
5402+
5403+}
5404+
5405+
5406+
5407+/*
5408+ * chkuser_realrcpt ()
5409+ *
5410+ * Returns a simple status:
5411+ *
5412+ *      CHKUSER_OK = 1 = Ok, recipients does exists
5413+ *
5414+ *      CHKUSER_NORCPTHOSTS = Not in rcpthosts
5415+ *
5416+ *      CHKUSER_KO = ERROR
5417+ *
5418+ *
5419+ * Parameters:
5420+ *      stralloc *sender = sender address
5421+ *      stralloc *rcpt = rcpt address to check
5422+ *
5423+ *
5424+*/
5425+
5426+int chkuser_realrcpt (stralloc *sender, stralloc *rcpt) {
5427+
5428+int retstat;
5429+
5430+  if (first_time_init_flag) {
5431+        first_time_init ();
5432+  }
5433+
5434+
5435+  retstat = realrcpt (sender, rcpt);
5436+
5437+       switch (retstat) {
5438+
5439+               case CHKUSER_OK:
5440+#if defined CHKUSER_LOG_VALID_RCPT
5441+                       chkuser_commonlog (sender->s, rcpt->s, "accepted rcpt", "found existing recipient");
5442+#endif
5443+                       return CHKUSER_OK;
5444+                       break;
5445+
5446+               case CHKUSER_OK_NOCHECKALL:
5447+#if defined CHKUSER_LOG_VALID_RCPT
5448+                        chkuser_commonlog (sender->s, rcpt->s, "accepted any rcpt", "accepted any recipient for any rcpt domain");
5449+#endif
5450+                        return CHKUSER_OK;
5451+                        break;
5452+
5453+                case CHKUSER_OK_NOCHECKDOMAIN:
5454+#if defined CHKUSER_LOG_VALID_RCPT
5455+                        chkuser_commonlog (sender->s, rcpt->s, "accepted any rcpt", "accepted any recipient for this domain");
5456+#endif
5457+                        return CHKUSER_OK;
5458+                        break;
5459+
5460+                case CHKUSER_RELAYING:
5461+#if defined CHKUSER_LOG_VALID_RCPT
5462+                        chkuser_commonlog (sender->s, rcpt->s, "relaying rcpt", "client allowed to relay");
5463+#endif
5464+                        return CHKUSER_RELAYING;
5465+                        break;
5466+
5467+               case CHKUSER_NORCPTHOSTS:
5468+                        chkuser_commonlog (sender->s, rcpt->s, "rejected relaying", "client not allowed to relay");
5469+                       CHKUSER_RCPT_DELAY_ANY();
5470+                       out(CHKUSER_NORELAY_STRING);
5471+                       break;
5472+
5473+               case CHKUSER_KO:
5474+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "not existing recipient");
5475+                       CHKUSER_DELAY();
5476+                       out(CHKUSER_NORCPT_STRING);
5477+                       break;
5478+
5479+               case CHKUSER_ERR_AUTH_RESOURCE:
5480+                       chkuser_commonlog (sender->s, rcpt->s, "no auth resource", "no auth resource available");
5481+                       CHKUSER_RCPT_DELAY_ANY();
5482+                       out(CHKUSER_RESOURCE_STRING);
5483+                       break;
5484+
5485+                case CHKUSER_ERR_MUSTAUTH:
5486+                        chkuser_commonlog (sender->s, rcpt->s, "must auth", "sender not authenticated/authorized");
5487+                        CHKUSER_RCPT_DELAY_ANY();
5488+                        out(CHKUSER_MUSTAUTH_STRING);
5489+                        break;
5490+
5491+               case CHKUSER_ERR_MBXFULL:
5492+                       chkuser_commonlog (sender->s, rcpt->s, "mbx overquota", "rcpt mailbox is overquota");
5493+                       CHKUSER_RCPT_DELAY_ANY();
5494+                       out(CHKUSER_MBXFULL_STRING);
5495+                       break;
5496+
5497+               case CHKUSER_ERR_MAXRCPT:
5498+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of recipients");
5499+                       CHKUSER_DELAY ();
5500+                       out(CHKUSER_MAXRCPT_STRING);
5501+                       break;
5502+
5503+               case CHKUSER_ERR_MAXWRONGRCPT:
5504+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "max number of invalid recipients");
5505+                       CHKUSER_DELAY ();
5506+                       out(CHKUSER_MAXWRONGRCPT_STRING);
5507+                       break;
5508+
5509+               case CHKUSER_ERR_INTRUSION_THRESHOLD:
5510+                       chkuser_commonlog (sender->s, rcpt->s, "rejected intrusion", "rcpt ignored, session over intrusion threshold");
5511+                       CHKUSER_DELAY ();
5512+                       out(CHKUSER_INTRUSIONTHRESHOLD_STRING);
5513+                       break;
5514+
5515+               case CHKUSER_ERR_DOMAIN_MISSING:
5516+                       CHKUSER_DELAY ();
5517+                       out(CHKUSER_DOMAINMISSING_STRING);
5518+                       break;
5519+
5520+                case CHKUSER_ERR_RCPT_FORMAT:
5521+                        chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt address format");
5522+                       CHKUSER_RCPT_DELAY_ANY();
5523+                       out(CHKUSER_RCPTFORMAT_STRING);
5524+                        break;
5525+
5526+                case CHKUSER_ERR_RCPT_MX:
5527+                       chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "invalid rcpt MX domain");
5528+                       CHKUSER_RCPT_DELAY_ANY();
5529+                       out(CHKUSER_RCPTMX_STRING);
5530+                        break;
5531+
5532+                case CHKUSER_ERR_RCPT_MX_TMP:
5533+                        chkuser_commonlog (sender->s, rcpt->s, "rejected rcpt", "temporary DNS problem");
5534+                        CHKUSER_RCPT_DELAY_ANY();
5535+                        out(CHKUSER_RCPTMX_TMP_STRING);
5536+                        break;
5537+       }
5538+
5539+
5540+
5541+#if defined CHKUSER_WRONGRCPT_LIMIT_VARIABLE
5542+       if ((retstat == CHKUSER_KO) || (retstat == CHKUSER_ERR_DOMAIN_MISSING)) {
5543+               ++wrong_recipients;
5544+               if ((intrusion_threshold_reached == 0) && (maxwrongrcpt_limit > 0) && (wrong_recipients >= maxwrongrcpt_limit)) {
5545+                       chkuser_commonlog (sender->s, rcpt->s, "intrusion threshold", "max number of allowed invalid rcpt");
5546+                       intrusion_threshold_reached = 1;
5547+               }
5548+       }
5549+#endif
5550+
5551+       return retstat;
5552+}
5553+
5554+
5555+/*
5556+ *
5557+ * This routine checks for sender format and MX
5558+ *
5559+ */
5560+
5561+
5562+int chkuser_sender (stralloc *sender) {
5563+
5564+int count;
5565+
5566+       if (first_time_init_flag) {
5567+               first_time_init ();
5568+       }
5569+
5570+#if defined CHKUSER_EXTRA_MUSTAUTH_VARIABLE
5571+       if (mustauth_value == 1) {
5572+               out(CHKUSER_MUSTAUTH_STRING);
5573+#if defined CHKUSER_LOG_VALID_SENDER
5574+                        chkuser_commonlog (sender->s, "", "must auth", "sender not authenticated/authorized");
5575+                        CHKUSER_SENDER_DELAY_ANY();
5576+#endif
5577+               return CHKUSER_ERR_MUSTAUTH;
5578+       }
5579+#endif
5580+
5581+        if (sender->len <= 1) {
5582+#if defined CHKUSER_LOG_VALID_SENDER
5583+                chkuser_commonlog (sender->s, "", "accepted sender", "accepted null sender always");
5584+#endif
5585+                return CHKUSER_OK;
5586+        }
5587+
5588+       if ((starting_value == -1) || (sender_nocheck == 1)) {
5589+#if defined CHKUSER_LOG_VALID_SENDER
5590+                        chkuser_commonlog (sender->s, "", "accepted sender", "accepted any sender always");
5591+#endif
5592+               return CHKUSER_OK;
5593+       }
5594+
5595+#if defined CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE
5596+       if ((enable_doublebounce) && str_equal(sender->s,"#@[]")) {
5597+#if defined CHKUSER_LOG_VALID_SENDER
5598+                chkuser_commonlog (sender->s, "", "accepted doublebounce", "accepted qmail doublebounce #@[]");
5599+#endif
5600+                return CHKUSER_OK;
5601+       }
5602+#endif
5603+
5604+#if defined CHKUSER_SENDER_FORMAT || defined CHKUSER_SENDER_MX
5605+        count = byte_rchr(sender->s,sender->len,'@');
5606+        if (count < sender->len) {
5607+                if (!stralloc_copyb (&sender_user, sender->s, count)) DIE_NOMEM();
5608+                if (!stralloc_copys (&sender_domain, sender->s + count + 1)) DIE_NOMEM();
5609+        } else {
5610+                if (!stralloc_copys (&sender_user, sender->s)) DIE_NOMEM();
5611+                sender_domain.len = 0;
5612+        }
5613+        if (!stralloc_0 (&sender_user)) DIE_NOMEM();
5614+        if (!stralloc_0 (&sender_domain)) DIE_NOMEM();
5615+
5616+#if defined CHKUSER_SENDER_FORMAT
5617+        if (check_sender_address_format (&sender_user, &sender_domain) == 0) {
5618+                chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender address format");
5619+               CHKUSER_SENDER_DELAY_ANY();
5620+               out(CHKUSER_SENDERFORMAT_STRING);
5621+               return CHKUSER_ERR_SENDER_FORMAT;
5622+        }
5623+
5624+#endif
5625+
5626+#if defined CHKUSER_SENDER_MX
5627+
5628+       switch (chkuser_mx_lookup(&sender_domain)) {
5629+
5630+               case DNS_HARD:
5631+                       CHKUSER_SENDER_DELAY_ANY();
5632+                       out(CHKUSER_SENDERMX_STRING);
5633+                       chkuser_commonlog (sender->s, "", "rejected sender", "invalid sender MX domain");
5634+                       return CHKUSER_ERR_SENDER_MX;
5635+                       break;
5636+
5637+               case DNS_SOFT:
5638+                       CHKUSER_SENDER_DELAY_ANY();
5639+                       out(CHKUSER_SENDERMX_TMP_STRING);
5640+                       chkuser_commonlog (sender->s, "", "rejected sender", "temporary DNS problem");
5641+                       return CHKUSER_ERR_SENDER_MX_TMP;
5642+                       break;
5643+       }
5644+
5645+#endif
5646+#endif
5647+
5648+#if defined CHKUSER_LOG_VALID_SENDER
5649+                        chkuser_commonlog (sender->s, "", "accepted sender", "sender accepted");
5650+#endif
5651+
5652+       return CHKUSER_OK;
5653+
5654+}
5655+
5656+
5657diff -ruN ../netqmail-1.06-original/chkuser.h netqmail-1.06/chkuser.h
5658--- ../netqmail-1.06-original/chkuser.h 1970-01-01 01:00:00.000000000 +0100
5659+++ netqmail-1.06/chkuser.h     2016-11-22 21:03:57.106528299 +0100
5660@@ -0,0 +1,55 @@
5661+
5662+/*
5663+ *
5664+ * 'chkuser.h' v.2.0.9
5665+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
5666+ *
5667+ * Author: Antonio Nati tonix@interazioni.it
5668+ * All rights on this software and
5669+ * the identifying words chkusr and chkuser reserved by the author
5670+ *
5671+ * This software may be freely used, modified and distributed,
5672+ * but this lines must be kept in every original or derived version.
5673+ * Original author "Antonio Nati" and the web URL
5674+ * "http://www.interazioni.it/opensource"
5675+ * must be indicated in every related work or web page
5676+ *
5677+ */
5678+
5679+#define CHKUSER
5680+#define CHKUSER_VERSION                "2.0.9"
5681+#define CHKUSER_VERSION_RL     2
5682+#define CHKUSER_VERSION_MJ     0
5683+#define CHKUSER_VERSION_MN     9
5684+
5685+#define CHKUSER_OK_NOCHECKALL          11
5686+#define CHKUSER_OK_NOCHECKDOMAIN       10
5687+#define CHKUSER_OK                     1
5688+#define CHKUSER_RELAYING               0
5689+#define CHKUSER_KO                     -1
5690+#define CHKUSER_NORCPTHOSTS            -10
5691+#define CHKUSER_ERR_MUSTAUTH           -15
5692+#define CHKUSER_ERR_AUTH_RESOURCE      -20
5693+#define CHKUSER_ERR_MBXFULL            -30
5694+#define CHKUSER_ERR_MAXRCPT            -40
5695+#define CHKUSER_ERR_MAXWRONGRCPT       -50
5696+#define CHKUSER_ERR_DOMAIN_MISSING     -60
5697+#define CHKUSER_ERR_RCPT_FORMAT                -70
5698+#define CHKUSER_ERR_RCPT_MX            -75
5699+#define CHKUSER_ERR_RCPT_MX_TMP                -76
5700+#define CHKUSER_ERR_SENDER_FORMAT      -80
5701+#define CHKUSER_ERR_SENDER_MX          -85
5702+#define CHKUSER_ERR_SENDER_MX_TMP      -86
5703+#define CHKUSER_ERR_INTRUSION_THRESHOLD        -90
5704+
5705+
5706+void chkuser_cleanup (int exit_value);
5707+int chkuser_realrcpt (stralloc *sender, stralloc *rcpt);
5708+int chkuser_sender (stralloc *sender);
5709+
5710+#ifdef TLS_H
5711+#undef _exit
5712+#define _exit(value) { if (ssl) ssl_free(ssl); chkuser_cleanup(value); }
5713+#else
5714+#define _exit(value) chkuser_cleanup(value);
5715+#endif
5716diff -ruN ../netqmail-1.06-original/chkuser_settings.h netqmail-1.06/chkuser_settings.h
5717--- ../netqmail-1.06-original/chkuser_settings.h        1970-01-01 01:00:00.000000000 +0100
5718+++ netqmail-1.06/chkuser_settings.h    2016-11-28 17:35:36.567382036 +0100
5719@@ -0,0 +1,468 @@
5720+/*
5721+ *
5722+ * 'chkuser_settings.h' v.2.0.9
5723+ * for qmail/netqmail > 1.0.3 and vpopmail > 5.3.x
5724+ *
5725+ * Author: Antonio Nati tonix@interazioni.it
5726+ * All rights on this software and
5727+ * the identifying words chkusr and chkuser reserved by the author
5728+ *
5729+ * This software may be freely used, modified and distributed,
5730+ * but this lines must be kept in every original or derived version.
5731+ * Original author "Antonio Nati" and the web URL
5732+ * "http://www.interazioni.it/opensource"
5733+ * must be indicated in every related work or web page
5734+ *
5735+ */
5736+
5737+/*
5738+ * the following line enables debugging of chkuser
5739+ */
5740+#define CHKUSER_DEBUG
5741+
5742+/*
5743+ * The following line moves DEBUG output from STDOUT (default) to STDERR
5744+ * Example of usage within sh: ./qmail-smtpd 2> /var/log/smtpd-debug.log
5745+ */
5746+#define CHKUSER_DEBUG_STDERR
5747+
5748+/*
5749+ * Uncomment the following define if you want chkuser ALWAYS enabled.
5750+ * If uncommented, it will check for rcpt existance despite any .qmail-default
5751+ * setting.
5752+ * So, unsomments this if you are aware that ALL rcpt in all domains will be
5753+ * ALWAYS checked.
5754+ */
5755+/* #define CHKUSER_ALWAYS_ON */
5756+
5757+/*
5758+ * The following defines which virtual manager is used.
5759+ * Up to know, only vpopmail, but versions with pure qmail are in the mind.
5760+ */
5761+#define CHKUSER_VPOPMAIL
5762+
5763+/*
5764+ * Uncomment the following line if you want chkuser to work depending on a VARIABLE setting
5765+ * VALUE HERE DEFINED is the name of the variable
5766+ * Values admitted inside the variable: NONE | ALWAYS | DOMAIN
5767+ *             NONE    = chkuser will not work
5768+ *             ALWAYS  = chkuser will work always
5769+ *             DOMAIN  = chkuser will work depending by single domain settings
5770+ * CHKUSER_STARTING_VARIABLE cannot be defined together with CHKUSER_ALWAYS_ON
5771+ * if CHKUSER_STARTING_VARIABLE is defined, and no variable or no value is set, then chkuser is disabled
5772+ */
5773+#define CHKUSER_STARTING_VARIABLE "CHKUSER_START"
5774+
5775+/*
5776+ * Uncomment this to enable uid/gid changing
5777+ * (switching UID/GID is NOT compatible with TLS; you may keep this commented if you have TLS)
5778+ */
5779+/* #define CHKUSER_ENABLE_UIDGID */
5780+
5781+/*
5782+ * Uncomment this to check if a domain is ALWAYS specified in rcpt addresses
5783+ */
5784+#define CHKUSER_DOMAIN_WANTED
5785+
5786+/*
5787+ * Uncomment this to check for vpopmail users
5788+ */
5789+#define CHKUSER_ENABLE_USERS
5790+
5791+/*
5792+ * Uncomment this to check for alias
5793+ */
5794+#define CHKUSER_ENABLE_ALIAS
5795+
5796+/*
5797+ * The following #define set the character used for lists extensions
5798+ * be careful: this is a  single char '-' definition, not a "string"
5799+ */
5800+#define CHKUSER_EZMLM_DASH '-'
5801+
5802+/*
5803+ * Uncomment this to set an alternative way to check for bouncing enabling;
5804+ * with this option enabled, the file here defined
5805+ * will be searched, inside the domain dir, in order to check if bouncing is enabled
5806+ * The content of this file is not important, just it's existence is enough
5807+ */
5808+/* #define CHKUSER_SPECIFIC_BOUNCING ".qmailchkuser-bouncing" */
5809+
5810+/*
5811+ * This is the string to look for inside .qmail-default
5812+ * Be careful, chkuser looks within the first 1023 characters of .qmail-default for
5813+ * this string (despite the line containing the string is working or commented).
5814+ */
5815+#define CHKUSER_BOUNCE_STRING "bounce-no-mailbox"
5816+
5817+
5818+/*
5819+ * Uncomment to enable logging of rejected recipients and variuos limits reached
5820+ */
5821+#define CHKUSER_ENABLE_LOGGING
5822+
5823+/*
5824+ * Uncomment to enable logging of "good" rcpts
5825+ * valid only if CHKUSER_ENABLE_LOGGING is defined
5826+ */
5827+#define CHKUSER_LOG_VALID_RCPT
5828+
5829+/*
5830+ * Uncomment to enable usage of a variable escluding any check on the sender.
5831+ * The variable should be set in tcp.smtp for clients, with static IP, whose mailer
5832+ * is composing bad sender addresses
5833+ * Defining it as "RELAYCLIENT" will avoid sender checking for authenticated/authorized users.
5834+ *     Senders will be logged anyway if CHKUSER_LOG_VALID_SENDER is defined.
5835+ */
5836+#define CHKUSER_SENDER_NOCHECK_VARIABLE "RELAYCLIENT"
5837+
5838+/*
5839+ * Uncomment to enable usage of "#" and "+" characters within sender address
5840+ * This is used by SRS (Sender Rewriting Scheme) products
5841+ */
5842+#define CHKUSER_ALLOW_SENDER_SRS
5843+
5844+/*
5845+ * The following #define sets the minimum length of a domain:
5846+ * as far as I know, "k.st" is the shortest domain, so 4 characters is the
5847+ * minimum length.
5848+ * This value is used to check formally a domain name validity.
5849+ * if CHKUSER_SENDER_FORMAT is undefined, no check on length is done.
5850+ * If you comment this define, no check on length is done.
5851+ */
5852+#define CHKUSER_MIN_DOMAIN_LEN 4
5853+
5854+/*
5855+ * Uncomment to enable logging of "good" senders
5856+ * valid only if CHKUSER_ENABLE_LOGGING is defined
5857+ */
5858+#define CHKUSER_LOG_VALID_SENDER
5859+
5860+/*
5861+ * Uncomment to define a variable which contains the max recipients number
5862+ * this will return always error if total recipients exceed this limit.
5863+ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE,
5864+ * makes chkuser rejecting everything else
5865+ */
5866+#define CHKUSER_RCPT_LIMIT_VARIABLE "CHKUSER_RCPTLIMIT"
5867+
5868+/*
5869+ * Uncomment to define a variable which contains the max unknown recipients number
5870+ * this will return always error if not existing recipients exceed this limit.
5871+ * The first reached, between CHKUSER_RCPT_LIMIT_VARIABLE and CHKUSER_WRONGRCPT_LIMIT_VARIABLE,
5872+ * makes chkuser rejecting everything else
5873+ */
5874+#define CHKUSER_WRONGRCPT_LIMIT_VARIABLE "CHKUSER_WRONGRCPTLIMIT"
5875+
5876+/*
5877+ * Uncomment to define the variable containing the percent to check for.
5878+ * Remember to define externally (i.e. in tcp.smtp) the environment variable containing
5879+ * the limit percent.
5880+ * If the variable is not defined, or it is <= 0, quota checking is not performed.
5881+ */
5882+#define CHKUSER_MBXQUOTA_VARIABLE "CHKUSER_MBXQUOTA"
5883+
5884+/*
5885+ * Delay to wait for each not existing recipient
5886+ * value is expressed in milliseconds
5887+ */
5888+#define CHKUSER_ERROR_DELAY 1000
5889+
5890+/*
5891+ * Uncomment to consider rcpt errors on address format and MX as intrusive
5892+ *
5893+ */
5894+#define CHKUSER_RCPT_DELAY_ANYERROR
5895+
5896+/*
5897+ * Uncomment to consider sender errors on address format and MX as intrusive
5898+ *
5899+ */
5900+#define CHKUSER_SENDER_DELAY_ANYERROR
5901+
5902+
5903+/***************************************************
5904+ *
5905+ *      new/modified defines in/from 2.0.6
5906+ *
5907+ **************************************************/
5908+
5909+/*
5910+ * Before version 5.3.25, vpopmail used the function vget_real_domain()
5911+ * to get the real name of a domain (useful if rcpt domain is aliasing
5912+ * another domain).
5913+ * From version 5.3.25, this call is not available and has been
5914+ * substituted by other calls.
5915+ *
5916+ *        must be enabled if vpopmail version< 5.3.5
5917+ *        must be disabled  if vpopmail version => 5.3.5 *
5918+ */
5919+/* #define CHKUSER_ENABLE_VGET_REAL_DOMAIN */
5920+
5921+/***************************************************
5922+ *
5923+ *      new/modified defines in/from 2.0.7
5924+ *
5925+ **************************************************/
5926+
5927+/*
5928+ * Uncomment next define to accept recipients for
5929+ * aliases that have a -default extension
5930+ */
5931+/* #define CHKUSER_ENABLE_ALIAS_DEFAULT */
5932+
5933+
5934+/*
5935+ * Uncomment to enable usage of "#" and "+" characters within rcpt address
5936+ * This is used by SRS (Sender Rewriting Scheme) products
5937+ */
5938+#define CHKUSER_ALLOW_RCPT_SRS
5939+
5940+/*
5941+ * This define has been eliminated and its usage will generate an error.
5942+ * Turning it ON or OFF has no effect, as we consider the existence
5943+ * of #define VALIAS inside ~vpopmail/include/vpopmail_config.h
5944+ */
5945+/* #define CHKUSER_ENABLE_VALIAS */
5946+
5947+/*
5948+ * Uncomment this to enable user extension on names (i.e. TMDA)
5949+ * (for mailing lists this is done without checking this define)
5950+ * This define substitutes #define CHKUSER_ENABLE_EXTENSIONS
5951+ */
5952+/* #define CHKUSER_ENABLE_USERS_EXTENSIONS */
5953+
5954+/*
5955+ * Enables checking for EZMLM lists
5956+ * this define substitutes #define CHKUSER_ENABLE_LISTS
5957+ *
5958+ */
5959+#define CHKUSER_ENABLE_EZMLM_LISTS
5960+
5961+/*
5962+ * Help identifying remote authorized IPs giving them a descriptive name
5963+ * Can be put in tcp.smtp, and will be displayed inside chkuser log
5964+ */
5965+#define CHKUSER_IDENTIFY_REMOTE_VARIABLE "CHKUSER_IDENTIFY"
5966+
5967+/*
5968+ * The following #define set the character used for users extensions
5969+ * be careful: this is a  single char '-' definition, not a "string"
5970+ * this define substitutes #define CHKUSER_EXTENSION_DASH
5971+ * MUST be defined if CHKUSER_ENABLE_USERS_EXTENSIONS is defined
5972+ */
5973+#define CHKUSER_USERS_DASH '-'
5974+
5975+/*
5976+ * Enables checking for mailman lists
5977+ *
5978+ */
5979+/* #define CHKUSER_ENABLE_MAILMAN_LISTS */
5980+
5981+/*
5982+ * Identifies the pattern string to be searched within mailman aliases
5983+ *
5984+ */
5985+#define CHKUSER_MAILMAN_STRING "mailman"
5986+
5987+/*
5988+ * The following #define set the character used for mailman lists extensions
5989+ * be careful: this is a  single char '-' definition, not a "string"
5990+ */
5991+#define CHKUSER_MAILMAN_DASH '-'
5992+
5993+
5994+/*
5995+ * Enables final clean-up routine of chkuser
5996+ * This routine cleans open DB connections used for checking users and valiases
5997+ */
5998+#define CHKUSER_DB_CLEANUP
5999+
6000+/***************************************************
6001+ *
6002+ *      new/modified defines in/from 2.0.8
6003+ *
6004+ **************************************************/
6005+
6006+/*
6007+ * The following defines are NO MORE used. NULL SENDER rejecting breaks RFC
6008+ * compatibility, and makes harder to handle e-mail receipts.
6009+ * Please comment or delete them from your chkuser_settings.h.
6010+ */
6011+/* #define CHKUSER_ACCEPT_NULL_SENDER */
6012+/* #define CHKUSER_ENABLE_NULL_SENDER_WITH_TCPREMOTEHOST */
6013+
6014+/*
6015+ * Uncomment to enable checking of user and domain format for rcpt addresses
6016+ *      user    =       [a-z0-9_-]
6017+ *      domain  =       [a-z0-9-.] with not consecutive "-.", not leading or ending "-."
6018+ */
6019+#define CHKUSER_RCPT_FORMAT
6020+
6021+/*
6022+ * Uncomment to enable checking of domain MX for rcpt addresses
6023+ * It works on any rcpt address domain that is not inside rcpthosts
6024+ */
6025+#define CHKUSER_RCPT_MX
6026+
6027+/*
6028+ * Uncomment to enable checking of user and domain format for sender address
6029+ *      user    =       [a-z0-9_-]
6030+ *      domain  =       [a-z0-9-.] with not consecutive "-.", not leading or ending "-."
6031+ */
6032+#define CHKUSER_SENDER_FORMAT
6033+
6034+/*
6035+ * Uncomment to enable checking of domain MX for sender address
6036+ * it works on the first rcpt address, despite of any domain setting on chkuser
6037+ */
6038+#define CHKUSER_SENDER_MX
6039+
6040+/*
6041+ * Delay to add, for each not existing recipient, to the initial CHKUSER_ERROR_DELAY value
6042+ * value is expressed in milliseconds
6043+ */
6044+#define CHKUSER_ERROR_DELAY_INCREASE 300
6045+
6046+/***************************************************
6047+ *
6048+ *      new/modified defines in/from 2.0.9
6049+ *
6050+ **************************************************/
6051+
6052+/*
6053+ * A new class of defines is introduced
6054+ *     CHKUSER_EXTRA_xxxxx
6055+ *
6056+ *     These defines will be used for features/behaviours that may work despite of other CHKUSER enable/disable settings
6057+ *
6058+ */
6059+
6060+/*
6061+ * If you want to accept only authenticated/authorized users you MUST enable this define and set the related variable.
6062+ *
6063+ * if this define is uncommented and the variable is set (to whatever value) then RELAYCLIENT must be set
6064+ *      otherwise any message will be rejected giving "not authorized" error.
6065+ *
6066+ */
6067+/* #define CHKUSER_EXTRA_MUSTAUTH_VARIABLE "CHKUSER_MUSTAUTH" */
6068+
6069+
6070+/*
6071+ * This is to check DB availability
6072+ * It avoids bouncing messages with wrong codes if MySQL/LDAP/PostGRES/etc are down or not reachable
6073+ *
6074+ * If you are using MySQL in normal installation use #define CHKUSER_VAUTH_OPEN_CALL vauth_open_update
6075+ * If you are using MySQL with separate servers for read and write use #define CHKUSER_VAUTH_OPEN_CALL vauth_open
6076+ * If you are using other DB, check the most appropriate function for your DB within dedicated vpopmail module
6077+ *
6078+ * This define substitutes CHKUSER_ENABLE_VAUTH_OPEN
6079+ */
6080+
6081+/* #define CHKUSER_VAUTH_OPEN_CALL vauth_open   */
6082+/* #define CHKUSER_VAUTH_OPEN_CALL vauth_open_update */
6083+
6084+/*
6085+ * Variable to be set in order to disable chkuser
6086+ * You may set it to any value you like. If it exists chkuser will be disabled.
6087+ *     Setting it to RELAYCLIENT helps disabling chkuser when sender is a known/authenticated mail client
6088+ *     This is useful because Outlook/Eudora and other clients are not able to handle a KO when multiple recipients
6089+ *             are present in the message. They should always relay to a SMTP service accepting all.
6090+ *
6091+ *     Recipients will be logged anyway if CHKUSER_LOG_VALID_RCPT is defined.
6092+ *
6093+ * Important changes from 2.0.9
6094+ *     CHKUSER_ALWAYS_ON and CHKUSER_STARTING_VARIABLE cannot be defined together and in such a case a fatal error is displayed
6095+ *     (in the previous versions CHKUSER_ALWAYS_ON would automatically disable CHKUSER_STARTING_VARIABLE definition)
6096+ *
6097+ *     CHKUSER_DISABLE_VARIABLE is always evaluated after CHKUSER_ALWAYS_ON is set or CHKUSER_STARTING_VARIABLE is evaluated, so
6098+ *             CHKUSER_ALWAYS_ON or CHKUSER_STARTING_VARIABLE can set the general behaviour, while CHKUSER_DISABLE_VARIABLE
6099+ *             should be invoked to handle exceptions.
6100+ *
6101+ */
6102+/* #define CHKUSER_DISABLE_VARIABLE "RELAYCLIENT" */
6103+
6104+
6105+/*
6106+ * Error strings (SMTP error answers)
6107+ * If you don't like these definitions you can change them here
6108+ *
6109+ */
6110+#define CHKUSER_NORCPT_STRING "550 5.1.1 sorry, no mailbox here by that name (chkuser)\r\n"
6111+#define CHKUSER_RESOURCE_STRING "451 4.3.0 system temporary unavailable, try again later (chkuser)\r\n"
6112+#define CHKUSER_MBXFULL_STRING "552 5.2.2 sorry, recipient mailbox is full (chkuser)\r\n"
6113+#define CHKUSER_MAXRCPT_STRING "550 5.5.3 sorry, reached maximum number of recipients allowed in one session (chkuser)\r\n"
6114+#define CHKUSER_MAXWRONGRCPT_STRING "550 5.5.3 sorry, you are violating our security policies (chkuser)\r\n"
6115+#define CHKUSER_DOMAINMISSING_STRING "550 5.1.2 sorry, you must specify a domain (chkuser)\r\n"
6116+#define CHKUSER_RCPTFORMAT_STRING "553 5.1.3 sorry, mailbox syntax not allowed (chkuser)\r\n"
6117+#define CHKUSER_RCPTMX_STRING "550 5.1.2 sorry, can't find a valid MX for rcpt domain (chkuser)\r\n"
6118+#define CHKUSER_SENDERFORMAT_STRING "553 5.1.7 sorry, mailbox syntax not allowed (chkuser)\r\n"
6119+#define CHKUSER_SENDERMX_STRING "550 5.1.8 sorry, can't find a valid MX for sender domain (chkuser)\r\n"
6120+#define CHKUSER_INTRUSIONTHRESHOLD_STRING "550 5.7.1 sorry, you are violating our security policies (chkuser)\r\n"
6121+#define CHKUSER_NORELAY_STRING "553 5.7.1 sorry, that domain isn't in my list of allowed rcpthosts (chkuser)\r\n"
6122+
6123+#define CHKUSER_RCPTMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n"
6124+#define CHKUSER_SENDERMX_TMP_STRING "451 4.4.0 DNS temporary failure (chkuser)\r\n"
6125+
6126+#define CHKUSER_MUSTAUTH_STRING "530 5.7.0 Authentication required (chkuser)\r\n"
6127+
6128+/*
6129+ * No more used defines
6130+ *     Following defines are eliminated since 2.0.9
6131+ *     They will make compilation errors and must be deleted/commented
6132+ *
6133+ *                     #define CHKUSER_ENABLE_VAUTH_OPEN -> Substituted by CHKUSER_VAUTH_OPEN_CALL
6134+ */
6135+
6136+
6137+/*
6138+ * If you need more additional characters to be accepted within sender address
6139+ * uncomment one of the following #define and edit the character value.
6140+ * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the
6141+ * wanted char.
6142+ *
6143+ * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_SENDER_SRS
6144+ *
6145+ */
6146+#define CHKUSER_ALLOW_SENDER_CHAR_1 "'"
6147+#define CHKUSER_ALLOW_SENDER_CHAR_2 '='
6148+/* #define CHKUSER_ALLOW_SENDER_CHAR_2 '%' */
6149+/* #define CHKUSER_ALLOW_SENDER_CHAR_3 '£' */
6150+/* #define CHKUSER_ALLOW_SENDER_CHAR_4 '?' */
6151+/* #define CHKUSER_ALLOW_SENDER_CHAR_5 '*' */
6152+/* #define CHKUSER_ALLOW_SENDER_CHAR_6 '^' */
6153+/* #define CHKUSER_ALLOW_SENDER_CHAR_7 '~' */
6154+/* #define CHKUSER_ALLOW_SENDER_CHAR_8 '&' */  /* available for other characters */
6155+/* #define CHKUSER_ALLOW_SENDER_CHAR_9 '#' */  /* available for other characters */
6156+/* #define CHKUSER_ALLOW_SENDER_CHAR_10 '=' */         /* available for other characters */
6157+
6158+
6159+/*
6160+ * If you need more additional characters to be accepted within rcpt address
6161+ * uncomment one of the following #define and edit the character value.
6162+ * Be careful to use '*' (single hiphen) and NOT "*" (double hiphen) around the
6163+ * wanted char.
6164+ *
6165+ * Remember: '#' and '+' are accepted by CHKUSER_ALLOW_RCPT_SRS
6166+ *
6167+ */
6168+/* #define CHKUSER_ALLOW_RCPT_CHAR_1 '$' */
6169+/* #define CHKUSER_ALLOW_RCPT_CHAR_2 '%' */
6170+/* #define CHKUSER_ALLOW_RCPT_CHAR_3 '£' */
6171+/* #define CHKUSER_ALLOW_RCPT_CHAR_4 '?' */
6172+/* #define CHKUSER_ALLOW_RCPT_CHAR_5 '*' */
6173+/* #define CHKUSER_ALLOW_RCPT_CHAR_6 '^' */
6174+/* #define CHKUSER_ALLOW_RCPT_CHAR_7 '~' */
6175+/* #define CHKUSER_ALLOW_RCPT_CHAR_8 '&' */    /* available for other characters */
6176+/* #define CHKUSER_ALLOW_RCPT_CHAR_9 '#' */    /* available for other characters */
6177+/* #define CHKUSER_ALLOW_RCPT_CHAR_10 '=' */   /* available for other characters */
6178+
6179+
6180+/*
6181+ * This define tells chkuser which variable must be set to accept a <#@[]> sender
6182+ * This kind of sender is usually generated from qmail when there is a doublebounce
6183+ * and all the job is done within the same system.
6184+ * You may need to accept double bounces from outside when you are migrating servers and
6185+ * doublebounces are forwarded between systems
6186+ */
6187+#define CHKUSER_ENABLE_DOUBLEBOUNCE_VARIABLE "CHKUSER_DOUBLEBOUNCE"
6188diff -ruN ../netqmail-1.06-original/condredirect.c netqmail-1.06/condredirect.c
6189--- ../netqmail-1.06-original/condredirect.c    1998-06-15 12:53:16.000000000 +0200
6190+++ netqmail-1.06/condredirect.c        2016-11-22 21:03:57.107528266 +0100
6191@@ -10,6 +10,8 @@
6192 #include "strerr.h"
6193 #include "substdio.h"
6194 #include "fmt.h"
6195+#include "stralloc.h"
6196+#include "srs.h"
6197 
6198 #define FATAL "condredirect: fatal: "
6199 
6200@@ -68,6 +70,16 @@
6201   dtline = env_get("DTLINE");
6202   if (!dtline) strerr_die2x(100,FATAL,"DTLINE not set");
6203 
6204+  if (str_len(sender)) {
6205+    switch(srsforward(sender)) {
6206+      case -3: strerr_die2x(100,FATAL,srs_error.s); break;
6207+      case -2: strerr_die2x(111,FATAL,"out of memory"); break;
6208+      case -1: strerr_die2x(111,FATAL,"unable to read controls"); break;
6209+      case 0: break; // nothing
6210+      case 1: sender = srs_result.s; break;
6211+    }
6212+  }
6213+
6214   if (qmail_open(&qqt) == -1)
6215     strerr_die2sys(111,FATAL,"unable to fork: ");
6216   qmail_puts(&qqt,dtline);
6217diff -ruN ../netqmail-1.06-original/conf-cc netqmail-1.06/conf-cc
6218--- ../netqmail-1.06-original/conf-cc   1998-06-15 12:53:16.000000000 +0200
6219+++ netqmail-1.06/conf-cc       2016-11-22 21:03:57.107528266 +0100
6220@@ -1,3 +1,3 @@
6221-cc -O2
6222+cc -O2 -g -DEXTERNAL_TODO -DTLS=20160918 -I/usr/local/ssl/include -I/home/vpopmail/include
6223 
6224 This will be used to compile .c files.
6225diff -ruN ../netqmail-1.06-original/conf-domainkeys netqmail-1.06/conf-domainkeys
6226--- ../netqmail-1.06-original/conf-domainkeys   1970-01-01 01:00:00.000000000 +0100
6227+++ netqmail-1.06/conf-domainkeys       2016-11-22 21:03:57.107528266 +0100
6228@@ -0,0 +1 @@
6229+-DDOMAIN_KEYS
6230diff -ruN ../netqmail-1.06-original/conf-ld netqmail-1.06/conf-ld
6231--- ../netqmail-1.06-original/conf-ld   1998-06-15 12:53:16.000000000 +0200
6232+++ netqmail-1.06/conf-ld       2016-11-22 21:03:57.107528266 +0100
6233@@ -1,3 +1,3 @@
6234-cc -s
6235+cc -g
6236 
6237 This will be used to link .o files into an executable.
6238diff -ruN ../netqmail-1.06-original/conf-policy netqmail-1.06/conf-policy
6239--- ../netqmail-1.06-original/conf-policy       1970-01-01 01:00:00.000000000 +0100
6240+++ netqmail-1.06/conf-policy   2016-11-22 21:03:57.107528266 +0100
6241@@ -0,0 +1,17 @@
6242+-DPOLICY_FILENAME="/var/qmail/control/policy" -DPOLICY_DEALLOCATE -DPOLICY_ENFORCE_AUTHENTICATION
6243+
6244+POLICY_FILENAME
6245+   Sets where the policy file is located
6246+
6247+POLICY_ENFORCE_AUTHENTICATION
6248+   Require that senders who use a local name for envelope
6249+   authenticate.  This is the recommended setting.
6250+
6251+POLICY_DEALLOCATE
6252+   For every MAIL FROM, RCPT TO combination, re-read all
6253+   policy information.  This makes policies more dynamic,
6254+   and stops remote users from causing lots of memory usage,
6255+   however, it also increases disk i/o, and slows down
6256+   policy enforcement.  If this is not defined, domain policies
6257+   will stay in memory until the SMTP session is ended.
6258+
6259diff -ruN ../netqmail-1.06-original/conf-spawn netqmail-1.06/conf-spawn
6260--- ../netqmail-1.06-original/conf-spawn        1998-06-15 12:53:16.000000000 +0200
6261+++ netqmail-1.06/conf-spawn    2016-11-22 21:03:57.108528232 +0100
6262@@ -1,4 +1,4 @@
6263-120
6264+1000
6265 
6266 This is a silent concurrency limit. You can't set it above 255. On some
6267 systems you can't set it above 125. qmail will refuse to compile if the
6268diff -ruN ../netqmail-1.06-original/config.h netqmail-1.06/config.h
6269--- ../netqmail-1.06-original/config.h  1970-01-01 01:00:00.000000000 +0100
6270+++ netqmail-1.06/config.h      2016-11-22 21:03:57.108528232 +0100
6271@@ -0,0 +1,10 @@
6272+/* config.h.  Generated from config.h.in by configure.  */
6273+/* config.h.in.  Generated from configure.in by autoheader.  */
6274+
6275+/* Define to 1 if you have the <arpa/nameser.h> header file. */
6276+
6277+/* Define to 1 if you have the <dlfcn.h> header file. */
6278+
6279+/* HAVE_EVP_SHA256 */
6280+#define HAVE_EVP_SHA256 1
6281+
6282diff -ruN ../netqmail-1.06-original/control.c netqmail-1.06/control.c
6283--- ../netqmail-1.06-original/control.c 1998-06-15 12:53:16.000000000 +0200
6284+++ netqmail-1.06/control.c     2016-11-22 21:03:57.108528232 +0100
6285@@ -85,6 +85,82 @@
6286  return 1;
6287 }
6288 
6289+int
6290+control_readulong(i, fn)
6291+       unsigned long  *i;
6292+       char           *fn;
6293+{
6294+       unsigned long   u;
6295+
6296+       switch (control_readline(&line, fn))
6297+       {
6298+       case 0:
6299+               return 0;
6300+       case -1:
6301+               return -1;
6302+       }
6303+       if (!stralloc_0(&line))
6304+               return -1;
6305+       if (!scan_ulong(line.s, &u))
6306+               return 0;
6307+       *i = u;
6308+       return 1;
6309+}
6310+
6311+/*
6312+ * read entire file in variable sa
6313+ * without any interpretation (e.g. comments)
6314+ * To be used in case a file contains '#' character
6315+ * in the first column (which control_readfile() will
6316+ * skip
6317+ */
6318+int
6319+control_readnativefile(sa, fn, mode)
6320+      stralloc       *sa;
6321+      char           *fn;
6322+      int             mode;
6323+{
6324+      substdio        ss;
6325+      int             fd, match;
6326+
6327+      if (!stralloc_copys(sa, ""))
6328+              return -1;
6329+      if ((fd = open_read(fn)) == -1)
6330+      {
6331+              if (errno == error_noent)
6332+                      return(0);
6333+              return -1;
6334+      }
6335+      substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf));
6336+      for (;;)
6337+      {
6338+              if (getln(&ss, &line, &match, '\n') == -1)
6339+                      break;
6340+              if (!match && !line.len)
6341+              {
6342+                      close(fd);
6343+                      return 1;
6344+              }
6345+              if (mode) /* for qmail-dk */
6346+              {
6347+                      striptrailingwhitespace(&line);
6348+                      if (!stralloc_0(&line))
6349+                              break;
6350+                      if (line.s[0] && !stralloc_cat(sa, &line))
6351+                              break;
6352+              } else
6353+              if (!stralloc_cat(sa, &line))
6354+                      break;
6355+              if (!match)
6356+              {
6357+                      close(fd);
6358+                      return 1;
6359+              }
6360+      }
6361+      close(fd);
6362+      return -1;
6363+}
6364+
6365 int control_readfile(sa,fn,flagme)
6366 stralloc *sa;
6367 char *fn;
6368diff -ruN ../netqmail-1.06-original/control.h netqmail-1.06/control.h
6369--- ../netqmail-1.06-original/control.h 1998-06-15 12:53:16.000000000 +0200
6370+++ netqmail-1.06/control.h     2016-11-22 21:03:57.108528232 +0100
6371@@ -3,8 +3,10 @@
6372 
6373 extern int control_init();
6374 extern int control_readline();
6375+extern int control_readulong();
6376 extern int control_rldef();
6377 extern int control_readint();
6378+extern int control_readnativefile();
6379 extern int control_readfile();
6380 
6381 #endif
6382diff -ruN ../netqmail-1.06-original/date822fmt.c netqmail-1.06/date822fmt.c
6383--- ../netqmail-1.06-original/date822fmt.c      1998-06-15 12:53:16.000000000 +0200
6384+++ netqmail-1.06/date822fmt.c  2016-11-22 21:03:57.108528232 +0100
6385@@ -1,3 +1,4 @@
6386+#include <time.h>
6387 #include "datetime.h"
6388 #include "fmt.h"
6389 #include "date822fmt.h"
6390@@ -12,18 +13,51 @@
6391 {
6392   unsigned int i;
6393   unsigned int len;
6394+  time_t now;
6395+  datetime_sec utc;
6396+  datetime_sec local;
6397+  struct tm *tm;
6398+  struct datetime new_dt;
6399+  int minutes;
6400+
6401+  utc = datetime_untai(dt);
6402+  now = (time_t)utc;
6403+  tm = localtime(&now);
6404+  new_dt.year = tm->tm_year;
6405+  new_dt.mon = tm->tm_mon;
6406+  new_dt.mday = tm->tm_mday;
6407+  new_dt.hour = tm->tm_hour;
6408+  new_dt.min = tm->tm_min;
6409+  new_dt.sec = tm->tm_sec;
6410+  local = datetime_untai(&new_dt);
6411+
6412   len = 0;
6413-  i = fmt_uint(s,dt->mday); len += i; if (s) s += i;
6414+  i = fmt_uint(s,new_dt.mday); len += i; if (s) s += i;
6415   i = fmt_str(s," "); len += i; if (s) s += i;
6416-  i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i;
6417+  i = fmt_str(s,montab[new_dt.mon]); len += i; if (s) s += i;
6418   i = fmt_str(s," "); len += i; if (s) s += i;
6419-  i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i;
6420+  i = fmt_uint(s,new_dt.year + 1900); len += i; if (s) s += i;
6421   i = fmt_str(s," "); len += i; if (s) s += i;
6422-  i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i;
6423+  i = fmt_uint0(s,new_dt.hour,2); len += i; if (s) s += i;
6424   i = fmt_str(s,":"); len += i; if (s) s += i;
6425-  i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i;
6426+  i = fmt_uint0(s,new_dt.min,2); len += i; if (s) s += i;
6427   i = fmt_str(s,":"); len += i; if (s) s += i;
6428-  i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i;
6429-  i = fmt_str(s," -0000\n"); len += i; if (s) s += i;
6430+  i = fmt_uint0(s,new_dt.sec,2); len += i; if (s) s += i;
6431+
6432+  if (local < utc) {
6433+    minutes = (utc - local + 30) / 60;
6434+    i = fmt_str(s," -"); len += i; if (s) s += i;
6435+    i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i;
6436+    i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i;
6437+  }
6438+  else {
6439+    minutes = (local - utc + 30) / 60;
6440+    i = fmt_str(s," +"); len += i; if (s) s += i;
6441+    i = fmt_uint0(s,minutes / 60,2); len += i; if (s) s += i;
6442+    i = fmt_uint0(s,minutes % 60,2); len += i; if (s) s += i;
6443+  }
6444+
6445+  i = fmt_str(s,"\n"); len += i; if (s) s += i;
6446+
6447   return len;
6448 }
6449diff -ruN ../netqmail-1.06-original/dk-filter.9 netqmail-1.06/dk-filter.9
6450--- ../netqmail-1.06-original/dk-filter.9       1970-01-01 01:00:00.000000000 +0100
6451+++ netqmail-1.06/dk-filter.9   2016-11-22 21:03:57.108528232 +0100
6452@@ -0,0 +1,102 @@
6453+.TH dk-filter 8
6454+.SH NAME
6455+dk-filter \- sign/verify using DK/DKIM (SSP/ADSP optionally) and deliver a mail message for delivery
6456+.SH SYNOPSIS
6457+.B FILTERARGS=QMAILHOME/bin/dk-filter
6458+.SH DESCRIPTION
6459+.B dk-filter
6460+is a qfilter which can be set as a filter for
6461+.BR spawn-filter(8) .
6462+The filter can be set either as
6463+.B FILTERARGS
6464+or in the control file
6465+.BR filterargs .
6466+
6467+.B dk-filter
6468+supports DK/DKIM signing and verification and can optionally use
6469+.B Sender Signing Practice (SSP)
6470+or
6471+.B Author Domain Signing Practice.
6472+It uses the libdkim and OpenSSL libraries.  To sign a message, set the
6473+.B DKIMSIGN
6474+or
6475+.B DKSIGIN
6476+environment variables to the pathname of the private key that will be
6477+used to sign the message. If there is a % character in the environment
6478+variable, it is removed and replaced by the domain name in the From: header.
6479+If, after substituting the %, that file does not exist, QMAILHOME/control/domainkeys/%/default
6480+will be used as the key. If again, after substituting the % sign, the file does not exist,
6481+QMAILHOME/control/domainkeys/default will be used as the key.
6482+After all substitutions, if the key file does not exist, the message will not be signed.
6483+If there is no % and the file does not exist, the message will be rejected with error 35.
6484+The selector (s=) will be taken from the basename of the file.
6485+The private key should be created by
6486+.BR dknewkey(8) .
6487+
6488+You can set various DK options in getopt style, by setting the environment variable DKSIGNOPTIONS
6489+ b <advice_length>    Length of Advice
6490+ c <canonicalization> simple, nofws
6491+ s <privkeyfile>
6492+ h                    show headers included
6493+ r                    Skip Duplicate Headers
6494+.EX
6495+ DKSIGNOPTIONS="-h -r -c nofws"
6496+ sets the h= tag, skips duplicate headers and sets nofws canonicalization
6497+.EE
6498+
6499+You can set various DKIM options in getopt style, by setting the environment variable DKIMSIGNOPTIONS
6500+
6501+ b <standard>         1 - allman, 2 - ietf or 3 - both
6502+ c <canonicalization> r for relaxed [DEFAULT], s - simple,
6503+                      t relaxed/simple, u - simple/relaxed
6504+ l                    include body length tag
6505+ q                    include query method tag;
6506+ t                    include a timestamp tag
6507+ h
6508+ i <identity>         the identity, if not provided it will not be included
6509+ x <expire_time>      the expire time in seconds since epoch
6510+                      ( DEFAULT = current time + 604800)
6511+                      if set to - then it will not be included
6512+ z <hash>             1 for sha1, 2 for sha256, 3 for both
6513+ s <privkeyfile>
6514+ y <selector>
6515+
6516+.EX
6517+ DKIMSIGNOPTIONS="-b 1 -c r -q"
6518+ sets allman standard, with relaxed canonicalization and include query method tag
6519+.EE
6520+
6521+.B dk-filter
6522+uses the domain found in the Sender: header to set the domain tag. If not it uses the From: header. You can override this by
6523+setting
6524+.B DKIMDOMAIN
6525+environment variable.
6526+.B DKIMDOMAIN
6527+can be set to an email address or a domain (without the at sign).
6528+To verify a message, set the
6529+.B DKIMVERIFY
6530+or
6531+.B DKVERIFY
6532+environment variables
6533+.B dk-filter
6534+always inserts the
6535+.B DKIM-Status
6536+or
6537+.B DomainKey-Status
6538+header, so that messages can be
6539+rejected later at delivery time, or in the mail reader. In that case you may set
6540+.B DKIMVERIFY
6541+or
6542+.B DKVERIFY
6543+to an empty string.
6544+.B dk-filter
6545+does not use any signing practice byd default. You can override this by setting the SIGN_PRACTICE to ssp or adsp (lowercase).
6546+
6547+.SH "EXIT CODES"
6548+0 for success, non-zero failure
6549+
6550+.SH "SEE ALSO"
6551+dknewkey(8),
6552+dktest(8),
6553+dkim(8),
6554+spawn-filter(8)
6555diff -ruN ../netqmail-1.06-original/dk-filter.sh netqmail-1.06/dk-filter.sh
6556--- ../netqmail-1.06-original/dk-filter.sh      1970-01-01 01:00:00.000000000 +0100
6557+++ netqmail-1.06/dk-filter.sh  2016-11-22 21:03:57.109528199 +0100
6558@@ -0,0 +1,315 @@
6559+#
6560+# $Log: dk-filter.sh,v $
6561+# Revision 1.14  2011-02-10 22:47:01+05:30  Cprogrammer
6562+# fixed exit code of dk-filter when doing verification
6563+#
6564+# Revision 1.13  2011-02-08 22:02:29+05:30  Cprogrammer
6565+# use sender domain when replacing '%' in private key
6566+#
6567+# Revision 1.12  2010-05-04 08:37:42+05:30  Cprogrammer
6568+# do DK signing before DKIM signing to prevent DK_SYNTAX error
6569+#
6570+# Revision 1.11  2009-12-10 19:25:13+05:30  Cprogrammer
6571+# added RCS id
6572+#
6573+# Revision 1.10  2009-12-10 16:41:14+05:30  Cprogrammer
6574+# continue of message gives DK_SYNTAX_ERR
6575+#
6576+# Revision 1.9  2009-05-04 10:30:32+05:30  Cprogrammer
6577+# fixed argument expected error
6578+#
6579+# Revision 1.8  2009-04-21 20:42:44+05:30  Cprogrammer
6580+# added check for dktest, dkim executables
6581+#
6582+# Revision 1.7  2009-04-20 10:06:58+05:30  Cprogrammer
6583+# added DKSIGNOPTS
6584+#
6585+# Revision 1.6  2009-04-19 13:38:24+05:30  Cprogrammer
6586+# added full set of dkim options
6587+# replaced indimail/bin/echo with echo 1>&2
6588+#
6589+# Revision 1.5  2009-04-06 16:37:50+05:30  Cprogrammer
6590+# added SIGN_PRACTICE
6591+# use ietf standard insted of allman so that Yahoo verification does not fail
6592+#
6593+# Revision 1.4  2009-04-03 14:39:00+05:30  Cprogrammer
6594+# added return status
6595+#
6596+# Revision 1.3  2009-04-03 08:55:29+05:30  Cprogrammer
6597+# print error messages to stderr
6598+#
6599+# Revision 1.2  2009-04-02 20:36:25+05:30  Cprogrammer
6600+# added -h option to dktest
6601+# added -x - option to dkim
6602+#
6603+# Revision 1.1  2009-04-02 14:52:27+05:30  Cprogrammer
6604+# Initial revision
6605+#
6606+# $Id: dk-filter.sh,v 1.14 2011-02-10 22:47:01+05:30 Cprogrammer Stab mbhangui $
6607+#
6608+if [ -z "$QMAILREMOTE" -a -z "$QMAILLOCAL" ]; then
6609+       echo "dk-filter should be run by spawn-filter" 1>&2
6610+       exit 1
6611+fi
6612+dksign=0
6613+dkimsign=0
6614+dkverify=0
6615+dkimverify=0
6616+if [ -z "$DKSIGN" -a -z "$DKVERIFY" ] ; then
6617+       DKSIGN=QMAILHOME/control/domainkeys/%/default
6618+       dksign=2
6619+fi
6620+if [ -z "$DKIMSIGN" -a -z "$DKIMVERIFY" ] ; then
6621+       DKIMSIGN=QMAILHOME/control/domainkeys/%/default
6622+       dkimsign=2
6623+fi
6624+if [ ! -z "$DKSIGN" ] ; then
6625+       if [ ! -f QMAILHOME/bin/dktest ] ; then
6626+               echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2
6627+               exit 1
6628+       fi
6629+       percent_found=0
6630+       echo $DKSIGN|grep "%" >/dev/null 2>&1
6631+       if [ $? -eq 0 ] ; then
6632+               percent_found=1
6633+       fi
6634+       if [ ! " $_SENDER" = " " ] ; then
6635+               # replace '%' in filename with domain
6636+               domain=`echo $_SENDER | cut -d@ -f2`
6637+               dkkeyfn=`echo $DKSIGN | sed s{%{$domain{g`
6638+       else
6639+               dkkeyfn=$DKSIGN
6640+       fi
6641+       if [ $dksign -eq 2 -a ! -f $dkkeyfn ] ; then
6642+               dkkeyfn=QMAILHOME/control/domainkeys/default
6643+       fi
6644+       if [ -f $dkkeyfn ] ; then
6645+               dksign=1
6646+       else
6647+               dksign=0
6648+       fi
6649+       if [ $dksign -eq 0 -a $percent_found -ne 1 ] ; then
6650+               exit 32
6651+       fi
6652+       dkselector=`basename $dkkeyfn`
6653+fi
6654+if [ ! -z "$DKIMSIGN" ] ; then
6655+       if [ ! -f QMAILHOME/bin/dkim ] ; then
6656+               echo "QMAILHOME/bin/dkim: No such file or directory" 1>&2
6657+               exit 1
6658+       fi
6659+       percent_found=0
6660+       echo $DKIMSIGN|grep "%" >/dev/null 2>&1
6661+       if [ $? -eq 0 ] ; then
6662+               percent_found=1
6663+       fi
6664+       if [ ! " $_SENDER" = " " ] ; then
6665+               # replace '%' in filename with domain
6666+               domain=`echo $_SENDER | cut -d@ -f2`
6667+               dkimkeyfn=`echo $DKIMSIGN | sed s{%{$domain{g`
6668+       else
6669+               dkimkeyfn=$DKIMSIGN
6670+       fi
6671+       if [ $dkimsign -eq 2 -a ! -f $dkimkeyfn ] ; then
6672+               dkimkeyfn=QMAILHOME/control/domainkeys/default
6673+       fi
6674+       if [ -f $dkimkeyfn ] ; then
6675+               dkimsign=1
6676+       else
6677+               dkimsign=0
6678+       fi
6679+       if [ $dkimsign -eq 0 -a $percent_found -ne 1 ] ; then
6680+               exit 32
6681+       fi
6682+       dkimselector=`basename $dkimkeyfn`
6683+fi
6684+if [ ! -z "$DKVERIFY" ] ; then
6685+       if [ ! -f QMAILHOME/bin/dktest ] ; then
6686+               echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2
6687+               exit 1
6688+       fi
6689+       dkverify=1
6690+fi
6691+if [ ! -z "$DKIMVERIFY" ] ; then
6692+       if [ ! -f QMAILHOME/bin/dkim ] ; then
6693+               echo "QMAILHOME/bin/dkim: No such file or directory" 1>&2
6694+               exit 1
6695+       fi
6696+       dkimverify=1
6697+fi
6698+cat > /tmp/dk.$$
6699+if [ $dkimsign -eq 1 ] ; then
6700+       # DKIMSIGNOPTIONS="-z 1 -b 2 -x - -y $dkimselector -s $dkimkeyfn"
6701+       set -- `getopt lqthb:c:d:i:x:z:y:s: $DKIMSIGNOPTIONS`
6702+       bopt=0
6703+       xopt=0
6704+       zopt=0
6705+       yopt=0
6706+       sopt=0
6707+       dkimopts="QMAILHOME/bin/dkim"
6708+       while [ $1 != -- ]
6709+       do
6710+               case $1 in
6711+               -l)
6712+               dkimopts="$dkimopts -l"
6713+               ;;
6714+               -q)
6715+               dkimopts="$dkimopts -q"
6716+               ;;
6717+               -t)
6718+               dkimopts="$dkimopts -t"
6719+               ;;
6720+               -h)
6721+               dkimopts="$dkimopts -h"
6722+               ;;
6723+
6724+               -b)
6725+               bopt=1
6726+               dkimopts="$dkimopts -b $2"
6727+               shift
6728+               ;;
6729+
6730+               -c)
6731+               dkimopts="$dkimopts -c $2"
6732+               shift
6733+               ;;
6734+
6735+               -i)
6736+               dkimopts="$dkimopts -i $2"
6737+               shift
6738+               ;;
6739+
6740+               -x)
6741+               xopt=1
6742+               dkimopts="$dkimopts -x $2"
6743+               shift
6744+               ;;
6745+
6746+               -z)
6747+               zopt=1
6748+               dkimopts="$dkimopts -z $2"
6749+               shift
6750+               ;;
6751+
6752+               -y)
6753+               yopt=1
6754+               dkimopts="$dkimopts -y $2"
6755+               shift
6756+               ;;
6757+
6758+               -s)
6759+               sopt=1
6760+               dkimopts="$dkimopts -s $2"
6761+               shift
6762+               ;;
6763+               esac
6764+               shift   # next flag
6765+       done
6766+       if [ $zopt -eq 0 ] ; then
6767+               dkimopts="$dkimopts -z 1"
6768+       fi
6769+       if [ $bopt -eq 0 ] ; then
6770+               dkimopts="$dkimopts -b 2"
6771+       fi
6772+       if [ $xopt -eq 0 ] ; then
6773+               dkimopts="$dkimopts -x -"
6774+       fi
6775+       if [ $yopt -eq 0 ] ; then
6776+               dkimopts="$dkimopts -y $dkimselector"
6777+       fi
6778+       if [ $sopt -eq 0 ] ; then
6779+               dkimopts="$dkimopts -s $dkimkeyfn"
6780+       fi
6781+       exec 0</tmp/dk.$$
6782+       eval $dkimopts
6783+       if [ $? -ne 0 ] ; then
6784+               /bin/rm -f /tmp/dk.$$
6785+               exit 1
6786+       fi
6787+fi
6788+if [ $dksign -eq 1 ] ; then
6789+       #dktest: [-f] [-b advice_length] [-c nofws|simple] [-v|-s selector] [-h] [-t#] [-r] [-T][-d dnsrecord]
6790+       # DKSIGNOPTIONS="-z 1 -b 2 -x - -y $dkimselector -s $dkimkeyfn"
6791+       set -- `getopt hrb:c:s: $DKSIGNOPTIONS`
6792+       dkopts="QMAILHOME/bin/dktest"
6793+       sopt=0
6794+       while [ $1 != -- ]
6795+       do
6796+               case $1 in
6797+               -h)
6798+               dkopts="$dkopts -h"
6799+               ;;
6800+               -r)
6801+               dkopts="$dkopts -r"
6802+               ;;
6803+
6804+               -b)
6805+               dkopts="$dkopts -b $2"
6806+               shift
6807+               ;;
6808+
6809+               -c)
6810+               dkopts="$dkopts -c $2"
6811+               shift
6812+               ;;
6813+
6814+               -s)
6815+               sopt=1
6816+               dkopts="$dkopts -s $2"
6817+               shift
6818+               ;;
6819+               esac
6820+               shift   # next flag
6821+       done
6822+       if [ $sopt -eq 0 ] ; then
6823+               dkopts="$dkopts -s $dkkeyfn"
6824+       fi
6825+       exec 0</tmp/dk.$$
6826+       #QMAILHOME/bin/dktest -h -s $dkkeyfn
6827+       eval $dkopts
6828+       exit_val=$?
6829+       # allow error due to duplicate DomainKey-Header
6830+       if [ $exit_val -ne 0 -a $exit_val -ne 6 ] ; then
6831+               /bin/rm -f /tmp/dk.$$
6832+               exit $exit_val
6833+       fi
6834+fi
6835+if [ $dkimverify -eq 1 ] ; then
6836+       practice=$SIGN_PRACTICE
6837+       if [ " $practice" = " " ] ; then
6838+               practice=0
6839+       elif [ " $practice" = " ssp" ] ; then
6840+               practice=1
6841+       elif [ " $practice" = " adsp" ] ; then
6842+               practice=2
6843+       fi
6844+       exec 0</tmp/dk.$$
6845+       QMAILHOME/bin/dkim -p $practice -v
6846+       ret=$?
6847+       case $ret in
6848+               14)
6849+               /bin/rm -f /tmp/dk.$$
6850+               exit 100
6851+               ;;
6852+               88)
6853+               /bin/rm -f /tmp/dk.$$
6854+               exit 111
6855+               ;;
6856+       esac
6857+       if [ $ret -lt 0 ] ; then
6858+               /bin/rm -f /tmp/dk.$$
6859+               exit 1
6860+       fi
6861+fi
6862+if [ $dkverify -eq 1 ] ; then
6863+       exec 0</tmp/dk.$$
6864+       QMAILHOME/bin/dktest -v
6865+       if [ $? -ne 0 ] ; then
6866+               /bin/rm -f /tmp/dk.$$
6867+               exit 1
6868+       fi
6869+fi
6870+exec 0</tmp/dk.$$
6871+/bin/rm -f /tmp/dk.$$
6872+cat
6873+exit $?
6874diff -ruN ../netqmail-1.06-original/dkim.9 netqmail-1.06/dkim.9
6875--- ../netqmail-1.06-original/dkim.9    1970-01-01 01:00:00.000000000 +0100
6876+++ netqmail-1.06/dkim.9        2018-04-03 14:46:51.362411599 +0200
6877@@ -0,0 +1,98 @@
6878+.TH dkim 8
6879+.SH NAME
6880+dkim \- exercise the domainkeys library
6881+.SH SYNOPSIS
6882+.B dkim
6883+.I opts
6884+
6885+.I opts
6886+is a series of getopt-style options.
6887+
6888+.SH DESCRIPTION
6889+.B dkim
6890+exercises the dkim library. Both signing and verification merely print out the DKIM header.
6891+They do not keep a copy of the input file. You will need to do something like this:
6892+
6893+.EX
6894+ (./dkim -s QMAILHOME/control/domainkeys/dog </tmp/testmsg; cat /tmp/testmsg)\
6895+ | ./dkim -v
6896+.EE
6897+
6898+.SH OPTIONS
6899+.TP
6900+-s \fIkey\fR
6901+.I key
6902+is a path to a file containing a PEM-format private key. The base name of
6903+the file is  used  as  the  selector. Reads the email message on stdin. Prints the
6904+.B DKIM-Signature
6905+header.
6906+
6907+.TP
6908+-v
6909+Verifies the email on stdin. Exits with a non-zero exit code and a message to
6910+stderr if there was a problem with the signature. Always prints a
6911+.B DKIM-Status:
6912+header to stdout. This option requires the \fBs\fR._domainkey.\fBd\fR txt record in
6913+dns (public key). Here \fBs\fR is the selector and \fBd\fR is the domain
6914+
6915+.EX
6916+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
6917+d=gmail.com; s=gamma; h=DomainKey-Status:
6918+To:Subject:Message-Id:Date:From; bh=IarZI4AMTl/vy1jTbPphDcOl4YNS
6919+POk7Vn0tDdgkbV4=; b=VpIS6XNOLU2WWWlsYGeLB3wMbyFQwkg7F0hr7blu1W8f
6920+0RRtuyw9igFwY7q7FNaPVlfZ0cfLPh0mRrlExu4V7uQaTP8nnnHO2cAokYbncGS5
6921+ADU9NbAcpDh+E2YQwzCX
6922+.EE
6923+
6924+.TP
6925+-l
6926+include body length tag
6927+.TP
6928+-q
6929+include query method tag
6930+.TP
6931+-t
6932+include a timestamp tag
6933+.TP
6934+-f
6935+issue error if not all message's From headers are in signature
6936+.TP
6937+-h
6938+include Copied Headers
6939+.TP
6940+-p \fIssp\fR | \fIadsp\fR
6941+0 - disable practice (default), 1- SSP, or 2 - ADSP verification
6942+.TP
6943+-b \fIstandard\fR
6944+1 - allman, 2 - ietf or 3 - both
6945+.TP
6946+-c \fIcanonicalization\fR
6947+r for relaxed [DEFAULT], s - simple, t relaxed/simple, u - simple/relaxed
6948+-d \fIdomain\fR
6949+the domain tag, if not provided, determined from the sender/from header
6950+.TP
6951+-i \fIidentity\fR
6952+the identity, if not provided it will not be included
6953+.TP
6954+-x \fIexpire_time\fR
6955+the expire time in seconds since epoch ( DEFAULT = current time + 604800). If set to - then it will not be included
6956+.TP
6957+-z \fIhash\fR
6958+1 for sha1, 2 for sha256, 3 for both
6959+.TP
6960+-y \fIselector\fR
6961+the selector tag DEFAULT=private
6962+.TP
6963+-s \fIprivkeyfile\fR
6964+sign the message using the private key in privkeyfile
6965+.TP
6966+-H
6967+this help
6968+
6969+.SH "SEE ALSO"
6970+dktest(8),
6971+qmail-dk(8),
6972+qmail-dkim(8),
6973+dknewkey(8),
6974+rfc-4870(5),
6975+rfc-4871(5)
6976diff -ruN ../netqmail-1.06-original/dkim.c netqmail-1.06/dkim.c
6977--- ../netqmail-1.06-original/dkim.c    1970-01-01 01:00:00.000000000 +0100
6978+++ netqmail-1.06/dkim.c        2018-04-03 14:46:51.363411603 +0200
6979@@ -0,0 +1,899 @@
6980+/*
6981+ * $Log: dkim.c,v $
6982+ * Revision 1.19  2016-03-01 16:23:38+05:30  Cprogrammer
6983+ * added -S option to allow email with unsigned subject
6984+ *
6985+ * Revision 1.18  2016-02-01 10:53:32+05:30  Cprogrammer
6986+ * use basename of private key as the selector in absense of -y option
6987+ *
6988+ * Revision 1.17  2015-12-15 15:36:01+05:30  Cprogrammer
6989+ * added case 3 for 3rd party signature without SSP and ADSP
6990+ * increased buffer size for Apple mail with X-BrightMail-Tracker header issue
6991+ *
6992+ * Revision 1.16  2012-08-16 08:01:19+05:30  Cprogrammer
6993+ * do not skip X-Mailer headers
6994+ *
6995+ * Revision 1.15  2011-06-04 13:55:50+05:30  Cprogrammer
6996+ * set AllowUnsignedFromHeaders
6997+ *
6998+ * Revision 1.14  2011-06-04 09:36:36+05:30  Cprogrammer
6999+ * added AllowUnsignedFromHeaders option
7000+ *
7001+ * Revision 1.13  2011-02-07 22:05:23+05:30  Cprogrammer
7002+ * added case DKIM_3PS_SIGNATURE
7003+ *
7004+ * Revision 1.12  2010-05-04 14:00:13+05:30  Cprogrammer
7005+ * make option '-z' work on systems without SHA_256
7006+ *
7007+ * Revision 1.11  2009-04-20 08:35:45+05:30  Cprogrammer
7008+ * corrected usage()
7009+ *
7010+ * Revision 1.10  2009-04-15 21:30:32+05:30  Cprogrammer
7011+ * added DKIM-Signature to list of excluded headers
7012+ *
7013+ * Revision 1.9  2009-04-15 20:45:04+05:30  Cprogrammer
7014+ * corrected usage
7015+ *
7016+ * Revision 1.8  2009-04-05 19:04:44+05:30  Cprogrammer
7017+ * improved formating of usage
7018+ *
7019+ * Revision 1.7  2009-04-03 12:05:25+05:30  Cprogrammer
7020+ * minor changes on usage display
7021+ *
7022+ * Revision 1.6  2009-03-28 20:15:23+05:30  Cprogrammer
7023+ * invoke DKIMVerifyGetDetails()
7024+ *
7025+ * Revision 1.5  2009-03-27 20:43:48+05:30  Cprogrammer
7026+ * added HAVE_OPENSSL_EVP_H conditional
7027+ *
7028+ * Revision 1.4  2009-03-27 20:19:28+05:30  Cprogrammer
7029+ * added ADSP
7030+ *
7031+ * Revision 1.3  2009-03-26 15:10:53+05:30  Cprogrammer
7032+ * added ADSP
7033+ *
7034+ * Revision 1.2  2009-03-25 08:37:45+05:30  Cprogrammer
7035+ * added dkim_error
7036+ *
7037+ * Revision 1.1  2009-03-21 08:24:47+05:30  Cprogrammer
7038+ * Initial revision
7039+ *
7040+ *
7041+ * This code incorporates intellectual property owned by Yahoo! and licensed
7042+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
7043+ *
7044+ * Unless required by applicable law or agreed to in writing, software
7045+ * distributed under the License is distributed on an "AS IS" BASIS,
7046+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7047+ * See the License for the specific language governing permissions and
7048+ * limitations under the License.
7049+ */
7050+/*
7051+ * (cat /tmp/test.msg|./dkimtest -z 2 -b 1 -y private \
7052+ * -s /var/indimail/control/domainkeys/private ;cat /tmp/test.msg )|./dkimtest -v
7053+ */
7054+#ifndef __cplusplus
7055+#error A C++ compiler is required!
7056+#endif
7057+#ifdef HAVE_CONFIG_H
7058+#include "config.h"
7059+#endif
7060+
7061+#include <stdio.h>
7062+#include <fcntl.h>
7063+#include <errno.h>
7064+#include <unistd.h>
7065+#include <string.h>
7066+#include <time.h>
7067+#include <stdlib.h>
7068+#include <sys/types.h>
7069+#include <sys/stat.h>
7070+#include "dkim.h"
7071+#include "dkimdns.h"
7072+
7073+#ifdef HAVE_OPENSSL_EVP_H
7074+#include <openssl/evp.h>
7075+#define DKIM_MALLOC(s)     OPENSSL_malloc(s)
7076+#define DKIM_MFREE(s)      OPENSSL_free(s); s = NULL;
7077+#else
7078+#define DKIM_MALLOC(s)     malloc(s)
7079+#define DKIM_MFREE(s)      free(s); s = NULL;
7080+#endif
7081+
7082+int DKIM_CALL
7083+SignThisHeader(const char *szHeader)
7084+{
7085+       if ((!strncasecmp(szHeader, "X-", 2) && strncasecmp(szHeader, "X-Mailer", 8))
7086+               || !strncasecmp(szHeader, "Received:", 9)
7087+               || !strncasecmp(szHeader, "DKIM-Signature:", 15)
7088+               || !strncasecmp(szHeader, "Authentication-Results:", 23)
7089+               || !strncasecmp(szHeader, "DomainKey-Signature", 19)
7090+               || !strncasecmp(szHeader, "Return-Path:", 12))
7091+       {
7092+               return 0;
7093+       }
7094+       return 1;
7095+}
7096+
7097+char           *program;
7098+
7099+void
7100+usage()
7101+{
7102+#ifdef HAVE_EVP_SHA256
7103+       fprintf(stderr, "usage: %s [-lqthvH] [-p <0|1|2>] [-b <1|2|3>] [-c <r|s|t|u>]\n\t[-d domain] [-i you@domain] [-x expire_time] [-z hash] [-y selector] -s privkeyfile\n", program);
7104+#else
7105+       fprintf(stderr, "usage: %s [-lqthvH] [-p <0|1|2>] [-b <1|2|3>] [-c <r|s|t|u>]\n\t[-d domain] [-i you@domain] [-x expire_time] [-y selector] -s privkeyfile\n", program);
7106+#endif
7107+       fprintf(stderr, "l                    include body length tag\n");
7108+       fprintf(stderr, "q                    include query method tag\n");
7109+       fprintf(stderr, "t                    include a timestamp tag\n");
7110+       fprintf(stderr, "h                    include Copied Headers\n");
7111+       fprintf(stderr, "v                    verify the message\n");
7112+       fprintf(stderr, "p <ssp|adsp>         0 - disable practice (default), 1- SSP, or 2 - ADSP verification\n");
7113+       fprintf(stderr, "b <standard>         1 - allman, 2 - ietf or 3 - both\n");
7114+       fprintf(stderr, "c <canonicalization> r for relaxed [DEFAULT], s - simple, t relaxed/simple, u - simple/relaxed\n");
7115+       fprintf(stderr, "d <domain>           the domain tag, if not provided, determined from the sender/from header\n");
7116+       fprintf(stderr, "i <identity>         the identity, if not provided it will not be included\n");
7117+       fprintf(stderr, "x <expire_time>      the expire time in seconds since epoch ( DEFAULT = current time + 604800)\n");
7118+       fprintf(stderr, "                     if set to - then it will not be included\n");
7119+#ifdef HAVE_EVP_SHA256
7120+       fprintf(stderr, "z <hash>             1 for sha1, 2 for sha256, 3 for both\n");
7121+#endif
7122+       fprintf(stderr, "f                    0 = From headers not included in the signature are not allowed\n");
7123+       fprintf(stderr, "                     1 = allowed\n");
7124+       fprintf(stderr, "y <selector>         the selector tag DEFAULT=private\n");
7125+       fprintf(stderr, "s <privkeyfile>      sign the message using the private key in privkeyfile\n");
7126+       fprintf(stderr, "H                    this help\n");
7127+       exit(1);
7128+}
7129+
7130+unsigned int str_chr(char *s, int c)
7131+{
7132+       register char   ch;
7133+       register char  *t;
7134+
7135+       ch = c;
7136+       t = s;
7137+       for (;;)
7138+       {
7139+               if (!*t)
7140+                       break;
7141+               if (*t == ch)
7142+                       break;
7143+               ++t;
7144+               if (!*t)
7145+                       break;
7146+               if (*t == ch)
7147+                       break;
7148+               ++t;
7149+               if (!*t)
7150+                       break;
7151+               if (*t == ch)
7152+                       break;
7153+               ++t;
7154+               if (!*t)
7155+                       break;
7156+               if (*t == ch)
7157+                       break;
7158+               ++t;
7159+       }
7160+       return t - s;
7161+}
7162+
7163+void
7164+dkim_error(int e)
7165+{
7166+       switch (e)
7167+       {
7168+       case DKIM_OUT_OF_MEMORY:
7169+               fprintf(stderr, "memory allocation failed\n");
7170+               break;
7171+       case DKIM_INVALID_CONTEXT:
7172+               fprintf(stderr, "DKIMContext structure invalid for this operation\n");
7173+               break;
7174+       case DKIM_NO_SENDER:
7175+               fprintf(stderr, "Could not find From: or Sender: header in message\n");
7176+               break;
7177+       case DKIM_BAD_PRIVATE_KEY:
7178+               fprintf(stderr, "Could not parse private key\n");
7179+               break;
7180+       case DKIM_BUFFER_TOO_SMALL:
7181+               fprintf(stderr, "Buffer passed in is not large enough");
7182+               break;
7183+       }
7184+}
7185+
7186+/*
7187+ * Allows you to add the headers contain the results and DKIM ADSP
7188+ */
7189+int writeHeader(int ret, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP )
7190+{
7191+       char           *dkimStatus, *sspStatus, *adspStatus;
7192+
7193+       dkimStatus = sspStatus = adspStatus = (char *) "";
7194+       switch (ret)
7195+       {
7196+       case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
7197+               dkimStatus = (char *) "signature result: signature verified but it did not include all of the body";
7198+               break;
7199+       case DKIM_NEUTRAL:                      /*- 3 verify result: no signatures verified but message is not suspicious */
7200+               dkimStatus = (char *) "verify result: no signatures verified but message is not suspicious";
7201+               break;
7202+       case DKIM_PARTIAL_SUCCESS:      /*- 2 verify result: at least one but not all signatures verified */
7203+               dkimStatus = (char *) "verify result: at least none but not all signatures verified";
7204+               break;
7205+       case DKIM_FINISHED_BODY:        /*- 1 process result: no more message body is needed */
7206+               dkimStatus = (char *) "process result: no more message body is needed";
7207+               break;
7208+       case DKIM_SUCCESS:
7209+               dkimStatus = (char *) "good        ";
7210+               break;
7211+       case DKIM_FAIL:
7212+               dkimStatus = (char *) "failed      ";
7213+               break;
7214+       case DKIM_BAD_SYNTAX:
7215+               dkimStatus = (char *) "signature error: DKIM-Signature could not parse or has bad tags/values";
7216+               break;
7217+       case DKIM_SIGNATURE_BAD:
7218+               dkimStatus = (char *) "signature error: RSA verify failed";
7219+               break;
7220+       case DKIM_SIGNATURE_BAD_BUT_TESTING:
7221+               dkimStatus = (char *) "signature error: RSA verify failed but testing";
7222+               break;
7223+       case DKIM_SIGNATURE_EXPIRED:
7224+               dkimStatus = (char *) "signature error: x= is old";
7225+               break;
7226+       case DKIM_SELECTOR_INVALID:
7227+               dkimStatus = (char *) "signature error: selector doesn't parse or contains invalid values";
7228+               break;
7229+       case DKIM_SELECTOR_GRANULARITY_MISMATCH:
7230+               dkimStatus = (char *) "signature error: selector g= doesn't match i=";
7231+               break;
7232+       case DKIM_SELECTOR_KEY_REVOKED:
7233+               dkimStatus = (char *) "signature error: selector p= empty";
7234+               break;
7235+       case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
7236+               dkimStatus = (char *) "signature error: selector domain name too long to request";
7237+               break;
7238+       case DKIM_SELECTOR_DNS_TEMP_FAILURE:
7239+               dkimStatus = (char *) "signature error: temporary dns failure requesting selector";
7240+               break;
7241+       case DKIM_SELECTOR_DNS_PERM_FAILURE:
7242+               dkimStatus = (char *) "signature error: permanent dns failure requesting selector";
7243+               break;
7244+       case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
7245+               dkimStatus = (char *) "signature error: selector p= value invalid or wrong format";
7246+               break;
7247+       case DKIM_NO_SIGNATURES:
7248+               dkimStatus = (char *) "process error, no sigs";
7249+               break;
7250+       case DKIM_NO_VALID_SIGNATURES:
7251+               dkimStatus = (char *) "process error, no valid sigs";
7252+               break;
7253+       case DKIM_BODY_HASH_MISMATCH:
7254+               dkimStatus = (char *) "sigature verify error: message body does not hash to bh value";
7255+               break;
7256+       case DKIM_SELECTOR_ALGORITHM_MISMATCH:
7257+               dkimStatus = (char *) "signature error: selector h= doesn't match signature a=";
7258+               break;
7259+       case DKIM_STAT_INCOMPAT:
7260+               dkimStatus = (char *) "signature error: incompatible v=";
7261+               break;
7262+       default:
7263+               dkimStatus = (char *) "error";
7264+               break;
7265+       }
7266+       if (useSSP && resDKIMSSP != -1)
7267+       {
7268+               switch(resDKIMSSP)
7269+               {
7270+                       case DKIM_SSP_ALL:
7271+                               sspStatus = (char *) "all;";
7272+                               break;
7273+                       case DKIM_SSP_STRICT:
7274+                               sspStatus = (char *) "strict;";
7275+                               break;
7276+                       case DKIM_SSP_SCOPE:
7277+                               sspStatus = (char *) "out of scope;";
7278+                               break;
7279+                       case DKIM_SSP_TEMPFAIL:
7280+                               sspStatus = (char *) "temporary failure;";
7281+                               break;
7282+                       case DKIM_SSP_UNKNOWN:
7283+                       default:
7284+                               sspStatus = (char *) "unknown;";
7285+                               break;
7286+               }
7287+       }
7288+       if (useADSP && resDKIMADSP != -1)
7289+       {
7290+               switch(resDKIMADSP)
7291+               {
7292+                       case DKIM_ADSP_ALL:
7293+                               adspStatus = (char *) "all;";
7294+                               break;
7295+                       case DKIM_ADSP_DISCARDABLE:
7296+                               adspStatus = (char *) "discardable;";
7297+                               break;
7298+                       case DKIM_ADSP_SCOPE:
7299+                               adspStatus = (char *) "out of scope;";
7300+                               break;
7301+                       case DKIM_ADSP_TEMPFAIL:
7302+                               adspStatus = (char *) "temporary failure;";
7303+                               break;
7304+                       case DKIM_ADSP_UNKNOWN:
7305+                       default:
7306+                               adspStatus = (char *) "unknown ;";
7307+                               break;
7308+               }
7309+       }
7310+       printf("DKIM-Status: %s\n", dkimStatus);
7311+       if (useSSP && *sspStatus)
7312+               printf("X-DKIM-SSP: %s\n", sspStatus);
7313+       if (useADSP && *adspStatus)
7314+               printf("X-DKIM-ADSP: %s\n", adspStatus);
7315+}
7316+
7317+int
7318+ParseTagValues(char *list, char *letters[], char *values[])
7319+{
7320+       char           *tmp, *ptr, *key;
7321+       int             i;
7322+
7323+       /*- start with all args unset */
7324+       for (i = 0; letters[i]; i++)
7325+               values[i] = 0;
7326+       key = 0;
7327+       for(ptr = list;*ptr;)
7328+       {
7329+               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */
7330+                       *ptr++ = 0;
7331+               if (!key)
7332+                       key = ptr;
7333+               if (*ptr == '=')
7334+               {
7335+                       *ptr = 0;
7336+                       for (i = 0;letters[i];i++)
7337+                       {
7338+                               if (!strcmp(letters[i], key))
7339+                               {
7340+                                       ptr++;
7341+                                       for (;*ptr;)
7342+                                       {
7343+                                               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n'))
7344+                                               {
7345+                                                       ptr++;
7346+                                                       continue;
7347+                                               }
7348+                                               break;
7349+                                       }
7350+                                       values[i] = ptr;
7351+                                       for(;*ptr && *ptr != ';';ptr++);
7352+                                       tmp = ptr;
7353+                                       if (*ptr)
7354+                                               *ptr++ = 0;
7355+                                       for(;tmp != values[i];tmp--) /*- RFC 4871 3.2 */
7356+                                       {
7357+                                               if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n'))
7358+                                               {
7359+                                                       *tmp = 0;
7360+                                                       continue;
7361+                                               }
7362+                                               break;
7363+                                       }
7364+                                       key = 0;
7365+                                       break;
7366+                               }
7367+                       }
7368+               } else
7369+                       ptr++;
7370+       }
7371+       return (0);
7372+}
7373+
7374+int
7375+GetSSP(char *domain, int *bTesting)
7376+{
7377+       char           *query, *results;
7378+       char           *tags[] = { (char *) "dkim", (char *) "t", (char *) 0};
7379+       char           *values[2];
7380+       int             bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN;
7381+
7382+       *bTesting = 0;
7383+       if (!(query = (char *) DKIM_MALLOC(strlen("_ssp._domainkey.") + strlen(domain) + 1)))
7384+       {
7385+               fprintf(stderr, "malloc: %d: %s\n", strlen("_ssp._domainkey.") + strlen(domain) + 1,
7386+                       strerror(errno));
7387+               exit(1);
7388+       }
7389+       sprintf(query, "_ssp._domainkey.%s", domain);
7390+       results = dns_text(query);
7391+       DKIM_MFREE(query);
7392+       if (!strcmp(results, "e=temp;"))
7393+       {
7394+               DKIM_MFREE(results);
7395+               return DKIM_SSP_TEMPFAIL;
7396+       } else
7397+       if (!strcmp(results, "e=perm;"))
7398+       {
7399+               DKIM_MFREE(results);
7400+               results = dns_text(domain);
7401+               if (!strcmp(results, "e=temp;"))
7402+               {
7403+                       DKIM_MFREE(results);
7404+                       return DKIM_SSP_TEMPFAIL;
7405+               } else
7406+               if (!strcmp(results, "e=perm;"))
7407+               {
7408+                       DKIM_MFREE(results);
7409+                       return DKIM_SSP_SCOPE;
7410+               }
7411+               bIsParentSSP = 1;
7412+       }
7413+       if (!ParseTagValues(results, tags, values))
7414+       {
7415+               DKIM_MFREE(results);
7416+               return DKIM_SSP_UNKNOWN;
7417+       }
7418+       DKIM_MFREE(results);
7419+       if (values[0] != NULL) {
7420+               if (strcasecmp(values[0], "all") == 0)
7421+                       iSSP = DKIM_SSP_ALL;
7422+               else
7423+               if (strcasecmp(values[0], "strict") == 0)
7424+                       iSSP = DKIM_SSP_STRICT;
7425+       }
7426+       // flags
7427+       if (values[1] != NULL) {
7428+               char           *s, *p;
7429+               for (p = values[1], s = values[1]; *p; p++)
7430+               {
7431+                       if (*p == '|')
7432+                               *p = 0;
7433+                       else
7434+                               continue;
7435+                       if (!strcmp(s, "y"))
7436+                               *bTesting = 1;
7437+                       else
7438+                       if (!strcmp(s, "s")) {
7439+                               if (bIsParentSSP) {
7440+                                       /*
7441+                                        * this is a parent's SSP record that should not apply to subdomains
7442+                                        * the message is non-suspicious
7443+                                        */
7444+                                       *bTesting = 0;
7445+                                       return (DKIM_SSP_UNKNOWN);
7446+                               }
7447+                       }
7448+                       s = p + 1;
7449+               }
7450+       }
7451+       return iSSP; /*- No ADSP Record */
7452+}
7453+
7454+int
7455+GetADSP(char *domain)
7456+{
7457+       char           *query, *results;
7458+       char           *tags[] = {(char *) "dkim", (char *) 0};
7459+       char           *values[1];
7460+
7461+       results = dns_text(domain);
7462+       if (!strcmp(results, "e=perm;"))
7463+       {
7464+               DKIM_MFREE(results);
7465+               return DKIM_ADSP_SCOPE;
7466+       } else
7467+       if (!strcmp(results, "e=temp;"))
7468+       {
7469+               DKIM_MFREE(results);
7470+               return DKIM_ADSP_TEMPFAIL;
7471+       }
7472+       if (!(query = (char *) DKIM_MALLOC(strlen((char *) "_adsp._domainkey.") + strlen(domain) + 1)))
7473+       {
7474+               fprintf(stderr, "malloc: %d: %s\n", strlen("_adsp._domainkey.") + strlen(domain) + 1,
7475+                       strerror(errno));
7476+               exit(1);
7477+       }
7478+       sprintf(query, "_adsp._domainkey.%s", domain);
7479+       results = dns_text(query);
7480+       DKIM_MFREE(query);
7481+       if (!strcmp(results, "e=perm;"))
7482+       {
7483+               DKIM_MFREE(results);
7484+               return DKIM_ADSP_SCOPE;
7485+       } else
7486+       if (!strcmp(results, "e=temp;"))
7487+       {
7488+               DKIM_MFREE(results);
7489+               return DKIM_ADSP_TEMPFAIL;
7490+       }
7491+       if (!ParseTagValues(results, tags, values))
7492+       {
7493+               DKIM_MFREE(results);
7494+               return DKIM_ADSP_UNKNOWN;
7495+       }
7496+       DKIM_MFREE(results);
7497+       if (values[0] != NULL) {
7498+               if (strcasecmp(values[0], "all") == 0)
7499+                       return (DKIM_ADSP_ALL);
7500+               else
7501+               if (strcasecmp(values[0], "discardable") == 0)
7502+                       return (DKIM_ADSP_DISCARDABLE);
7503+       }
7504+       return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */
7505+}
7506+
7507+int
7508+main(int argc, char **argv)
7509+{
7510+       char           *PrivKey, *PrivKeyFile = NULL, *pSig = NULL, *dkimverify;
7511+       int             i, ret, ch, nPrivKeyLen, PrivKeyFD, verbose = 0;
7512+       int             bSign = 1, nSigCount = 0, useSSP = 0, useADSP = 0, accept3ps = 0;
7513+       int             sCount = 0, sSize = 0, resDKIMSSP = -1, resDKIMADSP = -1;
7514+       int             nAllowUnsignedFromHeaders = 0;
7515+       int             nAllowUnsignedSubject = 1;
7516+       char            Buffer[4096], szPolicy[512];
7517+       time_t          t;
7518+       struct stat     statbuf;
7519+       DKIMContext     ctxt;
7520+       DKIMSignOptions opts = { 0 };
7521+       DKIMVerifyDetails *pDetails;
7522+       DKIMVerifyOptions vopts = { 0 };
7523+
7524+       if (!(program = strrchr(argv[0], '/')))
7525+               program = argv[0];
7526+       else
7527+               program++;
7528+       t = time(0);
7529+#ifdef HAVE_EVP_SHA256
7530+       opts.nHash = DKIM_HASH_SHA1_AND_256;
7531+#else
7532+       opts.nHash = DKIM_HASH_SHA1;
7533+#endif
7534+       opts.nCanon = DKIM_SIGN_RELAXED;
7535+       opts.nIncludeBodyLengthTag = 0;
7536+       opts.nIncludeQueryMethod = 0;
7537+       opts.nIncludeTimeStamp = 0;
7538+       opts.expireTime = t + 604800;   // expires in 1 week
7539+       opts.nIncludeCopiedHeaders = 0;
7540+       opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH;
7541+       strcpy(opts.szRequiredHeaders, "NonExistent");
7542+       opts.pfnHeaderCallback = SignThisHeader;
7543+       while (1)
7544+       {
7545+               if ((ch = getopt(argc, argv, "lqtfhHSvVp:b:c:d:i:s:x:y:z:")) == -1)
7546+                       break;
7547+               switch (ch)
7548+               {
7549+               case 'l': /*- body length tag */
7550+                       opts.nIncludeBodyLengthTag = 1;
7551+                       break;
7552+               case 'q': /*- query method tag */
7553+                       opts.nIncludeQueryMethod = 1;
7554+                       break;
7555+               case 'S':
7556+                       nAllowUnsignedSubject = 0;
7557+                       break;
7558+               case 'f':
7559+                       nAllowUnsignedFromHeaders = 1;
7560+               case 't': /*- timestamp tag */
7561+                       opts.nIncludeTimeStamp = 1;
7562+                       break;
7563+               case 'h':
7564+                       opts.nIncludeCopiedHeaders = 1;
7565+                       break;
7566+               case 'H':
7567+                       usage();
7568+                       break;
7569+               case 'v': /*- verify */
7570+                       bSign = 0;
7571+                       break;
7572+               case 'V': /*- verbose */
7573+                       verbose = 1;
7574+                       break;
7575+               case 'p':
7576+                       switch(*optarg)
7577+                       {
7578+                       case '1':
7579+                               accept3ps = 1;
7580+                               useSSP = 1;
7581+                               useADSP = 0;
7582+                               break;
7583+                       case '2':
7584+                               accept3ps = 1;
7585+                               useSSP = 0;
7586+                               useADSP = 1;
7587+                               break;
7588+                       case '3':
7589+                               accept3ps = 1;
7590+                               useSSP = 0;
7591+                               useADSP = 0;
7592+                               break;
7593+                       case '0':
7594+                               accept3ps = 0;
7595+                               useSSP = 0;
7596+                               useADSP = 0;
7597+                               break;
7598+                       default:
7599+                               fprintf(stderr, "%s: unrecognized practice %c.\n", program, *optarg);
7600+                               return (1);
7601+                       }
7602+                       break;
7603+               case 'b': /*- allman or ietf draft 1 or both */
7604+                       switch (*optarg)
7605+                       {
7606+                       case '1':
7607+                               opts.nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1;
7608+                               break;
7609+                       case '2':
7610+                               opts.nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
7611+                               break;
7612+                       case '3':
7613+                               opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH;
7614+                               break;
7615+                       default:
7616+                               fprintf(stderr, "%s: unrecognized standard %c.\n", program, *optarg);
7617+                               return (1);
7618+                       }
7619+                       break;
7620+               case 'c':
7621+                       switch (*optarg)
7622+                       {
7623+                       case 'r':
7624+                               opts.nCanon = DKIM_SIGN_RELAXED;
7625+                               break;
7626+                       case 's':
7627+                               opts.nCanon = DKIM_SIGN_SIMPLE;
7628+                               break;
7629+                       case 't':
7630+                               opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE;
7631+                               break;
7632+                       case 'u':
7633+                               opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED;
7634+                               break;
7635+                       default:
7636+                               fprintf(stderr, "%s: unrecognized canonicalization.\n", program);
7637+                               return (1);
7638+                       }
7639+                       break;
7640+               case 'd':
7641+                       strncpy(opts.szDomain, optarg, sizeof (opts.szDomain) - 1);
7642+                       break;
7643+               case 'i':       /*- identity */
7644+                       if (*optarg == '-')
7645+                               opts.szIdentity[0] = '\0';
7646+                       else
7647+                               strncpy(opts.szIdentity, optarg, sizeof (opts.szIdentity) - 1);
7648+                       break;
7649+               case 's': /*- sign */
7650+                       bSign = 1;
7651+                       PrivKeyFile = optarg;
7652+                       break;
7653+               case 'x': /*- expire time */
7654+                       if (*optarg == '-')
7655+                               opts.expireTime = 0;
7656+                       else
7657+                               opts.expireTime = t + atoi(optarg);
7658+                       break;
7659+               case 'y':
7660+                       strncpy(opts.szSelector, optarg, sizeof (opts.szSelector) - 1);
7661+                       break;
7662+               case 'z': /*- sign w/ sha1, sha256 or both */
7663+#ifdef HAVE_EVP_SHA256
7664+                       switch (*optarg)
7665+                       {
7666+                       case '1':
7667+                               opts.nHash = DKIM_HASH_SHA1;
7668+                               break;
7669+                       case '2':
7670+                               opts.nHash = DKIM_HASH_SHA256;
7671+                               break;
7672+                       case '3':
7673+                               opts.nHash = DKIM_HASH_SHA1_AND_256;
7674+                               break;
7675+                       default:
7676+                               fprintf(stderr, "%s: unrecognized hash.\n", program);
7677+                               return (1);
7678+                       }
7679+#else
7680+                       opts.nHash = DKIM_HASH_SHA1;
7681+#endif
7682+                       break;
7683+               } /*- switch (ch) */
7684+       }
7685+       if (bSign) { /*- sign */
7686+               if (!PrivKeyFile)
7687+               {
7688+                       fprintf(stderr, "Private Key not provided\n");
7689+                       usage();
7690+                       return (1);
7691+               }
7692+               if (!opts.szSelector[0]) {
7693+                       if ((pSig = strrchr(PrivKeyFile, '/'))) {
7694+                               pSig++;
7695+                               strcpy(opts.szSelector, pSig);
7696+                       } else
7697+                               strcpy(opts.szSelector, "private");
7698+               }
7699+               if ((PrivKeyFD = open(PrivKeyFile, O_RDONLY)) == -1) {
7700+                       fprintf(stderr, "%s: %s\n", PrivKeyFile, strerror(errno));
7701+                       return (1);
7702+               }
7703+               if (fstat(PrivKeyFD, &statbuf) == -1)
7704+               {
7705+                       fprintf(stderr, "fstat: %s: %s\n", PrivKeyFile, strerror(errno));
7706+                       return (1);
7707+               }
7708+               if (!(PrivKey = (char *) DKIM_MALLOC(sizeof(char) * ((nPrivKeyLen = statbuf.st_size) + 1))))
7709+               {
7710+                       fprintf(stderr, "malloc: %ld bytes: %s\n", statbuf.st_size + 1, strerror(errno));
7711+                       return (1);
7712+               }
7713+               if (read(PrivKeyFD, PrivKey, nPrivKeyLen) != nPrivKeyLen)
7714+               {
7715+                       fprintf(stderr, "%s: read: %s\n", strerror(errno), program);
7716+                       return (1);
7717+               }
7718+               close(PrivKeyFD);
7719+               PrivKey[nPrivKeyLen] = '\0';
7720+               if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS)
7721+               {
7722+                       fprintf(stderr, "DKIMSignInit: failed to initialize signature %s\n", PrivKeyFile);
7723+                       return (1);
7724+               }
7725+               for (;;)
7726+               {
7727+                       if ((ret = read(0, Buffer, sizeof(Buffer) - 1)) == -1)
7728+                       {
7729+                               fprintf(stderr, "read: %s\n", strerror(errno));
7730+                               DKIMSignFree(&ctxt);
7731+                               return (1);
7732+                       } else
7733+                       if (!ret)
7734+                               break;
7735+                       if (DKIMSignProcess(&ctxt, Buffer, ret) == DKIM_INVALID_CONTEXT)
7736+                       {
7737+                               fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n");
7738+                               DKIMSignFree(&ctxt);
7739+                               return (1);
7740+                       }
7741+               }
7742+               if (DKIMSignGetSig2(&ctxt, PrivKey, &pSig) == DKIM_INVALID_CONTEXT)
7743+               {
7744+                       fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n");
7745+                       DKIMSignFree(&ctxt);
7746+                       return (1);
7747+               }
7748+               if (pSig)
7749+               {
7750+                       fwrite(pSig, 1, strlen(pSig), stdout);
7751+                       fwrite("\n", 1, 1, stdout);
7752+               }
7753+               DKIMSignFree(&ctxt);
7754+               return 0;
7755+       } else { /*- verify */
7756+               if (useADSP)
7757+                       vopts.nCheckPractices = useADSP;
7758+               else
7759+               if (useSSP)
7760+                       vopts.nCheckPractices = useSSP;
7761+               else
7762+                       vopts.nCheckPractices = 0;
7763+               vopts.nAccept3ps = accept3ps;
7764+               vopts.pfnSelectorCallback = NULL;       /*- SelectorCallback; */
7765+               vopts.nAllowUnsignedFromHeaders = nAllowUnsignedFromHeaders;
7766+               vopts.nSubjectRequired = nAllowUnsignedSubject;
7767+               DKIMVerifyInit(&ctxt, &vopts);          /*- this is always successful */
7768+               for (;;)
7769+               {
7770+                       if ((i = read(0, Buffer, sizeof(Buffer) - 1)) == -1)
7771+                       {
7772+                               fprintf(stderr, "read: %s\n", strerror(errno));
7773+                               DKIMVerifyFree(&ctxt);
7774+                               return (1);
7775+                       } else
7776+                       if (!i)
7777+                               break;
7778+                       ret = DKIMVerifyProcess(&ctxt, Buffer, i);
7779+                       dkim_error(ret);
7780+                       if (ret > 0 && ret < DKIM_FINISHED_BODY)
7781+                               ret = DKIM_FAIL;
7782+                       if (ret)
7783+                               break;
7784+               }
7785+               if (!ret)
7786+               {
7787+                       if ((ret = DKIMVerifyResults(&ctxt, &sCount, &sSize)) != DKIM_SUCCESS)
7788+                               dkim_error(ret);
7789+                       if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS)
7790+                               dkim_error(ret);
7791+                       else
7792+                       {
7793+                               for (ret = 0,i = 0; i < nSigCount; i++) {
7794+                                       if (verbose)
7795+                                               printf("Signature # %02d: ", i + 1);
7796+                                       if (pDetails[i].nResult >= 0)
7797+                                       {
7798+                                               if (verbose)
7799+                                                       printf("Success\n");
7800+                                               continue;
7801+                                       } else
7802+                                       {
7803+                                               ret = pDetails[i].nResult;
7804+                                               if (verbose)
7805+                                                       printf("Failure %d\n", ret);
7806+                                       }
7807+                               }
7808+                               if (!nSigCount)
7809+                                       ret = DKIM_NO_SIGNATURES;
7810+                       }
7811+               }
7812+               if (ret < 0 || ret == DKIM_3PS_SIGNATURE) {
7813+                       if (useADSP)
7814+                       {
7815+                               char           *domain;
7816+       
7817+                               if ((domain = DKIMVerifyGetDomain(&ctxt)))
7818+                                       resDKIMADSP = GetADSP(domain);
7819+                               if (sCount > 0) {
7820+                                       if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL)
7821+                                               ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
7822+                               }
7823+                               /* if the message should be signed, return fail */
7824+                               if (resDKIMADSP == DKIM_ADSP_DISCARDABLE)
7825+                                       ret = DKIM_FAIL;
7826+                               ret = DKIM_NEUTRAL;
7827+                       } else
7828+                       if (useSSP)
7829+                       {
7830+                               int             bTestingPractices = 0;
7831+                               char           *domain;
7832+
7833+                               if ((domain = DKIMVerifyGetDomain(&ctxt)))
7834+                                       resDKIMSSP = GetSSP(domain, &bTestingPractices);
7835+                               if (sCount > 0) {
7836+                                       if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL))
7837+                                               ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
7838+                               }
7839+                               // if the SSP is testing, return neutral
7840+                               if (bTestingPractices)
7841+                                       return(DKIM_NEUTRAL);
7842+                               /* if the message should be signed, return fail */
7843+                               if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT)
7844+                                       return(DKIM_FAIL);
7845+                               ret = DKIM_NEUTRAL;
7846+                       }
7847+               }
7848+               DKIMVerifyFree(&ctxt);
7849+               writeHeader(ret, resDKIMSSP, resDKIMADSP, useSSP, useADSP);
7850+               if ((dkimverify = getenv("DKIMVERIFY")))
7851+               {
7852+                       if (ret < 0)
7853+                       {
7854+                               if (dkimverify[str_chr(dkimverify, 'F' - ret)])
7855+                                       ret = 14; /*- return permanent error */
7856+                               if (dkimverify[str_chr(dkimverify, 'f' - ret)])
7857+                                       ret = 88; /*- return temporary error */
7858+                       } else
7859+                       {
7860+                               if (dkimverify[str_chr(dkimverify, 'A' + ret)])
7861+                                       ret = 14; /*- return permanent error */
7862+                               if (dkimverify[str_chr(dkimverify, 'a' + ret)])
7863+                                       ret = 88; /*- return temporary error */
7864+                       }
7865+               }
7866+               return (ret);
7867+       }
7868+       /*- Not Reached */
7869+       _exit(0);
7870+}
7871+
7872+void
7873+getversion_dkim_c()
7874+{
7875+       static char    *x = (char *) "$Id: dkim.c,v 1.19 2016-03-01 16:23:38+05:30 Cprogrammer Exp mbhangui $";
7876+
7877+       x++;
7878+}
7879diff -ruN ../netqmail-1.06-original/dkim.h netqmail-1.06/dkim.h
7880--- ../netqmail-1.06-original/dkim.h    1970-01-01 01:00:00.000000000 +0100
7881+++ netqmail-1.06/dkim.h        2018-04-03 14:46:51.364411606 +0200
7882@@ -0,0 +1,193 @@
7883+/*
7884+ * $Log: dkim.h,v $
7885+ * Revision 1.8  2015-12-15 16:03:18+05:30  Cprogrammer
7886+ * use time_t for expireTime
7887+ *
7888+ * Revision 1.7  2011-06-04 13:56:06+05:30  Cprogrammer
7889+ * corrected return codes
7890+ *
7891+ * Revision 1.6  2011-06-04 10:04:00+05:30  Cprogrammer
7892+ * unified error code for signing & verifcation
7893+ * added signature and identity domain information to
7894+ *     DKIMVerifyDetails structure
7895+ *
7896+ * Revision 1.5  2009-03-27 20:19:05+05:30  Cprogrammer
7897+ * major changes made for incorporating ADSP
7898+ *
7899+ * Revision 1.4  2009-03-26 19:28:15+05:30  Cprogrammer
7900+ * removed DKIM_3PS_PARTIAL_SUCCESS
7901+ *
7902+ * Revision 1.3  2009-03-26 15:11:33+05:30  Cprogrammer
7903+ * added ADSP
7904+ *
7905+ * Revision 1.2  2009-03-25 08:37:58+05:30  Cprogrammer
7906+ * changed definitions of constants to avoid clash between error and success
7907+ *
7908+ * Revision 1.1  2009-03-21 08:50:19+05:30  Cprogrammer
7909+ * Initial revision
7910+ *
7911+ *
7912+ *  Copyright 2005 Alt-N Technologies, Ltd.
7913+ *
7914+ *  Licensed under the Apache License, Version 2.0 (the "License");
7915+ *  you may not use this file except in compliance with the License.
7916+ *  You may obtain a copy of the License at
7917+ *
7918+ *      http://www.apache.org/licenses/LICENSE-2.0
7919+ *
7920+ *  This code incorporates intellectual property owned by Yahoo! and licensed
7921+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
7922+ *
7923+ *  Unless required by applicable law or agreed to in writing, software
7924+ *  distributed under the License is distributed on an "AS IS" BASIS,
7925+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7926+ *  See the License for the specific language governing permissions and
7927+ *  limitations under the License.
7928+ *
7929+ */
7930+
7931+#define DKIM_CALL
7932+#define MAKELONG(a,b) ((long)(((unsigned)(a) & 0xffff) | (((unsigned)(b) & 0xffff) << 16)))
7933+
7934+#ifdef __cplusplus
7935+extern          "C" {
7936+#endif
7937+
7938+#include <time.h>
7939+// DKIM Body hash versions
7940+#define DKIM_BODYHASH_ALLMAN_1 1
7941+#define DKIM_BODYHASH_IETF_1   2
7942+#define DKIM_BODYHASH_BOTH             DKIM_BODYHASH_ALLMAN_1 | DKIM_BODYHASH_IETF_1
7943+
7944+// DKIM hash algorithms
7945+#define DKIM_HASH_SHA1                 1
7946+#define DKIM_HASH_SHA256               2
7947+#define DKIM_HASH_SHA1_AND_256  DKIM_HASH_SHA1 | DKIM_HASH_SHA256
7948+
7949+// DKIM canonicalization methods
7950+#define DKIM_CANON_SIMPLE              1
7951+#define DKIM_CANON_NOWSP               2
7952+#define DKIM_CANON_RELAXED             3
7953+
7954+#define DKIM_SIGN_SIMPLE                       MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_SIMPLE)
7955+#define DKIM_SIGN_SIMPLE_RELAXED       MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_SIMPLE)
7956+#define DKIM_SIGN_RELAXED                      MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_RELAXED)
7957+#define DKIM_SIGN_RELAXED_SIMPLE       MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_RELAXED)
7958+
7959+// DKIM_SUCCESS                                 // verify result: all signatures verified
7960+// signature result: signature verified
7961+#define DKIM_SUCCESS                                            0      // operation successful
7962+#define DKIM_FINISHED_BODY                                      1      // process result: no more message body is needed
7963+#define DKIM_PARTIAL_SUCCESS                            2      // verify result: at least one but not all signatures verified
7964+#define DKIM_NEUTRAL                                            3      // verify result: no signatures verified but message is not suspicous
7965+#define DKIM_SUCCESS_BUT_EXTRA                          4      // signature result: signature verified but it did not include all of the body
7966+#define DKIM_3PS_SIGNATURE                                      5      // 3rd-party signature
7967+
7968+// DKIM Error codes
7969+#define DKIM_FAIL                                                      -1      // verify error: message is suspicious
7970+#define DKIM_BAD_SYNTAX                                                -2      // signature error: DKIM-Signature could not parse or has bad tags/values
7971+#define DKIM_SIGNATURE_BAD                                     -3      // signature error: RSA verify failed
7972+#define DKIM_SIGNATURE_BAD_BUT_TESTING         -4      // signature error: RSA verify failed but testing
7973+#define DKIM_SIGNATURE_EXPIRED                         -5      // signature error: x= is old
7974+#define DKIM_SELECTOR_INVALID                          -6      // signature error: selector doesn't parse or contains invalid values
7975+#define DKIM_SELECTOR_GRANULARITY_MISMATCH     -7      // signature error: selector g= doesn't match i=
7976+#define DKIM_SELECTOR_KEY_REVOKED                      -8      // signature error: selector p= empty
7977+#define DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG     -9      // signature error: selector domain name too long to request
7978+#define DKIM_SELECTOR_DNS_TEMP_FAILURE         -10     // signature error: temporary dns failure requesting selector
7979+#define DKIM_SELECTOR_DNS_PERM_FAILURE         -11     // signature error: permanent dns failure requesting selector
7980+#define DKIM_SELECTOR_PUBLIC_KEY_INVALID       -12     // signature error: selector p= value invalid or wrong format
7981+#define DKIM_NO_SIGNATURES                                     -13     // process error, no sigs
7982+#define DKIM_NO_VALID_SIGNATURES                       -14     // process error, no valid sigs
7983+#define DKIM_BODY_HASH_MISMATCH                                -15     // sigature verify error: message body does not hash to bh value
7984+#define DKIM_SELECTOR_ALGORITHM_MISMATCH       -16     // signature error: selector h= doesn't match signature a=
7985+#define DKIM_STAT_INCOMPAT                                     -17     // signature error: incompatible v=
7986+#define DKIM_UNSIGNED_FROM                  -18 // signature error: not all message's From headers in signature
7987+#define DKIM_OUT_OF_MEMORY                  -19 // memory allocation failed
7988+#define DKIM_INVALID_CONTEXT                -20 // DKIMContext structure invalid for this operation
7989+#define DKIM_NO_SENDER                      -21 // signing error: Could not find From: or Sender: header in message
7990+#define DKIM_BAD_PRIVATE_KEY                -22 // signing error: Could not parse private key
7991+#define DKIM_BUFFER_TOO_SMALL               -23 // signing error: Buffer passed in is not large enough
7992+#define DKIM_MAX_ERROR                      -24 // set this to 1 greater than the highest error code (but negative)
7993+
7994+#define DKIM_SSP_UNKNOWN                        1 /*- some messages may be signed */
7995+#define DKIM_SSP_ALL                            2 /*- all messages are signed, 3rd party allowed */
7996+#define DKIM_SSP_STRICT                                 3 /*- all messages are signed, no 3rd party allowed */
7997+#define DKIM_SSP_SCOPE                          4 /*- the domain should be considered invalid */
7998+#define DKIM_SSP_TEMPFAIL                       5 /*- Temporary Error */
7999+
8000+#define DKIM_ADSP_UNKNOWN                       1 /*- some messages may be signed */
8001+#define DKIM_ADSP_ALL                           2 /*- All message are signed with author signature */
8002+#define DKIM_ADSP_DISCARDABLE           3 /*- messages which fail verification are Discardable */
8003+#define DKIM_ADSP_SCOPE                                 4 /*- domain is out of scope */
8004+#define DKIM_ADSP_TEMPFAIL                      5 /*- Temporary Error */
8005+
8006+
8007+// This function is called once for each header in the message
8008+// return 1 to include this header in the signature and 0 to exclude.
8009+typedef int     (DKIM_CALL * DKIMHEADERCALLBACK) (const char *szHeader);
8010+
8011+// This function is called to retrieve a TXT record from DNS
8012+typedef int     (DKIM_CALL * DKIMDNSCALLBACK) (const char *szFQDN, char *szBuffer, int nBufLen);
8013+
8014+typedef struct DKIMContext_t {
8015+       unsigned int    reserved1;
8016+       unsigned int    reserved2;
8017+       void           *reserved3;
8018+} DKIMContext;
8019+
8020+typedef struct DKIMSignOptions_t {
8021+       int             nCanon; // canonization
8022+       int             nIncludeBodyLengthTag;  // 0 = don't include l= tag, 1 = include l= tag
8023+       int             nIncludeTimeStamp;      // 0 = don't include t= tag, 1 = include t= tag
8024+       int             nIncludeQueryMethod;    // 0 = don't include q= tag, 1 = include q= tag
8025+       char            szSelector[80]; // selector - required
8026+       char            szDomain[256];  // domain - optional - if empty, domain is computed from sender
8027+       char            szIdentity[256];        // for i= tag, if empty tag will not be included in sig
8028+       time_t          expireTime;     // for x= tag, if 0 tag will not be included in sig
8029+       DKIMHEADERCALLBACK pfnHeaderCallback;   // header callback
8030+       char            szRequiredHeaders[256]; // colon-separated list of headers that must be signed
8031+       int             nHash;  // use one of the DKIM_HASH_xx constants here
8032+       // even if not present in the message
8033+       int             nIncludeCopiedHeaders;  // 0 = don't include z= tag, 1 = include z= tag
8034+       int             nIncludeBodyHash;       // use one of the DKIM_BODYHASH_xx constants here
8035+} DKIMSignOptions;
8036+
8037+typedef struct DKIMVerifyOptions_t {
8038+       DKIMDNSCALLBACK pfnSelectorCallback;    // selector record callback
8039+       DKIMDNSCALLBACK pfnPracticesCallback;   // SSP record callback
8040+       int             nHonorBodyLengthTag;    // 0 = ignore l= tag, 1 = use l= tag to limit the amount of body verified
8041+       int             nCheckPractices;                // 0 = use default (unknown) practices, 1 = request and use sender's signing practices
8042+       int             nSubjectRequired;               // 0 = subject is required to be signed, 1 = not required
8043+       int             nSaveCanonicalizedData; // 0 = canonicalized data is not saved, 1 = canonicalized data is saved
8044+       int             nAllowUnsignedFromHeaders;      // 0 = From headers not included in the signature are not allowed, 1 = allowed
8045+       int             nAccept3ps;                             // 0 = don't check 3rd party signature(s), 1 = check 3rd party signature(s)
8046+} DKIMVerifyOptions;
8047+
8048+typedef struct DKIMVerifyDetails_t {
8049+       char           *szSignature;
8050+       char           *DNS;
8051+       char           *szSignatureDomain;
8052+       char           *szIdentityDomain;
8053+       char           *szCanonicalizedData;
8054+       int             nResult;
8055+} DKIMVerifyDetails;
8056+
8057+int DKIM_CALL   DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions * pOptions);
8058+int DKIM_CALL   DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength);
8059+int DKIM_CALL   DKIMSignGetSig(DKIMContext *pSignContext, char *szPrivKey, char *szSignature, int nSigLength);
8060+int DKIM_CALL   DKIMSignGetSig2(DKIMContext *pSignContext, char *szPrivKey, char **pszSignature);
8061+void DKIM_CALL  DKIMSignFree(DKIMContext *pSignContext);
8062+char           *DKIM_CALL DKIMSignGetDomain(DKIMContext *pSignContext);
8063+
8064+int DKIM_CALL   DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions * pOptions);
8065+int DKIM_CALL   DKIMVerifyProcess(DKIMContext *pVerifyContext, char *szBuffer, int nBufLength);
8066+int DKIM_CALL   DKIMVerifyResults(DKIMContext *pVerifyContext , int *sCount, int *sSize);
8067+int DKIM_CALL   DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices);
8068+char           *DKIM_CALL DKIMVerifyGetDomain(DKIMContext *pVerifyContext);
8069+void DKIM_CALL  DKIMVerifyFree(DKIMContext *pVerifyContext);
8070+char           *DKIM_CALL DKIMVersion();
8071+char           *DKIM_CALL DKIMGetErrorString(int ErrorCode);
8072+#include "macros.h"
8073+#ifdef __cplusplus
8074+}
8075+#endif
8076diff -ruN ../netqmail-1.06-original/dkimbase.cpp netqmail-1.06/dkimbase.cpp
8077--- ../netqmail-1.06-original/dkimbase.cpp      1970-01-01 01:00:00.000000000 +0100
8078+++ netqmail-1.06/dkimbase.cpp  2018-04-03 14:46:51.363411603 +0200
8079@@ -0,0 +1,336 @@
8080+/*
8081+ * $Log: dkimbase.cpp,v $
8082+ * Revision 1.4  2017-09-05 10:58:26+05:30  Cprogrammer
8083+ * removed compiler warnings
8084+ *
8085+ * Revision 1.3  2009-03-26 15:10:32+05:30  Cprogrammer
8086+ * fixed indentation
8087+ *
8088+ * Revision 1.2  2009-03-25 08:37:27+05:30  Cprogrammer
8089+ * fixed indentation
8090+ *
8091+ * Revision 1.1  2009-03-21 08:43:08+05:30  Cprogrammer
8092+ * Initial revision
8093+ *
8094+ *
8095+ *  Copyright 2005 Alt-N Technologies, Ltd.
8096+ *
8097+ *  Licensed under the Apache License, Version 2.0 (the "License");
8098+ *  you may not use this file except in compliance with the License.
8099+ *  You may obtain a copy of the License at
8100+ *
8101+ *      http://www.apache.org/licenses/LICENSE-2.0
8102+ *
8103+ *  This code incorporates intellectual property owned by Yahoo! and licensed
8104+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
8105+ *
8106+ *  Unless required by applicable law or agreed to in writing, software
8107+ *  distributed under the License is distributed on an "AS IS" BASIS,
8108+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8109+ *  See the License for the specific language governing permissions and
8110+ *  limitations under the License.
8111+ *
8112+ */
8113+
8114+#include "dkim.h"
8115+#include "dkimbase.h"
8116+#include <string.h>
8117+#include <algorithm>
8118+
8119+CDKIMBase::CDKIMBase()
8120+{
8121+       m_From = NULL;
8122+       m_Sender = NULL;
8123+       m_hTag = NULL;
8124+       m_hTagSize = 0;
8125+       m_hTagPos = 0;
8126+       m_Line = NULL;
8127+       m_LineSize = 0;
8128+       m_LinePos = 0;
8129+       m_InHeaders = true;
8130+}
8131+
8132+CDKIMBase::~CDKIMBase()
8133+{
8134+       Free(m_Line);
8135+       Free(m_From);
8136+       Free(m_Sender);
8137+       Free(m_hTag);
8138+}
8139+
8140+int
8141+CDKIMBase::Init(void)
8142+{
8143+       return DKIM_SUCCESS;
8144+}
8145+
8146+////////////////////////////////////////////////////////////////////////////////
8147+//
8148+// Alloc - allocate buffer
8149+//
8150+////////////////////////////////////////////////////////////////////////////////
8151+int CDKIMBase::Alloc(char *&szBuffer, int nRequiredSize)
8152+{
8153+       szBuffer = new char[nRequiredSize];
8154+
8155+       return (szBuffer == NULL) ? DKIM_OUT_OF_MEMORY : DKIM_SUCCESS;
8156+}
8157+
8158+
8159+////////////////////////////////////////////////////////////////////////////////
8160+//
8161+// ReAlloc - extend buffer if necessary, leaving room for future expansion
8162+//
8163+////////////////////////////////////////////////////////////////////////////////
8164+int CDKIMBase::ReAlloc(char *&szBuffer, int &nBufferSize, int nRequiredSize)
8165+{
8166+       if (nRequiredSize > nBufferSize) {
8167+               char           *
8168+                       newp;
8169+               int
8170+                       nNewSize = nRequiredSize + BUFFER_ALLOC_INCREMENT;
8171+
8172+               if (Alloc(newp, nNewSize) == DKIM_SUCCESS) {
8173+                       if (szBuffer != NULL && nBufferSize > 0) {
8174+                               memcpy(newp, szBuffer, nBufferSize);
8175+                               delete[]szBuffer;
8176+                       }
8177+                       szBuffer = newp;
8178+                       nBufferSize = nNewSize;
8179+               } else {
8180+                       return DKIM_OUT_OF_MEMORY;      // memory alloc error!
8181+               }
8182+       }
8183+       return DKIM_SUCCESS;
8184+}
8185+
8186+////////////////////////////////////////////////////////////////////////////////
8187+//
8188+// Process - split buffers into lines without any CRs or LFs at the end.
8189+//
8190+////////////////////////////////////////////////////////////////////////////////
8191+void CDKIMBase::Free(char *szBuffer)
8192+{
8193+       if (szBuffer)
8194+               delete[]szBuffer;
8195+}
8196+
8197+////////////////////////////////////////////////////////////////////////////////
8198+//
8199+// Process - split buffers into lines without any CRs or LFs at the end.
8200+//
8201+////////////////////////////////////////////////////////////////////////////////
8202+int CDKIMBase::Process(char *szBuffer, int nBufLength, bool bEOF)
8203+{
8204+       char           *p = szBuffer;
8205+       char           *e = szBuffer + nBufLength;
8206+
8207+       while (p < e) {
8208+               if (*p != '\n' && *p != '\r') {
8209+                       if (m_LinePos >= m_LineSize) {
8210+                               int nRet = ReAlloc(m_Line, m_LineSize, m_LinePos + 1);
8211+                               if (nRet != DKIM_SUCCESS)
8212+                                                       /*
8213+                                                        * How to distinguish between
8214+                                                        * DKIM_FINISHED_BODY & DKIM_OUT_OF_MEMORY
8215+                                                        */
8216+                                       return nRet;
8217+                       }
8218+                       m_Line[m_LinePos++] = *p;
8219+               } else {
8220+                       if (*p == '\r' && p + 1 < e && *(p + 1) == '\n')
8221+                               p++;
8222+                       if (m_InHeaders) {
8223+                       // process header line
8224+                               if (m_LinePos == 0) {
8225+                                       m_InHeaders = false;
8226+                                       int Result = ProcessHeaders();
8227+                                       if (Result != DKIM_SUCCESS)
8228+                                               return Result;
8229+                               } else {
8230+                               // append the header to the headers list
8231+                                       if (m_Line[0] != ' ' && m_Line[0] != '\t') {
8232+                                               HeaderList.push_back(string(m_Line, m_LinePos));
8233+                                       } else {
8234+                                               if (!HeaderList.empty()) {
8235+                                                       HeaderList.back().append("\r\n", 2).append(m_Line, m_LinePos);
8236+                                               } else {
8237+                                               // no header to append to...
8238+                                               }
8239+                                       }
8240+                               }
8241+                       } else {
8242+                               // process body line
8243+                               int Result = ProcessBody(m_Line, m_LinePos, bEOF);
8244+                               if (Result != DKIM_SUCCESS) {
8245+                                       m_LinePos = 0;
8246+                                       return Result;
8247+                               }
8248+                       }
8249+                       m_LinePos = 0;
8250+               }
8251+               p++;
8252+       }
8253+       return DKIM_SUCCESS;
8254+}
8255+
8256+
8257+////////////////////////////////////////////////////////////////////////////////
8258+//
8259+// ProcessFinal - process leftovers if stopping before the body or mid-line
8260+//
8261+////////////////////////////////////////////////////////////////////////////////
8262+int CDKIMBase::ProcessFinal(void)
8263+{
8264+       if (m_LinePos > 0) {
8265+               Process((char *) "\r\n", 2, true);
8266+       }
8267+
8268+       if (m_InHeaders) {
8269+               m_InHeaders = false;
8270+               ProcessHeaders();
8271+               ProcessBody((char *) "", 0, true);
8272+       }
8273+
8274+       return DKIM_SUCCESS;
8275+}
8276+
8277+
8278+////////////////////////////////////////////////////////////////////////////////
8279+//
8280+// ProcessHeaders - process the headers (to be implemented by derived class)
8281+//
8282+////////////////////////////////////////////////////////////////////////////////
8283+int CDKIMBase::ProcessHeaders()
8284+{
8285+       return DKIM_SUCCESS;
8286+}
8287+
8288+
8289+////////////////////////////////////////////////////////////////////////////////
8290+//
8291+// ProcessBody - process body line (to be implemented by derived class)
8292+//
8293+////////////////////////////////////////////////////////////////////////////////
8294+int CDKIMBase::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
8295+{
8296+       return DKIM_SUCCESS;
8297+}
8298+
8299+
8300+////////////////////////////////////////////////////////////////////////////////
8301+//
8302+// RemoveSWSP - remove streaming white space from buffer/string inline
8303+//
8304+////////////////////////////////////////////////////////////////////////////////
8305+
8306+struct isswsp {
8307+       bool
8308+       operator() (char ch) {
8309+               return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
8310+       }
8311+};
8312+
8313+void CDKIMBase::RemoveSWSP(char *szBuffer)
8314+{
8315+       *remove_if(szBuffer, szBuffer + strlen(szBuffer), isswsp()) = '\0';
8316+}
8317+
8318+void CDKIMBase::RemoveSWSP(char *pBuffer, int &nBufLength)
8319+{
8320+       nBufLength = remove_if(pBuffer, pBuffer + nBufLength, isswsp()) - pBuffer;
8321+}
8322+
8323+void CDKIMBase::RemoveSWSP(string &sBuffer)
8324+{
8325+       sBuffer.erase(remove_if(sBuffer.begin(), sBuffer.end(), isswsp()), sBuffer.end());
8326+}
8327+
8328+
8329+//////////////////////////////////////////////////////////////////////////////////////////
8330+//
8331+// CompressSWSP - compress streaming white space into single spaces from buffer/string inline
8332+//
8333+//////////////////////////////////////////////////////////////////////////////////////////
8334+
8335+void CDKIMBase::CompressSWSP(char *pBuffer, int &nBufLength)
8336+{
8337+       char           *pSrc = pBuffer;
8338+       char           *pDst = pBuffer;
8339+       char           *pEnd = pBuffer + nBufLength;
8340+
8341+       while (pSrc != pEnd) {
8342+               if (isswsp()(*pSrc)) {
8343+                       do {
8344+                               ++pSrc;
8345+                       } while (pSrc != pEnd && isswsp()(*pSrc));
8346+                       if (pSrc == pEnd)
8347+                               break;
8348+                       *pDst++ = ' ';
8349+               }
8350+               *pDst++ = *pSrc++;
8351+       }
8352+       nBufLength = pDst - pBuffer;
8353+}
8354+
8355+void CDKIMBase::CompressSWSP(string &sBuffer)
8356+{
8357+       string::iterator iSrc = sBuffer.begin();
8358+       string::iterator iDst = sBuffer.begin();
8359+       string::iterator iEnd = sBuffer.end();
8360+
8361+       while (iSrc != iEnd) {
8362+               if (isswsp()(*iSrc)) {
8363+                       do {
8364+                               ++iSrc;
8365+                       } while (iSrc != iEnd && isswsp()(*iSrc));
8366+
8367+                       if (iSrc == iEnd)
8368+                               break;
8369+                       *iDst++ = ' ';
8370+               }
8371+               *iDst++ = *iSrc++;
8372+       }
8373+       sBuffer.erase(iDst, iEnd);
8374+}
8375+
8376+//////////////////////////////////////////////////////////////////////////////////////////
8377+//
8378+// RelaxHeader - relax a header field (lower case the name, remove swsp before and after :)
8379+//
8380+// modified 4/21/06 STB to remove white space before colon
8381+//
8382+//////////////////////////////////////////////////////////////////////////////////////////
8383+
8384+string CDKIMBase::RelaxHeader(const string &sHeader)
8385+{
8386+       string sTemp = sHeader;
8387+
8388+       CompressSWSP(sTemp);
8389+
8390+       int cpos = sTemp.find(':');
8391+       if (cpos == -1) {
8392+       // no colon?!
8393+       } else {
8394+       // lower case the header field name
8395+               for (int i = 0; i < cpos; i++) {
8396+                       if (sTemp[i] >= 'A' && sTemp[i] <= 'Z')
8397+                               sTemp[i] += 'a' - 'A';
8398+               }
8399+       // remove the space after the :
8400+               if ((unsigned int) (cpos + 1) < sTemp.length() && sTemp[cpos + 1] == ' ')
8401+                       sTemp.erase(cpos + 1, 1);
8402+       // remove the space before the :
8403+               if (cpos > 0 && sTemp[cpos - 1] == ' ')
8404+                       sTemp.erase(cpos - 1, 1);
8405+       }
8406+       return sTemp;
8407+}
8408+
8409+void
8410+getversion_dkimbase_cpp()
8411+{
8412+       static char    *x = (char *) "$Id: dkimbase.cpp,v 1.4 2017-09-05 10:58:26+05:30 Cprogrammer Exp mbhangui $";
8413+
8414+       x++;
8415+}
8416diff -ruN ../netqmail-1.06-original/dkimbase.h netqmail-1.06/dkimbase.h
8417--- ../netqmail-1.06-original/dkimbase.h        1970-01-01 01:00:00.000000000 +0100
8418+++ netqmail-1.06/dkimbase.h    2018-04-03 14:46:51.363411603 +0200
8419@@ -0,0 +1,72 @@
8420+/*
8421+ * $Log: dkimbase.h,v $
8422+ * Revision 1.1  2009-03-21 08:50:18+05:30  Cprogrammer
8423+ * Initial revision
8424+ *
8425+ *
8426+ *  Copyright 2005 Alt-N Technologies, Ltd.
8427+ *
8428+ *  Licensed under the Apache License, Version 2.0 (the "License");
8429+ *  you may not use this file except in compliance with the License.
8430+ *  You may obtain a copy of the License at
8431+ *
8432+ *      http://www.apache.org/licenses/LICENSE-2.0
8433+ *
8434+ *  This code incorporates intellectual property owned by Yahoo! and licensed
8435+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
8436+ *
8437+ *  Unless required by applicable law or agreed to in writing, software
8438+ *  distributed under the License is distributed on an "AS IS" BASIS,
8439+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8440+ *  See the License for the specific language governing permissions and
8441+ *  limitations under the License.
8442+ *
8443+ */
8444+
8445+#ifndef DKIMBASE_H
8446+#define DKIMBASE_H
8447+
8448+#include <openssl/evp.h>
8449+#include <openssl/pem.h>
8450+#include <openssl/err.h>
8451+
8452+#define BUFFER_ALLOC_INCREMENT 256
8453+
8454+#include <string>
8455+#include <list>
8456+
8457+using namespace std;
8458+class           CDKIMBase {
8459+public:
8460+
8461+       CDKIMBase();
8462+       ~CDKIMBase();
8463+
8464+       int             Init(void);
8465+       int             Process(char *szBuffer, int nBufLength, bool bEOF);
8466+       int             ProcessFinal(void);
8467+       int             Alloc(char *&szBuffer, int nRequiredSize);
8468+       int             ReAlloc(char *&szBuffer, int &nBufferLength, int nRequiredSize);
8469+       void            Free(char *szBuffer);
8470+       static void     RemoveSWSP(char *szBuffer);
8471+       static void     RemoveSWSP(char *pBuffer, int &nBufLength);
8472+       static void     RemoveSWSP(string & sBuffer);
8473+       static void     CompressSWSP(char *pBuffer, int &nBufLength);
8474+       static void     CompressSWSP(string & sBuffer);
8475+       static string   RelaxHeader(const string & sHeader);
8476+       virtual int     ProcessHeaders(void);
8477+       virtual int     ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
8478+
8479+protected:
8480+       char           *m_From;
8481+       char           *m_Sender;
8482+       char           *m_hTag;
8483+       int             m_hTagSize;
8484+       int             m_hTagPos;
8485+       char           *m_Line;
8486+       int             m_LineSize;
8487+       int             m_LinePos;
8488+       bool            m_InHeaders;
8489+                       list < string > HeaderList;
8490+};
8491+#endif /*- DKIMBASE_H */
8492diff -ruN ../netqmail-1.06-original/dkimdns.cpp netqmail-1.06/dkimdns.cpp
8493--- ../netqmail-1.06-original/dkimdns.cpp       1970-01-01 01:00:00.000000000 +0100
8494+++ netqmail-1.06/dkimdns.cpp   2018-04-03 14:46:51.364411606 +0200
8495@@ -0,0 +1,322 @@
8496+/*
8497+ * $Log: dns.cpp,v $
8498+ * Revision 1.10  2017-09-05 11:01:06+05:30  Cprogrammer
8499+ * removed unused variables
8500+ *
8501+ * Revision 1.9  2017-09-01 12:46:27+05:30  Cprogrammer
8502+ * fixed double free() of dnresult
8503+ *
8504+ * Revision 1.8  2017-08-09 22:06:33+05:30  Cprogrammer
8505+ * fixed resolve() function
8506+ *
8507+ * Revision 1.7  2017-05-16 12:40:23+05:30  Cprogrammer
8508+ * refactored dns_text() function
8509+ *
8510+ * Revision 1.6  2017-05-10 14:58:06+05:30  Cprogrammer
8511+ * increase responselen to 1024 for long text records
8512+ *
8513+ * Revision 1.5  2017-05-10 12:27:49+05:30  Cprogrammer
8514+ * use packetsize > 512 to avoid dkim failures for sites having long txt records (hotmail.com)
8515+ *
8516+ * Revision 1.4  2009-06-11 13:58:39+05:30  Cprogrammer
8517+ * port for DARWIN
8518+ *
8519+ * Revision 1.3  2009-03-27 19:22:45+05:30  Cprogrammer
8520+ * dns functions
8521+ *
8522+ */
8523+#include <netdb.h>
8524+#include <stdlib.h>
8525+#include <errno.h>
8526+#include <sys/types.h>
8527+#include <netinet/in.h>
8528+#ifdef DARWIN
8529+#include <nameser8_compat.h>
8530+#endif
8531+#include <arpa/nameser.h>
8532+#include <resolv.h>
8533+#include <string.h>
8534+#include "dkimdns.h"
8535+
8536+static unsigned short
8537+getshort(unsigned char *cp)
8538+{
8539+       return (cp[0] << 8) | cp[1];
8540+}
8541+
8542+static struct
8543+{
8544+       unsigned char  *buf;
8545+} response;
8546+static int      responsebuflen = 0;
8547+static int      responselen;
8548+static unsigned char *responseend;
8549+static unsigned char *responsepos;
8550+static u_long   saveresoptions;
8551+static int      numanswers;
8552+static char     name[MAXDNAME];
8553+
8554+static int
8555+resolve(char *domain, int type)
8556+{
8557+       int             n, i;
8558+       unsigned char  *ptr;
8559+
8560+       errno = 0;
8561+       if (!responsebuflen) {
8562+               if ((response.buf = (unsigned char *) malloc(PACKETSZ + 1)))
8563+                       responsebuflen = PACKETSZ + 1;
8564+               else
8565+                       return DNS_MEM;
8566+       }
8567+       responselen = res_query(domain, C_IN, type, response.buf, responsebuflen);
8568+       if ((responselen >= responsebuflen) || (responselen > 0 && (((HEADER *) response.buf)->tc))) {
8569+               if (responsebuflen < 65536) {
8570+                       if ((ptr = (unsigned char *) realloc((void *) response.buf, 65536))) {
8571+                               response.buf = ptr;
8572+                               responsebuflen = 65536;
8573+                       } else {
8574+                               free(response.buf);
8575+                               responsebuflen = 0;
8576+                               return DNS_MEM;
8577+                       }
8578+               }
8579+               saveresoptions = _res.options;
8580+               _res.options |= RES_USEVC;
8581+               responselen = res_query(domain, C_IN, type, response.buf, responsebuflen);
8582+               _res.options = saveresoptions;
8583+       }
8584+       if (responselen <= 0) {
8585+               if (errno == ECONNREFUSED)
8586+                       return DNS_SOFT;
8587+               if (h_errno == TRY_AGAIN)
8588+                       return DNS_SOFT;
8589+               return DNS_HARD;
8590+       }
8591+       responseend = response.buf + responselen;
8592+       responsepos = response.buf + sizeof(HEADER);
8593+       n = ntohs(((HEADER *) response.buf)->qdcount);
8594+       while (n-- > 0) {
8595+               if ((i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME)) < 0)
8596+                       return DNS_SOFT;
8597+               responsepos += i;
8598+               i = responseend - responsepos;
8599+               if (i < QFIXEDSZ)
8600+                       return DNS_SOFT;
8601+               responsepos += QFIXEDSZ;
8602+       }
8603+       numanswers = ntohs(((HEADER *) response.buf)->ancount);
8604+       return 0;
8605+}
8606+
8607+void
8608+byte_copy(register char *to, register unsigned int n, register char *from)
8609+{
8610+       for (;;) {
8611+               if (!n)
8612+                       return;
8613+               *to++ = *from++;
8614+               --n;
8615+               if (!n)
8616+                       return;
8617+               *to++ = *from++;
8618+               --n;
8619+               if (!n)
8620+                       return;
8621+               *to++ = *from++;
8622+               --n;
8623+               if (!n)
8624+                       return;
8625+               *to++ = *from++;
8626+               --n;
8627+       }
8628+}
8629+
8630+static char     *txt;
8631+static int      txtlen;
8632+
8633+static int
8634+findtxt(int wanttype, int *txt_strlen)
8635+{
8636+       unsigned short  rrtype, rrdlen;
8637+       char           *ptr;
8638+       int             i;
8639+
8640+       if (numanswers <= 0)
8641+               return 2;
8642+       --numanswers;
8643+       if (responsepos == responseend)
8644+               return DNS_SOFT;
8645+
8646+       if ((i = dn_expand(response.buf, responseend, responsepos, name, MAXDNAME)) < 0)
8647+               return DNS_SOFT;
8648+       responsepos += i;
8649+
8650+       if ((i = responseend - responsepos) < 4 + 3 * 3)
8651+               return DNS_SOFT;
8652+
8653+       rrtype = getshort(responsepos);
8654+       rrdlen = getshort(responsepos + 8);
8655+       responsepos += 10;
8656+
8657+       *txt_strlen = 0;
8658+       if (!txtlen) {
8659+               if (!(txt = (char *) malloc(PACKETSZ * 2 * sizeof(char))))
8660+                       return DNS_MEM;
8661+               txtlen = PACKETSZ * 2;
8662+       }
8663+       if (rrtype == wanttype) {
8664+               unsigned short  txtpos;
8665+               unsigned char   n;
8666+
8667+               *txt = 0;
8668+               for (txtpos = 0; txtpos < rrdlen; txtpos += n) {
8669+                       n = responsepos[txtpos++];
8670+                       if (n > rrdlen - txtpos)
8671+                               n = rrdlen - txtpos;
8672+                       if ((*txt_strlen + n + 1) > txtlen) {
8673+                               if (!(ptr = (char *) realloc(txt, (*txt_strlen + n) * 2)))
8674+                                       return DNS_MEM;
8675+                               txtlen = (*txt_strlen + n) * 2;
8676+                       }
8677+                       byte_copy(txt + *txt_strlen, n, (char *) &responsepos[txtpos]);
8678+                       *txt_strlen += n;
8679+               }
8680+               responsepos += rrdlen;
8681+               txt[*txt_strlen] = 0;
8682+               return 1;
8683+       }
8684+       responsepos += rrdlen;
8685+       return 0;
8686+}
8687+
8688+static char    *dnresult;
8689+static int      dnresultlen;
8690+
8691+static int
8692+dns_txtplus(char *domain)
8693+{
8694+       int             r, len, total;
8695+       char           *ptr;
8696+
8697+       switch (resolve(domain, T_TXT))
8698+       {
8699+       case DNS_MEM:
8700+               return DNS_MEM;
8701+       case DNS_SOFT:
8702+               return DNS_SOFT;
8703+       case DNS_HARD:
8704+               return DNS_HARD;
8705+       }
8706+       total = 0;
8707+       if (!dnresultlen) {
8708+               if (!(dnresult = (char *) malloc ((2 * PACKETSZ) * sizeof(char))))
8709+                       return DNS_MEM;
8710+               dnresultlen = 2 * PACKETSZ;
8711+       }
8712+       while ((r = findtxt(T_TXT, &len)) != 2) {
8713+               if (r == DNS_SOFT) {
8714+                       if (txtlen) {
8715+                               txtlen = 0;
8716+                               free(txt);
8717+                       }
8718+                       return DNS_SOFT;
8719+               }
8720+               if (r == 1) {
8721+                       if ((total + len + 1) >= dnresultlen) {
8722+                               if (!(ptr = (char *) realloc(dnresult, (total + len) * 2))) {
8723+                                       dnresultlen = 0;
8724+                                       if (txtlen) {
8725+                                               txtlen = 0;
8726+                                               free(txt);
8727+                                       }
8728+                                       return DNS_MEM;
8729+                               }
8730+                               dnresultlen = (total + len) * 2;
8731+                       }
8732+                       byte_copy(dnresult + total, len, txt);
8733+                       total += len;
8734+               }
8735+       }
8736+       if (txtlen) {
8737+               txtlen = 0;
8738+               free(txt);
8739+       }
8740+       if (total) {
8741+               dnresult[total] = 0;
8742+               return (0);
8743+       }
8744+       return DNS_HARD;
8745+}
8746+
8747+/*
8748+ * we always return a null-terminated string which has been malloc'ed.  The string
8749+ * is always in the tag=value form.  If a temporary or permanent error occurs,
8750+ * the string will be exactly "e=perm;" or "e=temp;".
8751+ * Note that it never returns NULL.
8752+ */
8753+char           *
8754+dns_text(char *dn)
8755+{
8756+       int             r;
8757+       
8758+       switch (r = dns_txtplus(dn))
8759+       {
8760+       case DNS_MEM:
8761+       case DNS_SOFT:
8762+               if (responsebuflen) {
8763+                       free(response.buf);
8764+                       responsebuflen = 0;
8765+               }
8766+               return strdup("e=temp;");
8767+       case DNS_HARD:
8768+               if (responsebuflen) {
8769+                       free(response.buf);
8770+                       responsebuflen = 0;
8771+               }
8772+               return strdup("e=perm;");
8773+       }
8774+       if (responsebuflen) {
8775+               free(response.buf);
8776+               responsebuflen = 0;
8777+       }
8778+       return dnresult;
8779+}
8780+
8781+
8782+int
8783+DNSGetTXT(const char *domain, char *buffer, int maxlen)
8784+{
8785+       char           *results;
8786+       int             len;
8787+
8788+       results = dns_text((char *) domain);
8789+       if (!strcmp(results, "e=perm;")) {
8790+               free(results);
8791+               dnresultlen = 0;
8792+               return DNSRESP_PERM_FAIL;
8793+       } else
8794+       if (!strcmp(results, "e=temp;")) {
8795+               free(results);
8796+               dnresultlen = 0;
8797+               return DNSRESP_TEMP_FAIL;
8798+       }
8799+       if ((len = strlen(results)) > maxlen - 1) {
8800+               free(results);
8801+               dnresultlen = 0;
8802+               return DNSRESP_DOMAIN_NAME_TOO_LONG;
8803+       }
8804+       byte_copy(buffer, len, results);
8805+       buffer[len] = 0;
8806+       free(results);
8807+       dnresultlen = 0;
8808+       return DNSRESP_SUCCESS;
8809+}
8810+
8811+void
8812+getversion_dkimdns_cpp()
8813+{
8814+       static char    *x = (char *) "$Id: dns.cpp,v 1.10 2017-09-05 11:01:06+05:30 Cprogrammer Exp mbhangui $";
8815+
8816+       x++;
8817+}
8818diff -ruN ../netqmail-1.06-original/dkimdns.h netqmail-1.06/dkimdns.h
8819--- ../netqmail-1.06-original/dkimdns.h 1970-01-01 01:00:00.000000000 +0100
8820+++ netqmail-1.06/dkimdns.h     2018-04-03 14:46:51.364411606 +0200
8821@@ -0,0 +1,54 @@
8822+/*
8823+ * $Log: dns.h,v $
8824+ * Revision 1.4  2017-05-16 12:40:39+05:30  Cprogrammer
8825+ * define DNS_SOFT, DNS_HARD and DNS_MEM
8826+ *
8827+ * Revision 1.3  2009-03-27 20:40:16+05:30  Cprogrammer
8828+ * removed windows definitions
8829+ *
8830+ * Revision 1.2  2009-03-27 19:23:01+05:30  Cprogrammer
8831+ * added dns_text()
8832+ *
8833+ * Revision 1.1  2009-03-21 08:50:24+05:30  Cprogrammer
8834+ * Initial revision
8835+ *
8836+ *  Copyright 2005 Alt-N Technologies, Ltd.
8837+ *
8838+ *  Licensed under the Apache License, Version 2.0 (the "License");
8839+ *  you may not use this file except in compliance with the License.
8840+ *  You may obtain a copy of the License at
8841+ *
8842+ *      http://www.apache.org/licenses/LICENSE-2.0
8843+ *
8844+ *  This code incorporates intellectual property owned by Yahoo! and licensed
8845+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
8846+ *
8847+ *  Unless required by applicable law or agreed to in writing, software
8848+ *  distributed under the License is distributed on an "AS IS" BASIS,
8849+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8850+ *  See the License for the specific language governing permissions and
8851+ *  limitations under the License.
8852+ *
8853+ */
8854+
8855+// These DNS resolution routines are encapsulated by the API below
8856+
8857+// return values for DNS functions:
8858+
8859+
8860+#define MAX_DOMAIN                     254
8861+
8862+#define DNSRESP_SUCCESS                                        0       // DNS lookup returned sought after records
8863+#define DNSRESP_TEMP_FAIL                              1       // No response from DNS server
8864+#define DNSRESP_PERM_FAIL                              2       // DNS server returned error or no records
8865+#define DNSRESP_DOMAIN_NAME_TOO_LONG   3       // Domain name too long
8866+#define DNSRESP_NXDOMAIN                               4       // DNS server returned Name Error
8867+#define DNSRESP_EMPTY                                  5       // DNS server returned successful response but no records
8868+
8869+#define DNS_SOFT -1
8870+#define DNS_HARD -2
8871+#define DNS_MEM  -3
8872+
8873+// Pass in the FQDN to get the TXT record
8874+int             DNSGetTXT(const char *szFQDN, char *Buffer, int nBufLen);
8875+char           *dns_text(char *szFQDN);
8876diff -ruN ../netqmail-1.06-original/dkimfuncs.cpp netqmail-1.06/dkimfuncs.cpp
8877--- ../netqmail-1.06-original/dkimfuncs.cpp     1970-01-01 01:00:00.000000000 +0100
8878+++ netqmail-1.06/dkimfuncs.cpp 2018-04-03 14:46:51.364411606 +0200
8879@@ -0,0 +1,236 @@
8880+/*
8881+ * $Log: dkimfuncs.cpp,v $
8882+ * Revision 1.4  2011-06-04 10:06:33+05:30  Cprogrammer
8883+ * unified error strings for signing & verification
8884+ *
8885+ * Revision 1.3  2009-04-15 20:45:29+05:30  Cprogrammer
8886+ * code beautified
8887+ *
8888+ * Revision 1.2  2009-03-26 15:11:12+05:30  Cprogrammer
8889+ * added GetDomain(), ADSP
8890+ *
8891+ * Revision 1.1  2009-03-21 08:43:10+05:30  Cprogrammer
8892+ * Initial revision
8893+ *
8894+ *
8895+ *  Copyright 2005 Alt-N Technologies, Ltd.
8896+ *
8897+ *  Licensed under the Apache License, Version 2.0 (the "License");
8898+ *  you may not use this file except in compliance with the License.
8899+ *  You may obtain a copy of the License at
8900+ *
8901+ *      http://www.apache.org/licenses/LICENSE-2.0
8902+ *
8903+ *  This code incorporates intellectual property owned by Yahoo! and licensed
8904+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
8905+ *
8906+ *  Unless required by applicable law or agreed to in writing, software
8907+ *  distributed under the License is distributed on an "AS IS" BASIS,
8908+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8909+ *  See the License for the specific language governing permissions and
8910+ *  limitations under the License.
8911+ *
8912+ */
8913+#ifdef HAVE_CONFIG_H
8914+#include "config.h"
8915+#endif
8916+#include "dkim.h"
8917+#include "dkimsign.h"
8918+#include "dkimverify.h"
8919+#include <string.h>
8920+
8921+#define DKIMID ('D' | 'K'<<8 | 'I'<<16 | 'M'<<24)
8922+
8923+static void
8924+InitContext(DKIMContext *pContext, bool bSign, void *pObject)
8925+{
8926+       pContext->reserved1 = DKIMID;
8927+       pContext->reserved2 = bSign ? 1 : 0;
8928+       pContext->reserved3 = pObject;
8929+}
8930+
8931+static void    *
8932+ValidateContext(DKIMContext *pContext, bool bSign)
8933+{
8934+       if (pContext->reserved1 != DKIMID)
8935+               return NULL;
8936+       if (pContext->reserved2 != (unsigned int) (bSign ? 1 : 0))
8937+               return NULL;
8938+       return pContext->reserved3;
8939+}
8940+
8941+int             DKIM_CALL
8942+DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions)
8943+{
8944+       int             nRet = DKIM_OUT_OF_MEMORY;
8945+       CDKIMSign      *pSign = new CDKIMSign;
8946+
8947+       if (pSign) {
8948+               nRet = pSign->Init(pOptions);
8949+               if (nRet != DKIM_SUCCESS)
8950+                       delete          pSign;
8951+       }
8952+       if (nRet == DKIM_SUCCESS)
8953+               InitContext(pSignContext, true, pSign);
8954+       return nRet;
8955+}
8956+
8957+int             DKIM_CALL
8958+DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength)
8959+{
8960+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
8961+       if (pSign)
8962+               return pSign->Process(szBuffer, nBufLength, false);
8963+       return DKIM_INVALID_CONTEXT;
8964+}
8965+
8966+int             DKIM_CALL
8967+DKIMSignGetSig(DKIMContext *pSignContext, char *szPrivKey, char *szSignature, unsigned int nSigLength)
8968+{
8969+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
8970+       if (pSign)
8971+               return pSign->GetSig(szPrivKey, szSignature, nSigLength);
8972+       return DKIM_INVALID_CONTEXT;
8973+}
8974+
8975+int             DKIM_CALL
8976+DKIMSignGetSig2(DKIMContext *pSignContext, char *szPrivKey, char **pszSignature)
8977+{
8978+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
8979+       if (pSign)
8980+               return pSign->GetSig2(szPrivKey, pszSignature);
8981+       return DKIM_INVALID_CONTEXT;
8982+}
8983+
8984+char           *DKIM_CALL
8985+DKIMSignGetDomain(DKIMContext *pSignContext)
8986+{
8987+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
8988+       if (pSign)
8989+               return pSign->GetDomain();
8990+       return ((char *) 0);
8991+}
8992+
8993+void            DKIM_CALL
8994+DKIMSignFree(DKIMContext *pSignContext)
8995+{
8996+       CDKIMSign      *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
8997+       if (pSign) {
8998+               delete          pSign;
8999+               pSignContext->reserved3 = NULL;
9000+       }
9001+}
9002+
9003+int             DKIM_CALL
9004+DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOptions)
9005+{
9006+       int             nRet = DKIM_OUT_OF_MEMORY;
9007+       CDKIMVerify    *pVerify = new CDKIMVerify;
9008+       if (pVerify) {
9009+               nRet = pVerify->Init(pOptions);
9010+               if (nRet != DKIM_SUCCESS)
9011+                       delete          pVerify;
9012+       }
9013+       if (nRet == DKIM_SUCCESS)
9014+               InitContext(pVerifyContext, false, pVerify);
9015+       return nRet;
9016+}
9017+
9018+int             DKIM_CALL
9019+DKIMVerifyProcess(DKIMContext *pVerifyContext, char *szBuffer, int nBufLength)
9020+{
9021+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9022+       if (pVerify)
9023+               return pVerify->Process(szBuffer, nBufLength, false);
9024+       return DKIM_INVALID_CONTEXT;
9025+}
9026+
9027+int             DKIM_CALL
9028+DKIMVerifyResults( DKIMContext *pVerifyContext , int *sCount, int *sSize)
9029+{
9030+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9031+       if (pVerify)
9032+               return pVerify->GetResults(sCount, sSize);
9033+       return DKIM_INVALID_CONTEXT;
9034+}
9035+
9036+int             DKIM_CALL
9037+DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices)
9038+{
9039+       szPractices[0] = '\0';
9040+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9041+       if (pVerify) {
9042+               strcpy(szPractices, pVerify->GetPractices());
9043+               return pVerify->GetDetails(nSigCount, pDetails);
9044+       }
9045+       return DKIM_INVALID_CONTEXT;
9046+}
9047+
9048+void            DKIM_CALL
9049+DKIMVerifyFree(DKIMContext *pVerifyContext)
9050+{
9051+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9052+       if (pVerify) {
9053+               delete          pVerify;
9054+               pVerifyContext->reserved3 = NULL;
9055+       }
9056+}
9057+
9058+char           *DKIM_CALL
9059+DKIMVerifyGetDomain(DKIMContext *pVerifyContext)
9060+{
9061+       CDKIMVerify    *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
9062+       if (pVerify)
9063+               return pVerify->GetDomain();
9064+       return ((char *) 0);
9065+}
9066+
9067+char           *DKIM_CALL
9068+DKIMVersion()
9069+{
9070+       return (char *) "1.4";
9071+}
9072+
9073+static char    *DKIMErrorStrings[-1 - DKIM_MAX_ERROR] = {
9074+       (char *) "DKIM_FAIL",
9075+       (char *) "DKIM_BAD_SYNTAX",
9076+       (char *) "DKIM_SIGNATURE_BAD",
9077+       (char *) "DKIM_SIGNATURE_BAD_BUT_TESTING",
9078+       (char *) "DKIM_SIGNATURE_EXPIRED",
9079+       (char *) "DKIM_SELECTOR_INVALID",
9080+       (char *) "DKIM_SELECTOR_GRANULARITY_MISMATCH",
9081+       (char *) "DKIM_SELECTOR_KEY_REVOKED",
9082+       (char *) "DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG",
9083+       (char *) "DKIM_SELECTOR_DNS_TEMP_FAILURE",
9084+       (char *) "DKIM_SELECTOR_DNS_PERM_FAILURE",
9085+       (char *) "DKIM_SELECTOR_PUBLIC_KEY_INVALID",
9086+       (char *) "DKIM_NO_SIGNATURES",
9087+       (char *) "DKIM_NO_VALID_SIGNATURES",
9088+       (char *) "DKIM_BODY_HASH_MISMATCH",
9089+       (char *) "DKIM_SELECTOR_ALGORITHM_MISMATCH",
9090+       (char *) "DKIM_STAT_INCOMPAT",
9091+       (char *) "DKIM_UNSIGNED_FROM",
9092+       (char *) "DKIM_OUT_OF_MEMORY",
9093+       (char *) "DKIM_INVALID_CONTEXT",
9094+       (char *) "DKIM_NO_SENDER",
9095+       (char *) "DKIM_BAD_PRIVATE_KEY",
9096+       (char *) "DKIM_BUFFER_TOO_SMALL"
9097+};
9098+
9099+char           *DKIM_CALL
9100+DKIMGetErrorString(int ErrorCode)
9101+{
9102+       if (ErrorCode >= 0 || ErrorCode <= DKIM_MAX_ERROR)
9103+               return (char *) "Unknown";
9104+
9105+       else
9106+               return DKIMErrorStrings[-1 - ErrorCode];
9107+}
9108+
9109+void
9110+getversion_dkimfuncs_cpp()
9111+{
9112+       static char    *x = (char *) "$Id: dkimfuncs.cpp,v 1.4 2011-06-04 10:06:33+05:30 Cprogrammer Exp mbhangui $";
9113+
9114+       x++;
9115+}
9116diff -ruN ../netqmail-1.06-original/dkimsign.cpp netqmail-1.06/dkimsign.cpp
9117--- ../netqmail-1.06-original/dkimsign.cpp      1970-01-01 01:00:00.000000000 +0100
9118+++ netqmail-1.06/dkimsign.cpp  2018-04-03 14:46:51.365411610 +0200
9119@@ -0,0 +1,1016 @@
9120+/*
9121+ * $Log: dkimsign.cpp,v $
9122+ * Revision 1.11  2017-09-05 10:59:03+05:30  Cprogrammer
9123+ * removed compiler warnings
9124+ *
9125+ * Revision 1.10  2017-08-09 22:02:13+05:30  Cprogrammer
9126+ * replaced EVP_MD_CTX_free() with EVP_MD_CTX_reset()
9127+ *
9128+ * Revision 1.9  2017-08-08 23:50:19+05:30  Cprogrammer
9129+ * openssl 1.1.0 port
9130+ *
9131+ * Revision 1.8  2013-07-16 20:18:03+05:30  Cprogrammer
9132+ * replace '%' with domain name in selector
9133+ *
9134+ * Revision 1.7  2013-06-11 00:02:39+05:30  Cprogrammer
9135+ * removed header iostream
9136+ *
9137+ * Revision 1.6  2013-06-09 16:41:28+05:30  Cprogrammer
9138+ * parse address properly from From and Sender header
9139+ *
9140+ * Revision 1.5  2009-04-16 10:32:38+05:30  Cprogrammer
9141+ * added DKIMDOMAIN env variable
9142+ *
9143+ * Revision 1.4  2009-04-15 21:32:12+05:30  Cprogrammer
9144+ * added DKIM-Signature, Received to list of excluded headers
9145+ *
9146+ * Revision 1.3  2009-03-26 15:11:46+05:30  Cprogrammer
9147+ * added GetDomain
9148+ *
9149+ * Revision 1.2  2009-03-21 11:57:19+05:30  Cprogrammer
9150+ * fixed indentation
9151+ *
9152+ * Revision 1.1  2009-03-21 08:43:11+05:30  Cprogrammer
9153+ * Initial revision
9154+ *
9155+ *
9156+ *  Copyright 2005 Alt-N Technologies, Ltd.
9157+ *
9158+ *  Licensed under the Apache License, Version 2.0 (the "License");
9159+ *  you may not use this file except in compliance with the License.
9160+ *  You may obtain a copy of the License at
9161+ *
9162+ *      http://www.apache.org/licenses/LICENSE-2.0
9163+ *
9164+ *  This code incorporates intellectual property owned by Yahoo! and licensed
9165+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
9166+ *
9167+ *  Unless required by applicable law or agreed to in writing, software
9168+ *  distributed under the License is distributed on an "AS IS" BASIS,
9169+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9170+ *  See the License for the specific language governing permissions and
9171+ *  limitations under the License.
9172+ *
9173+ */
9174+
9175+#ifdef HAVE_CONFIG_H
9176+#include "config.h"
9177+#endif
9178+#define _strnicmp strncasecmp
9179+#define _stricmp strcasecmp
9180+#define LOWORD(l) ((unsigned)(l) & 0xffff)
9181+#define HIWORD(l) ((unsigned)(l) >> 16)
9182+
9183+#include <string.h>
9184+#include <map>
9185+#include "dkim.h"
9186+#include "dkimsign.h"
9187+
9188+CDKIMSign::CDKIMSign()
9189+{
9190+       m_EmptyLineCount = 0;
9191+       m_pfnHdrCallback = NULL;
9192+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9193+       if (!m_allman_sha1ctx)
9194+               m_allman_sha1ctx = EVP_MD_CTX_new();
9195+       EVP_SignInit(m_allman_sha1ctx, EVP_sha1());
9196+       if (!m_Hdr_ietf_sha1ctx)
9197+               m_Hdr_ietf_sha1ctx = EVP_MD_CTX_new();
9198+       EVP_SignInit(m_Hdr_ietf_sha1ctx, EVP_sha1());
9199+       if (!m_Bdy_ietf_sha1ctx)
9200+               m_Bdy_ietf_sha1ctx = EVP_MD_CTX_new();
9201+       EVP_DigestInit(m_Bdy_ietf_sha1ctx, EVP_sha1());
9202+#ifdef HAVE_EVP_SHA256
9203+       if (!m_Hdr_ietf_sha256ctx)
9204+               m_Hdr_ietf_sha256ctx = EVP_MD_CTX_new();
9205+       EVP_SignInit(m_Hdr_ietf_sha256ctx, EVP_sha256());
9206+       if (!m_Bdy_ietf_sha256ctx)
9207+               m_Bdy_ietf_sha256ctx = EVP_MD_CTX_new();
9208+       EVP_DigestInit(m_Bdy_ietf_sha256ctx, EVP_sha256());
9209+#endif
9210+#else
9211+       EVP_SignInit(&m_allman_sha1ctx, EVP_sha1());
9212+       EVP_SignInit(&m_Hdr_ietf_sha1ctx, EVP_sha1());
9213+       EVP_DigestInit(&m_Bdy_ietf_sha1ctx, EVP_sha1());
9214+#ifdef HAVE_EVP_SHA256
9215+       EVP_SignInit(&m_Hdr_ietf_sha256ctx, EVP_sha256());
9216+       EVP_DigestInit(&m_Bdy_ietf_sha256ctx, EVP_sha256());
9217+#endif
9218+#endif
9219+}
9220+
9221+CDKIMSign::~CDKIMSign()
9222+{
9223+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9224+       EVP_MD_CTX_reset(m_allman_sha1ctx);
9225+       EVP_MD_CTX_reset(m_Hdr_ietf_sha1ctx);
9226+       EVP_MD_CTX_reset(m_Bdy_ietf_sha1ctx);
9227+#ifdef HAVE_EVP_SHA256
9228+       EVP_MD_CTX_reset(m_Hdr_ietf_sha256ctx);
9229+       EVP_MD_CTX_reset(m_Bdy_ietf_sha256ctx);
9230+#endif
9231+#else
9232+       EVP_MD_CTX_cleanup(&m_allman_sha1ctx);
9233+       EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha1ctx);
9234+       EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha1ctx);
9235+#ifdef HAVE_EVP_SHA256
9236+       EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha256ctx);
9237+       EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha256ctx);
9238+#endif
9239+#endif
9240+}
9241+
9242+
9243+////////////////////////////////////////////////////////////////////////////////
9244+//
9245+// Init - save the options
9246+//
9247+////////////////////////////////////////////////////////////////////////////////
9248+int
9249+CDKIMSign::Init(DKIMSignOptions * pOptions)
9250+{
9251+       int             nRet = CDKIMBase::Init();
9252+       m_Canon = pOptions->nCanon;
9253+
9254+// as of draft 01, these are the only allowed signing types:
9255+       if ((m_Canon != DKIM_SIGN_SIMPLE_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED_SIMPLE)) {
9256+               m_Canon = DKIM_SIGN_SIMPLE;
9257+       }
9258+       sSelector.assign(pOptions->szSelector);
9259+       m_pfnHdrCallback = pOptions->pfnHeaderCallback;
9260+       sDomain.assign(pOptions->szDomain);
9261+       m_IncludeBodyLengthTag = (pOptions->nIncludeBodyLengthTag != 0);
9262+       m_nBodyLength = 0;
9263+       m_ExpireTime = pOptions->expireTime;
9264+       sIdentity.assign(pOptions->szIdentity);
9265+       m_nIncludeTimeStamp = pOptions->nIncludeTimeStamp;
9266+       m_nIncludeQueryMethod = pOptions->nIncludeQueryMethod;
9267+       m_nIncludeCopiedHeaders = pOptions->nIncludeCopiedHeaders;
9268+       m_nIncludeBodyHash = pOptions->nIncludeBodyHash;
9269+
9270+// NOTE: the following line is not backwards compatible with MD 8.0.3
9271+// because the szRequiredHeaders member was added after the release
9272+//sRequiredHeaders.assign( pOptions->szRequiredHeaders );
9273+
9274+//make sure there is a colon after the last header in the list
9275+       if ((sRequiredHeaders.size() > 0) && sRequiredHeaders.at(sRequiredHeaders.size() - 1) != ':')
9276+               sRequiredHeaders.append(":");
9277+       m_nHash = pOptions->nHash;
9278+       m_bReturnedSigAssembled = false;
9279+       m_sCopiedHeaders.erase();
9280+       return nRet;
9281+}
9282+
9283+
9284+// Hash - update the hash
9285+void
9286+CDKIMSign::Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly)
9287+{
9288+       EVP_MD_CTX     *p1, *p2, *p3, *p4, *p5;
9289+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9290+       p1 = m_allman_sha1ctx;
9291+       p2 = m_Hdr_ietf_sha1ctx;
9292+       p3 = m_Hdr_ietf_sha256ctx;
9293+       p4 = m_Bdy_ietf_sha1ctx;
9294+       p5 = m_Bdy_ietf_sha256ctx;
9295+#else
9296+       p1 = &m_allman_sha1ctx;
9297+       p2 = &m_Hdr_ietf_sha1ctx;
9298+       p3 = &m_Hdr_ietf_sha256ctx;
9299+       p4 = &m_Bdy_ietf_sha1ctx;
9300+       p5 = &m_Bdy_ietf_sha256ctx;
9301+#endif
9302+       if (bAllmanOnly) {
9303+               if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1)
9304+                       EVP_SignUpdate(p1, szBuffer, nBufLength);
9305+       } else {
9306+               if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1)
9307+                       EVP_SignUpdate(p1, szBuffer, nBufLength);
9308+               else
9309+               if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) {
9310+                       if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1)
9311+                               EVP_SignUpdate(p1, szBuffer, nBufLength);
9312+#ifdef HAVE_EVP_SHA256
9313+                       if (m_nHash & DKIM_HASH_SHA256) {
9314+                               if (bHdr)
9315+                                       EVP_SignUpdate(p3, szBuffer, nBufLength);
9316+                               else
9317+                                       EVP_DigestUpdate(p5, szBuffer, nBufLength);
9318+                       }
9319+                       if (m_nHash != DKIM_HASH_SHA256) {
9320+                               if (bHdr)
9321+                                       EVP_SignUpdate(p2, szBuffer, nBufLength);
9322+                               else
9323+                                       EVP_DigestUpdate(p4, szBuffer, nBufLength);
9324+                       }
9325+#else
9326+                       if (bHdr)
9327+                               EVP_SignUpdate(p2, szBuffer, nBufLength);
9328+                       else
9329+                               EVP_DigestUpdate(p4, szBuffer, nBufLength);
9330+#endif
9331+               }
9332+       }
9333+}
9334+
9335+
9336+////////////////////////////////////////////////////////////////////////////////
9337+//
9338+// SignThisTag - return boolean whether or not to sign this tag
9339+//
9340+////////////////////////////////////////////////////////////////////////////////
9341+bool CDKIMSign::SignThisTag(const string &sTag)
9342+{
9343+       bool            bRet = true;
9344+
9345+       if (_strnicmp(sTag.c_str(), "X-", 2) == 0
9346+               || _stricmp(sTag.c_str(), "Authentication-Results:") == 0
9347+               || _stricmp(sTag.c_str(), "DKIM-Signature:") == 0
9348+               || _stricmp(sTag.c_str(), "Received:") == 0
9349+               || _stricmp(sTag.c_str(), "Return-Path:") == 0)
9350+       {
9351+               bRet = false;
9352+       }
9353+       return bRet;
9354+}
9355+
9356+bool
9357+ConvertHeaderToQuotedPrintable(const char *source, char *dest)
9358+{
9359+       bool            bConvert = false;
9360+
9361+// do quoted printable
9362+       static unsigned char hexchars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
9363+       unsigned char  *d = (unsigned char *) dest;
9364+       for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++) {
9365+               if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|') {
9366+                       *d++ = *s;
9367+               }
9368+
9369+               else {
9370+                       bConvert = true;
9371+                       *d++ = '=';
9372+                       *d++ = hexchars[*s >> 4];
9373+                       *d++ = hexchars[*s & 15];
9374+               }
9375+       }
9376+       *d = '\0';
9377+       return bConvert;
9378+}
9379+
9380+
9381+////////////////////////////////////////////////////////////////////////////////
9382+//
9383+// GetHeaderParams - Extract any needed header parameters
9384+//
9385+////////////////////////////////////////////////////////////////////////////////
9386+void
9387+CDKIMSign::GetHeaderParams(const string & sHdr)
9388+{
9389+       string::size_type pos1, pos2;
9390+
9391+       if (_strnicmp(sHdr.c_str(), "X", 1) == 0)
9392+               return;
9393+       if (_strnicmp(sHdr.c_str(), "From:", 5) == 0) {
9394+               sFrom.assign(sHdr.c_str() + 5);
9395+               pos1 = sFrom.find('(');
9396+               pos2 = sFrom.find(')');
9397+               if (pos1 != 0 && pos1 != string::npos && pos2 != 0 && pos2 != string::npos)
9398+                       sFrom.erase(pos1, pos2);
9399+       }
9400+       if (_strnicmp(sHdr.c_str(), "Sender:", 7) == 0) {
9401+               sSender.assign(sHdr.c_str() + 7);
9402+               pos1 = sSender.find('(');
9403+               pos2 = sSender.find(')');
9404+               if (pos1 != 0 && pos1 != string::npos && pos2 != 0 && pos2 != string::npos)
9405+                       sSender.erase(pos1, pos2);
9406+       }
9407+       if (_strnicmp(sHdr.c_str(), "Return-Path:", 12) == 0)
9408+               sReturnPath.assign(sHdr.c_str() + 12);
9409+       if (m_nIncludeCopiedHeaders) {
9410+               string::size_type pos = sHdr.find(':');
9411+               if (pos != string::npos) {
9412+                       string          sTag, sValue;
9413+                       char           *workBuffer = new char[sHdr.size() * 3 + 1];
9414+                       sTag.assign(sHdr.substr(0, pos));
9415+                       sValue.assign(sHdr.substr(pos + 1, string::npos));
9416+                       ConvertHeaderToQuotedPrintable(sTag.c_str(), workBuffer);
9417+                       if (!m_sCopiedHeaders.empty()) {
9418+                               m_sCopiedHeaders.append("|");
9419+                       }
9420+                       m_sCopiedHeaders.append(workBuffer);
9421+                       m_sCopiedHeaders.append(":");
9422+                       ConvertHeaderToQuotedPrintable(sValue.c_str(), workBuffer);
9423+                       m_sCopiedHeaders.append(workBuffer);
9424+                       delete[]workBuffer;
9425+               }
9426+       }
9427+}
9428+
9429+// ProcessHeaders - sign headers and save needed parameters
9430+int
9431+CDKIMSign::ProcessHeaders(void)
9432+{
9433+       map <string, list <string>::reverse_iterator> IterMap;
9434+       map <string, list <string>::reverse_iterator>::iterator IterMapIter;
9435+       list <string>::reverse_iterator riter;
9436+       list <string>::iterator iter;
9437+       string          sTag;
9438+       bool            bFromHeaderFound = false;
9439+
9440+       // walk the header list
9441+       for (iter = HeaderList.begin(); iter != HeaderList.end(); iter++) {
9442+               sTag.assign(*iter);
9443+               // look for a colon
9444+               string::size_type pos = sTag.find(':');
9445+               if (pos != string::npos) {
9446+                       int             nSignThisTag = 1;
9447+                       // hack off anything past the colon
9448+                       sTag.erase(pos + 1, string::npos);
9449+                       // is this the From: header?
9450+                       if (_stricmp(sTag.c_str(), "From:") == 0) {
9451+                               bFromHeaderFound = true;
9452+                               nSignThisTag = 1;
9453+                               IsRequiredHeader(sTag); // remove from required header list
9454+                       }
9455+                       // is this in the list of headers that must be signed?
9456+                       else
9457+                       if (IsRequiredHeader(sTag))
9458+                               nSignThisTag = 1;
9459+                       else {
9460+                               if (m_pfnHdrCallback)
9461+                                       nSignThisTag = m_pfnHdrCallback(iter->c_str());
9462+                               else
9463+                                       nSignThisTag = SignThisTag(sTag) ? 1 : 0;
9464+                       }
9465+                       // save header parameters
9466+                       GetHeaderParams(*iter);
9467+                       if (nSignThisTag > 0) {
9468+                               // add this tag to h=
9469+                               hParam.append(sTag);
9470+                               IterMapIter = IterMap.find(sTag);
9471+                               riter = (IterMapIter == IterMap.end())? HeaderList.rbegin() : IterMapIter->second;
9472+                               // walk the list in reverse looking for the last instance of this header
9473+                               while (riter != HeaderList.rend()) {
9474+                                       if (_strnicmp(riter->c_str(), sTag.c_str(), sTag.size()) == 0) {
9475+                                               ProcessHeader(*riter);
9476+                                               // save the reverse iterator position for this tag
9477+                                               riter++;
9478+                                               IterMap[sTag] = riter;
9479+                                               break;
9480+                                       }
9481+                                       riter++;
9482+                               }
9483+                       }
9484+               }
9485+       }
9486+       Hash("\r\n", 2, true, true);    // only for Allman sig
9487+       if (!bFromHeaderFound) {
9488+               string sFrom("From:");
9489+               hParam.append(sFrom);
9490+               IsRequiredHeader(sFrom);        // remove from required header list
9491+       }
9492+       hParam.append(sRequiredHeaders);
9493+       if (hParam.at(hParam.size() - 1) == ':')
9494+               hParam.erase(hParam.size() - 1, string::npos);
9495+       return DKIM_SUCCESS;
9496+}
9497+
9498+char           *DKIM_CALL
9499+CDKIMSign::GetDomain(void)
9500+{
9501+       if (ParseFromAddress() == false)
9502+               return ((char *) 0);
9503+       return ((char *) sDomain.c_str());
9504+}
9505+
9506+void
9507+CDKIMSign::ProcessHeader(const string & sHdr)
9508+{
9509+       switch (HIWORD(m_Canon)) {
9510+       case DKIM_CANON_SIMPLE:
9511+               Hash(sHdr.c_str(), sHdr.size(), true);
9512+               Hash("\r\n", 2, true);
9513+               break;
9514+       case DKIM_CANON_NOWSP:
9515+               {
9516+               string sTemp = sHdr;
9517+               RemoveSWSP(sTemp);
9518+               // convert characters before ':' to lower case
9519+               for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) {
9520+                       if (*s >= 'A' && *s <= 'Z')
9521+                               *s += 'a' - 'A';
9522+               }
9523+               Hash(sTemp.c_str(), sTemp.size(), true);
9524+               Hash("\r\n", 2, true);
9525+               }
9526+               break;
9527+       case DKIM_CANON_RELAXED:
9528+               {
9529+               string sTemp = RelaxHeader(sHdr);
9530+               Hash(sTemp.c_str(), sTemp.length(), true);
9531+               Hash("\r\n", 2, true);
9532+               }
9533+               break;
9534+       }
9535+}
9536+
9537+int CDKIMSign::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
9538+{
9539+       switch (LOWORD(m_Canon)) {
9540+       case DKIM_CANON_SIMPLE:
9541+               if (nBufLength > 0) {
9542+                       while (m_EmptyLineCount > 0) {
9543+                               Hash("\r\n", 2, false);
9544+                               m_nBodyLength += 2;
9545+                               m_EmptyLineCount--;
9546+                       }
9547+                       Hash(szBuffer, nBufLength, false);
9548+                       Hash("\r\n", 2, false);
9549+                       m_nBodyLength += nBufLength + 2;
9550+               } else {
9551+                       m_EmptyLineCount++;
9552+                       if (bEOF) {
9553+                               Hash("\r\n", 2, false);
9554+                               m_nBodyLength += 2;
9555+                       }
9556+               }
9557+               break;
9558+       case DKIM_CANON_NOWSP:
9559+               RemoveSWSP(szBuffer, nBufLength);
9560+               if (nBufLength > 0) {
9561+                       Hash(szBuffer, nBufLength, false);
9562+                       m_nBodyLength += nBufLength;
9563+               }
9564+               break;
9565+       case DKIM_CANON_RELAXED:
9566+               CompressSWSP(szBuffer, nBufLength);
9567+               if (nBufLength > 0) {
9568+                       while (m_EmptyLineCount > 0) {
9569+                               Hash("\r\n", 2, false);
9570+                               m_nBodyLength += 2;
9571+                               m_EmptyLineCount--;
9572+                       }
9573+                       Hash(szBuffer, nBufLength, false);
9574+                       m_nBodyLength += nBufLength;
9575+                       if (!bEOF) {
9576+                               Hash("\r\n", 2, false);
9577+                               m_nBodyLength += 2;
9578+                       }
9579+               } else
9580+                       m_EmptyLineCount++;
9581+               break;
9582+       }
9583+       return DKIM_SUCCESS;
9584+}
9585+
9586+bool CDKIMSign::ParseFromAddress(void)
9587+{
9588+       string::size_type pos;
9589+       string          sAddress;
9590+       char           *p, *at;
9591+
9592+       /* thanks to fred */
9593+       if (!sReturnPath.empty())
9594+               sAddress.assign(sReturnPath);
9595+       else
9596+       if (!sSender.empty())
9597+               sAddress.assign(sSender);
9598+       else
9599+       if (!sFrom.empty())
9600+               sAddress.assign(sFrom);
9601+       else
9602+               return false;
9603+       // simple for now, beef it up later
9604+       // remove '<' and anything before it
9605+       pos = sAddress.find('<');
9606+       if (pos != string::npos)
9607+               sAddress.erase(0, pos);
9608+       // remove '>' and anything after it
9609+       pos = sAddress.find('>');
9610+       if (pos != string::npos)
9611+               sAddress.erase(pos, string::npos);
9612+       // look for '@' symbol
9613+       pos = sAddress.find('@');
9614+       if (pos == string::npos)
9615+               return false;
9616+       if (sDomain.empty()) {
9617+               p = getenv("DKIMDOMAIN");
9618+               if (p && *p)
9619+               {
9620+                       if (!(at = strchr(p, '@')))
9621+                               at = p;
9622+                       else
9623+                               at++;
9624+                       sDomain.assign(at);
9625+               } else
9626+                       sDomain.assign(sAddress.c_str() + pos + 1);
9627+               RemoveSWSP(sDomain);
9628+       }
9629+       return true;
9630+}
9631+
9632+
9633+////////////////////////////////////////////////////////////////////////////////
9634+//
9635+// InitSig - initialize signature folding algorithm
9636+//
9637+////////////////////////////////////////////////////////////////////////////////
9638+void
9639+CDKIMSign::InitSig(void)
9640+{
9641+       m_sSig.reserve(1024);
9642+       m_sSig.assign("DKIM-Signature:");
9643+       m_nSigPos = m_sSig.size();
9644+}
9645+
9646+////////////////////////////////////////////////////////////////////////////////
9647+//
9648+// AddTagToSig - add tag and value to signature folding if necessary
9649+//               if bFold, fold at cbrk char
9650+//
9651+////////////////////////////////////////////////////////////////////////////////
9652+void CDKIMSign::AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold)
9653+{
9654+       int
9655+                       nTagLen = strlen(Tag);
9656+       AddInterTagSpace((!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2);
9657+       m_sSig.append(Tag);
9658+       m_sSig.append("=");
9659+       m_nSigPos += 1 + nTagLen;
9660+       if (!bFold) {
9661+               m_sSig.append(sValue);
9662+               m_nSigPos += sValue.size();
9663+       }
9664+
9665+       else {
9666+               AddFoldedValueToSig(sValue, cbrk);
9667+       }
9668+       m_sSig.append(";");
9669+       m_nSigPos++;
9670+}
9671+
9672+
9673+////////////////////////////////////////////////////////////////////////////////
9674+//
9675+// AddTagToSig - add tag and numeric value to signature folding if necessary
9676+//
9677+////////////////////////////////////////////////////////////////////////////////
9678+void CDKIMSign::AddTagToSig(char *Tag, unsigned long nValue)
9679+{
9680+       char            szValue[64];
9681+       sprintf(szValue, "%lu", nValue);
9682+       AddTagToSig(Tag, szValue, 0, false);
9683+}
9684+
9685+////////////////////////////////////////////////////////////////////////////////
9686+//
9687+// AddInterTagSpace - add space or fold here
9688+//
9689+////////////////////////////////////////////////////////////////////////////////
9690+void CDKIMSign::AddInterTagSpace(int nSizeOfNextTag)
9691+{
9692+       if (m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength) {
9693+               m_sSig.append("\n\t");
9694+               m_nSigPos = 1;
9695+       }
9696+
9697+       else {
9698+               m_sSig.append(" ");
9699+               m_nSigPos++;
9700+       }
9701+}
9702+
9703+
9704+////////////////////////////////////////////////////////////////////////////////
9705+//
9706+// AddTagToSig - add value to signature folding if necessary
9707+//               if cbrk == 0 fold anywhere, otherwise fold only at cbrk
9708+//
9709+////////////////////////////////////////////////////////////////////////////////
9710+void CDKIMSign::AddFoldedValueToSig(const string & sValue, char cbrk)
9711+{
9712+       string::size_type pos = 0;
9713+       if (cbrk == 0) {
9714+
9715+       // fold anywhere
9716+               while (pos < sValue.size()) {
9717+                       string::size_type len = OptimalHeaderLineLength - m_nSigPos;
9718+                       if (len > sValue.size() - pos)
9719+                               len = sValue.size() - pos;
9720+                       m_sSig.append(sValue.substr(pos, len));
9721+                       m_nSigPos += len;
9722+                       pos += len;
9723+                       if (pos < sValue.size()) {
9724+                               m_sSig.append("\n\t");
9725+                               m_nSigPos = 1;
9726+                       }
9727+               }
9728+       }
9729+
9730+       else {
9731+
9732+       // fold only at cbrk
9733+               while (pos < sValue.size()) {
9734+                       string::size_type len = OptimalHeaderLineLength - m_nSigPos;
9735+                       string::size_type brkpos;
9736+                       if (sValue.size() - pos < len) {
9737+                               brkpos = sValue.size() - 1;
9738+                       }
9739+
9740+                       else {
9741+                               brkpos = sValue.rfind(cbrk, pos + len);
9742+                       }
9743+                       if (brkpos == string::npos || brkpos < pos) {
9744+                               brkpos = sValue.find(cbrk, pos);
9745+                               if (brkpos == string::npos) {
9746+                                       brkpos = sValue.size();
9747+                               }
9748+                       }
9749+                       len = brkpos - pos + 1;
9750+                       m_sSig.append(sValue.substr(pos, len));
9751+                       m_nSigPos += len;
9752+                       pos += len;
9753+                       if (pos < sValue.size()) {
9754+
9755+                       //m_sSig.append( "\r\n\t" );
9756+                               m_sSig.append("\n\t");
9757+                               m_nSigPos = 1;
9758+                       }
9759+               }
9760+       }
9761+}
9762+
9763+
9764+////////////////////////////////////////////////////////////////////////////////
9765+//
9766+// GetSig - compute hash and return signature header in szSignature
9767+//
9768+////////////////////////////////////////////////////////////////////////////////
9769+int CDKIMSign::GetSig(char *szPrivKey, char *szSignature, unsigned int nSigLength)
9770+{
9771+       if (szPrivKey == NULL) {
9772+               return DKIM_BAD_PRIVATE_KEY;
9773+       }
9774+       if (szSignature == NULL) {
9775+               return DKIM_BUFFER_TOO_SMALL;
9776+       }
9777+       int             nRet = AssembleReturnedSig(szPrivKey);
9778+       if (nRet != DKIM_SUCCESS)
9779+               return nRet;
9780+       if (m_sReturnedSig.size() + 1 < nSigLength)
9781+               strcpy(szSignature, m_sReturnedSig.c_str());
9782+       else
9783+               return DKIM_BUFFER_TOO_SMALL;
9784+       return DKIM_SUCCESS;
9785+}
9786+
9787+
9788+////////////////////////////////////////////////////////////////////////////////
9789+//
9790+// GetSig - compute hash and return signature header in szSignature
9791+//
9792+////////////////////////////////////////////////////////////////////////////////
9793+int CDKIMSign::GetSig2(char *szPrivKey, char **pszSignature)
9794+{
9795+       if (szPrivKey == NULL) {
9796+               return DKIM_BAD_PRIVATE_KEY;
9797+       }
9798+       if (pszSignature == NULL) {
9799+               return DKIM_BUFFER_TOO_SMALL;
9800+       }
9801+       int             nRet = AssembleReturnedSig(szPrivKey);
9802+       if (nRet != DKIM_SUCCESS)
9803+               return nRet;
9804+       *pszSignature = (char *) m_sReturnedSig.c_str();
9805+       return DKIM_SUCCESS;
9806+}
9807+
9808+
9809+////////////////////////////////////////////////////////////////////////////////
9810+//
9811+// IsRequiredHeader - Check if header in required list. If so, delete
9812+//                    header from list.
9813+//
9814+////////////////////////////////////////////////////////////////////////////////
9815+bool CDKIMSign::IsRequiredHeader(const string & sTag)
9816+{
9817+       string::size_type start = 0;
9818+       string::size_type end = sRequiredHeaders.find(':');
9819+       while (end != string::npos) {
9820+
9821+       // check for a zero-length header
9822+               if (start == end) {
9823+                       sRequiredHeaders.erase(start, 1);
9824+               }
9825+
9826+               else {
9827+                       if (_stricmp(sTag.c_str(), sRequiredHeaders.substr(start, end - start + 1).c_str()) == 0) {
9828+                               sRequiredHeaders.erase(start, end - start + 1);
9829+                               return true;
9830+                       }
9831+
9832+                       else {
9833+                               start = end + 1;
9834+                       }
9835+               }
9836+               end = sRequiredHeaders.find(':', start);
9837+       }
9838+       return false;
9839+}
9840+
9841+int CDKIMSign::ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256)
9842+{
9843+       string          sSignedSig;
9844+       unsigned char  *sig;
9845+       EVP_PKEY       *pkey;
9846+       BIO            *bio, *b64;
9847+       unsigned int    siglen;
9848+       int             size, len;
9849+       char           *buf, *cptr;
9850+       const char     *ptr, *dptr, *sptr;
9851+       EVP_MD_CTX     *p1, *p2, *p3, *p4, *p5;
9852+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
9853+       p1 = m_allman_sha1ctx;
9854+       p2 = m_Hdr_ietf_sha1ctx;
9855+       p3 = m_Hdr_ietf_sha256ctx;
9856+       p4 = m_Bdy_ietf_sha1ctx;
9857+       p5 = m_Bdy_ietf_sha256ctx;
9858+#else
9859+       p1 = &m_allman_sha1ctx;
9860+       p2 = &m_Hdr_ietf_sha1ctx;
9861+       p3 = &m_Hdr_ietf_sha256ctx;
9862+       p4 = &m_Bdy_ietf_sha1ctx;
9863+       p5 = &m_Bdy_ietf_sha256ctx;
9864+#endif
9865+
9866+// construct the DKIM-Signature: header and add to hash
9867+       InitSig();
9868+       if (bUseIetfBodyHash) {
9869+               AddTagToSig((char *) "v", (char *) "1", 0, false);
9870+       }
9871+#ifdef HAVE_EVP_SHA256
9872+       AddTagToSig((char *) "a", bUseSha256 ? "rsa-sha256" : "rsa-sha1", 0, false);
9873+#else
9874+       AddTagToSig((char *) "a", "rsa-sha1", 0, false);
9875+#endif
9876+       switch (m_Canon) {
9877+       case DKIM_SIGN_SIMPLE:
9878+               AddTagToSig((char *) "c", "simple", 0, false);
9879+               break;
9880+       case DKIM_SIGN_SIMPLE_RELAXED:
9881+               AddTagToSig((char *) "c", "simple/relaxed", 0, false);
9882+               break;
9883+       case DKIM_SIGN_RELAXED:
9884+               AddTagToSig((char *) "c", "relaxed/relaxed", 0, false);
9885+               break;
9886+       case DKIM_SIGN_RELAXED_SIMPLE:
9887+               AddTagToSig((char *) "c", "relaxed", 0, false);
9888+               break;
9889+       }
9890+       AddTagToSig((char *) "d", sDomain, 0, false);
9891+       /*- replace % with domain name */
9892+       ptr = sSelector.c_str();
9893+       if ((sptr = strchr(ptr, '%'))) {
9894+               dptr = sDomain.c_str();
9895+               for (sptr = ptr, len = 0;*sptr;sptr++) {
9896+                       if (*sptr == '%')
9897+                               len += (int) strlen(dptr);
9898+                       else
9899+                               len++;
9900+               }
9901+               if (!(buf = new char[len])) {
9902+                       return DKIM_OUT_OF_MEMORY;
9903+               }
9904+               for (cptr = buf, sptr = ptr; *sptr; sptr++) {
9905+                       if (*sptr == '%') {
9906+                               strncpy(cptr, dptr, (len = strlen(dptr)));
9907+                               cptr += len;
9908+                       } else {
9909+                               *cptr++ = *sptr;
9910+                       }
9911+               }
9912+               *cptr = 0;
9913+               sSelector.assign(buf);
9914+               delete[]buf;
9915+       }
9916+       AddTagToSig((char *) "s", sSelector, 0, false);
9917+       if (m_IncludeBodyLengthTag) {
9918+               AddTagToSig((char *) "l", m_nBodyLength);
9919+       }
9920+       if (m_nIncludeTimeStamp != 0) {
9921+               time_t t;
9922+               time(&t);
9923+               AddTagToSig((char *) "t", t);
9924+       }
9925+       if (m_ExpireTime != 0) {
9926+               AddTagToSig((char *) "x", m_ExpireTime);
9927+       }
9928+       if (!sIdentity.empty()) {
9929+               AddTagToSig((char *) "i", sIdentity, 0, false);
9930+       }
9931+       if (m_nIncludeQueryMethod) {
9932+               AddTagToSig((char *) "q", bUseIetfBodyHash ? "dns/txt" : "dns", 0, false);
9933+       }
9934+       AddTagToSig((char *) "h", hParam, ':', true);
9935+       if (m_nIncludeCopiedHeaders) {
9936+               AddTagToSig((char *) "z", m_sCopiedHeaders, 0, true);
9937+       }
9938+       if (bUseIetfBodyHash) {
9939+               unsigned char Hash[EVP_MAX_MD_SIZE];
9940+               unsigned int nHashLen = 0;
9941+#ifdef HAVE_EVP_SHA256
9942+               EVP_DigestFinal(bUseSha256 ? p5 : p4, Hash, &nHashLen);
9943+#else
9944+               EVP_DigestFinal(p4, Hash, &nHashLen);
9945+#endif
9946+               bio = BIO_new(BIO_s_mem());
9947+               if (!bio) {
9948+                       return DKIM_OUT_OF_MEMORY;
9949+               }
9950+               b64 = BIO_new(BIO_f_base64());
9951+               if (!b64) {
9952+                       BIO_free(bio);
9953+                       return DKIM_OUT_OF_MEMORY;
9954+               }
9955+               BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
9956+               BIO_push(b64, bio);
9957+               if (BIO_write(b64, Hash, nHashLen) < (int) nHashLen) {
9958+                       BIO_free_all(b64);
9959+                       return DKIM_OUT_OF_MEMORY;
9960+               }
9961+               BIO_flush(b64);
9962+               len = nHashLen * 2;
9963+               buf = new char[len];
9964+               if (buf == NULL) {
9965+                       BIO_free_all(b64);
9966+                       return DKIM_OUT_OF_MEMORY;
9967+               }
9968+               size = BIO_read(bio, buf, len);
9969+               BIO_free_all(b64);
9970+               // this should never happen
9971+               if (size >= len) {
9972+                       delete[]buf;
9973+                       return DKIM_OUT_OF_MEMORY;
9974+               }
9975+               buf[size] = '\0';
9976+               AddTagToSig((char *) "bh", buf, 0, true);
9977+               delete[]buf;
9978+       }
9979+       AddInterTagSpace(3);
9980+       m_sSig.append("b=");
9981+       m_nSigPos += 2;
9982+       // Force a full copy - no reference copies please
9983+       sSignedSig.assign(m_sSig.c_str());
9984+       // note that since we're not calling hash here, need to dump this
9985+       // to the debug file if you want the full canonical form
9986+       string          sTemp;
9987+       if (HIWORD(m_Canon) == DKIM_CANON_RELAXED)
9988+               sTemp = RelaxHeader(sSignedSig);
9989+       else
9990+               sTemp = sSignedSig.c_str();
9991+       if (bUseIetfBodyHash) {
9992+#ifdef HAVE_EVP_SHA256
9993+               EVP_SignUpdate(bUseSha256 ? p3 : p2, sTemp.c_str(), sTemp.size());
9994+#else
9995+               EVP_SignUpdate(p2, sTemp.c_str(), sTemp.size());
9996+#endif
9997+       } else
9998+               EVP_SignUpdate(p1, sTemp.c_str(), sTemp.size());
9999+       if (!(bio = BIO_new_mem_buf(szPrivKey, -1)))
10000+               return DKIM_OUT_OF_MEMORY;
10001+       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
10002+       BIO_free(bio);
10003+       if (!pkey) {
10004+               return DKIM_BAD_PRIVATE_KEY;
10005+       }
10006+       siglen = EVP_PKEY_size(pkey);
10007+       int             nSignRet;
10008+       sig = (unsigned char *) OPENSSL_malloc(siglen);
10009+       if (sig == NULL) {
10010+               EVP_PKEY_free(pkey);
10011+               return DKIM_OUT_OF_MEMORY;
10012+       }
10013+       if (bUseIetfBodyHash) {
10014+#ifdef HAVE_EVP_SHA256
10015+               nSignRet = EVP_SignFinal(bUseSha256 ? p3 : p2, sig, &siglen, pkey);
10016+#else
10017+               nSignRet = EVP_SignFinal(p2, sig, &siglen, pkey);
10018+#endif
10019+       } else
10020+               nSignRet = EVP_SignFinal(p1, sig, &siglen, pkey);
10021+       EVP_PKEY_free(pkey);
10022+       if (!nSignRet) {
10023+               OPENSSL_free(sig);
10024+               return DKIM_BAD_PRIVATE_KEY;    // key too small
10025+       }
10026+       bio = BIO_new(BIO_s_mem());
10027+       if (!bio) {
10028+               return DKIM_OUT_OF_MEMORY;
10029+       }
10030+       b64 = BIO_new(BIO_f_base64());
10031+       if (!b64) {
10032+               BIO_free(bio);
10033+               return DKIM_OUT_OF_MEMORY;
10034+       }
10035+       BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
10036+       BIO_push(b64, bio);
10037+       if (BIO_write(b64, sig, siglen) < (int) siglen) {
10038+               OPENSSL_free(sig);
10039+               BIO_free_all(b64);
10040+               return DKIM_OUT_OF_MEMORY;
10041+       }
10042+       BIO_flush(b64);
10043+       OPENSSL_free(sig);
10044+       len = siglen * 2;
10045+       buf = new char[len];
10046+       if (buf == NULL) {
10047+               BIO_free_all(b64);
10048+               return DKIM_OUT_OF_MEMORY;
10049+       }
10050+       size = BIO_read(bio, buf, len);
10051+       BIO_free_all(b64);
10052+       // this should never happen
10053+       if (size >= len) {
10054+               delete[]buf;
10055+               return DKIM_OUT_OF_MEMORY;
10056+       }
10057+       buf[size] = '\0';
10058+       AddFoldedValueToSig(buf, 0);
10059+       delete[]buf;
10060+       return DKIM_SUCCESS;
10061+}
10062+
10063+int CDKIMSign::AssembleReturnedSig(char *szPrivKey)
10064+{
10065+       int             nRet;
10066+       if (m_bReturnedSigAssembled)
10067+               return DKIM_SUCCESS;
10068+       ProcessFinal();
10069+       if (ParseFromAddress() == false) {
10070+               //return DKIM_NO_SENDER;
10071+       }
10072+       Hash("\r\n", 2, true, true);    // only for Allman sig
10073+       string allmansha1sig,
10074+#ifdef HAVE_EVP_SHA256
10075+               ietfsha256Sig,
10076+#endif
10077+               ietfsha1Sig;
10078+       if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1) {
10079+               nRet = ConstructSignature(szPrivKey, false, false);
10080+               if (nRet == DKIM_SUCCESS)
10081+                       allmansha1sig.assign(m_sSig);
10082+               else
10083+                       return nRet;
10084+       } else
10085+       if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) {
10086+               if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1) {
10087+                       if ((nRet = ConstructSignature(szPrivKey, false, false)) == DKIM_SUCCESS)
10088+                               allmansha1sig.assign(m_sSig);
10089+                       else
10090+                               return nRet;
10091+               }
10092+#ifdef HAVE_EVP_SHA256
10093+               if (m_nHash & DKIM_HASH_SHA256) {
10094+                       if ((nRet = ConstructSignature(szPrivKey, true, true)) == DKIM_SUCCESS)
10095+                               ietfsha256Sig.assign(m_sSig);
10096+                       else
10097+                               return nRet;
10098+               }
10099+               if (m_nHash != DKIM_HASH_SHA256) {
10100+                       if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCCESS)
10101+                               ietfsha1Sig.assign(m_sSig);
10102+                       else
10103+                               return nRet;
10104+               }
10105+#else
10106+               if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCCESS)
10107+                       ietfsha1Sig.assign(m_sSig);
10108+               else
10109+                       return nRet;
10110+#endif
10111+       }
10112+       m_sReturnedSig.assign(allmansha1sig);
10113+       if (!ietfsha1Sig.empty()) {
10114+               if (!m_sReturnedSig.empty())
10115+                       m_sReturnedSig.append("\n");
10116+               m_sReturnedSig.append(ietfsha1Sig);
10117+       }
10118+#ifdef HAVE_EVP_SHA256
10119+       if (!ietfsha256Sig.empty()) {
10120+               if (!m_sReturnedSig.empty())
10121+                       m_sReturnedSig.append("\n");
10122+               m_sReturnedSig.append(ietfsha256Sig);
10123+       }
10124+#endif
10125+       m_bReturnedSigAssembled = true;
10126+       return DKIM_SUCCESS;
10127+}
10128+
10129+void
10130+getversion_dkimsign_cpp()
10131+{
10132+       static char    *x = (char *) "$Id: dkimsign.cpp,v 1.11 2017-09-05 10:59:03+05:30 Cprogrammer Exp mbhangui $";
10133+
10134+       x++;
10135+}
10136diff -ruN ../netqmail-1.06-original/dkimsign.h netqmail-1.06/dkimsign.h
10137--- ../netqmail-1.06-original/dkimsign.h        1970-01-01 01:00:00.000000000 +0100
10138+++ netqmail-1.06/dkimsign.h    2018-04-03 14:46:51.366411614 +0200
10139@@ -0,0 +1,108 @@
10140+/*
10141+ * $Log: dkimsign.h,v $
10142+ * Revision 1.3  2017-08-09 22:03:09+05:30  Cprogrammer
10143+ * initialized EVP_MD_CTX variables
10144+ *
10145+ * Revision 1.2  2017-08-08 23:50:33+05:30  Cprogrammer
10146+ * openssl 1.1.0 port
10147+ *
10148+ * Revision 1.1  2009-04-16 10:34:02+05:30  Cprogrammer
10149+ * Initial revision
10150+ *
10151+ *
10152+ *  Copyright 2005 Alt-N Technologies, Ltd.
10153+ *
10154+ *  Licensed under the Apache License, Version 2.0 (the "License");
10155+ *  you may not use this file except in compliance with the License.
10156+ *  You may obtain a copy of the License at
10157+ *
10158+ *      http://www.apache.org/licenses/LICENSE-2.0
10159+ *
10160+ *  This code incorporates intellectual property owned by Yahoo! and licensed
10161+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
10162+ *
10163+ *  Unless required by applicable law or agreed to in writing, software
10164+ *  distributed under the License is distributed on an "AS IS" BASIS,
10165+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10166+ *  See the License for the specific language governing permissions and
10167+ *  limitations under the License.
10168+ *
10169+ */
10170+
10171+#ifndef DKIMSIGN_H
10172+#define DKIMSIGN_H
10173+
10174+#include "dkimbase.h"
10175+
10176+class           CDKIMSign:public CDKIMBase {
10177+public:
10178+
10179+       CDKIMSign();
10180+       ~CDKIMSign();
10181+       int             Init(DKIMSignOptions * pOptions);
10182+       int             GetSig(char *szPrivKey, char *szSignature, unsigned int nSigLength);
10183+       int             GetSig2(char *szPrivKey, char **pszSignature);
10184+       virtual int     ProcessHeaders(void);
10185+       virtual int     ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
10186+       enum CKDKIMConstants { OptimalHeaderLineLength = 65 };
10187+       char           *DKIM_CALL GetDomain(void);
10188+
10189+protected:
10190+       void            Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly = false);
10191+       bool            SignThisTag(const string & sTag);
10192+       void            GetHeaderParams(const string & sHdr);
10193+       void            ProcessHeader(const string & sHdr);
10194+       bool            ParseFromAddress(void);
10195+       void            InitSig(void);
10196+       void            AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold);
10197+       void            AddTagToSig(char *Tag, unsigned long nValue);
10198+       void            AddInterTagSpace(int nSizeOfNextTag);
10199+       void            AddFoldedValueToSig(const string & sValue, char cbrk);
10200+       bool            IsRequiredHeader(const string & sTag);
10201+       int             ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256);
10202+       int             AssembleReturnedSig(char *szPrivKey);
10203+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10204+       EVP_MD_CTX     *m_Hdr_ietf_sha1ctx = NULL;      /* the header hash for ietf sha1  */
10205+       EVP_MD_CTX     *m_Bdy_ietf_sha1ctx = NULL;      /* the body hash for ietf sha1  */
10206+#ifdef HAVE_EVP_SHA256
10207+       EVP_MD_CTX     *m_Hdr_ietf_sha256ctx = NULL;    /* the header hash for ietf sha256 */
10208+       EVP_MD_CTX     *m_Bdy_ietf_sha256ctx = NULL;    /* the body hash for ietf sha256 */
10209+#endif
10210+       EVP_MD_CTX     *m_allman_sha1ctx = NULL;        /* the hash for allman sha1  */
10211+#else
10212+       EVP_MD_CTX      m_Hdr_ietf_sha1ctx;     /* the header hash for ietf sha1  */
10213+       EVP_MD_CTX      m_Bdy_ietf_sha1ctx;     /* the body hash for ietf sha1  */
10214+#ifdef HAVE_EVP_SHA256
10215+       EVP_MD_CTX      m_Hdr_ietf_sha256ctx;   /* the header hash for ietf sha256 */
10216+       EVP_MD_CTX      m_Bdy_ietf_sha256ctx;   /* the body hash for ietf sha256 */
10217+#endif
10218+       EVP_MD_CTX      m_allman_sha1ctx;       /* the hash for allman sha1  */
10219+#endif
10220+       int             m_Canon;        // canonization method
10221+       int             m_EmptyLineCount;
10222+       string          hParam;
10223+       string          sFrom;
10224+       string          sSender;
10225+       string          sSelector;
10226+       string          sReturnPath;
10227+       string          sDomain;
10228+       string          sIdentity;      // for i= tag, if empty tag will not be included in sig
10229+       string          sRequiredHeaders;
10230+       bool            m_IncludeBodyLengthTag;
10231+       int             m_nBodyLength;
10232+       time_t          m_ExpireTime;
10233+       int             m_nIncludeTimeStamp;    // 0 = don't include t= tag, 1 = include t= tag
10234+       int             m_nIncludeQueryMethod;  // 0 = don't include q= tag, 1 = include q= tag
10235+       int             m_nHash;        // use one of the DKIM_HASH_xx constants here
10236+       int             m_nIncludeCopiedHeaders;        // 0 = don't include z= tag, 1 = include z= tag
10237+       int             m_nIncludeBodyHash;     // 0 = calculate sig using draft 0, 1 = include bh= tag and
10238+       // use new signature computation algorithm
10239+       DKIMHEADERCALLBACK m_pfnHdrCallback;
10240+       string          m_sSig;
10241+       int             m_nSigPos;
10242+       string          m_sReturnedSig;
10243+       bool            m_bReturnedSigAssembled;
10244+       string          m_sCopiedHeaders;
10245+};
10246+
10247+#endif /*- DKIMSIGN_H */
10248diff -ruN ../netqmail-1.06-original/dkimverify.cpp netqmail-1.06/dkimverify.cpp
10249--- ../netqmail-1.06-original/dkimverify.cpp    1970-01-01 01:00:00.000000000 +0100
10250+++ netqmail-1.06/dkimverify.cpp        2018-04-03 14:46:51.367411617 +0200
10251@@ -0,0 +1,1285 @@
10252+/*
10253+ * $Log: dkimverify.cpp,v $
10254+ * Revision 1.18  2017-09-05 11:00:33+05:30  Cprogrammer
10255+ * removed extra whitespace
10256+ *
10257+ * Revision 1.17  2017-09-03 14:02:04+05:30  Cprogrammer
10258+ * call EVP_MD_CTX_init() only once
10259+ *
10260+ * Revision 1.16  2017-09-01 12:46:05+05:30  Cprogrammer
10261+ * removed dkimd2i_PUBKEY function
10262+ *
10263+ * Revision 1.15  2017-08-31 17:04:34+05:30  Cprogrammer
10264+ * replaced d2i_PUBKEY() with dkimd2i_PUBKEY() to avoid SIGSEGV on X509_PUBKEY_free()
10265+ *
10266+ * Revision 1.14  2017-08-09 21:59:39+05:30  Cprogrammer
10267+ * fixed segmentation fault. Use EVP_MD_CTX_reset() instead of EVP_MD_CTX_free()
10268+ *
10269+ * Revision 1.13  2017-08-08 23:50:41+05:30  Cprogrammer
10270+ * openssl 1.1.0 port
10271+ *
10272+ * Revision 1.12  2017-05-23 09:23:45+05:30  Cprogrammer
10273+ * use strtok_r instead of strtok() for thread safe operation
10274+ *
10275+ * Revision 1.11  2016-03-01 16:24:00+05:30  Cprogrammer
10276+ * reverse value of m_SubjectIsRequired
10277+ *
10278+ * Revision 1.10  2015-12-15 16:05:00+05:30  Cprogrammer
10279+ * fixed issue with time comparision. Use time_t for time variables
10280+ *
10281+ * Revision 1.9  2011-06-04 10:05:01+05:30  Cprogrammer
10282+ * added signature and identity domain information to
10283+ *     DKIMVerifyDetails structure
10284+ *
10285+ * Revision 1.8  2011-06-04 09:37:13+05:30  Cprogrammer
10286+ * added AllowUnsignedFromHeaders
10287+ *
10288+ * Revision 1.7  2009-06-11 13:58:34+05:30  Cprogrammer
10289+ * port for DARWIN
10290+ *
10291+ * Revision 1.6  2009-05-31 21:09:29+05:30  Cprogrammer
10292+ * changed cast
10293+ *
10294+ * Revision 1.5  2009-03-27 20:19:58+05:30  Cprogrammer
10295+ * added ADSP code
10296+ *
10297+ * Revision 1.4  2009-03-26 15:12:05+05:30  Cprogrammer
10298+ * added ADSP code
10299+ *
10300+ * Revision 1.3  2009-03-25 08:38:20+05:30  Cprogrammer
10301+ * fixed indentation
10302+ *
10303+ * Revision 1.2  2009-03-21 11:57:40+05:30  Cprogrammer
10304+ * fixed indentation
10305+ *
10306+ * Revision 1.1  2009-03-21 08:43:13+05:30  Cprogrammer
10307+ * Initial revision
10308+ *
10309+ *
10310+ *  Copyright 2005 Alt-N Technologies, Ltd.
10311+ *
10312+ *  Licensed under the Apache License, Version 2.0 (the "License");
10313+ *  you may not use this file except in compliance with the License.
10314+ *  You may obtain a copy of the License at
10315+ *
10316+ *      http://www.apache.org/licenses/LICENSE-2.0
10317+ *
10318+ *  This code incorporates intellectual property owned by Yahoo! and licensed
10319+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
10320+ *
10321+ *  Unless required by applicable law or agreed to in writing, software
10322+ *  distributed under the License is distributed on an "AS IS" BASIS,
10323+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10324+ *  See the License for the specific language governing permissions and
10325+ *  limitations under the License.
10326+ *
10327+ */
10328+#ifdef HAVE_CONFIG_H
10329+#include "config.h"
10330+#endif
10331+#define _strnicmp strncasecmp
10332+#define _stricmp strcasecmp
10333+#include <string.h>
10334+#include <ctype.h>
10335+#include <assert.h>
10336+#include <vector>
10337+#include <algorithm>
10338+#include "dkim.h"
10339+#include "dkimverify.h"
10340+#include "dkimdns.h"
10341+
10342+#define MAX_SIGNATURES 10              // maximum number of DKIM signatures to process in a message
10343+
10344+SignatureInfo::SignatureInfo(bool s)
10345+{
10346+       VerifiedBodyCount = 0;
10347+       UnverifiedBodyCount = 0;
10348+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10349+       if (m_Hdr_ctx)
10350+               EVP_MD_CTX_init(m_Hdr_ctx);
10351+       else
10352+               m_Hdr_ctx = EVP_MD_CTX_new();
10353+       if (m_Bdy_ctx)
10354+               EVP_MD_CTX_init(m_Bdy_ctx);
10355+       else
10356+               m_Bdy_ctx = EVP_MD_CTX_new();
10357+#else
10358+       EVP_MD_CTX_init(&m_Hdr_ctx);
10359+       EVP_MD_CTX_init(&m_Bdy_ctx);
10360+#endif
10361+       m_pSelector = NULL;
10362+       Status = DKIM_SUCCESS;
10363+       m_nHash = 0;
10364+       EmptyLineCount = 0;
10365+       m_SaveCanonicalizedData = s;
10366+}
10367+
10368+SignatureInfo::~SignatureInfo()
10369+{
10370+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10371+       if (m_Hdr_ctx)
10372+               EVP_MD_CTX_reset(m_Hdr_ctx);
10373+       if (m_Bdy_ctx)
10374+               EVP_MD_CTX_reset(m_Bdy_ctx);
10375+#else
10376+       EVP_MD_CTX_cleanup(&m_Hdr_ctx);
10377+       EVP_MD_CTX_cleanup(&m_Bdy_ctx);
10378+#endif
10379+}
10380+
10381+inline          bool
10382+isswsp(char ch)
10383+{
10384+       return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
10385+}
10386+
10387+
10388+////////////////////////////////////////////////////////////////////////////////
10389+//
10390+// Parse a DKIM tag-list.  Returns true for success
10391+//
10392+////////////////////////////////////////////////////////////////////////////////
10393+bool
10394+ParseTagValueList(char *tagvaluelist, const char *wanted[], char *values[])
10395+{
10396+       char           *s = tagvaluelist;
10397+
10398+       for (;;) {
10399+               // skip whitespace
10400+               while (isswsp(*s))
10401+                       s++;
10402+               // if at the end of the string, return success.  note: this allows a list with no entries
10403+               if (*s == '\0')
10404+                       return true;
10405+               // get tag name
10406+               if (!isalpha(*s))
10407+                       return false;
10408+               char           *tag = s;
10409+               do {
10410+                       s++;
10411+               } while (isalnum(*s) || *s == '-');
10412+               char           *endtag = s;
10413+               // skip whitespace before equals
10414+               while (isswsp(*s))
10415+                       s++;
10416+               // next character must be equals
10417+               if (*s != '=')
10418+                       return false;
10419+               s++;
10420+               // null-terminate tag name
10421+               *endtag = '\0';
10422+               // skip whitespace after equals
10423+               while (isswsp(*s))
10424+                       s++;
10425+               // get tag value
10426+               char           *value = s;
10427+               while (*s != ';' && ((*s == '\t' || *s == '\r' || *s == '\n') || (*s >= ' ' && *s <= '~')))
10428+                       s++;
10429+               char           *e = s;
10430+               // make sure the next character is the null terminator (which means we're done) or a semicolon (not done)
10431+               bool            done = false;
10432+               if (*s == '\0')
10433+                       done = true;
10434+               else {
10435+                       if (*s != ';')
10436+                               return false;
10437+                       s++;
10438+               }
10439+               // skip backwards past any trailing whitespace
10440+               while (e > value && isswsp(e[-1]))
10441+                       e--;
10442+               // null-terminate tag value
10443+               *e = '\0';
10444+               // check to see if we want this tag
10445+               for (unsigned i = 0; wanted[i] != NULL; i++) {
10446+                       if (strcmp(wanted[i], tag) == 0) {
10447+                               // return failure if we already have a value for this tag (duplicates not allowed)
10448+                               if (values[i] != NULL)
10449+                                       return false;
10450+                               values[i] = value;
10451+                               break;
10452+                       }
10453+               }
10454+               if (done)
10455+                       return true;
10456+       }
10457+}
10458+
10459+// Convert hex char to value (0-15)
10460+char
10461+tohex(char ch)
10462+{
10463+       if (ch >= '0' && ch <= '9')
10464+               return (ch - '0');
10465+       else
10466+       if (ch >= 'A' && ch <= 'F')
10467+               return (ch - 'A' + 10);
10468+       else
10469+       if (ch >= 'a' && ch <= 'f')
10470+               return (ch - 'a' + 10);
10471+       else {
10472+               assert(0);
10473+               return 0;
10474+       }
10475+}
10476+
10477+
10478+////////////////////////////////////////////////////////////////////////////////
10479+//
10480+// Decode quoted printable string in-place
10481+//
10482+////////////////////////////////////////////////////////////////////////////////
10483+void
10484+DecodeQuotedPrintable(char *ptr)
10485+{
10486+       char           *s = ptr;
10487+       while (*s != '\0' && *s != '=')
10488+               s++;
10489+       if (*s == '\0')
10490+               return;
10491+       char           *d = s;
10492+       do {
10493+               if (*s == '=' && isxdigit(s[1]) && isxdigit(s[2])) {
10494+                       *d++ = (tohex(s[1]) << 4) | tohex(s[2]);
10495+                       s += 3;
10496+               } else {
10497+                       *d++ = *s++;
10498+               }
10499+       } while (*s != '\0');
10500+       *d = '\0';
10501+}
10502+
10503+
10504+////////////////////////////////////////////////////////////////////////////////
10505+//
10506+// Decode base64 string in-place, returns number of bytes output
10507+//
10508+////////////////////////////////////////////////////////////////////////////////
10509+unsigned
10510+DecodeBase64(char *ptr)
10511+{
10512+       static const char base64_table[256] =
10513+               { -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,
10514+               -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,
10515+               -1, -1,
10516+               -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,
10517+               -1, -1,
10518+               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,
10519+               -1, -1,
10520+               -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,
10521+               -1, -1,
10522+               -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,
10523+               -1, -1,
10524+               -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,
10525+               -1, -1,
10526+               -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
10527+       };
10528+       unsigned char  *s = (unsigned char *) ptr;
10529+       unsigned char  *d = (unsigned char *) ptr;
10530+       unsigned        b64accum = 0;
10531+       unsigned char   b64shift = 0;
10532+       while (*s != '\0') {
10533+               unsigned char value = base64_table[*s++];
10534+               if ((signed char) value >= 0) {
10535+                       b64accum = (b64accum << 6) | value;
10536+                       b64shift += 6;
10537+                       if (b64shift >= 8) {
10538+                               b64shift -= 8;
10539+                               *d++ = (b64accum >> b64shift);
10540+                       }
10541+               }
10542+       }
10543+       return (char *) d - ptr;
10544+}
10545+
10546+////////////////////////////////////////////////////////////////////////////////
10547+//
10548+// Match a string with a pattern (used for g= value)
10549+// Supports a single, optional "*" wildcard character.
10550+//
10551+////////////////////////////////////////////////////////////////////////////////
10552+bool
10553+WildcardMatch(const char *p, const char *s)
10554+{
10555+       // special case: An empty "g=" value never matches any addresses
10556+       if (*p == '\0')
10557+               return false;
10558+       const char     *wildcard = strchr(p, '*');
10559+       if (wildcard == NULL)
10560+               return strcmp(s, p) == 0;
10561+       else {
10562+               unsigned beforewildcardlen = wildcard - p;
10563+               unsigned afterwildcardlen = strlen(wildcard + 1);
10564+               unsigned slen = strlen(s);
10565+               return (slen >= beforewildcardlen + afterwildcardlen) && (strncmp(s, p, beforewildcardlen) == 0)
10566+                       && strcmp(s + slen - afterwildcardlen, wildcard + 1) == 0;
10567+       }
10568+}
10569+
10570+
10571+////////////////////////////////////////////////////////////////////////////////
10572+//
10573+// Parse addresses from a string.  Returns true if at least one address found
10574+//
10575+////////////////////////////////////////////////////////////////////////////////
10576+bool
10577+ParseAddresses(string str, vector < string > &Addresses)
10578+{
10579+       char           *s = (char *) str.c_str();
10580+       while (*s != '\0') {
10581+               char           *start = s;
10582+               char           *from = s;
10583+               char           *to = s;
10584+               char           *lt = NULL;      // pointer to less than character (<) which starts the address if found
10585+               while (*from != '\0') {
10586+                       if (*from == '(') {
10587+                               // skip over comment
10588+                               from++;
10589+                               for (int depth = 1; depth != 0; from++) {
10590+                                       if (*from == '\0')
10591+                                               break;
10592+                                       else
10593+                                       if (*from == '(')
10594+                                               depth++;
10595+                                       else
10596+                                       if (*from == ')')
10597+                                               depth--;
10598+                                       else
10599+                                       if (*from == '\\' && from[1] != '\0')
10600+                                               from++;
10601+                               }
10602+                       }
10603+                       else
10604+                       if (*from == ')') {
10605+                               // ignore closing parenthesis outside of comment
10606+                               from++;
10607+                       }
10608+
10609+                       else
10610+                       if (*from == ',' || *from == ';') {
10611+                               // comma/selicolon ends the address
10612+                               from++;
10613+                               break;
10614+                       }
10615+                       else
10616+                       if (*from == ' ' || *from == '\t' || *from == '\r' || *from == '\n') {
10617+                               // ignore whitespace
10618+                               from++;
10619+                       }
10620+                       else
10621+                       if (*from == '"') {
10622+                               // copy the contents of a quoted string
10623+                               from++;
10624+                               while (*from != '\0') {
10625+                                       if (*from == '"') {
10626+                                               from++;
10627+                                               break;
10628+                                       }
10629+                                       else
10630+                                       if (*from == '\\' && from[1] != '\0')
10631+                                               *to++ = *from++;
10632+                                       *to++ = *from++;
10633+                               }
10634+                       }
10635+                       else
10636+                       if (*from == '\\' && from[1] != '\0') {
10637+                               // copy quoted-pair
10638+                               *to++ = *from++;
10639+                               *to++ = *from++;
10640+                       } else {
10641+                               // copy any other char
10642+                               *to = *from++;
10643+                               // save pointer to '<' for later...
10644+                               if (*to == '<')
10645+                                       lt = to;
10646+                               to++;
10647+                       }
10648+               }
10649+               *to = '\0';
10650+               // if there's < > get what's inside
10651+               if (lt != NULL) {
10652+                       start = lt + 1;
10653+                       char           *gt = strchr(start, '>');
10654+                       if (gt != NULL)
10655+                               *gt = '\0';
10656+               } else {
10657+                       // look for and strip group name
10658+                       char           *colon = strchr(start, ':');
10659+                       if (colon != NULL) {
10660+                               char           *at = strchr(start, '@');
10661+                               if (at == NULL || colon < at)
10662+                                       start = colon + 1;
10663+                       }
10664+               }
10665+               if (*start != '\0' && strchr(start, '@') != NULL)
10666+                       Addresses.push_back(start);
10667+               s = from;
10668+       }
10669+       return !Addresses.empty();
10670+}
10671+
10672+CDKIMVerify::CDKIMVerify()
10673+{
10674+       m_pfnSelectorCallback = NULL;
10675+       m_pfnPracticesCallback = NULL;
10676+       m_HonorBodyLengthTag = false;
10677+       m_CheckPractices = false;
10678+       m_Accept3ps = false;
10679+       m_SubjectIsRequired = true;
10680+       m_SaveCanonicalizedData = false;
10681+       m_AllowUnsignedFromHeaders = false;
10682+}
10683+
10684+CDKIMVerify::~CDKIMVerify()
10685+{
10686+}
10687+
10688+// Init - save the options
10689+int
10690+CDKIMVerify::Init(DKIMVerifyOptions *pOptions)
10691+{
10692+       int             nRet = CDKIMBase::Init();
10693+       m_pfnSelectorCallback = pOptions->pfnSelectorCallback;
10694+       m_pfnPracticesCallback = pOptions->pfnPracticesCallback;
10695+
10696+       m_HonorBodyLengthTag = pOptions->nHonorBodyLengthTag != 0;
10697+       m_CheckPractices = pOptions->nCheckPractices != 0;
10698+       m_SubjectIsRequired = pOptions->nSubjectRequired != 0;
10699+       m_Accept3ps = pOptions->nAccept3ps != 0;                //TBS(Luc)
10700+       m_SaveCanonicalizedData = pOptions->nSaveCanonicalizedData != 0;
10701+       m_AllowUnsignedFromHeaders = pOptions->nAllowUnsignedFromHeaders != 0;
10702+       return nRet;
10703+}
10704+
10705+// GetResults - return the pass/fail/neutral verification result
10706+int
10707+CDKIMVerify::GetResults(int *sCount, int *sSize)
10708+{
10709+       ProcessFinal();
10710+       unsigned int    SuccessCount = 0;
10711+       int             TestingFailures = 0;
10712+       int             RealFailures = 0;
10713+       list <string>   SuccessfulDomains;      // can contain duplicates
10714+       /* get the From address's domain if we might need it */
10715+       string          sFromDomain;
10716+
10717+       for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
10718+               if (i->Status == DKIM_SUCCESS) {
10719+                       if (!i->BodyHashData.empty()) {
10720+                               // check the body hash
10721+                               unsigned char   md[EVP_MAX_MD_SIZE];
10722+                               unsigned        len = 0;
10723+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10724+                               int             res = EVP_DigestFinal(i->m_Bdy_ctx, md, &len);
10725+#else
10726+                               int             res = EVP_DigestFinal(&i->m_Bdy_ctx, md, &len);
10727+#endif
10728+                               if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(), md, len) != 0) {
10729+                                       // body hash mismatch
10730+                                       // if the selector is in testing mode...
10731+                                       if (i->m_pSelector->Testing) {
10732+                                               i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING;     // todo: make a new error code for this?
10733+                                               TestingFailures++;
10734+                                       } else {
10735+                                               i->Status = DKIM_BODY_HASH_MISMATCH;
10736+                                               RealFailures++;
10737+                                       }
10738+                                       continue;
10739+                               }
10740+                       } else {
10741+                               // hash CRLF separating the body from the signature
10742+                               i->Hash("\r\n", 2);
10743+                       }
10744+                       // check the header hash
10745+                       string          sSignedSig = i->Header;
10746+                       string          sSigValue = sSignedSig.substr(sSignedSig.find(':') + 1);
10747+                       static const char *tags[] = { "b", NULL };
10748+                       char           *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
10749+                       char           *pSigValue = (char *) sSigValue.c_str();
10750+                       if (ParseTagValueList(pSigValue, tags, values) && values[0] != NULL) {
10751+                               sSignedSig.erase(15 + values[0] - pSigValue, strlen(values[0]));
10752+                       }
10753+                       if (i->HeaderCanonicalization == DKIM_CANON_RELAXED) {
10754+                               sSignedSig = RelaxHeader(sSignedSig);
10755+                       } else
10756+                       if (i->HeaderCanonicalization == DKIM_CANON_NOWSP) {
10757+                               RemoveSWSP(sSignedSig);
10758+                               // convert "DKIM-Signature" to lower case
10759+                               sSignedSig.replace(0, 14, "dkim-signature", 14);
10760+                       }
10761+                       i->Hash(sSignedSig.c_str(), sSignedSig.length());
10762+                       assert(i->m_pSelector != NULL);
10763+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10764+                       int             res = EVP_VerifyFinal(i->m_Hdr_ctx, (unsigned char *) i->SignatureData.data(),
10765+                                                       i->SignatureData.length(), i->m_pSelector->PublicKey);
10766+#else
10767+                       int             res = EVP_VerifyFinal(&i->m_Hdr_ctx, (unsigned char *) i->SignatureData.data(),
10768+                                                       i->SignatureData.length(), i->m_pSelector->PublicKey);
10769+#endif
10770+                       if (res == 1) {
10771+                               if (i->UnverifiedBodyCount == 0)
10772+                                       i->Status = DKIM_SUCCESS;
10773+                               else
10774+                                       i->Status = DKIM_SUCCESS_BUT_EXTRA;
10775+                               SuccessCount++;
10776+                               SuccessfulDomains.push_back(i->Domain);
10777+                       } else {
10778+                               // if the selector is in testing mode...
10779+                               if (i->m_pSelector->Testing) {
10780+                                       i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING;
10781+                                       TestingFailures++;
10782+                               } else {
10783+                                       i->Status = DKIM_SIGNATURE_BAD;
10784+                                       RealFailures++;
10785+                               }
10786+                       }
10787+               } else
10788+               if (i->Status == DKIM_SELECTOR_GRANULARITY_MISMATCH
10789+                       || i->Status == DKIM_SELECTOR_ALGORITHM_MISMATCH
10790+                       || i->Status == DKIM_SELECTOR_KEY_REVOKED) {
10791+                       // treat these as failures
10792+                       // todo: maybe see if the selector is in testing mode?
10793+                       RealFailures++;
10794+               }
10795+       }
10796+       if (SuccessCount > 0 || m_CheckPractices) {
10797+               for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
10798+                       if (_strnicmp(i->c_str(), "From", 4) == 0) {
10799+                               // skip over whitespace between the header name and :
10800+                               const char     *s = i->c_str() + 4;
10801+                               while (*s == ' ' || *s == '\t')
10802+                                       s++;
10803+                               if (*s == ':') {
10804+                                       vector <string> Addresses;
10805+                                       if (ParseAddresses(s + 1, Addresses)) {
10806+                                               unsigned atpos = Addresses[0].find('@');
10807+                                               sFromDomain = Addresses[0].substr(atpos + 1);
10808+                                               break;
10809+                                       }
10810+                               }
10811+                       }
10812+               }
10813+       }
10814+       // if a signature from the From domain verified successfully, return success now
10815+       // without checking the sender signing practices
10816+       if (SuccessCount > 0 && !sFromDomain.empty()) {
10817+               for (list < string >::iterator i = SuccessfulDomains.begin(); i != SuccessfulDomains.end(); ++i) {
10818+                       // see if the successful domain is the same as or a parent of the From domain
10819+                       if (i->length() > sFromDomain.length())
10820+                               continue;
10821+                       if (_stricmp(i->c_str(), sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0)
10822+                               continue;
10823+                       if (i->length() == sFromDomain.length() || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] == '.')
10824+                               return ((SuccessCount == Signatures.size()) ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
10825+               }
10826+       }
10827+       if (!m_Accept3ps)
10828+               return DKIM_NEUTRAL;
10829+       *sCount = SuccessCount;
10830+       *sSize = Signatures.size();
10831+       return DKIM_3PS_SIGNATURE;
10832+}
10833+
10834+////////////////////////////////////////////////////////////////////////////////
10835+//
10836+// Hash - update the hash
10837+//
10838+////////////////////////////////////////////////////////////////////////////////
10839+void
10840+SignatureInfo::Hash(const char *szBuffer, unsigned nBufLength, bool IsBody)
10841+{
10842+
10843+       if (IsBody && BodyLength != -1) {
10844+               VerifiedBodyCount += nBufLength;
10845+               if (VerifiedBodyCount > BodyLength) {
10846+                       nBufLength = BodyLength - (VerifiedBodyCount - nBufLength);
10847+                       UnverifiedBodyCount += VerifiedBodyCount - BodyLength;
10848+                       VerifiedBodyCount = BodyLength;
10849+                       if (nBufLength == 0)
10850+                               return;
10851+               }
10852+       }
10853+       if (IsBody && !BodyHashData.empty()) {
10854+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10855+               EVP_DigestUpdate(m_Bdy_ctx, szBuffer, nBufLength);
10856+#else
10857+               EVP_DigestUpdate(&m_Bdy_ctx, szBuffer, nBufLength);
10858+#endif
10859+       } else {
10860+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10861+               EVP_VerifyUpdate(m_Hdr_ctx, szBuffer, nBufLength);
10862+#else
10863+               EVP_VerifyUpdate(&m_Hdr_ctx, szBuffer, nBufLength);
10864+#endif
10865+       }
10866+       if (m_SaveCanonicalizedData) {
10867+               CanonicalizedData.append(szBuffer, nBufLength);
10868+       }
10869+}
10870+
10871+
10872+// ProcessHeaders - Look for DKIM-Signatures and start processing them
10873+int
10874+CDKIMVerify::ProcessHeaders(void)
10875+{
10876+
10877+       // look for DKIM-Signature header(s)
10878+       for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
10879+               if (_strnicmp(i->c_str(), "DKIM-Signature", 14) == 0) {
10880+                       // skip over whitespace between the header name and :
10881+                       const char     *s = i->c_str() + 14;
10882+                       while (*s == ' ' || *s == '\t')
10883+                               s++;
10884+                       if (*s == ':') {
10885+                               // found
10886+                               SignatureInfo   sig(m_SaveCanonicalizedData);
10887+                               sig.Status = ParseDKIMSignature(*i, sig);
10888+                               Signatures.push_back(sig);
10889+                               if (Signatures.size() >= MAX_SIGNATURES)
10890+                                       break;
10891+                       }
10892+               }
10893+       }
10894+       if (Signatures.empty())
10895+               return DKIM_NO_SIGNATURES;
10896+       bool            ValidSigFound = false;
10897+       for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) {
10898+               SignatureInfo &sig = *s;
10899+               if (sig.Status != DKIM_SUCCESS)
10900+                       continue;
10901+               SelectorInfo &sel = GetSelector(sig.Selector, sig.Domain);
10902+               sig.m_pSelector = &sel;
10903+               if (sel.Status != DKIM_SUCCESS) {
10904+                       sig.Status = sel.Status;
10905+                       return (sig.Status);
10906+               } else {
10907+                       // check the granularity
10908+                       if (!WildcardMatch(sel.Granularity.c_str(), sig.IdentityLocalPart.c_str()))
10909+                               sig.Status = DKIM_SELECTOR_GRANULARITY_MISMATCH;        // this error causes the signature to fail
10910+                       // check the hash algorithm
10911+#ifdef HAVE_EVP_SHA256
10912+                       if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1) || (sig.m_nHash == DKIM_HASH_SHA256 && !sel.AllowSHA256))
10913+#else
10914+                       if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1))
10915+#endif
10916+                               sig.Status = DKIM_SELECTOR_ALGORITHM_MISMATCH;  // causes signature to fail
10917+                       // check for same domain
10918+                       if (sel.SameDomain && _stricmp(sig.Domain.c_str(), sig.IdentityDomain.c_str()) != 0)
10919+                               sig.Status = DKIM_BAD_SYNTAX;
10920+               }
10921+               if (sig.Status != DKIM_SUCCESS)
10922+                       continue;
10923+               // initialize the hashes
10924+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
10925+#ifdef HAVE_EVP_SHA256
10926+               if (sig.m_nHash == DKIM_HASH_SHA256) {
10927+                       EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha256());
10928+                       EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha256());
10929+               } else {
10930+                       EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha1());
10931+                       EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha1());
10932+               }
10933+#else
10934+               EVP_VerifyInit(sig.m_Hdr_ctx, EVP_sha1());
10935+               EVP_DigestInit(sig.m_Bdy_ctx, EVP_sha1());
10936+#endif
10937+#else
10938+#ifdef HAVE_EVP_SHA256
10939+               if (sig.m_nHash == DKIM_HASH_SHA256) {
10940+                       EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha256());
10941+                       EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha256());
10942+               } else {
10943+                       EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1());
10944+                       EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1());
10945+               }
10946+#else
10947+               EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1());
10948+               EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1());
10949+#endif
10950+#endif
10951+               // compute the hash of the header
10952+               vector < list < string >::reverse_iterator > used;
10953+               for (vector < string >::iterator x = sig.SignedHeaders.begin(); x != sig.SignedHeaders.end(); ++x) {
10954+                       list < string >::reverse_iterator i;
10955+                       for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) {
10956+                               if (_strnicmp(i->c_str(), x->c_str(), x->length()) == 0) {
10957+                                       // skip over whitespace between the header name and :
10958+                                       const char     *s = i->c_str() + x->length();
10959+                                       while (*s == ' ' || *s == '\t')
10960+                                               s++;
10961+                                       if (*s == ':' && find(used.begin(), used.end(), i) == used.end())
10962+                                               break;
10963+                               }
10964+                       }
10965+                       if (i != HeaderList.rend()) {
10966+                               used.push_back(i);
10967+                               // hash this header
10968+                               if (sig.HeaderCanonicalization == DKIM_CANON_SIMPLE)
10969+                                       sig.Hash(i->c_str(), i->length());
10970+                               else
10971+                               if (sig.HeaderCanonicalization == DKIM_CANON_RELAXED) {
10972+                                       string          sTemp = RelaxHeader(*i);
10973+                                       sig.Hash(sTemp.c_str(), sTemp.length());
10974+                               } else
10975+                               if (sig.HeaderCanonicalization == DKIM_CANON_NOWSP) {
10976+                                       string          sTemp = *i;
10977+                                       RemoveSWSP(sTemp);
10978+                                       // convert characters before ':' to lower case
10979+                                       for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) {
10980+                                               if (*s >= 'A' && *s <= 'Z')
10981+                                                       *s += 'a' - 'A';
10982+                                       }
10983+                                       sig.Hash(sTemp.c_str(), sTemp.length());
10984+                               }
10985+                               sig.Hash("\r\n", 2);
10986+                       }
10987+               }
10988+               if (sig.BodyHashData.empty()) {
10989+                       // hash CRLF separating headers from body
10990+                       sig.Hash("\r\n", 2);
10991+               }
10992+               if (!m_AllowUnsignedFromHeaders) {
10993+                       // make sure the message has no unsigned From headers
10994+                       list<string>::reverse_iterator i;
10995+                       for( i = HeaderList.rbegin(); i != HeaderList.rend(); ++i ) {
10996+                               if( _strnicmp(i->c_str(), "From", 4 ) == 0 ) {
10997+                                       // skip over whitespace between the header name and :
10998+                                       const char *s = i->c_str()+4;
10999+                                       while (*s == ' ' || *s == '\t')
11000+                                               s++;
11001+                                       if (*s == ':') {
11002+                                               if (find(used.begin(), used.end(), i) == used.end()) {
11003+                                                       // this From header was not signed
11004+                                                       break;
11005+                                               }
11006+                                       }
11007+                               }
11008+                       }
11009+                       if (i != HeaderList.rend()) {
11010+                               // treat signature as invalid
11011+                               sig.Status = DKIM_UNSIGNED_FROM;
11012+                               continue;
11013+                       }
11014+               }
11015+               ValidSigFound = true;
11016+       } /*- for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) { */
11017+       if (!ValidSigFound)
11018+               return DKIM_NO_VALID_SIGNATURES;
11019+       return DKIM_SUCCESS;
11020+}
11021+
11022+
11023+////////////////////////////////////////////////////////////////////////////////
11024+//
11025+// Strictly parse an unsigned integer.  Don't allow spaces, negative sign,
11026+// 0x prefix, etc.  Values greater than 2^32-1 are capped at 2^32-1
11027+//
11028+////////////////////////////////////////////////////////////////////////////////
11029+bool
11030+ParseUnsigned(const char *s, unsigned long *result)
11031+{
11032+       unsigned        temp = 0, last = 0;
11033+       bool            overflowed = false;
11034+
11035+       do {
11036+               if (*s < '0' || *s > '9')
11037+                       return false;           // returns false for an initial '\0'
11038+               temp = temp * 10 + (*s - '0');
11039+               if (temp < last)
11040+                       overflowed = true;
11041+               last = temp;
11042+               s++;
11043+       } while (*s != '\0');
11044+       if (overflowed)
11045+               *result = -1;
11046+       else
11047+               *result = temp;
11048+       return true;
11049+}
11050+
11051+
11052+// ParseDKIMSignature - Parse a DKIM-Signature header field
11053+int
11054+CDKIMVerify::ParseDKIMSignature(const string &sHeader, SignatureInfo &sig)
11055+{
11056+
11057+       // save header for later
11058+       sig.Header = sHeader;
11059+       string          sValue = sHeader.substr(sHeader.find(':') + 1);
11060+       static const char *tags[] = { "v", "a", "b", "d", "h", "s", "c", "i", "l", "q", "t", "x", "bh", NULL };
11061+       char           *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
11062+       char           *saveptr;
11063+
11064+       if (!ParseTagValueList((char *) sValue.c_str(), tags, values))
11065+               return DKIM_BAD_SYNTAX;
11066+       // check signature version
11067+       if (values[0] != NULL) {
11068+               if (strcmp(values[0], "1") == 0 || strcmp(values[0], "0.5") == 0 || strcmp(values[0], "0.4") == 0
11069+                       || strcmp(values[0], "0.3") == 0 || strcmp(values[0], "0.2") == 0) {
11070+                       sig.Version = DKIM_SIG_VERSION_02_PLUS;
11071+               } else {
11072+               // unknown version
11073+                       return DKIM_STAT_INCOMPAT;
11074+               }
11075+       } else {
11076+               // Note:  DKIM Interop 1 pointed out that v= is now required, but we do
11077+               // not enforce that in order to verify signatures made by older drafts.
11078+
11079+               // prior to 0.2, there MUST NOT have been a v=
11080+               // (optionally) support these signatures, for backwards compatibility
11081+               if (true) {
11082+                       sig.Version = DKIM_SIG_VERSION_PRE_02;
11083+               } else {
11084+                       return DKIM_BAD_SYNTAX;
11085+               }
11086+       }
11087+       // signature MUST have a=, b=, d=, h=, s=
11088+       if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL || values[5] == NULL)
11089+               return DKIM_BAD_SYNTAX;
11090+       // algorithm can be "rsa-sha1" or "rsa-sha256"
11091+       if (strcmp(values[1], "rsa-sha1") == 0) {
11092+               sig.m_nHash = DKIM_HASH_SHA1;
11093+       }
11094+#ifdef HAVE_EVP_SHA256
11095+       else if (strcmp(values[1], "rsa-sha256") == 0) {
11096+               sig.m_nHash = DKIM_HASH_SHA256;
11097+       }
11098+#endif
11099+       else {
11100+               return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown algorithm
11101+       }
11102+       // make sure the signature data is not empty
11103+       unsigned SigDataLen = DecodeBase64(values[2]);
11104+       if (SigDataLen == 0)
11105+               return DKIM_BAD_SYNTAX;
11106+       sig.SignatureData.assign(values[2], SigDataLen);
11107+       // check for body hash
11108+       if (values[12] == NULL) {
11109+               // use the old single hash way for backwards compatibility
11110+               if (sig.Version != DKIM_SIG_VERSION_PRE_02)
11111+                       return DKIM_BAD_SYNTAX;
11112+       } else {
11113+               unsigned BodyHashLen = DecodeBase64(values[12]);
11114+               if (BodyHashLen == 0)
11115+                       return DKIM_BAD_SYNTAX;
11116+               sig.BodyHashData.assign(values[12], BodyHashLen);
11117+       }
11118+       // domain must not be empty
11119+       if (*values[3] == '\0')
11120+               return DKIM_BAD_SYNTAX;
11121+       sig.Domain = values[3];
11122+       // signed headers must not be empty (more verification is done later)
11123+       if (*values[4] == '\0')
11124+               return DKIM_BAD_SYNTAX;
11125+       // selector must not be empty
11126+       if (*values[5] == '\0')
11127+               return DKIM_BAD_SYNTAX;
11128+       sig.Selector = values[5];
11129+       // canonicalization
11130+       if (values[6] == NULL) {
11131+               sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_SIMPLE;
11132+       }
11133+       else
11134+       if (sig.Version == DKIM_SIG_VERSION_PRE_02 && strcmp(values[6], "nowsp") == 0) {
11135+       // for backwards compatibility
11136+               sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_NOWSP;
11137+       } else {
11138+               char           *slash = strchr(values[6], '/');
11139+               if (slash != NULL)
11140+                       *slash = '\0';
11141+               if (strcmp(values[6], "simple") == 0)
11142+                       sig.HeaderCanonicalization = DKIM_CANON_SIMPLE;
11143+               else
11144+               if (strcmp(values[6], "relaxed") == 0)
11145+                       sig.HeaderCanonicalization = DKIM_CANON_RELAXED;
11146+               else
11147+                       return DKIM_BAD_SYNTAX;
11148+               if (slash == NULL || strcmp(slash + 1, "simple") == 0)
11149+                       sig.BodyCanonicalization = DKIM_CANON_SIMPLE;
11150+               else
11151+               if (strcmp(slash + 1, "relaxed") == 0)
11152+                       sig.BodyCanonicalization = DKIM_CANON_RELAXED;
11153+               else
11154+                       return DKIM_BAD_SYNTAX;
11155+       }
11156+       // identity
11157+       if (values[7] == NULL) {
11158+               sig.IdentityLocalPart.erase();
11159+               sig.IdentityDomain = sig.Domain;
11160+       } else {
11161+               // quoted-printable decode the value
11162+               DecodeQuotedPrintable(values[7]);
11163+               // must have a '@' separating the local part from the domain
11164+               char           *at = strchr(values[7], '@');
11165+               if (at == NULL)
11166+                       return DKIM_BAD_SYNTAX;
11167+               *at = '\0';
11168+               char           *ilocalpart = values[7];
11169+               char           *idomain = at + 1;
11170+               // i= domain must be the same as or a subdomain of the d= domain
11171+               int idomainlen = strlen(idomain);
11172+               int ddomainlen = strlen(values[3]);
11173+
11174+               // todo: maybe create a new error code for invalid identity domain
11175+               if (idomainlen < ddomainlen)
11176+                       return DKIM_BAD_SYNTAX;
11177+               if (_stricmp(idomain + idomainlen - ddomainlen, values[3]) != 0)
11178+                       return DKIM_BAD_SYNTAX;
11179+               if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] != '.')
11180+                       return DKIM_BAD_SYNTAX;
11181+               sig.IdentityLocalPart = ilocalpart;
11182+               sig.IdentityDomain = idomain;
11183+       }
11184+       // body count
11185+       if (values[8] == NULL || !m_HonorBodyLengthTag) {
11186+               sig.BodyLength = -1;
11187+       } else {
11188+               if (!ParseUnsigned(values[8], (unsigned long *) &sig.BodyLength))
11189+                       return DKIM_BAD_SYNTAX;
11190+       }
11191+       // query methods
11192+       if (values[9] != NULL) {
11193+
11194+               // make sure "dns" is in the list
11195+               bool            HasDNS = false;
11196+               char           *s = strtok_r(values[9], ":", &saveptr);
11197+               while (s != NULL) {
11198+                       if (strncmp(s, "dns", 3) == 0 && (s[3] == '\0' || s[3] == '/')) {
11199+                               HasDNS = true;
11200+                               break;
11201+                       }
11202+                       s = strtok_r(NULL, ": \t", &saveptr);
11203+               }
11204+               if (!HasDNS)
11205+                       return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown query method
11206+       }
11207+       // signature time
11208+       time_t          SignedTime = -1;
11209+       if (values[10] != NULL) {
11210+               if (!ParseUnsigned(values[10], (unsigned long *) &SignedTime))
11211+                       return DKIM_BAD_SYNTAX;
11212+       }
11213+       // expiration time
11214+       if (values[11] == NULL) {
11215+               sig.ExpireTime = -1;
11216+       } else {
11217+               if (!ParseUnsigned(values[11], (unsigned long *) &sig.ExpireTime))
11218+                       return DKIM_BAD_SYNTAX;
11219+               if (sig.ExpireTime != -1) {
11220+                       // the value of x= MUST be greater than the value of t= if both are present
11221+                       if (SignedTime != -1 && sig.ExpireTime <= SignedTime)
11222+                               return DKIM_BAD_SYNTAX;
11223+                       // todo: if possible, use the received date/time instead of the current time
11224+                       time_t curtime = time(NULL);
11225+                       if (curtime > sig.ExpireTime)
11226+                               return DKIM_SIGNATURE_EXPIRED;
11227+               }
11228+       }
11229+       // parse the signed headers list
11230+       bool            HasFrom = false, HasSubject = false;
11231+       RemoveSWSP(values[4]);          // header names shouldn't have spaces in them so this should be ok...
11232+       char           *s = strtok_r(values[4], ":", &saveptr);
11233+       while (s != NULL) {
11234+               if (_stricmp(s, "From") == 0)
11235+                       HasFrom = true;
11236+               else
11237+               if (_stricmp(s, "Subject") == 0)
11238+                       HasSubject = true;
11239+               sig.SignedHeaders.push_back(s);
11240+               s = strtok_r(NULL, ":", &saveptr);
11241+       }
11242+       if (!HasFrom)
11243+               return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing From
11244+       if (m_SubjectIsRequired && !HasSubject)
11245+               return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing Subject
11246+       return DKIM_SUCCESS;
11247+}
11248+
11249+
11250+// ProcessBody - Process message body data
11251+int
11252+CDKIMVerify::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
11253+{
11254+       bool            MoreBodyNeeded = false;
11255+
11256+       for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
11257+               if (i->Status == DKIM_SUCCESS) {
11258+                       if (i->BodyCanonicalization == DKIM_CANON_SIMPLE) {
11259+                               if (nBufLength > 0) {
11260+                                       while (i->EmptyLineCount > 0) {
11261+                                               i->Hash("\r\n", 2, true);
11262+                                               i->EmptyLineCount--;
11263+                                       }
11264+                                       i->Hash(szBuffer, nBufLength, true);
11265+                                       i->Hash("\r\n", 2, true);
11266+                               } else {
11267+                                       i->EmptyLineCount++;
11268+                                       if (bEOF)
11269+                                               i->Hash("\r\n", 2, true);
11270+                               }
11271+                       } else
11272+                       if (i->BodyCanonicalization == DKIM_CANON_RELAXED) {
11273+                               CompressSWSP(szBuffer, nBufLength);
11274+                               if (nBufLength > 0) {
11275+                                       while (i->EmptyLineCount > 0) {
11276+                                               i->Hash("\r\n", 2, true);
11277+                                               i->EmptyLineCount--;
11278+                                       }
11279+                                       i->Hash(szBuffer, nBufLength, true);
11280+                                       if (!bEOF)
11281+                                               i->Hash("\r\n", 2, true);
11282+                               } else
11283+                                       i->EmptyLineCount++;
11284+                       } else
11285+                       if (i->BodyCanonicalization == DKIM_CANON_NOWSP) {
11286+                               RemoveSWSP(szBuffer, nBufLength);
11287+                               i->Hash(szBuffer, nBufLength, true);
11288+                       }
11289+                       if (i->UnverifiedBodyCount == 0)
11290+                               MoreBodyNeeded = true;
11291+               }
11292+       }
11293+       if (!MoreBodyNeeded)
11294+               return DKIM_FINISHED_BODY;
11295+       return DKIM_SUCCESS;
11296+}
11297+
11298+SelectorInfo::SelectorInfo(const string &sSelector, const string &sDomain):Selector(sSelector), Domain(sDomain)
11299+{
11300+       AllowSHA1 = true;
11301+#ifdef HAVE_EVP_SHA256
11302+       AllowSHA256 = true;
11303+#else
11304+       AllowSHA256 = false;
11305+#endif
11306+       PublicKey = NULL;
11307+       Testing = false;
11308+       SameDomain = false;
11309+       Status = DKIM_SUCCESS;
11310+} SelectorInfo::~SelectorInfo()
11311+{
11312+       if (PublicKey != NULL) {
11313+               EVP_PKEY_free(PublicKey);
11314+       }
11315+}
11316+
11317+////////////////////////////////////////////////////////////////////////////////
11318+//
11319+// Parse - Parse a DKIM selector
11320+//
11321+////////////////////////////////////////////////////////////////////////////////
11322+int
11323+SelectorInfo::Parse(char *Buffer)
11324+{
11325+       static const char *tags[] = { "v", "g", "h", "k", "p", "s", "t", "n", NULL };
11326+       char           *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
11327+       char           *saveptr;
11328+
11329+       if (!ParseTagValueList(Buffer, tags, values))
11330+               return DKIM_SELECTOR_INVALID;
11331+       if (values[0] != NULL) {
11332+
11333+               // make sure the version is "DKIM1"
11334+               if (strcmp(values[0], "DKIM1") != 0)
11335+                       return DKIM_SELECTOR_INVALID;   // todo: maybe create a new error code for unsupported selector version
11336+               // make sure v= is the first tag in the response    // todo: maybe don't enforce this, it seems unnecessary
11337+               for (unsigned int j = 1; j < sizeof (values) / sizeof (values[0]); j++) {
11338+                       if (values[j] != NULL && values[j] < values[0]) {
11339+                               return DKIM_SELECTOR_INVALID;
11340+                       }
11341+               }
11342+       }
11343+       // selector MUST have p= tag
11344+       if (values[4] == NULL)
11345+               return DKIM_SELECTOR_INVALID;
11346+       // granularity
11347+       if (values[1] == NULL)
11348+               Granularity = "*";
11349+
11350+       else
11351+               Granularity = values[1];
11352+       // hash algorithm
11353+       if (values[2] == NULL) {
11354+               AllowSHA1 = true;
11355+#ifdef HAVE_EVP_SHA256
11356+               AllowSHA256 = true;
11357+#else
11358+               AllowSHA256 = false;
11359+#endif
11360+       } else {
11361+               // MUST include "sha1" or "sha256"
11362+               char           *s = strtok_r(values[2], ":", &saveptr);
11363+               while (s != NULL) {
11364+                       if (strcmp(s, "sha1") == 0)
11365+                               AllowSHA1 = true;
11366+#ifdef HAVE_EVP_SHA256
11367+                       else if (strcmp(s, "sha256") == 0)
11368+                               AllowSHA256 = true;
11369+#endif
11370+                       s = strtok_r(NULL, ":", &saveptr);
11371+               }
11372+#ifdef HAVE_EVP_SHA256
11373+               if (!(AllowSHA1 || AllowSHA256))
11374+#else
11375+               if (!AllowSHA1)
11376+#endif
11377+                       return DKIM_SELECTOR_INVALID;   // todo: maybe create a new error code for unsupported hash algorithm
11378+       }
11379+       // key type
11380+       if (values[3] != NULL) {
11381+               // key type MUST be "rsa"
11382+               if (strcmp(values[3], "rsa") != 0)
11383+                       return DKIM_SELECTOR_INVALID;
11384+       }
11385+       // service type
11386+       if (values[5] != NULL) {
11387+               // make sure "*" or "email" is in the list
11388+               bool            ServiceTypeMatch = false;
11389+               char           *s = strtok_r(values[5], ":", &saveptr);
11390+               while (s != NULL) {
11391+                       if (strcmp(s, "*") == 0 || strcmp(s, "email") == 0) {
11392+                               ServiceTypeMatch = true;
11393+                               break;
11394+                       }
11395+                       s = strtok_r(NULL, ":", &saveptr);
11396+               }
11397+               if (!ServiceTypeMatch)
11398+                       return DKIM_SELECTOR_INVALID;
11399+       }
11400+       // flags
11401+       if (values[6] != NULL) {
11402+               char           *s = strtok_r(values[6], ":", &saveptr);
11403+               while (s != NULL) {
11404+                       if (strcmp(s, "y") == 0) {
11405+                               Testing = true;
11406+                       } else
11407+                       if (strcmp(s, "s") == 0) {
11408+                               SameDomain = true;
11409+                       }
11410+                       s = strtok_r(NULL, ":", &saveptr);
11411+               }
11412+       }
11413+#define M_ToConstUCharPtr(p)       reinterpret_cast<const unsigned char*>(p)          // Cast to unsigned char*
11414+       /*- public key data */
11415+       unsigned        PublicKeyLen = DecodeBase64(values[4]);
11416+       if (PublicKeyLen == 0)
11417+               return DKIM_SELECTOR_KEY_REVOKED;       // this error causes the signature to fail
11418+       else {
11419+               EVP_PKEY       *pkey;
11420+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11421+               int             rtype;
11422+#endif
11423+               const unsigned char  *qq; /*- public key data */
11424+
11425+               qq = M_ToConstUCharPtr(values[4]);
11426+#ifdef DARWIN
11427+               pkey = d2i_PUBKEY(NULL, (unsigned char **) &qq, PublicKeyLen);
11428+#else
11429+               pkey = d2i_PUBKEY(NULL, &qq, PublicKeyLen);
11430+#endif
11431+               if (!pkey)
11432+                       return DKIM_SELECTOR_PUBLIC_KEY_INVALID;
11433+               /*- make sure public key is the correct type (we only support rsa) */
11434+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11435+               rtype = EVP_PKEY_base_id(pkey);
11436+               if (rtype == EVP_PKEY_RSA || rtype == EVP_PKEY_RSA2)
11437+#else
11438+               if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2)
11439+#endif
11440+                       PublicKey = pkey;
11441+               else {
11442+                       EVP_PKEY_free(pkey);
11443+                       return DKIM_SELECTOR_PUBLIC_KEY_INVALID;
11444+               }
11445+       }
11446+       return DKIM_SUCCESS;
11447+}
11448+
11449+// GetSelector - Get a DKIM selector for a domain
11450+SelectorInfo &CDKIMVerify::GetSelector(const string &sSelector, const string &sDomain)
11451+{
11452+
11453+       // see if we already have this selector
11454+       for (list < SelectorInfo >::iterator i = Selectors.begin(); i != Selectors.end(); ++i) {
11455+               if (_stricmp(i->Selector.c_str(), sSelector.c_str()) == 0 && _stricmp(i->Domain.c_str(), sDomain.c_str()) == 0) {
11456+                       return *i;
11457+               }
11458+       }
11459+       Selectors.push_back(SelectorInfo(sSelector, sDomain));
11460+       SelectorInfo &sel = Selectors.back();
11461+       string sFQDN = sSelector;
11462+       sFQDN += "._domainkey.";
11463+       sFQDN += sDomain;
11464+       char            Buffer[1024];
11465+       int             DNSResult;
11466+
11467+       if (m_pfnSelectorCallback)
11468+               DNSResult = m_pfnSelectorCallback(sFQDN.c_str(), Buffer, sizeof(Buffer));
11469+       else
11470+               DNSResult = DNSGetTXT(sFQDN.c_str(), Buffer, sizeof(Buffer));
11471+       switch (DNSResult) {
11472+       case DNSRESP_SUCCESS:
11473+               sel.Status = sel.Parse(Buffer);
11474+               break;
11475+       case DNSRESP_TEMP_FAIL:
11476+               sel.Status = DKIM_SELECTOR_DNS_TEMP_FAILURE;
11477+               break;
11478+       case DNSRESP_PERM_FAIL:
11479+       default:
11480+               sel.Status = DKIM_SELECTOR_DNS_PERM_FAILURE;
11481+               break;
11482+       case DNSRESP_DOMAIN_NAME_TOO_LONG:
11483+               sel.Status = DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG;
11484+               break;
11485+       }
11486+       return sel;
11487+}
11488+
11489+// GetDetails - Get DKIM verification details (per signature)
11490+int
11491+CDKIMVerify::GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails)
11492+{
11493+       Details.clear();
11494+       for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
11495+               DKIMVerifyDetails d;
11496+               d.szSignature = (char *) i->Header.c_str();
11497+               d.szSignatureDomain = (char*)i->Domain.c_str();
11498+               d.szIdentityDomain = (char*)i->IdentityDomain.c_str();
11499+               d.nResult = i->Status;
11500+               d.szCanonicalizedData = (char *) i->CanonicalizedData.c_str();
11501+               Details.push_back(d);
11502+       } *nSigCount = Details.size();
11503+       *pDetails = (*nSigCount != 0) ? &Details[0] : NULL;
11504+       return DKIM_SUCCESS;
11505+}
11506+
11507+char           *DKIM_CALL
11508+CDKIMVerify::GetDomain(void)
11509+{
11510+       string          sFromDomain;
11511+       for (list <string>::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
11512+               if (_strnicmp(i->c_str(), "From", 4) == 0) {
11513+                       // skip over whitespace between the header name and :
11514+                       const char     *s = i->c_str() + 4;
11515+                       while (*s == ' ' || *s == '\t')
11516+                               s++;
11517+                       if (*s == ':') {
11518+                               vector <string> Addresses;
11519+                               if (ParseAddresses(s + 1, Addresses)) {
11520+                                       unsigned atpos = Addresses[0].find('@');
11521+                                       sFromDomain = Addresses[0].substr(atpos + 1);
11522+                                       break;
11523+                               }
11524+                       }
11525+               }
11526+       }
11527+       return ((char *) sFromDomain.c_str());
11528+}
11529+
11530+void
11531+getversion_dkimverify_cpp()
11532+{
11533+       static char    *x = (char *) "$Id: dkimverify.cpp,v 1.18 2017-09-05 11:00:33+05:30 Cprogrammer Exp mbhangui $";
11534+
11535+       x++;
11536+}
11537diff -ruN ../netqmail-1.06-original/dkimverify.h netqmail-1.06/dkimverify.h
11538--- ../netqmail-1.06-original/dkimverify.h      1970-01-01 01:00:00.000000000 +0100
11539+++ netqmail-1.06/dkimverify.h  2018-04-03 14:46:51.367411617 +0200
11540@@ -0,0 +1,139 @@
11541+/*
11542+ * $Log: dkimverify.h,v $
11543+ * Revision 1.7  2017-08-31 17:07:45+05:30  Cprogrammer
11544+ * fixed g++ compiler warning
11545+ *
11546+ * Revision 1.6  2017-08-09 22:03:46+05:30  Cprogrammer
11547+ * initialized EVP_MD_CTX variables
11548+ *
11549+ * Revision 1.5  2017-08-08 23:50:47+05:30  Cprogrammer
11550+ * openssl 1.1.0 port
11551+ *
11552+ * Revision 1.4  2015-12-15 16:03:09+05:30  Cprogrammer
11553+ * use time_t for ExpireTime
11554+ *
11555+ * Revision 1.3  2011-06-04 09:37:25+05:30  Cprogrammer
11556+ * added AllowUnsignedFromHeaders
11557+ *
11558+ * Revision 1.2  2009-03-26 15:12:15+05:30  Cprogrammer
11559+ * changes for ADSP
11560+ *
11561+ * Revision 1.1  2009-03-21 08:50:22+05:30  Cprogrammer
11562+ * Initial revision
11563+ *
11564+ *
11565+ *  Copyright 2005 Alt-N Technologies, Ltd.
11566+ *
11567+ *  Licensed under the Apache License, Version 2.0 (the "License");
11568+ *  you may not use this file except in compliance with the License.
11569+ *  You may obtain a copy of the License at
11570+ *
11571+ *      http://www.apache.org/licenses/LICENSE-2.0
11572+ *
11573+ *  This code incorporates intellectual property owned by Yahoo! and licensed
11574+ *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
11575+ *
11576+ *  Unless required by applicable law or agreed to in writing, software
11577+ *  distributed under the License is distributed on an "AS IS" BASIS,
11578+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11579+ *  See the License for the specific language governing permissions and
11580+ *  limitations under the License.
11581+ *
11582+ */
11583+
11584+#ifndef DKIMVERIFY_H
11585+#define DKIMVERIFY_H
11586+
11587+#include "dkimbase.h"
11588+#include <vector>
11589+
11590+#define DKIM_SIG_VERSION_PRE_02                        0
11591+#define DKIM_SIG_VERSION_02_PLUS               1
11592+
11593+class           SelectorInfo {
11594+ public:
11595+       SelectorInfo(const string & sSelector, const string & sDomain);
11596+       ~SelectorInfo();
11597+
11598+       string          Domain;
11599+       string          Selector;
11600+       string          Granularity;
11601+       bool            AllowSHA1;
11602+       bool            AllowSHA256;
11603+       EVP_PKEY       *PublicKey;      /* the public key */
11604+       bool            Testing;
11605+       bool            SameDomain;
11606+       int             Status;
11607+       int             Parse(char *Buffer);
11608+};
11609+
11610+class           SignatureInfo {
11611+public:
11612+       SignatureInfo(bool SaveCanonicalizedData);
11613+       ~SignatureInfo();
11614+
11615+       void            Hash(const char *szBuffer, unsigned nBufLength, bool IsBody = false);
11616+       string          Header;
11617+       unsigned        Version;
11618+       string          Domain;
11619+       string          Selector;
11620+       string          SignatureData;
11621+       string          BodyHashData;
11622+       string          IdentityLocalPart;
11623+       string          IdentityDomain;
11624+       string          CanonicalizedData;
11625+       vector <string> SignedHeaders;
11626+       int             BodyLength;
11627+       unsigned        HeaderCanonicalization;
11628+       unsigned        BodyCanonicalization;
11629+       time_t          ExpireTime;
11630+       int             VerifiedBodyCount;
11631+       unsigned        UnverifiedBodyCount;
11632+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
11633+       EVP_MD_CTX     *m_Hdr_ctx = NULL;
11634+       EVP_MD_CTX     *m_Bdy_ctx = NULL;
11635+#else
11636+       EVP_MD_CTX      m_Hdr_ctx;
11637+       EVP_MD_CTX      m_Bdy_ctx;
11638+#endif
11639+       SelectorInfo   *m_pSelector;
11640+       int             Status;
11641+       int             m_nHash;        // use one of the DKIM_HASH_xxx constants here
11642+       unsigned        EmptyLineCount;
11643+       bool            m_SaveCanonicalizedData;
11644+};
11645+
11646+class           CDKIMVerify:public CDKIMBase {
11647+public:
11648+
11649+       CDKIMVerify();
11650+       ~CDKIMVerify();
11651+
11652+       int             Init(DKIMVerifyOptions * pOptions);
11653+       int             GetResults(int *sCount, int *sSize);
11654+       int             GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails);
11655+       virtual int     ProcessHeaders(void);
11656+       virtual int     ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
11657+       const char     *GetPractices() {return Practices.c_str();}
11658+       char           *DKIM_CALL GetDomain(void);
11659+
11660+protected:
11661+       int             ParseDKIMSignature(const string & sHeader, SignatureInfo & sig);
11662+       SelectorInfo   &GetSelector(const string & sSelector, const string & sDomain);
11663+       int             GetADSP(const string &sDomain, int &iADSP);
11664+       int             GetSSP(const string &sDomain, int &iSSP, bool & bTesting);
11665+       list <SignatureInfo> Signatures;
11666+       list <SelectorInfo> Selectors;
11667+       DKIMDNSCALLBACK m_pfnSelectorCallback;  // selector record callback
11668+       DKIMDNSCALLBACK m_pfnPracticesCallback; // SSP record callback
11669+       bool            m_HonorBodyLengthTag;
11670+       bool            m_CheckPractices;
11671+       bool            m_Accept3ps;            //TBS(Luc) : accept 3rd party signature(s)
11672+       bool            m_SubjectIsRequired;
11673+       bool            m_SaveCanonicalizedData;
11674+       bool            m_AllowUnsignedFromHeaders;
11675+       vector <DKIMVerifyDetails> Details;
11676+       string          Practices;
11677+};
11678+
11679+#endif /*- DKIMVERIFY_H */
11680diff -ruN ../netqmail-1.06-original/dknewkey.sh netqmail-1.06/dknewkey.sh
11681--- ../netqmail-1.06-original/dknewkey.sh       1970-01-01 01:00:00.000000000 +0100
11682+++ netqmail-1.06/dknewkey.sh   2016-11-22 21:03:57.112528099 +0100
11683@@ -0,0 +1,27 @@
11684+#
11685+# $Log: dknewkey.sh,v $
11686+# Revision 1.4  2010-05-16 19:59:48+05:30  Cprogrammer
11687+# fix for Mac OS X
11688+#
11689+# Revision 1.3  2004-11-02 20:48:31+05:30  Cprogrammer
11690+# fixed error when dknewkey was called without arguments
11691+#
11692+# Revision 1.2  2004-10-21 21:54:25+05:30  Cprogrammer
11693+# create public key file
11694+#
11695+# Revision 1.1  2004-10-20 20:40:56+05:30  Cprogrammer
11696+# Initial revision
11697+#
11698+#
11699+if [ $# -lt 1 ] ; then
11700+       echo "USAGE: dknewkey keyfile [bits]"
11701+       exit 1
11702+fi
11703+BITS=384
11704+if test -n "$2"; then BITS=$2; fi
11705+
11706+openssl genrsa -out $1 $BITS 2>/dev/null
11707+openssl rsa -in $1 -out /tmp/dknewkey.$$ -pubout -outform PEM 2>/dev/null
11708+printf "%s._domainkey\tIN\tTXT\t\"k=rsa; p=%s\"\n" `basename $1` `grep -v ^-- /tmp/dknewkey.$$ | tr -d '\n'` > $1.pub
11709+/bin/cat $1.pub
11710+/bin/rm -f /tmp/dknewkey.$$
11711diff -ruN ../netqmail-1.06-original/dktest.9 netqmail-1.06/dktest.9
11712--- ../netqmail-1.06-original/dktest.9  1970-01-01 01:00:00.000000000 +0100
11713+++ netqmail-1.06/dktest.9      2016-11-22 21:03:57.112528099 +0100
11714@@ -0,0 +1,86 @@
11715+.TH dktest 8
11716+.SH NAME
11717+dktest \- exercise the domainkeys library
11718+.SH SYNOPSIS
11719+.B dktest
11720+.I opts
11721+
11722+.I opts
11723+is a series of getopt-style options.
11724+
11725+.SH DESCRIPTION
11726+.B dktest
11727+exercises the domainkeys library. Both signing and verification merely print out the DK header.
11728+They do not keep a copy of the input file. You will need to do something like this:
11729+
11730+.EX
11731+ (./dktest -s QMAILHOME/control/domainkeys/dog </tmp/testmsg; cat /tmp/testmsg)\
11732+ | ./dktest -v
11733+.EE
11734+
11735+Here are the options:
11736+.IP \[bu] 2
11737+-s
11738+\fIkey\fR: Sign.
11739+.I key
11740+is a path to a file containing a PEM-format private key. The base name of
11741+the file is  used  as  the  selector. Reads the email message on stdin. Prints the
11742+.B DomainKey-Signature
11743+header.
11744+
11745+.IP \[bu]
11746+-v: Verify. Verifies the email on stdin. Exits with a non-zero exit code and a message to
11747+stderr if there was a problem with the signature. Always prints a
11748+.B DomainKey-Status:
11749+header to stdout. This option requires the \fBs\fR._domainkey.\fBd\fR txt record in
11750+dns (public key). Here \fBs\fR is the selector and \fBd\fR is the domain
11751+.EX
11752+
11753+ Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys
11754+ DomainKey-Signature: a=rsa-sha1; q=dns; c=simple;
11755+  s=dog; d=indimail.org;
11756+  b=CndyNmOBqXD+d6qLGVjVua/oDJetLzAKAh3JoId93GmWRg1Y40DUdvZZhw8tTIoZ;
11757+.EE
11758+
11759+.IP \[bu]
11760+-c \fI[simple|nofws]\fR Canonicalization.
11761+Defaults to simple.
11762+
11763+.IP \[bu]
11764+-t \fIn\fR: Test.
11765+.I n
11766+is a number indicating which internal test is being performed. The meanings of the numbers
11767+are documented in the source code.
11768+
11769+.IP \[bu]
11770+-d \fIdns\fR Test dns record
11771+
11772+.IP \[bu]
11773+-b \fIn\fR: Buffer size.
11774+.I n
11775+is a number which forces the buffer size. Only needed for testing.
11776+
11777+.IP \[bu]
11778+-D \fIdkdomain\fR: set d= tag.
11779+.I dkdomain
11780+is set as the value for d= tag
11781+
11782+.IP \[bu]
11783+-h:
11784+include h= tag when signing
11785+
11786+.IP \[bu]
11787+-r:
11788+only include unique headers in the signature generation, implies -h
11789+
11790+.IP \[bu]
11791+-T:
11792+Generate DomainKey-Trace: header, shows Diff header if Verifying.
11793+
11794+.SH "SEE ALSO"
11795+dkim(8),
11796+qmail-dk(8),
11797+qmail-dkim(8),
11798+dknewkey(8),
11799+rfc-4870(5),
11800+rfc-4871(5)
11801diff -ruN ../netqmail-1.06-original/dktest.c netqmail-1.06/dktest.c
11802--- ../netqmail-1.06-original/dktest.c  1970-01-01 01:00:00.000000000 +0100
11803+++ netqmail-1.06/dktest.c      2016-11-22 21:03:57.112528099 +0100
11804@@ -0,0 +1,431 @@
11805+/*
11806+ * $Log: dktest.c,v $
11807+ * Revision 1.16  2013-08-17 16:01:08+05:30  Cprogrammer
11808+ * added case for duplicate DomainKey-Signature header
11809+ *
11810+ * Revision 1.15  2011-07-29 09:28:09+05:30  Cprogrammer
11811+ * fixed gcc 4.6 warnings
11812+ *
11813+ * Revision 1.14  2011-07-22 14:39:27+05:30  Cprogrammer
11814+ * added -D option to specify d= tag
11815+ *
11816+ * Revision 1.13  2009-12-10 15:04:09+05:30  Cprogrammer
11817+ * exit with DK_STAT
11818+ *
11819+ * Revision 1.12  2009-04-20 22:20:55+05:30  Cprogrammer
11820+ * fixed compilation warning
11821+ *
11822+ * Revision 1.11  2009-04-16 22:39:33+05:30  Cprogrammer
11823+ * assign from = dk_from()
11824+ *
11825+ * Revision 1.10  2009-04-05 12:51:22+05:30  Cprogrammer
11826+ * added preprocessor warning
11827+ *
11828+ * Revision 1.9  2009-03-14 16:28:22+05:30  Cprogrammer
11829+ * Fixed dktest.c to check for DK_STAT_GRANULARITY
11830+ *
11831+ * Revision 1.8  2009-03-14 08:50:41+05:30  Cprogrammer
11832+ * Added -h option to dktest to add h= tag when signing
11833+ * Added -r option to dktest to enable ignoring duplicate headers when signing (implies -h)
11834+ * Added -T option to dktest to enable generation of trace headers
11835+ * Added -d option to fetch dns text record for domainkey
11836+ *
11837+ * Revision 1.7  2005-08-23 17:15:02+05:30  Cprogrammer
11838+ * gcc 4 compliance
11839+ *
11840+ * Revision 1.6  2005-04-25 22:52:45+05:30  Cprogrammer
11841+ * removed printing of comments
11842+ *
11843+ * Revision 1.5  2005-04-01 19:54:27+05:30  Cprogrammer
11844+ * libdomainkeys-0.64
11845+ *
11846+ * Revision 1.4  2004-10-25 14:54:02+05:30  Cprogrammer
11847+ * libdomainkeys-0.63
11848+ *
11849+ * Revision 1.3  2004-10-22 20:24:18+05:30  Cprogrammer
11850+ * added RCS id
11851+ *
11852+ * Revision 1.2  2004-10-20 20:01:58+05:30  Cprogrammer
11853+ * upgrade to libdomainkeys-0.62
11854+ *
11855+ * Revision 1.1  2004-09-22 23:30:32+05:30  Cprogrammer
11856+ * Initial revision
11857+ *
11858+ */
11859+#include <stdio.h>
11860+#include <string.h>
11861+#include <unistd.h>
11862+#include <stdlib.h>
11863+#include "domainkeys.h"
11864+
11865+#ifdef DOMAIN_KEYS
11866+int             optf = 0;
11867+
11868+void
11869+errorout(DK *dk, DK_STAT st)
11870+{
11871+       if (optf && dk)
11872+               fprintf(stderr, "%s(%d):", dk_errfile(dk), dk_errline(dk));
11873+       fprintf(stderr, "dktest: %s\n", DK_STAT_to_string(st));
11874+       exit(st);
11875+}
11876+
11877+int
11878+main(int argc, char *argv[])
11879+{
11880+       char            inbuf[1024];
11881+       char            advice[2048];
11882+       char            trace_count[BUFSIZ];
11883+       size_t          inlen;
11884+       size_t          advicelen = sizeof(advice);
11885+       DK             *dk;
11886+       DK_LIB         *dklib;
11887+       DK_STAT         st, dkt_st;
11888+       signed char     ch;
11889+       int             opts = 0, optv = 0, optt = 0, opth = 0, optr = 0, optT = 0,
11890+                                       optc = DK_CANON_SIMPLE;
11891+       char           *canon = "simple";
11892+       char           *keyfn  = 0, *selector  = 0;
11893+       char           *txtrec, *cp, *from, *dkdomain;
11894+       char            privkey[2048];
11895+       FILE           *privkeyf = 0;
11896+       size_t          privkeylen;
11897+       DK_FLAGS        dkf = 0;
11898+       int             i;
11899+       DK_TRACE_TYPE   dk_trace_tag[4] = {
11900+               DKT_RAW_HEADER,
11901+               DKT_CANON_HEADER,
11902+               DKT_RAW_BODY,
11903+               DKT_CANON_BODY
11904+       };
11905+
11906+       for (dkdomain = (char *) 0;;)
11907+       {
11908+               ch = getopt(argc, argv, "s:vt:fb:c:hrTd:D:");
11909+               if (ch == -1)
11910+                       break;
11911+               switch (ch)
11912+               {
11913+               case 'D':
11914+                       dkdomain = optarg;
11915+                       break;
11916+               case 'd': /*- optD */
11917+                       txtrec = dns_text(optarg);
11918+                       cp = txtrec;
11919+                       printf("%ld\n", (long) strlen(cp));
11920+                       while (*cp) {
11921+                               printf("%02x ", *cp++);
11922+                               if ((cp - txtrec) % 16 == 0)
11923+                                       printf("\n");
11924+                       }
11925+                       printf("\n");
11926+                       if (!strcmp(txtrec, "e=perm;"))
11927+                               exit(0);
11928+                       if (!strcmp(txtrec, "e=temp;"))
11929+                               exit(0);
11930+                       free(txtrec);
11931+                       return (0);
11932+                       break;
11933+               case 'T':
11934+                       optT = 1;
11935+                       break;
11936+               case 'v':
11937+                       optv = 1;
11938+                       break;
11939+               case 'f':
11940+                       optf = 1;
11941+                       break;
11942+               case 'r':
11943+                       optr = 1;
11944+                       opth = 1;
11945+                       break;
11946+               case 's':
11947+                       opts = 1;
11948+                       keyfn = optarg;
11949+                       selector = optarg;
11950+                       while (*optarg)
11951+                       {
11952+                               if (*optarg == '/')
11953+                                       selector = optarg + 1;
11954+                               optarg++;
11955+                       }
11956+                       break;
11957+               case 't':
11958+                       optt = atoi(optarg);
11959+                       break;
11960+               case 'h':
11961+                       opth = 1;
11962+                       break;
11963+               case 'b':
11964+                       advicelen = atoi(optarg);
11965+                       if (advicelen > sizeof(advice))
11966+                               advicelen = sizeof(advice);
11967+                       break;
11968+               case 'c':
11969+                       if (!strcmp(optarg, "simple"))
11970+                               optc = DK_CANON_SIMPLE, canon = "simple";
11971+                       else
11972+                       if (!strcmp(optarg, "nofws"))
11973+                               optc = DK_CANON_NOFWS, canon = "nofws";
11974+                       else
11975+                       {
11976+                               fprintf(stderr, "dktest: unrecognized canonicalization.\n");
11977+                               exit(1);
11978+                       }
11979+               }
11980+       }
11981+       if (opts)
11982+       {
11983+               if (!(privkeyf = fopen(keyfn, "r"))) /*- TC10 */
11984+               {
11985+                       fprintf(stderr, "dktest: can't open private key file %s\n", keyfn);
11986+                       exit(1);
11987+               }
11988+               if ((privkeylen = fread(privkey, 1, sizeof(privkey), privkeyf)) == sizeof(privkey))
11989+                                       /*- TC9 */
11990+               {                                       
11991+                       fprintf(stderr, "dktest: private key buffer isn't big enough, use a smaller private key or recompile.\n");
11992+                       exit(1);
11993+               }
11994+               privkey[privkeylen] = '\0';
11995+               fclose(privkeyf);
11996+       }
11997+       if (optt == 1)
11998+               errorout(NULL, 0);              /*- TC2 */
11999+       if (optt == 2)
12000+               errorout(NULL, 32767);  /*- TC3 */
12001+       dklib = dk_init(&st);
12002+       if (st != DK_STAT_OK)
12003+               errorout(NULL, st);
12004+       if (!dklib)
12005+               errorout(NULL, 200);
12006+       if (optv)
12007+       {
12008+               dk = dk_verify(dklib, &st);
12009+               if (st != DK_STAT_OK)
12010+                       errorout(dk, st);
12011+       } else
12012+       if (opts)
12013+       {
12014+               dk = dk_sign(dklib, &st, optc);
12015+               if (st != DK_STAT_OK)
12016+                       errorout(dk, st);
12017+               if (optr)
12018+                       st = dk_setopts(dk, DKOPT_RDUPE);
12019+               if (st != DK_STAT_OK)
12020+                       errorout(dk, st);
12021+       } else
12022+       {
12023+               fprintf(stderr, "dktest: [-f] [-b#] [-c nofws|simple] [-v|-s selector] [-h] [-t#] [-r] [-T][-d dnsrecord]\n"); /* TC1 */
12024+               exit(1);
12025+       }
12026+       if (optT) /*- trace */
12027+       {
12028+               st = dk_setopts(dk, (DKOPT_TRACE_h|DKOPT_TRACE_H|DKOPT_TRACE_b|DKOPT_TRACE_B));
12029+               if (st != DK_STAT_OK)
12030+                       errorout(dk, st);
12031+       }
12032+       if (optt == 3)
12033+               errorout(dk, dk_message(NULL, (const unsigned char *) "", 1));  /*- TC4 */
12034+       if (optt == 4)
12035+               errorout(dk, dk_message(dk, (const unsigned char *) NULL, 1));  /*- TC5 */
12036+       if (optt == 5)
12037+               errorout(dk, dk_message(dk, (const unsigned char *) "", 0));    /*- TC6 */
12038+       if (optt >= 100 && optt <= 140)
12039+               errorout(dk, optt - 100);       /*- TC53 */
12040+       st = DK_STAT_OK;
12041+       /*
12042+        * This should work with DOS or UNIX text files -Tim
12043+        * Reduced calls to dk_message, in lib dkhash called for EVERY char
12044+        * DOS formatted input (CRLF line terminated) will have fewer calls
12045+        * to dk_message() than UNIX (LF line terminated) input.
12046+        */
12047+       while (1)
12048+       {
12049+               char           *inp;
12050+
12051+               inlen = fread(inbuf, 1, sizeof(inbuf), stdin);
12052+               inp = inbuf;
12053+               while (inlen--)
12054+               {
12055+                       if (*inp == '\n')
12056+                               st = dk_message(dk, (const unsigned char *) "\r\n", 2);
12057+                       else
12058+                               st = dk_message(dk, (const unsigned char *) inp, 1);
12059+                       if (st != DK_STAT_OK)
12060+                               break;  //stop looping if there was an error
12061+                       inp++;
12062+               }
12063+               if ((inp-inbuf < sizeof(inbuf)) || (st != DK_STAT_OK))
12064+                       break; /*- if we read in the entire message or encountered an error */
12065+       }
12066+       if (st == DK_STAT_OK)
12067+       {
12068+               if (optt == 10)
12069+                       st = dk_end(dk, &dkf);
12070+               else
12071+                       st = dk_eom(dk, &dkf);
12072+       }
12073+       if (optT)
12074+       {
12075+               printf("DomainKey-Trace: U=http://domainkeys.sourceforge.net; V=TESTING;\n");
12076+               for (i = 0; i < 4; i++)
12077+               {
12078+                       if (dk_get_trace(dk, dk_trace_tag[i], trace_count, sizeof (trace_count)) != DK_STAT_OK)
12079+                       {
12080+                               fprintf(stderr, "dktest: Not enough resources for trace buffer output\n");
12081+                               break;
12082+                       } else
12083+                               printf("  %s\n", trace_count);
12084+               }
12085+               if (optv)
12086+               {
12087+                       printf("DomainKey-Trace-Diff:\n");
12088+                       for (i = 0; i < 4; i++) {
12089+                               dkt_st = dk_compare_trace(dk,dk_trace_tag[i],trace_count,sizeof(trace_count));
12090+                               if (dkt_st == DK_STAT_NOSIG)
12091+                               {
12092+                                       printf("  No DK-Trace: header found\n");
12093+                                       break;
12094+                               } else
12095+                               if (dkt_st != DK_STAT_OK)
12096+                               {
12097+                                       fprintf(stderr,"dktest: Not enough resources for trace buffer output\n");
12098+                                       break;
12099+                               } else
12100+                                       printf("  %s\n",trace_count);
12101+                       }
12102+               }
12103+       }
12104+       if ((optt == 6 || optt == 10) && optv)
12105+       {
12106+               printf("flags: ");
12107+               if (dkf & DK_FLAG_SET)
12108+                       printf("+");
12109+               if (dkf & DK_FLAG_TESTING)
12110+                       printf("t");
12111+               if (dkf & DK_FLAG_SIGNSALL)
12112+                       printf("s"); /*- wont be set if dk_end() is sucessful */
12113+               if (dkf & DK_FLAG_G)
12114+                       printf("g");
12115+               printf("\n");
12116+       } else
12117+       if (optt == 6 && opts)
12118+               errorout(dk, dk_getsig(dk, NULL, NULL, advicelen));     /*- TC14 */
12119+       else
12120+       if (optt == 7)
12121+       {
12122+               from = dk_from(dk);
12123+               if (!from)
12124+                       from = "";
12125+               printf("%s\n", from);   /*- TC14-1, TC14-2 */
12126+       } else
12127+       if (optt == 11)
12128+       {
12129+               from = dk_address(dk);
12130+               printf("%s\n", from);   /*- TC14-3, TC14-4 */
12131+       } else
12132+       if (optt == 9)
12133+       {
12134+               char           *s;
12135+
12136+               s = malloc(dk_headers(dk, NULL));
12137+               dk_headers(dk, s);
12138+               printf("%s\n", s);
12139+               free(s);
12140+       } else
12141+       if (optt == 8 && opts)
12142+       {
12143+               dk_getsig(dk, privkey, (unsigned char *) advice, advicelen);
12144+               if (st != DK_STAT_OK)
12145+                       errorout(dk, st);
12146+               printf("%d %d\n", (int) dk_siglen(privkey), (int) strlen(advice)); /*- TC39 */
12147+       } else
12148+       if (opts)
12149+       {
12150+               if (st != DK_STAT_OK)
12151+                       errorout(dk, st);
12152+               st = dk_getsig(dk, privkey, (unsigned char *) advice, advicelen);
12153+               if (st != DK_STAT_OK)
12154+                       errorout(dk, st);
12155+#if 0
12156+               printf("Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n");
12157+#endif
12158+               from = dk_from(dk);
12159+               printf("DomainKey-Signature: a=rsa-sha1; q=dns; c=%s;\n"
12160+                       "  s=%s; d=%s;\n" "  b=%s;\n", canon, selector, dkdomain ? dkdomain : from, advice);
12161+               if (opth == 1)
12162+               {
12163+                       if (dk_headers(dk, NULL) < sizeof(inbuf))
12164+                       {
12165+                               dk_headers(dk, inbuf);
12166+                               printf("  h=%s;\n", inbuf);
12167+                       }
12168+               }
12169+       } else
12170+       if (optv)
12171+       {
12172+               char           *status = 0;
12173+
12174+               switch (st)
12175+               {
12176+               case DK_STAT_OK:
12177+                       status = "good";
12178+                       break;
12179+               case DK_STAT_BADSIG:
12180+                       status = "bad";
12181+                       break;
12182+               case DK_STAT_NOSIG:
12183+                       status = "no signature";
12184+                       break;
12185+               case DK_STAT_NOKEY:
12186+               case DK_STAT_CANTVRFY:
12187+                       status = "no key";
12188+                       break;
12189+               case DK_STAT_BADKEY:
12190+                       status = "bad key";
12191+                       break;
12192+               case DK_STAT_INTERNAL:
12193+               case DK_STAT_ARGS:
12194+               case DK_STAT_SYNTAX:
12195+                       status = "bad format";
12196+                       break;
12197+               case DK_STAT_NORESOURCE:
12198+                       status = "no resources";
12199+                       break;
12200+               case DK_STAT_REVOKED:
12201+                       status = "revoked";
12202+                       break;
12203+               case DK_STAT_GRANULARITY:
12204+                       status = "bad sender (g=)";
12205+                       break;
12206+               }
12207+#if 0
12208+               printf("Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n");
12209+#endif
12210+               printf("DomainKey-Status: %s\n", status);
12211+               rewind(stdin);
12212+       }
12213+       if (st != DK_STAT_OK)
12214+               errorout(dk, st);
12215+       dk_free(dk, 1);//cleanup properly (not really necessary for single run process)
12216+       dk_shutdown(dklib);
12217+       return(0);
12218+}
12219+#else
12220+#warning "not compiled with -DDOMAIN_KEYS"
12221+int
12222+main(int argc, char *argv[])
12223+{
12224+       fprintf(stderr, "not compiled with -DDOMAIN_KEYS\n");
12225+       return(1);
12226+}
12227+#endif
12228+
12229+void
12230+getversion_dktest_c()
12231+{
12232+       static char    *x = "$Id: dktest.c,v 1.16 2013-08-17 16:01:08+05:30 Cprogrammer Exp mbhangui $";
12233+
12234+       x++;
12235+}
12236diff -ruN ../netqmail-1.06-original/dktrace.c netqmail-1.06/dktrace.c
12237--- ../netqmail-1.06-original/dktrace.c 1970-01-01 01:00:00.000000000 +0100
12238+++ netqmail-1.06/dktrace.c     2016-11-22 21:03:57.112528099 +0100
12239@@ -0,0 +1,277 @@
12240+/*
12241+ * $Log: dktrace.c,v $
12242+ * Revision 1.1  2009-03-14 09:00:52+05:30  Cprogrammer
12243+ * Initial revision
12244+ *
12245+ */
12246+/*
12247+ * $Id: dktrace.c,v 1.1 2009-03-14 09:00:52+05:30 Cprogrammer Stab mbhangui $
12248+ */
12249+
12250+#ifdef DOMAIN_KEYS
12251+#include <stdio.h>
12252+#include <stdlib.h>
12253+#include <string.h>
12254+#include "str.h"
12255+#include "case.h"
12256+#include "dktrace.h"
12257+
12258+#define strncasecmp(x,y,z) case_diffb((x), (z), (y))
12259+
12260+static int     *
12261+getPointer(DK_TRACE * dkp, DK_TRACE_TYPE type)
12262+{
12263+       switch (type) {
12264+       case DKT_RAW_HEADER:
12265+               return dkp->ccounts_h;
12266+       case DKT_CANON_HEADER:
12267+               return dkp->ccounts_H;
12268+       case DKT_RAW_BODY:
12269+               return dkp->ccounts_b;
12270+       case DKT_CANON_BODY:
12271+               return dkp->ccounts_B;
12272+       default:
12273+               return 0;
12274+       }
12275+}
12276+
12277+//Modified version of dkparselist()
12278+static int
12279+dkt_parselist(char *list, char *letters, char *values[])
12280+{
12281+       char            key;
12282+       int             i;
12283+       char           *value;
12284+
12285+       /*- start with all args unset */
12286+       for (i = 0; letters[i]; i++) {
12287+               values[i] = NULL;
12288+       }
12289+       key = 0;
12290+       while (*list) {
12291+               if ((*list == ' ') || (*list == '\t') || (*list == '\r') || (*list == '\n')) {
12292+                       list++;
12293+               } else if (*list == '=') {
12294+                       char           *ws;
12295+
12296+                       ++list;
12297+                       value = list;
12298+                       ws = list;
12299+                       while (1) {
12300+                       /*
12301+                        * copy up to null or semicolon, deleting whitespace as we go
12302+                        */
12303+                               *ws = *list;
12304+                               if ((*list == ' ') || (*list == '\t') || (*list == '\r') || (*list == '\n')) {
12305+                               /*
12306+                                * ignore
12307+                                */
12308+                               } else if (!*list) {
12309+                                       break;
12310+                               } else if (*list == ';') {
12311+                                       *ws = '\0';
12312+                                       list++;
12313+                                       break;
12314+                               } else {
12315+                                       ws++;
12316+                               }
12317+                               list++;
12318+                       }
12319+                       if (!key) {
12320+                               return 0;               //No key
12321+                       }
12322+               /*
12323+                * if we find a matching letter, remember the value
12324+                */
12325+                       for (i = 0; letters[i]; i++) {
12326+                               if (key == letters[i]) {
12327+                                       if (values[i]) {
12328+                                               return 0;       /* no duplicate keys. TC23 */
12329+                                       }
12330+                                       values[i] = value;
12331+                               }
12332+                       }
12333+                       key = 0;
12334+               } else {
12335+                       if (key) {
12336+                               return 0;               /* they already gave us a key. TC24 */
12337+                       }
12338+                       key = *list++;
12339+               }
12340+       }
12341+       return 1;
12342+}
12343+
12344+extern void
12345+dkt_add(DK_TRACE * dkp, DK_TRACE_TYPE type, const unsigned char *data, int dataLength)
12346+{
12347+       int            *ip;
12348+       ip = getPointer(dkp, type);
12349+       if (!ip)
12350+               return;
12351+
12352+       while (dataLength-- > 0)
12353+               ip[*data++]++;
12354+}
12355+
12356+//useful for building table directly
12357+extern void
12358+dkt_quickadd(DK_TRACE * dkp, DK_TRACE_TYPE type, int index, int count)
12359+{
12360+       int            *ip;
12361+       ip = getPointer(dkp, type);
12362+       if (!ip)
12363+               return;
12364+       if ((index < 256) && (index >= 0))
12365+               ip[index] = ip[index] + count;
12366+}
12367+
12368+//reverse of dkt_quickadd, reads data from table and returns the int count
12369+extern int
12370+dkt_getcount(DK_TRACE * dkp, DK_TRACE_TYPE type, int index, int count)
12371+{
12372+       int            *ip;
12373+       ip = getPointer(dkp, type);
12374+       if (!ip)
12375+               return 0;
12376+       if ((index < 256) && (index >= 0))
12377+               return ip[index];
12378+       return 0;
12379+}
12380+
12381+
12382+/*
12383+ * Fills in DK_TRACE *diff_table with the differences between
12384+ * *dka (before) and *dkb (after), (after - before = diff)
12385+ */
12386+extern int
12387+dkt_diff(DK_TRACE * dka, DK_TRACE * dkb, DK_TRACE_TYPE type, DK_TRACE * diff_table)
12388+{
12389+       int            *inputa, *inputb, *output;
12390+       int             i;
12391+       inputa = getPointer(dka, type);
12392+       if (!inputa)
12393+               return 0;
12394+       inputb = getPointer(dkb, type);
12395+       if (!inputb)
12396+               return 0;
12397+       output = getPointer(diff_table, type);
12398+       if (!output)
12399+               return 0;
12400+
12401+       for (i = 0; i < 256; i++) {
12402+               output[i] = (inputb[i] - inputa[i]);
12403+       }
12404+       return 1;
12405+}
12406+
12407+/*
12408+ * Generate the tag=value; data for a particular trace type
12409+ * returns length of generated C string including ending '\0'
12410+ */
12411+
12412+extern int
12413+dkt_generate(DK_TRACE * dkp, DK_TRACE_TYPE type, char *buffer, int maxBufferSize)
12414+{
12415+       int            *ip;
12416+       char           *cp;
12417+       int             ix;
12418+       int             len;
12419+       int             highest;
12420+
12421+       if (maxBufferSize < 20)
12422+               return 0;                               /* Getting too close, you lose */
12423+       cp = buffer;
12424+       ip = getPointer(dkp, type);
12425+       if (!ip)
12426+               return 0;
12427+       *buffer++ = (char) type;
12428+       --maxBufferSize;
12429+       *buffer++ = '=';
12430+       --maxBufferSize;
12431+
12432+/*
12433+ * Only produce as many entries as needed, rather than the full 256
12434+ */
12435+
12436+       for (ix = 0, highest = 0; ix < 256; ++ix) {
12437+               if (ip[ix] != 0)
12438+                       highest = ix;
12439+       }
12440+
12441+       for (ix = 0; ix <= highest; ++ix) {
12442+               if (ip[ix] != 0) {
12443+                       len = snprintf(buffer, maxBufferSize, "%d", ip[ix]);
12444+                       buffer += len;
12445+                       maxBufferSize -= len;
12446+               }
12447+               if (maxBufferSize < 10)
12448+                       return 0;                       /* Getting too close, you lose */
12449+               *buffer++ = ':';
12450+               --maxBufferSize;
12451+       }
12452+       /*
12453+        * Finish up the tag with a semi-colon and turn it into a C string
12454+        */
12455+       --buffer;
12456+       *buffer++ = ';'; //replace last ':'
12457+       *buffer++ = '\0';
12458+       --maxBufferSize;
12459+       return buffer - cp;
12460+}
12461+
12462+//converts a header to to a DK_TRACE table
12463+extern int
12464+dkt_hdrtotrace(char *ptr, DK_TRACE * store)
12465+{
12466+       char           *values[4];      // hHbB
12467+       int             idx;
12468+       int             delim_count;
12469+       char           *sptr, *eptr;
12470+       DK_TRACE_TYPE   dk_trace_tag[4] = {
12471+               DKT_RAW_HEADER,
12472+               DKT_CANON_HEADER,
12473+               DKT_RAW_BODY,
12474+               DKT_CANON_BODY
12475+       };
12476+       int            *ip;
12477+
12478+       if ((strncasecmp(ptr, "DomainKey-Trace:", 16)) || !store || (!dkt_parselist(ptr + 16, "hHbB", values))) {
12479+               return 0;
12480+       }
12481+       for (idx = 0; idx < 4; idx++) {
12482+               if (!values[idx])
12483+                       continue;
12484+               ip = getPointer(store, dk_trace_tag[idx]);
12485+               if (!ip)
12486+                       return 0;
12487+               sptr = values[idx];
12488+               for (delim_count = 0; ((delim_count < 256) && (*sptr != '\0')); sptr++) {
12489+                       if (*sptr == ':') {
12490+                               delim_count++;
12491+                               continue;
12492+                       }
12493+               //find the end of the int
12494+                       for (eptr = sptr + 1; ((*eptr != ':') && (*eptr != '\0')); eptr++);
12495+                       if (*eptr == '\0') //if end of values for key finish up
12496+                       {
12497+                               ip[delim_count] = atoi(sptr);
12498+                               break;
12499+                       }
12500+                       *eptr = '\0';
12501+                       ip[delim_count] = atoi(sptr);
12502+                       delim_count++;
12503+                       sptr = eptr;
12504+               }
12505+       }
12506+       return 1;
12507+}
12508+#endif
12509+
12510+void
12511+getversion_dktrace_c()
12512+{
12513+       static char    *x = "$Id: dktrace.c,v 1.1 2009-03-14 09:00:52+05:30 Cprogrammer Stab mbhangui $";
12514+
12515+       x++;
12516+}
12517diff -ruN ../netqmail-1.06-original/dktrace.h netqmail-1.06/dktrace.h
12518--- ../netqmail-1.06-original/dktrace.h 1970-01-01 01:00:00.000000000 +0100
12519+++ netqmail-1.06/dktrace.h     2016-11-22 21:03:57.112528099 +0100
12520@@ -0,0 +1,26 @@
12521+/* $Id: dktrace.h,v 1.3 2005/06/27 18:47:57 ted46045 Exp $ */
12522+
12523+#ifndef _DK_TRACE_H
12524+#define _DK_TRACE_H
12525+
12526+typedef struct {
12527+  int ccounts_h[256];
12528+  int ccounts_H[256];
12529+  int ccounts_b[256];
12530+  int ccounts_B[256];
12531+} DK_TRACE;
12532+
12533+typedef enum { DKT_RAW_HEADER='h', DKT_CANON_HEADER='H',
12534+         DKT_RAW_BODY='b', DKT_CANON_BODY='B' } DK_TRACE_TYPE;
12535+
12536+#define dkt_init(s) memset(s,0,sizeof(DK_TRACE))
12537+
12538+//extern void   dkt_init(DK_TRACE *dkp);
12539+extern void   dkt_add(DK_TRACE *dkp, DK_TRACE_TYPE type, const unsigned char *data, int dataLength);
12540+extern int    dkt_diff(DK_TRACE *dka, DK_TRACE *dkb, DK_TRACE_TYPE type, DK_TRACE *table);
12541+extern void   dkt_quickadd(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count);
12542+extern int    dkt_getcount(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count);
12543+extern int    dkt_generate(DK_TRACE *dkp, DK_TRACE_TYPE type, char *buffer, int maxBufferSize);
12544+extern int    dkt_hdrtotrace(char *ptr, DK_TRACE *store);
12545+
12546+#endif
12547diff -ruN ../netqmail-1.06-original/dns.c netqmail-1.06/dns.c
12548--- ../netqmail-1.06-original/dns.c     2007-11-30 21:22:54.000000000 +0100
12549+++ netqmail-1.06/dns.c 2018-04-02 11:45:03.323768372 +0200
12550@@ -1,4 +1,3 @@
12551-#include <stdio.h>
12552 #include <netdb.h>
12553 #include <sys/types.h>
12554 #include <netinet/in.h>
12555@@ -9,6 +8,7 @@
12556 extern int res_search();
12557 #include "ip.h"
12558 #include "ipalloc.h"
12559+#include "strsalloc.h"
12560 #include "fmt.h"
12561 #include "alloc.h"
12562 #include "str.h"
12563@@ -19,14 +19,17 @@
12564 static unsigned short getshort(c) unsigned char *c;
12565 { unsigned short u; u = c[0]; return (u << 8) + c[1]; }
12566 
12567-static union { HEADER hdr; unsigned char buf[PACKETSZ]; } response;
12568+static struct { unsigned char *buf; } response;
12569+static int responsebuflen = 0;
12570 static int responselen;
12571 static unsigned char *responseend;
12572 static unsigned char *responsepos;
12573+static u_long saveresoptions;
12574 
12575 static int numanswers;
12576 static char name[MAXDNAME];
12577 static struct ip_address ip;
12578+static stralloc txt = {0};
12579 unsigned short pref;
12580 
12581 static stralloc glue = {0};
12582@@ -43,18 +46,33 @@
12583  errno = 0;
12584  if (!stralloc_copy(&glue,domain)) return DNS_MEM;
12585  if (!stralloc_0(&glue)) return DNS_MEM;
12586- responselen = lookup(glue.s,C_IN,type,response.buf,sizeof(response));
12587+ if (!responsebuflen)
12588+  if (response.buf = (unsigned char *)alloc(PACKETSZ+1))
12589+   responsebuflen = PACKETSZ+1;
12590+   else return DNS_MEM;
12591+
12592+ responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
12593+ if ((responselen >= responsebuflen) ||
12594+     (responselen > 0 && (((HEADER *)response.buf)->tc)))
12595+  {
12596+   if (responsebuflen < 65536)
12597+    if (alloc_re(&response.buf, responsebuflen, 65536))
12598+     responsebuflen = 65536;
12599+    else return DNS_MEM;
12600+    saveresoptions = _res.options;
12601+    _res.options |= RES_USEVC;
12602+    responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen);
12603+    _res.options = saveresoptions;
12604+  }
12605  if (responselen <= 0)
12606   {
12607    if (errno == ECONNREFUSED) return DNS_SOFT;
12608    if (h_errno == TRY_AGAIN) return DNS_SOFT;
12609    return DNS_HARD;
12610   }
12611- if (responselen >= sizeof(response))
12612-   responselen = sizeof(response);
12613  responseend = response.buf + responselen;
12614  responsepos = response.buf + sizeof(HEADER);
12615- n = ntohs(response.hdr.qdcount);
12616+ n = ntohs(((HEADER *)response.buf)->qdcount);
12617  while (n-- > 0)
12618   {
12619    i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
12620@@ -64,7 +82,7 @@
12621    if (i < QFIXEDSZ) return DNS_SOFT;
12622    responsepos += QFIXEDSZ;
12623   }
12624- numanswers = ntohs(response.hdr.ancount);
12625+ numanswers = ntohs(((HEADER *)response.buf)->ancount);
12626  return 0;
12627 }
12628 
12629@@ -177,6 +195,49 @@
12630  return 0;
12631 }
12632 
12633+static int findtxt(wanttype)
12634+int wanttype;
12635+{
12636+ unsigned short rrtype;
12637+ unsigned short rrdlen;
12638+ int i;
12639+
12640+ if (numanswers <= 0) return 2;
12641+ --numanswers;
12642+ if (responsepos == responseend) return DNS_SOFT;
12643+
12644+ i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME);
12645+ if (i < 0) return DNS_SOFT;
12646+ responsepos += i;
12647+
12648+ i = responseend - responsepos;
12649+ if (i < 4 + 3 * 2) return DNS_SOFT;
12650+   
12651+ rrtype = getshort(responsepos);
12652+ rrdlen = getshort(responsepos + 8);
12653+ responsepos += 10;
12654+
12655+ if (rrtype == wanttype)
12656+  {
12657+   unsigned short txtpos;
12658+   unsigned char txtlen;
12659+
12660+   txt.len = 0;
12661+   for (txtpos = 0;txtpos < rrdlen;txtpos += txtlen)
12662+    {
12663+     txtlen = responsepos[txtpos++];
12664+     if (txtlen > rrdlen-txtpos) txtlen = rrdlen-txtpos;
12665+     if (!stralloc_catb(&txt,&responsepos[txtpos],txtlen)) return DNS_MEM;
12666+    }
12667+
12668+   responsepos += rrdlen;
12669+   return 1;
12670+ }
12671+
12672+ responsepos += rrdlen;
12673+ return 0;
12674+}
12675+
12676 void dns_init(flagsearch)
12677 int flagsearch;
12678 {
12679@@ -194,7 +255,7 @@
12680    if (!sa->len) return loop;
12681    if (sa->s[sa->len - 1] == ']') return loop;
12682    if (sa->s[sa->len - 1] == '.') { --sa->len; continue; }
12683-   switch(resolve(sa,T_ANY))
12684+   switch(resolve(sa,T_CNAME))
12685     {
12686      case DNS_MEM: return DNS_MEM;
12687      case DNS_SOFT: return DNS_SOFT;
12688@@ -235,15 +296,18 @@
12689  return len;
12690 }
12691 
12692-int dns_ptr(sa,ip)
12693-stralloc *sa;
12694+static int dns_ptrplus(ssa,ip)
12695+strsalloc *ssa;
12696 struct ip_address *ip;
12697 {
12698+ stralloc sa = {0};
12699  int r;
12700 
12701- if (!stralloc_ready(sa,iaafmt((char *) 0,ip))) return DNS_MEM;
12702- sa->len = iaafmt(sa->s,ip);
12703- switch(resolve(sa,T_PTR))
12704+ if (!stralloc_ready(&sa,iaafmt((char *) 0,ip))) return DNS_MEM;
12705+ sa.len = iaafmt(sa.s,ip);
12706+ r = resolve(&sa,T_PTR);
12707+ alloc_free(sa.s);
12708+ switch(r)
12709   {
12710    case DNS_MEM: return DNS_MEM;
12711    case DNS_SOFT: return DNS_SOFT;
12712@@ -254,25 +318,46 @@
12713    if (r == DNS_SOFT) return DNS_SOFT;
12714    if (r == 1)
12715     {
12716-     if (!stralloc_copys(sa,name)) return DNS_MEM;
12717-     return 0;
12718+     stralloc sa2 = {0};
12719+     if (!stralloc_copys(&sa2,name)) return DNS_MEM;
12720+     if (!strsalloc_append(ssa,&sa2)) return DNS_MEM;
12721     }
12722   }
12723+ if (ssa->len) return 0;
12724  return DNS_HARD;
12725 }
12726 
12727+int dns_ptr(ssa,ip)
12728+strsalloc *ssa;
12729+struct ip_address *ip;
12730+{
12731+ int r;
12732+ int j;
12733+
12734+ if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
12735+ ssa->len = 0;
12736+ r = dns_ptrplus(ssa,ip);
12737+ if (r < 0)
12738+  {
12739+   for (j = 0;j < ssa->len;++j)
12740+    alloc_free(ssa->sa[j].s);
12741+   ssa->len = 0;
12742+  }
12743+ return r;
12744+}
12745+
12746+
12747 static int dns_ipplus(ia,sa,pref)
12748 ipalloc *ia;
12749 stralloc *sa;
12750 int pref;
12751 {
12752  int r;
12753- struct ip_mx ix;
12754+ struct ip_mx ix = {0};
12755 
12756  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
12757  if (!stralloc_0(&glue)) return DNS_MEM;
12758  if (glue.s[0]) {
12759-   ix.pref = 0;
12760    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
12761     {
12762      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
12763@@ -291,9 +376,16 @@
12764    ix.ip = ip;
12765    ix.pref = pref;
12766    if (r == DNS_SOFT) return DNS_SOFT;
12767-   if (r == 1)
12768+   if (r == 1) {
12769+#ifdef IX_FQDN
12770+     ix.fqdn = glue.s;
12771+#endif
12772      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
12773   }
12774+  }
12775+#ifdef IX_FQDN
12776+ glue.s = 0;
12777+#endif
12778  return 0;
12779 }
12780 
12781@@ -313,7 +405,7 @@
12782 {
12783  int r;
12784  struct mx { stralloc sa; unsigned short p; } *mx;
12785- struct ip_mx ix;
12786+ struct ip_mx ix = {0};
12787  int nummx;
12788  int i;
12789  int j;
12790@@ -325,7 +417,6 @@
12791  if (!stralloc_copy(&glue,sa)) return DNS_MEM;
12792  if (!stralloc_0(&glue)) return DNS_MEM;
12793  if (glue.s[0]) {
12794-   ix.pref = 0;
12795    if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)])
12796     {
12797      if (!ipalloc_append(ia,&ix)) return DNS_MEM;
12798@@ -396,3 +487,49 @@
12799  alloc_free(mx);
12800  return flagsoft;
12801 }
12802+
12803+
12804+static int dns_txtplus(ssa,sa)
12805+strsalloc *ssa;
12806+stralloc *sa;
12807+{
12808+ int r;
12809+
12810+ switch(resolve(sa,T_TXT))
12811+  {
12812+   case DNS_MEM: return DNS_MEM;
12813+   case DNS_SOFT: return DNS_SOFT;
12814+   case DNS_HARD: return DNS_HARD;
12815+  }
12816+ while ((r = findtxt(T_TXT)) != 2)
12817+  {
12818+   if (r == DNS_SOFT) return DNS_SOFT;
12819+   if (r == 1)
12820+    {
12821+     stralloc sa = {0};
12822+     if (!stralloc_copy(&sa,&txt)) return DNS_MEM;
12823+     if (!strsalloc_append(ssa,&sa)) return DNS_MEM;
12824+    }
12825+  }
12826+ if (ssa->len) return 0;
12827+ return DNS_HARD;
12828+}
12829+
12830+int dns_txt(ssa,sa)
12831+strsalloc *ssa;
12832+stralloc *sa;
12833+{
12834+ int r;
12835+ int j;
12836+
12837+ if (!strsalloc_readyplus(ssa,0)) return DNS_MEM;
12838+ ssa->len = 0;
12839+ r = dns_txtplus(ssa,sa);
12840+ if (r < 0)
12841+  {
12842+   for (j = 0;j < ssa->len;++j)
12843+    alloc_free(ssa->sa[j].s);
12844+   ssa->len = 0;
12845+  }
12846+ return r;
12847+}
12848diff -ruN ../netqmail-1.06-original/dns.h netqmail-1.06/dns.h
12849--- ../netqmail-1.06-original/dns.h     1998-06-15 12:53:16.000000000 +0200
12850+++ netqmail-1.06/dns.h 2016-11-22 21:03:57.113528065 +0100
12851@@ -10,5 +10,6 @@
12852 int dns_mxip();
12853 int dns_ip();
12854 int dns_ptr();
12855+int dns_txt();
12856 
12857 #endif
12858diff -ruN ../netqmail-1.06-original/dnsfq.c netqmail-1.06/dnsfq.c
12859--- ../netqmail-1.06-original/dnsfq.c   1998-06-15 12:53:16.000000000 +0200
12860+++ netqmail-1.06/dnsfq.c       2016-11-22 21:03:57.113528065 +0100
12861@@ -5,15 +5,19 @@
12862 #include "dnsdoe.h"
12863 #include "ip.h"
12864 #include "ipalloc.h"
12865+#include "strsalloc.h"
12866 #include "exit.h"
12867 
12868 stralloc sa = {0};
12869+strsalloc ssa = {0};
12870 ipalloc ia = {0};
12871 
12872 void main(argc,argv)
12873 int argc;
12874 char **argv;
12875 {
12876+ int j;
12877+
12878  if (!argv[1]) _exit(100);
12879 
12880  if (!stralloc_copys(&sa,argv[1]))
12881@@ -25,8 +29,11 @@
12882   {
12883    substdio_putsflush(subfderr,"no IP addresses\n"); _exit(100);
12884   }
12885- dnsdoe(dns_ptr(&sa,&ia.ix[0].ip));
12886- substdio_putflush(subfdout,sa.s,sa.len);
12887- substdio_putsflush(subfdout,"\n");
12888+ dnsdoe(dns_ptr(&ssa,&ia.ix[0].ip));
12889+ for(j = 0;j < ssa.len;++j)
12890+  {
12891+   substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
12892+   substdio_putsflush(subfdout,"\n");
12893+  }
12894  _exit(0);
12895 }
12896diff -ruN ../netqmail-1.06-original/dnsptr.c netqmail-1.06/dnsptr.c
12897--- ../netqmail-1.06-original/dnsptr.c  1998-06-15 12:53:16.000000000 +0200
12898+++ netqmail-1.06/dnsptr.c      2016-11-22 21:03:57.113528065 +0100
12899@@ -6,22 +6,28 @@
12900 #include "dns.h"
12901 #include "dnsdoe.h"
12902 #include "ip.h"
12903+#include "strsalloc.h"
12904 #include "exit.h"
12905 
12906-stralloc sa = {0};
12907+strsalloc ssa = {0};
12908 struct ip_address ip;
12909 
12910 void main(argc,argv)
12911 int argc;
12912 char **argv;
12913 {
12914+ int j;
12915+
12916  if (!argv[1]) _exit(100);
12917 
12918  ip_scan(argv[1],&ip);
12919 
12920  dns_init(0);
12921- dnsdoe(dns_ptr(&sa,&ip));
12922- substdio_putflush(subfdout,sa.s,sa.len);
12923- substdio_putsflush(subfdout,"\n");
12924+ dnsdoe(dns_ptr(&ssa,&ip));
12925+ for(j = 0;j < ssa.len;++j)
12926+  {
12927+   substdio_putflush(subfdout,ssa.sa[j].s,ssa.sa[j].len);
12928+   substdio_putsflush(subfdout,"\n");
12929+  }
12930  _exit(0);
12931 }
12932diff -ruN ../netqmail-1.06-original/dnstxt.c netqmail-1.06/dnstxt.c
12933--- ../netqmail-1.06-original/dnstxt.c  1970-01-01 01:00:00.000000000 +0100
12934+++ netqmail-1.06/dnstxt.c      2016-11-22 21:03:57.113528065 +0100
12935@@ -0,0 +1,32 @@
12936+#include "substdio.h"
12937+#include "subfd.h"
12938+#include "stralloc.h"
12939+#include "str.h"
12940+#include "scan.h"
12941+#include "dns.h"
12942+#include "dnsdoe.h"
12943+#include "strsalloc.h"
12944+#include "exit.h"
12945+
12946+strsalloc ssa = {0};
12947+stralloc sa = {0};
12948+
12949+void main(argc,argv)
12950+int argc;
12951+char **argv;
12952+{
12953+ int j;
12954+
12955+ if (!argv[1]) _exit(100);
12956+
12957+ if (!stralloc_copys(&sa, argv[1]))
12958+  { substdio_putsflush(subfderr,"out of memory\n"); _exit(111); }
12959+ dns_init(0);
12960+ dnsdoe(dns_txt(&ssa,&sa));
12961+ for (j = 0;j < ssa.len;++j)
12962+  {
12963+   substdio_put(subfdout,ssa.sa[j].s,ssa.sa[j].len);
12964+   substdio_putsflush(subfdout,"\n");
12965+  }
12966+ _exit(0);
12967+}
12968diff -ruN ../netqmail-1.06-original/domainkeys.h netqmail-1.06/domainkeys.h
12969--- ../netqmail-1.06-original/domainkeys.h      1970-01-01 01:00:00.000000000 +0100
12970+++ netqmail-1.06/domainkeys.h  2018-03-31 15:08:54.373109416 +0200
12971@@ -0,0 +1,378 @@
12972+/* This file is automatically created from the corresponding .c file */
12973+/* Do not change this file; change the .c file instead. */
12974+/* This is libdomainkeys.  It's Copyright (c) 2004 Yahoo, Inc.
12975+ * This code incorporates intellectual property owned by
12976+ * Yahoo! and licensed pursuant to the Yahoo! DomainKeys Public License
12977+ * Agreement: http://domainkeys.sourceforge.net/license/softwarelicense1-0.html
12978+ */
12979+#include <openssl/evp.h>
12980+#include <openssl/pem.h>
12981+#include <openssl/err.h>
12982+
12983+#ifdef SWIG
12984+%module domainkeys
12985+%{
12986+#include "domainkeys.h"
12987+%}
12988+#endif
12989+
12990+#include "dktrace.h"
12991+
12992+/* Performance/Debug options.
12993+ * Uncomment below or use -D switch in gcc
12994+ * DK_DEBUG Dumps whatever dkhash() hashes in to stderr and turns on
12995+ *  some debug warnings that should never happen
12996+ * DK_HASH_BUFF Enables code that uses a buffer when processing the
12997+ *  canocalized message, reducing calls to the crypto library (from dkhash()),
12998+ *  but can use up slightly more memory
12999+*/
13000+//#define DK_DEBUG 1
13001+#define DK_HASH_BUFF 1
13002+
13003+
13004+#define DKMARK ('D' | 'K'<<8 | 'E'<<16 | 'Y'<<24)
13005+#define DK_SIGNING_SIGN 0
13006+#define DK_SIGNING_VERIFY 1
13007+#define DK_SIGNING_NOSIGN 2
13008+#define DK_SIGNING_NOVERIFY 3
13009+#define DK_MALLOC(s)  OPENSSL_malloc(s)
13010+#define DK_MFREE(s)   OPENSSL_free(s); s = NULL;
13011+#define DKERR(x) ((dk->errline=__LINE__),(dk->errfile=__FILE__),(x))
13012+#define DK_BLOCK 1024 //default size of malloc'd block
13013+
13014+/*
13015+ * Option Flags for dk_setopts
13016+ * OR together or run dk_setopts several times
13017+ * All option flags are OFF by default
13018+*/
13019+#define DKOPT_TRACE_h 0x01 //enables tracking character count in pre-canon header
13020+#define DKOPT_TRACE_H 0x02 //enables tracking character count in post-canon header
13021+#define DKOPT_TRACE_b 0x04 //enables tracking character count in pre-canon body
13022+#define DKOPT_TRACE_B 0x08 //enables tracking character count in post-canon header
13023+#define DKOPT_RDUPE 0x10 //enables skipping duplicate headers when generateing a signature
13024+
13025+typedef enum
13026+{
13027+  DK_STAT_OK, /* Function completed successfully */
13028+  DK_STAT_BADSIG, /* Signature was available but failed to verify against domain specified key */
13029+  DK_STAT_NOSIG, /* No signature available in message */
13030+  DK_STAT_NOKEY, /* No public key available (permanent failure) */
13031+  DK_STAT_BADKEY, /* Unusable key, public if verifying, private if signing */
13032+  DK_STAT_CANTVRFY, /* Cannot get domain key to verify signature (temporary failure) */
13033+  DK_STAT_SYNTAX, /* Message is not valid syntax. Signature could not be created/checked */
13034+  DK_STAT_NORESOURCE, /* Could not get critical resource (temporary failure) */
13035+  DK_STAT_ARGS, /* Arguments are not usable. */
13036+  DK_STAT_REVOKED,    /* Key has been revoked. */
13037+  DK_STAT_INTERNAL, /* cannot call this routine in this context.  Internal error. */
13038+  DK_STAT_GRANULARITY, /* Granularity mismatch: sender doesn't match g= option. */
13039+} DK_STAT;
13040+
13041+typedef enum
13042+{
13043+  DK_FLAG_TESTING = 1,    /* set when in testing mode. */
13044+  DK_FLAG_SIGNSALL = 2,   /* domain signs all outgoing email. */
13045+  DK_FLAG_SET = 4,    /* flags set from a successful DNS query */
13046+  DK_FLAG_G = 8,    /* g tag was present in the selector. */
13047+} DK_FLAGS;
13048+typedef enum
13049+{
13050+  DK_TXT_KEY = 0,
13051+  DK_TXT_POLICY
13052+} DK_TXT;
13053+
13054+typedef enum
13055+{
13056+  DK_CANON_SIMPLE = 0,
13057+  DK_CANON_NOFWS = 1,
13058+} DK_CANON;
13059+/* STARTSTRUCT */
13060+typedef struct
13061+{
13062+
13063+} DK_LIB;
13064+/* STOPSTRUCT */
13065+
13066+//UnixWare Fix -Tim
13067+/* STARTSTRUCT */
13068+typedef struct
13069+{
13070+} DK;
13071+/* STOPSTRUCT */
13072+
13073+
13074+/* returns the source file from which an error was returned. */
13075+char * dk_errfile(DK *dk)
13076+;
13077+
13078+
13079+/* returns the source line number from which an error was returned. */
13080+int dk_errline(DK *dk)
13081+;
13082+
13083+
13084+/* Per-process, one-time initialization
13085+ * Returns library structure for subsequent dk_sign or dk_verify calls.
13086+ * Consult statp before using.
13087+ *
13088+ * When terminating the PROCESS its a good idea to call dk_shutdown()
13089+ * When terminating a THREAD it's a good idea to call ERR_remove_state(0); defined in <openssl/err.h>
13090+ * NOTE: DK_LIB pointers are safe to use over multiple threads
13091+ *       DK pointers are NOT safe to use over multiple threads
13092+ */
13093+DK_LIB *dk_init(DK_STAT *statp)
13094+;
13095+
13096+
13097+/* Per-process, one-time cleanup
13098+ * Should be called just before the application ends.
13099+ * the dklib pointer is not valid anymore after this call
13100+ * This function should be called even if dk_init failed.
13101+ * It's safe to call dk_shutdown with a NULL pointer
13102+ */
13103+void dk_shutdown(DK_LIB * dklib)
13104+;
13105+
13106+
13107+/* Set dk options, use instead of dk_remdupe and dk_enable_trace
13108+ * Can be called multiple times.
13109+ * use after dk_sign()/dk_verify()
13110+ *
13111+ * the bits field can be an OR of any of the following
13112+ *DKOPT_TRACE_h Trace pre-canon header
13113+ *DKOPT_TRACE_H Trace post-canon header
13114+ *DKOPT_TRACE_b Trace pre-canon body
13115+ *DKOPT_TRACE_B Trace post-canon body
13116+ *DKOPT_RDUPE   Exclude duplicate headers from hash (Signing only)
13117+ */
13118+DK_STAT dk_setopts(DK *dk, int bits)
13119+;
13120+
13121+
13122+/* returns the int holding the options set
13123+ * See dk_setopts for bit flags
13124+ */
13125+int dk_getopts(DK *dk)
13126+;
13127+
13128+
13129+/* DEPRECATED in favor of calling dk_setopts().
13130+ * Enables character trace tracking
13131+ *
13132+ * use after dk_sign()/dk_verify()
13133+ */
13134+DK_STAT dk_enable_trace(DK *dk)
13135+;
13136+
13137+
13138+/* Prints trace table to *store variable (char string)
13139+ * *dk is the container for the table
13140+ * *store is a pointer to a character array to output to
13141+ * store_size is the size of the character array *store
13142+ *
13143+ */
13144+DK_STAT dk_get_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size)
13145+;
13146+
13147+
13148+/* Prints difference trace table to *store variable (char string)
13149+ * *dk is the container for the table
13150+ * *store is a pointer to a character array to output to
13151+ * store_size is the size of the character array *store
13152+ * return DK_STAT_NOSIG if no DK-Trace header was found
13153+ */
13154+DK_STAT dk_compare_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size)
13155+;
13156+
13157+
13158+/* Sets the DNS key/policy record manually (no DNS lookup)
13159+ * txtrecord needs to be set to "e=perm;" to force a permanent DNS failure
13160+ * txtrecord needs to be set to "e=temp;" to force a temporary DNS failure
13161+ * Valid DK_TXT types are:
13162+ * DK_TXT_KEY (normal selector record; for <selctor>._domainkey.<domain>)
13163+ * DK_TXT_POLICY (domain policy record; for _domainkey.<domain>)
13164+ */
13165+DK_STAT dk_settxt(DK *dk, DK_TXT recordtype, const char *txtrecord)
13166+;
13167+
13168+
13169+/* Per-message, may be threaded.
13170+ * canon is one of DK_CANON_*.
13171+ * Returns state structure for operation.  Consult statp before using.
13172+ */
13173+DK *dk_sign(DK_LIB *dklib, DK_STAT *statp, int canon)
13174+;
13175+
13176+
13177+/* Per-message, may be threaded.
13178+ * Returns state structure for operation.  Consult statp before using.
13179+ */
13180+DK *dk_verify(DK_LIB *dklib, DK_STAT *statp)
13181+;
13182+
13183+
13184+/* DEPRECATED in favor of calling dk_setopts()
13185+ * set option to remove dupe headers
13186+ * should be called after dk_sign();
13187+ * any int NOT 0 turns dupe removal on
13188+ */
13189+DK_STAT dk_remdupe(DK *dk,int i)
13190+;
13191+
13192+
13193+/* Returns the policy flags belonging to the signing domain.
13194+ * Sender: overrides From:, and the d= entry in the DK-Sig overrides both.
13195+ * If the policy flags were not successfully fetched, DK_FLAG_SET will not
13196+ * be set.
13197+ */
13198+DK_FLAGS dk_policy(DK *dk)
13199+;
13200+
13201+
13202+/* Copies the header names that were signed into the pointer.
13203+ * Returns the number of bytes copied.
13204+ * ptr may be NULL, in which case the bytes are just counted, not copied.
13205+ * Feel free to call this twice; once to get the length, and again to
13206+ * copy the data.
13207+ * NOTE: If the return value is 0 then an error occured.
13208+ *     It's a good idea to check for this
13209+ */
13210+int dk_headers(DK *dk, char *ptr)
13211+;
13212+
13213+
13214+/* Must NOT include dots inserted for SMTP encapsulation.
13215+ * Must NOT include CRLF.CRLF which terminates the message.
13216+ * Otherwise must be exactly that which is sent or received over the SMTP session.
13217+ * May be called multiple times (not necessary to read an entire message into memory).
13218+ */
13219+DK_STAT dk_message(DK *dk, const unsigned char *ptr, size_t len)
13220+;
13221+
13222+
13223+/* DEPRECATED in favor of calling dk_address().
13224+ * Returns a pointer to a null-terminated domain name portion of an RFC 2822 address.
13225+ * If a Sender: was encountered, it returns that domain.  Otherwise,
13226+ * if a From: was encountered, it returns that domain.  Otherwise,
13227+ * return NULL.
13228+ * return NULL if no domain name found in the address.
13229+ * return NULL if the dk is unusable for any reason.
13230+ * return NULL if the address is unusable for any reason.
13231+ */
13232+char *dk_from(DK *dk)
13233+;
13234+
13235+
13236+/* Returns a pointer to the selector name used or NULL if there isn't one
13237+ * Added by rjp
13238+ */
13239+const char *dk_selector(DK *dk)
13240+;
13241+
13242+
13243+/* Returns a pointer to the domain name used or NULL if there isn't one
13244+ */
13245+const char *dk_domain(DK *dk)
13246+;
13247+
13248+
13249+/*
13250+ * Returns a pointer to a string which begins with "N", "S", or "F",
13251+ * corresponding to None, Sender: and From:, respectively.
13252+ * This single character is followed by a null-terminated RFC 2822 address.
13253+ * The first character is "N" if no valid address has been seen yet,
13254+ * "S" if the address came from the Sender: field, and "F" if the
13255+ * address came from the From: field.
13256+ */
13257+char *dk_address(DK *dk)
13258+;
13259+
13260+
13261+/*
13262+ * Returns a pointer to a null-terminated string containing the granularity
13263+ * value found in the selector DNS record, if any, but only after dk_end
13264+ * has been called. Otherwise returns NULL.
13265+ */
13266+char *dk_granularity(DK *dk)
13267+;
13268+
13269+
13270+/*
13271+ * Called at end-of-message (before response to DATA-dot, if synchronous with SMTP session).
13272+ * If verifying, returns signature validity.
13273+ * This does not calculate the signature.  Call dk_getsig() for that.
13274+ * Flags are returned indirectly through dkf.
13275+ * If you pass in NULL for dkf, the flags will not be fetched.
13276+ * If there is a DK-Sig line, the d= entry will be used to fetch the flags.
13277+ * Otherwise the Sender: domain will be used to fetch the flags.
13278+ * Otherwise the From: domain will be used to fetch the flags.
13279+ *
13280+ * NOTE: If for some reason dk_end() returns an error (!DK_STAT_OK) dk_policy() should be called
13281+ * to get the domain signing policy (o=) and handle accordingly.
13282+ * dkf (selector flags) wont be set if dk_end() returns
13283+ * DK_STAT_NOSIG
13284+ * DK_STAT_NOKEY
13285+ * DK_STAT_SYNTAX
13286+ * DK_STAT_NORESOURCE
13287+ * DK_STAT_BADKEY
13288+ * DK_STAT_CANTVERIFY
13289+ */
13290+DK_STAT dk_end(DK *dk, DK_FLAGS *dkf)
13291+;
13292+
13293+
13294+/*
13295+ * DEPRECATED in favor of calling dk_end and dk_policy() directly.
13296+ * If you pass in NULL for dkf, the policy flags will not be fetched.
13297+ * If the message verified okay, the policy flags will not be fetched.
13298+ */
13299+DK_STAT dk_eom(DK *dk, DK_FLAGS *dkf)
13300+;
13301+
13302+
13303+/*
13304+ *
13305+ * privatekey is the private key used to create the signature; It should contain
13306+ * the entire contents of a PEM-format private key file, thusly it will begin with
13307+ * -----BEGIN RSA PRIVATE KEY-----.  It should be null-terminated.
13308+ */
13309+size_t dk_siglen(void *privatekey)
13310+;
13311+
13312+
13313+/*
13314+ * Sets buf to a null-terminated string.
13315+ * If the message is being signed, signature is stored in the buffer.
13316+ * If the message is being verified, returns DK_STAT_INTERNAL.
13317+ * privatekey is the private key used to create the signature; It should contain
13318+ * the entire contents of a PEM-format private key file, thus it will begin with
13319+ * -----BEGIN RSA PRIVATE KEY-----.  It should be null-terminated.
13320+ * If you pass in NULL for buf, you'll get back DK_STAT_NORESOURCE.
13321+ * If len is not big enough, you'll get back DK_STAT_NORESOURCE.
13322+ */
13323+DK_STAT dk_getsig(DK *dk, void *privatekey, unsigned char buf[], size_t len)
13324+;
13325+
13326+
13327+/*
13328+ * Free all resources associated with this message.
13329+ * dk is no longer usable.
13330+ * if doClearErrState != 0, the OpenSSL ErrorState is freed.
13331+ * Set clearErrState=0 if you use other openssl functions and
13332+ * want to call openssl's ERR_remove_state(0) by yourself
13333+ * ERR_remove_state(0) is declared in <openssl/err.h>
13334+ */
13335+DK_STAT dk_free(DK *dk, int doClearErrState)
13336+;
13337+
13338+
13339+/*
13340+ * return a pointer to a string which describes st.
13341+ * The string is structured.  All the characters up to the first colon
13342+ * contain the name of the DK_STAT constant.  From there to the end of
13343+ * string is a human-readable description of the error.
13344+ */
13345+const char *DK_STAT_to_string(DK_STAT st)
13346+;
13347+
13348+
13349+char           *dns_text(char *);
13350diff -ruN ../netqmail-1.06-original/forward.c netqmail-1.06/forward.c
13351--- ../netqmail-1.06-original/forward.c 1998-06-15 12:53:16.000000000 +0200
13352+++ netqmail-1.06/forward.c     2016-11-22 21:03:57.114528032 +0100
13353@@ -6,11 +6,11 @@
13354 #include "strerr.h"
13355 #include "substdio.h"
13356 #include "fmt.h"
13357+#include "stralloc.h"
13358+#include "srs.h"
13359 
13360 #define FATAL "forward: fatal: "
13361 
13362-void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
13363-
13364 struct qmail qqt;
13365 
13366 int mywrite(fd,buf,len) int fd; char *buf; int len;
13367@@ -42,6 +42,16 @@
13368   dtline = env_get("DTLINE");
13369   if (!dtline)
13370     strerr_die2x(100,FATAL,"DTLINE not set");
13371+
13372+  if (str_len(sender)) {
13373+    switch(srsforward(sender)) {
13374+      case -3: strerr_die2x(100,FATAL,srs_error.s); break;
13375+      case -2: strerr_die2x(111,FATAL,"out of memory"); break;
13376+      case -1: strerr_die2x(111,FATAL,"unable to read controls"); break;
13377+      case 0: break; // nothing
13378+      case 1: sender = srs_result.s; break;
13379+    }
13380+  }
13381 
13382   if (qmail_open(&qqt) == -1)
13383     strerr_die2sys(111,FATAL,"unable to fork: ");
13384diff -ruN ../netqmail-1.06-original/global.h netqmail-1.06/global.h
13385--- ../netqmail-1.06-original/global.h  1970-01-01 01:00:00.000000000 +0100
13386+++ netqmail-1.06/global.h      2016-11-22 21:03:57.114528032 +0100
13387@@ -0,0 +1,51 @@
13388+/* GLOBAL.H - RSAREF types and constants */
13389+
13390+#include <string.h>
13391+#include "uint32.h"
13392+
13393+/* Copyright (C) RSA Laboratories, a division of RSA Data Security,
13394+     Inc., created 1991. All rights reserved.
13395+ */
13396+
13397+#ifndef _GLOBAL_H_
13398+#define _GLOBAL_H_ 1
13399+
13400+/* PROTOTYPES should be set to one if and only if the compiler supports
13401+     function argument prototyping.
13402+   The following makes PROTOTYPES default to 1 if it has not already been
13403+     defined as 0 with C compiler flags.
13404+ */
13405+#ifndef PROTOTYPES
13406+#define PROTOTYPES 1
13407+#endif
13408+
13409+/* POINTER defines a generic pointer type */
13410+typedef unsigned char *POINTER;
13411+
13412+/* UINT2 defines a two byte word */
13413+typedef unsigned short int UINT2;
13414+
13415+/* UINT4 defines a four byte word */
13416+#ifdef UINT32_H
13417+#define UINT4 uint32
13418+#endif
13419+
13420+#ifndef NULL_PTR
13421+#define NULL_PTR ((POINTER)0)
13422+#endif
13423+
13424+#ifndef UNUSED_ARG
13425+#define UNUSED_ARG(x) x = *(&x);
13426+#endif
13427+
13428+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
13429+   If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
13430+     returns an empty list. 
13431+ */
13432+#if PROTOTYPES
13433+#define PROTO_LIST(list) list
13434+#else
13435+#define PROTO_LIST(list) ()
13436+#endif
13437+
13438+#endif /* end _GLOBAL_H_ */
13439diff -ruN ../netqmail-1.06-original/goodrcptto-12.patch netqmail-1.06/goodrcptto-12.patch
13440--- ../netqmail-1.06-original/goodrcptto-12.patch       1970-01-01 01:00:00.000000000 +0100
13441+++ netqmail-1.06/goodrcptto-12.patch   2004-11-09 20:12:53.000000000 +0100
13442@@ -0,0 +1,834 @@
13443+This is a goodrcptto patch for qmail-1.03 or netqmail-1.05:
13444+http://netdevice.com/qmail/patch/goodrcptto-12.patch
13445+See http://cr.yp.to/qmail.html or http://qmail.org/netqmail/.
13446+
13447+A qmail server will normally accept email for any recipient address at a domain.
13448+This patch causes the server to reject single recipient email to an invalid
13449+recipient, and filter out the invalid recipients from multiple recipient email,
13450+while accepting the message for the valid recipients.
13451+This occurs during the initial SMTP conversation for a reduction in disk I/O.
13452+The server rejects attempts to queue messages to non existent recipients, and
13453+joe job bounces to forged recipients, preventing them from becoming double
13454+bounces.
13455+To prevent dictionary attacks, the transmission channel is closed after the
13456+number of bad recipients set in control/brtlimit or BRTLIMIT, two by default.
13457+Repeated attempts from the same IPs may be handled by a cron that looks at the
13458+logs and updates tcprules accordingly.
13459+
13460+A goodrcptto list and or moregoodrcptto database is maintained.
13461+Relay and accept clients are not held to the address check, control/brtlimit or
13462+BRTLIMIT.
13463+If you need to wildcard domains, list them one per line like @example.net
13464+in control/goodrcptto only.
13465+Recipient addresses like name@example.com may be included in control/goodrcptto,
13466+but the check will run fastest if you put these into control/moregoodrcptto,
13467+then into control/moregoodrcptto.cdb using qmail-newmgrt.
13468+A check against a 50,000 address moregoodrcptto.cdb is virtually instantaneous
13469+on a 300Mhz machine.
13470+
13471+A user may want to participate in mailing list discussions, but doesn't want
13472+spam or off list replies to her now public address.
13473+Set ACCEPTCLIENT="" for the IPs of the mailing list servers with tcprules, and
13474+put the recipient address in control/protectedgood instead.
13475+
13476+For an example of how to automate this process, see the parent directory for an
13477+interactive user run script where one can remotely add, remove or list their
13478+disposable alias addresses, and the mail server cron that keeps the
13479+moregoodrcptto.cdb up to date.
13480+The patch assumes a Dave Sill type of installation with regards to extra control
13481+files concurrencyincoming and defaultdelivery, see http://lifewithqmail.org.
13482+
13483+Use http@ to get the patch onto your box, tab characters must be preserved.
13484+Here are examples of how to patch.
13485+
13486+Solaris:
13487+# gzip -cd qmail-1.03.tar.gz |tar -xf - ;cd qmail-1.03
13488+# gpatch </path/to/goodrcptto-12.patch
13489+or
13490+# gzip -cd netqmail-1.05.tar.gz |tar -xf - ;cd netqmail-1.05
13491+# sh -c "cat collate.sh |sed -e s/patch/gpatch/ >collate.sh"
13492+# ./collate.sh ;cd netqmail-1.05
13493+# gpatch </path/to/goodrcptto-12.patch
13494+
13495+Others:
13496+# gzip -cd qmail-1.03.tar.gz |tar -xf - ;cd qmail-1.03
13497+# patch </path/to/goodrcptto-12.patch
13498+or
13499+# gzip -cd netqmail-1.05.tar.gz |tar -xf - ;cd netqmail-1.05
13500+# ./collate.sh ;cd netqmail-1.05
13501+# patch </path/to/goodrcptto-12.patch
13502+
13503+Log example:
13504+2003-06-08 12:56:28.951415500 qmail-smtpd: !ok 29791 Bad recipient user nonexistent@example.com from DealsonWheels@321sm.com by 205.235.78.101 (HELO mx4.321sm.com).
13505+
13506+2003-06-08 01: Original version, based on John Levine's badrcptto patch:
13507+               http://www.iecc.com/bad-rcpt-noisy-patch.txt
13508+2003-06-15 02: Added support for domain wildcarding.
13509+2003-06-15 03: Running qmail-showctl also shows good recipient addresses.
13510+2003-07-01 04: The pid for the connection is included in the log.
13511+2003-07-11 05: Experimental.
13512+2003-07-13 06: Experimental.
13513+2003-07-20 07: Removed the message block on a mix of good and bad recipients.
13514+               Allowed for only using a goodrcptto list.
13515+               Corrected an error in the qmail-showctl.c patch.
13516+2003-09-02 08: Added publicly known recipient address protection using an
13517+               ACCEPTCLIENT tcprules variable.
13518+2003-10-04 09: Running qmail-showctl also shows protected recipient addresses.
13519+2003-11-07 10: Discontinued the non logging version of goodrcptto.
13520+               Added dictionary attack prevention within qmail-smtpd using
13521+               control/brtlimit and or BRTLIMIT.
13522+               Updated the qmail-smtpd.8 man page patch regarding ACCEPTCLIENT,
13523+               control/brtlimit and BRTLIMIT.
13524+               Updated the qmail-control.9 man page patch regarding brtlimit,
13525+               concurrencyincoming, defaultdelivery, goodrcptto, moregoodrcptto
13526+               and protectedgood.
13527+2004-02-14 11: Code cleanup and standardization with tcpserver logging at
13528+               getpid.
13529+               This single patch works with both qmail-1.03 and netqmail-1.05.
13530+               The brtcount is continued across rsets.
13531+2004-03-05 12: No changes, matched version number with goodrcptto-ms-12.patch.
13532+
13533+Eben Pratt, goodrcptto at netdevice dot com
13534+_____
13535+
13536+diff -ur qmail-1.03.orig/Makefile qmail-1.03/Makefile
13537+--- qmail-1.03.orig/Makefile   Mon Jun 15 06:53:16 1998
13538++++ qmail-1.03/Makefile        Sat Feb 14 02:41:03 2004
13539+@@ -803,7 +803,7 @@
13540+ predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
13541+ qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
13542+ qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
13543+-qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
13544++qmail-smtpd sendmail tcp-env qmail-newmrh qmail-newmgrt config config-fast dnscname \
13545+ dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
13546+ forward preline condredirect bouncesaying except maildirmake \
13547+ maildir2mbox maildirwatch qail elq pinq idedit install-big install \
13548+@@ -930,8 +930,8 @@
13549+ qmail-queue.0 qmail-inject.0 mailsubj.0 qmail-showctl.0 qmail-newu.0 \
13550+ qmail-pw2u.0 qmail-qread.0 qmail-qstat.0 qmail-tcpto.0 qmail-tcpok.0 \
13551+ qmail-pop3d.0 qmail-popup.0 qmail-qmqpc.0 qmail-qmqpd.0 qmail-qmtpd.0 \
13552+-qmail-smtpd.0 tcp-env.0 qmail-newmrh.0 qreceipt.0 qbiff.0 forward.0 \
13553+-preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \
13554++qmail-smtpd.0 tcp-env.0 qmail-newmrh.0 qmail-newmgrt.0 qreceipt.0 qbiff.0 \
13555++forward.0 preline.0 condredirect.0 bouncesaying.0 except.0 maildirmake.0 \
13556+ maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \
13557+ qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \
13558+ qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \
13559+@@ -1241,6 +1241,31 @@
13560+ uint32.h substdio.h
13561+       ./compile qmail-newmrh.c
13562+
13563++qmail-newmgrt: \
13564++load qmail-newmgrt.o cdbmss.o getln.a open.a cdbmake.a seek.a case.a \
13565++stralloc.a alloc.a strerr.a substdio.a error.a str.a auto_qmail.o
13566++      ./load qmail-newmgrt cdbmss.o getln.a open.a cdbmake.a \
13567++      seek.a case.a stralloc.a alloc.a strerr.a substdio.a \
13568++      error.a str.a auto_qmail.o
13569++
13570++qmail-newmgrt.0: \
13571++qmail-newmgrt.8
13572++      nroff -man qmail-newmgrt.8 > qmail-newmgrt.0
13573++
13574++qmail-newmgrt.8: \
13575++qmail-newmgrt.9 conf-break conf-spawn
13576++      cat qmail-newmgrt.9 \
13577++      | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
13578++      | sed s}BREAK}"`head -1 conf-break`"}g \
13579++      | sed s}SPAWN}"`head -1 conf-spawn`"}g \
13580++      > qmail-newmgrt.8
13581++
13582++qmail-newmgrt.o: \
13583++compile qmail-newmgrt.c strerr.h stralloc.h gen_alloc.h substdio.h \
13584++getln.h exit.h readwrite.h open.h auto_qmail.h cdbmss.h cdbmake.h \
13585++uint32.h substdio.h
13586++      ./compile qmail-newmgrt.c
13587++
13588+ qmail-newu: \
13589+ load qmail-newu.o cdbmss.o getln.a open.a seek.a cdbmake.a case.a \
13590+ stralloc.a alloc.a substdio.a error.a str.a auto_qmail.o
13591+@@ -1767,7 +1792,7 @@
13592+ maildirwatch.1 mailsubj.1 mbox.5 preline.1 qbiff.1 qmail-clean.8 \
13593+ qmail-command.8 qmail-control.9 qmail-getpw.9 qmail-header.5 \
13594+ qmail-inject.8 qmail-limits.9 qmail-local.8 qmail-log.5 \
13595+-qmail-lspawn.8 qmail-newmrh.9 qmail-newu.9 qmail-pop3d.8 \
13596++qmail-lspawn.8 qmail-newmrh.9 qmail-newmgrt.9 qmail-newu.9 qmail-pop3d.8 \
13597+ qmail-popup.8 qmail-pw2u.9 qmail-qmqpc.8 qmail-qmqpd.8 qmail-qmtpd.8 \
13598+ qmail-qread.8 qmail-qstat.8 qmail-queue.8 qmail-remote.8 \
13599+ qmail-rspawn.8 qmail-send.9 qmail-showctl.8 qmail-smtpd.8 \
13600+@@ -1774,7 +1799,7 @@
13601+ qmail-start.9 qmail-tcpok.8 qmail-tcpto.8 qmail-users.9 qmail.7 \
13602+ qreceipt.1 splogger.8 tcp-env.1 config.sh config-fast.sh \
13603+ qmail-clean.c qmail-getpw.c qmail-inject.c qmail-local.c \
13604+-qmail-lspawn.c qmail-newmrh.c qmail-newu.c qmail-pop3d.c \
13605++qmail-lspawn.c qmail-newmrh.c qmail-newmgrt.c qmail-newu.c qmail-pop3d.c \
13606+ qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
13607+ qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
13608+ qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
13609+diff -ur qmail-1.03.orig/TARGETS qmail-1.03/TARGETS
13610+--- qmail-1.03.orig/TARGETS    Mon Jun 15 06:53:16 1998
13611++++ qmail-1.03/TARGETS Sat Feb 14 02:41:03 2004
13612+@@ -257,6 +257,8 @@
13613+ tcp-env.o
13614+ remoteinfo.o
13615+ tcp-env
13616++qmail-newmgrt.o
13617++qmail-newmgrt
13618+ qmail-newmrh.o
13619+ qmail-newmrh
13620+ config
13621+@@ -352,6 +354,8 @@
13622+ qmail-qmtpd.0
13623+ qmail-smtpd.0
13624+ tcp-env.0
13625++qmail-newmgrt.8
13626++qmail-newmgrt.0
13627+ qmail-newmrh.8
13628+ qmail-newmrh.0
13629+ qreceipt.0
13630+diff -ur qmail-1.03.orig/conf-spawn qmail-1.03/conf-spawn
13631+--- qmail-1.03.orig/conf-spawn Mon Jun 15 06:53:16 1998
13632++++ qmail-1.03/conf-spawn      Sat Feb 14 02:41:03 2004
13633+@@ -1,4 +1,4 @@
13634+-120
13635++255
13636+
13637+ This is a silent concurrency limit. You can't set it above 255. On some
13638+ systems you can't set it above 125. qmail will refuse to compile if the
13639+diff -ur qmail-1.03.orig/hier.c qmail-1.03/hier.c
13640+--- qmail-1.03.orig/hier.c     Mon Jun 15 06:53:16 1998
13641++++ qmail-1.03/hier.c  Sat Feb 14 02:41:03 2004
13642+@@ -109,6 +109,7 @@
13643+   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
13644+   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
13645+   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
13646++  c(auto_qmail,"bin","qmail-newmgrt",auto_uido,auto_gidq,0700);
13647+   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
13648+   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
13649+   c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
13650+@@ -221,6 +222,8 @@
13651+   c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644);
13652+   c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644);
13653+   c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644);
13654++  c(auto_qmail,"man/man8","qmail-newmgrt.8",auto_uido,auto_gidq,0644);
13655++  c(auto_qmail,"man/cat8","qmail-newmgrt.0",auto_uido,auto_gidq,0644);
13656+   c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644);
13657+   c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644);
13658+   c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644);
13659+diff -ur qmail-1.03.orig/install-big.c qmail-1.03/install-big.c
13660+--- qmail-1.03.orig/install-big.c      Mon Jun 15 06:53:16 1998
13661++++ qmail-1.03/install-big.c   Sat Feb 14 02:41:03 2004
13662+@@ -109,6 +109,7 @@
13663+   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
13664+   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
13665+   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
13666++  c(auto_qmail,"bin","qmail-newmgrt",auto_uido,auto_gidq,0700);
13667+   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
13668+   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
13669+   c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
13670+@@ -221,6 +222,8 @@
13671+   c(auto_qmail,"man/cat8","qmail-inject.0",auto_uido,auto_gidq,0644);
13672+   c(auto_qmail,"man/man8","qmail-showctl.8",auto_uido,auto_gidq,0644);
13673+   c(auto_qmail,"man/cat8","qmail-showctl.0",auto_uido,auto_gidq,0644);
13674++  c(auto_qmail,"man/man8","qmail-newmgrt.8",auto_uido,auto_gidq,0644);
13675++  c(auto_qmail,"man/cat8","qmail-newmgrt.0",auto_uido,auto_gidq,0644);
13676+   c(auto_qmail,"man/man8","qmail-newmrh.8",auto_uido,auto_gidq,0644);
13677+   c(auto_qmail,"man/cat8","qmail-newmrh.0",auto_uido,auto_gidq,0644);
13678+   c(auto_qmail,"man/man8","qmail-newu.8",auto_uido,auto_gidq,0644);
13679+diff -ur qmail-1.03.orig/qmail-control.9 qmail-1.03/qmail-control.9
13680+--- qmail-1.03.orig/qmail-control.9    Mon Jun 15 06:53:16 1998
13681++++ qmail-1.03/qmail-control.9 Sat Feb 14 02:41:03 2004
13682+@@ -21,6 +21,7 @@
13683+ Comments are allowed
13684+ in
13685+ .IR badmailfrom ,
13686++.IR goodrcptto ,
13687+ .IR locals ,
13688+ .IR percenthack ,
13689+ .IR qmqpservers ,
13690+@@ -43,8 +44,11 @@
13691+ .I badmailfrom        \fR(none)       \fRqmail-smtpd
13692+ .I bouncefrom \fRMAILER-DAEMON        \fRqmail-send
13693+ .I bouncehost \fIme   \fRqmail-send
13694++.I brtlimit   \fR2    \fRqmail-smtpd
13695++.I concurrencyincoming        \fR40   \fRtcpserver
13696+ .I concurrencylocal   \fR10   \fRqmail-send
13697+ .I concurrencyremote  \fR20   \fRqmail-send
13698++.I defaultdelivery    \fR(none)       \fRqmail-start
13699+ .I defaultdomain      \fIme   \fRqmail-inject
13700+ .I defaulthost        \fIme   \fRqmail-inject
13701+ .I databytes  \fR0    \fRqmail-smtpd
13702+@@ -51,13 +55,16 @@
13703+ .I doublebouncehost   \fIme   \fRqmail-send
13704+ .I doublebounceto     \fRpostmaster   \fRqmail-send
13705+ .I envnoathost        \fIme   \fRqmail-send
13706++.I goodrcptto \fR(none)       \fRqmail-smtpd
13707+ .I helohost   \fIme   \fRqmail-remote
13708+ .I idhost     \fIme   \fRqmail-inject
13709+ .I localiphost        \fIme   \fRqmail-smtpd
13710+ .I locals     \fIme   \fRqmail-send
13711++.I moregoodrcptto     \fR(none)       \fRqmail-smtpd
13712+ .I morercpthosts      \fR(none)       \fRqmail-smtpd
13713+ .I percenthack        \fR(none)       \fRqmail-send
13714+ .I plusdomain \fIme   \fRqmail-inject
13715++.I protectedgood      \fR(none)       \fRqmail-showctl
13716+ .I qmqpservers        \fR(none)       \fRqmail-qmqpc
13717+ .I queuelifetime      \fR604800       \fRqmail-send
13718+ .I rcpthosts  \fR(none)       \fRqmail-smtpd
13719+diff -ur qmail-1.03.orig/qmail-newmgrt.9 qmail-1.03/qmail-newmgrt.9
13720+--- qmail-1.03.orig/qmail-newmgrt.9    Thu Jan  1 00:00:00 1970
13721++++ qmail-1.03/qmail-newmgrt.9 Sat Feb 14 02:41:04 2004
13722+@@ -0,0 +1,41 @@
13723++.TH qmail-newmgrt 8
13724++.SH NAME
13725++qmail-newmgrt \- prepare moregoodrcptto for qmail-smtpd
13726++.SH SYNOPSIS
13727++.B qmail-newmgrt
13728++.SH DESCRIPTION
13729++.B qmail-newmgrt
13730++reads the instructions in
13731++.B QMAILHOME/control/moregoodrcptto
13732++and writes them into
13733++.B QMAILHOME/control/moregoodrcptto.cdb
13734++in a binary format suited
13735++for quick access by
13736++.BR qmail-smtpd .
13737++
13738++If there is a problem with
13739++.BR control/moregoodrcptto ,
13740++.B qmail-newmgrt
13741++complains and leaves
13742++.B control/moregoodrcptto.cdb
13743++alone.
13744++
13745++.B qmail-newmgrt
13746++ensures that
13747++.B control/moregoodrcptto.cdb
13748++is updated atomically,
13749++so
13750++.B qmail-smtpd
13751++never has to wait for
13752++.B qmail-newmgrt
13753++to finish.
13754++However,
13755++.B qmail-newmgrt
13756++makes no attempt to protect against two simultaneous updates of
13757++.BR control/moregoodrcptto.cdb .
13758++
13759++The binary
13760++.B control/moregoodrcptto.cdb
13761++format is portable across machines.
13762++.SH "SEE ALSO"
13763++qmail-smtpd(8)
13764+diff -ur qmail-1.03.orig/qmail-newmgrt.c qmail-1.03/qmail-newmgrt.c
13765+--- qmail-1.03.orig/qmail-newmgrt.c    Thu Jan  1 00:00:00 1970
13766++++ qmail-1.03/qmail-newmgrt.c Sat Feb 14 02:41:04 2004
13767+@@ -0,0 +1,70 @@
13768++#include "strerr.h"
13769++#include "stralloc.h"
13770++#include "substdio.h"
13771++#include "getln.h"
13772++#include "exit.h"
13773++#include "readwrite.h"
13774++#include "open.h"
13775++#include "auto_qmail.h"
13776++#include "cdbmss.h"
13777++
13778++#define FATAL "qmail-newmgrt: fatal: "
13779++
13780++void die_read()
13781++{
13782++  strerr_die2sys(111,FATAL,"unable to read control/moregoodrcptto: ");
13783++}
13784++void die_write()
13785++{
13786++  strerr_die2sys(111,FATAL,"unable to write to control/moregoodrcptto.tmp: ");
13787++}
13788++
13789++char inbuf[1024];
13790++substdio ssin;
13791++
13792++int fd;
13793++int fdtemp;
13794++
13795++struct cdbmss cdbmss;
13796++stralloc line = {0};
13797++int match;
13798++
13799++void main()
13800++{
13801++  umask(033);
13802++  if (chdir(auto_qmail) == -1)
13803++    strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
13804++
13805++  fd = open_read("control/moregoodrcptto");
13806++  if (fd == -1) die_read();
13807++
13808++  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
13809++
13810++  fdtemp = open_trunc("control/moregoodrcptto.tmp");
13811++  if (fdtemp == -1) die_write();
13812++
13813++  if (cdbmss_start(&cdbmss,fdtemp) == -1) die_write();
13814++
13815++  for (;;) {
13816++    if (getln(&ssin,&line,&match,'\n') != 0) die_read();
13817++    case_lowerb(line.s,line.len);
13818++    while (line.len) {
13819++      if (line.s[line.len - 1] == ' ') { --line.len; continue; }
13820++      if (line.s[line.len - 1] == '\n') { --line.len; continue; }
13821++      if (line.s[line.len - 1] == '\t') { --line.len; continue; }
13822++      if (line.s[0] != '#')
13823++       if (cdbmss_add(&cdbmss,line.s,line.len,"",0) == -1)
13824++         die_write();
13825++      break;
13826++    }
13827++    if (!match) break;
13828++  }
13829++
13830++  if (cdbmss_finish(&cdbmss) == -1) die_write();
13831++  if (fsync(fdtemp) == -1) die_write();
13832++  if (close(fdtemp) == -1) die_write(); /* NFS stupidity */
13833++  if (rename("control/moregoodrcptto.tmp","control/moregoodrcptto.cdb") == -1)
13834++    strerr_die2sys(111,FATAL,"unable to move control/moregoodrcpto.tmp to control/moregoodrcptto.cdb");
13835++
13836++  _exit(0);
13837++}
13838+diff -ur qmail-1.03.orig/qmail-showctl.c qmail-1.03/qmail-showctl.c
13839+--- qmail-1.03.orig/qmail-showctl.c    Mon Jun 15 06:53:16 1998
13840++++ qmail-1.03/qmail-showctl.c Sat Feb 14 02:41:04 2004
13841+@@ -142,6 +142,8 @@
13842+   direntry *d;
13843+   struct stat stmrh;
13844+   struct stat stmrhcdb;
13845++  struct stat stmgrt;
13846++  struct stat stmgrtcdb;
13847+
13848+   substdio_puts(subfdout,"qmail home directory: ");
13849+   substdio_puts(subfdout,auto_qmail);
13850+@@ -217,9 +219,12 @@
13851+   do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM.");
13852+   do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is ");
13853+   do_str("bouncehost",1,"bouncehost","Bounce host name is ");
13854++  do_int("brtlimit","2","Transmission channel close after "," bad recipients");
13855++  do_int("concurrencyincoming","1","Incoming concurrency is ","");
13856+   do_int("concurrencylocal","10","Local concurrency is ","");
13857+   do_int("concurrencyremote","20","Remote concurrency is ","");
13858+   do_int("databytes","0","SMTP DATA limit is "," bytes");
13859++  do_str("defaultdelivery",1,"defaultdelivery","Default mailbox is ");
13860+   do_str("defaultdomain",1,"defaultdomain","Default domain name is ");
13861+   do_str("defaulthost",1,"defaulthost","Default host name is ");
13862+   do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: ");
13863+@@ -235,8 +240,8 @@
13864+   do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
13865+   do_int("queuelifetime","604800","Message lifetime in the queue is "," seconds");
13866+
13867+-  if (do_lst("rcpthosts","SMTP clients may send messages to any recipient.","SMTP clients may send messages to recipients at ","."))
13868+-    do_lst("morercpthosts","No effect.","SMTP clients may send messages to recipients at ",".");
13869++  if (do_lst("rcpthosts","SMTP relay clients may send to any recipient.","SMTP relay clients may send to recipients at ","."))
13870++    do_lst("morercpthosts","No effect.","SMTP relay clients may send to recipients at ",".");
13871+   else
13872+     do_lst("morercpthosts","No rcpthosts; morercpthosts is irrelevant.","No rcpthosts; doesn't matter that morercpthosts has ",".");
13873+   /* XXX: check morercpthosts.cdb contents */
13874+@@ -255,6 +260,27 @@
13875+       else
13876+         substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n");
13877+
13878++  if (do_lst("goodrcptto","Oops? moregoodrcptto must exist if this doesn't.","SMTP clients may send to ","."))
13879++    do_lst("moregoodrcptto","No effect.","SMTP clients may send to ",".");
13880++  else
13881++    do_lst("moregoodrcptto","Oops? goodrcptto must exist if this doesn't.","SMTP clients may send to ",".");
13882++  /* XXX: check moregoodrcptto.cdb contents */
13883++  substdio_puts(subfdout,"\nmoregoodrcptto.cdb: ");
13884++  if (stat("moregoodrcptto",&stmgrt) == -1)
13885++    if (stat("moregoodrcptto.cdb",&stmgrtcdb) == -1)
13886++      substdio_puts(subfdout,"(Default.) No effect.\n");
13887++    else
13888++      substdio_puts(subfdout,"Oops! moregoodrcptto.cdb exists but moregoodrcptto doesn't.\n");
13889++  else
13890++    if (stat("moregoodrcptto.cdb",&stmgrtcdb) == -1)
13891++      substdio_puts(subfdout,"Oops! moregoodrcptto exists but moregoodrcptto.cdb doesn't.\n");
13892++    else
13893++      if (stmgrt.st_mtime > stmgrtcdb.st_mtime)
13894++        substdio_puts(subfdout,"Oops! moregoodrcptto.cdb is older than moregoodrcptto.\n");
13895++      else
13896++        substdio_puts(subfdout,"Modified recently enough; hopefully up to date.\n");
13897++
13898++  do_lst("protectedgood","No accept client addresses.","SMTP accept clients may send to ",".");
13899+   do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
13900+   do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
13901+   do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
13902+@@ -265,19 +291,21 @@
13903+   while (d = readdir(dir)) {
13904+     if (str_equal(d->d_name,".")) continue;
13905+     if (str_equal(d->d_name,"..")) continue;
13906+-    if (str_equal(d->d_name,"bouncefrom")) continue;
13907+-    if (str_equal(d->d_name,"bouncehost")) continue;
13908+     if (str_equal(d->d_name,"badmailfrom")) continue;
13909+     if (str_equal(d->d_name,"bouncefrom")) continue;
13910+     if (str_equal(d->d_name,"bouncehost")) continue;
13911++    if (str_equal(d->d_name,"brtlimit")) continue;
13912++    if (str_equal(d->d_name,"concurrencyincoming")) continue;
13913+     if (str_equal(d->d_name,"concurrencylocal")) continue;
13914+     if (str_equal(d->d_name,"concurrencyremote")) continue;
13915+     if (str_equal(d->d_name,"databytes")) continue;
13916++    if (str_equal(d->d_name,"defaultdelivery")) continue;
13917+     if (str_equal(d->d_name,"defaultdomain")) continue;
13918+     if (str_equal(d->d_name,"defaulthost")) continue;
13919+     if (str_equal(d->d_name,"doublebouncehost")) continue;
13920+     if (str_equal(d->d_name,"doublebounceto")) continue;
13921+     if (str_equal(d->d_name,"envnoathost")) continue;
13922++    if (str_equal(d->d_name,"goodrcptto")) continue;
13923+     if (str_equal(d->d_name,"helohost")) continue;
13924+     if (str_equal(d->d_name,"idhost")) continue;
13925+     if (str_equal(d->d_name,"localiphost")) continue;
13926+@@ -285,8 +313,11 @@
13927+     if (str_equal(d->d_name,"me")) continue;
13928+     if (str_equal(d->d_name,"morercpthosts")) continue;
13929+     if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
13930++    if (str_equal(d->d_name,"moregoodrcptto")) continue;
13931++    if (str_equal(d->d_name,"moregoodrcptto.cdb")) continue;
13932+     if (str_equal(d->d_name,"percenthack")) continue;
13933+     if (str_equal(d->d_name,"plusdomain")) continue;
13934++    if (str_equal(d->d_name,"protectedgood")) continue;
13935+     if (str_equal(d->d_name,"qmqpservers")) continue;
13936+     if (str_equal(d->d_name,"queuelifetime")) continue;
13937+     if (str_equal(d->d_name,"rcpthosts")) continue;
13938+diff -ur qmail-1.03.orig/qmail-smtpd.8 qmail-1.03/qmail-smtpd.8
13939+--- qmail-1.03.orig/qmail-smtpd.8      Mon Jun 15 06:53:16 1998
13940++++ qmail-1.03/qmail-smtpd.8   Sat Feb 14 02:41:04 2004
13941+@@ -50,6 +50,20 @@
13942+ meaning every address at
13943+ .IR host .
13944+ .TP 5
13945++.I brtlimit
13946++Number of bad recipients before closing the transmission channel.
13947++.B qmail-smtpd
13948++will close the transmission channel after
13949++reaching the number of bad recipients in
13950++.IR brtlimit .
13951++
13952++If the environment variable
13953++.B BRTLIMIT
13954++is set, it overrides
13955++.IR brtlimit .
13956++
13957++Default and minimum: 2.
13958++.TP 5
13959+ .I databytes
13960+ Maximum number of bytes allowed in a message,
13961+ or 0 for no limit.
13962+@@ -77,6 +91,50 @@
13963+ is set, it overrides
13964+ .IR databytes .
13965+ .TP 5
13966++.I goodrcptto
13967++Allowed RCPT addresses.
13968++.B qmail-smtpd
13969++will reject
13970++any envelope recipient address not listed in
13971++.I goodrcptto
13972++or
13973++.IR moregoodrcptto .
13974++A line in
13975++.I goodrcptto
13976++may be of the form
13977++.BR @\fIhost ,
13978++meaning every address at
13979++.IR host .
13980++
13981++.I goodrcptto
13982++format:
13983++
13984++.EX
13985++   @heaven.af.mil
13986++   box@heaven.af.mil
13987++.EE
13988++
13989++Exceptions:
13990++If the environment variable
13991++.B RELAYCLIENT
13992++is set,
13993++.B qmail-smtpd
13994++will ignore
13995++.I goodrcptto
13996++and
13997++.IR moregoodrcptto ,
13998++and will append the value of
13999++.B RELAYCLIENT
14000++to each incoming recipient address.
14001++If the environment variable
14002++.B ACCEPTCLIENT
14003++is set,
14004++.B qmail-smtpd
14005++will ignore
14006++.I goodrcptto
14007++and
14008++.IR moregoodrcptto .
14009++.TP 5
14010+ .I localiphost
14011+ Replacement host name for local IP addresses.
14012+ Default:
14013+@@ -97,6 +155,38 @@
14014+ This is done before
14015+ .IR rcpthosts .
14016+ .TP 5
14017++.I moregoodrcptto
14018++Extra allowed RCPT addresses.
14019++If
14020++.I goodrcptto
14021++and
14022++.I moregoodrcptto
14023++both exist,
14024++.I moregoodrcptto
14025++is effectively appended to
14026++.IR goodrcptto .
14027++
14028++.I moregoodrcptto
14029++format:
14030++
14031++.EX
14032++   box@heaven.af.mil
14033++.EE
14034++
14035++You must run
14036++.B qmail-newmgrt
14037++whenever
14038++.I moregoodrcptto
14039++changes.
14040++
14041++Rule of thumb:
14042++Put your
14043++.BR @\fIhost
14044++wildcarded domains into
14045++.IR goodrcptto ,
14046++and the rest into
14047++.IR moregoodrcptto .
14048++.TP 5
14049+ .I morercpthosts
14050+ Extra allowed RCPT domains.
14051+ If
14052+@@ -150,7 +240,7 @@
14053+ .EE
14054+
14055+ Envelope recipient addresses without @ signs are
14056+-always allowed through.
14057++allowed through if added to goodrcptto or moregoodrcptto.
14058+ .TP 5
14059+ .I smtpgreeting
14060+ SMTP greeting message.
14061+@@ -174,6 +264,7 @@
14062+ tcp-environ(5),
14063+ qmail-control(5),
14064+ qmail-inject(8),
14065++qmail-newmgrt(8),
14066+ qmail-newmrh(8),
14067+ qmail-queue(8),
14068+ qmail-remote(8)
14069+diff -ur qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c
14070+--- qmail-1.03.orig/qmail-smtpd.c      Mon Jun 15 06:53:16 1998
14071++++ qmail-1.03/qmail-smtpd.c   Sat Feb 14 03:12:49 2004
14072+@@ -23,11 +23,19 @@
14073+ #include "timeoutread.h"
14074+ #include "timeoutwrite.h"
14075+ #include "commands.h"
14076++#include "cdb.h"
14077+
14078+ #define MAXHOPS 100
14079+ unsigned int databytes = 0;
14080+ int timeout = 1200;
14081+
14082++char *remoteip;
14083++char *remotehost;
14084++char *remoteinfo;
14085++char *local;
14086++char *relayclient;
14087++char *acceptclient;
14088++
14089+ int safewrite(fd,buf,len) int fd; char *buf; int len;
14090+ {
14091+   int r;
14092+@@ -42,12 +50,39 @@
14093+ void flush() { substdio_flush(&ssout); }
14094+ void out(s) char *s; { substdio_puts(&ssout,s); }
14095+
14096++char sserrbuf[512];
14097++substdio sserr = SUBSTDIO_FDBUF(safewrite,2,sserrbuf,sizeof sserrbuf);
14098++
14099++char strnum[FMT_ULONG];
14100++void log(s) char *s; { substdio_putsflush(&sserr,s); }
14101++void logs(s1,s2,s3) char *s1; char *s2; char *s3; {
14102++  substdio_putsflush(&sserr,s1);
14103++  substdio_putsflush(&sserr,s2);
14104++  substdio_putsflush(&sserr,s3);
14105++}
14106++void pid() { log("qmail-smtpd: !ok "); strnum[fmt_ulong(strnum,getpid())] = 0; log(strnum); }
14107++
14108+ void die_read() { _exit(1); }
14109+-void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
14110+-void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
14111+-void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
14112+-void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
14113+-void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
14114++void die_alarm() {
14115++  pid(); logs(" Connection to ",remoteip," timed out.\n");
14116++  out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1);
14117++}
14118++void die_nomem() {
14119++  pid(); logs(" Out of memory while connected to ",remoteip,"!\n");
14120++  out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1);
14121++}
14122++void die_control() {
14123++  pid(); log(" Unable to read controls!\n");
14124++  out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1);
14125++}
14126++void die_ipme() {
14127++  pid(); log(" Unable to figure out my IP addresses!\n");
14128++  out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1);
14129++}
14130++void straynewline() {
14131++  pid(); logs(" Stray newline from ",remoteip,".\n");
14132++  out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1);
14133++}
14134+
14135+ void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
14136+ void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
14137+@@ -76,12 +111,6 @@
14138+   smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
14139+ }
14140+
14141+-char *remoteip;
14142+-char *remotehost;
14143+-char *remoteinfo;
14144+-char *local;
14145+-char *relayclient;
14146+-
14147+ stralloc helohost = {0};
14148+ char *fakehelo; /* pointer into helohost, or 0 */
14149+
14150+@@ -96,6 +125,11 @@
14151+ int bmfok = 0;
14152+ stralloc bmf = {0};
14153+ struct constmap mapbmf;
14154++int grtok = 0;
14155++stralloc grt = {0};
14156++struct constmap mapgrt;
14157++int fdmgrt;
14158++int brtlimit = 0;
14159+
14160+ void setup()
14161+ {
14162+@@ -117,6 +151,19 @@
14163+   if (bmfok)
14164+     if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
14165
14166++  grtok = control_readfile(&grt,"control/goodrcptto",0);
14167++  if (grtok == -1) die_control();
14168++  if (grtok)
14169++    if (!constmap_init(&mapgrt,grt.s,grt.len,0)) die_nomem();
14170++
14171++  fdmgrt = open_read("control/moregoodrcptto.cdb");
14172++  if (fdmgrt == -1) if (errno != error_noent) die_control();
14173++
14174++  if (control_readint(&brtlimit,"control/brtlimit") == -1) die_control();
14175++  x = env_get("BRTLIMIT");
14176++  if (x) { scan_ulong(x,&u); brtlimit = u; };
14177++  if (brtlimit <= 1) brtlimit = 2;
14178++
14179+   if (control_readint(&databytes,"control/databytes") == -1) die_control();
14180+   x = env_get("DATABYTES");
14181+   if (x) { scan_ulong(x,&u); databytes = u; }
14182+@@ -131,6 +178,7 @@
14183+   if (!remotehost) remotehost = "unknown";
14184+   remoteinfo = env_get("TCPREMOTEINFO");
14185+   relayclient = env_get("RELAYCLIENT");
14186++  acceptclient = env_get("ACCEPTCLIENT");
14187+   dohelo(remotehost);
14188+ }
14189+
14190+@@ -197,6 +245,16 @@
14191+   return 1;
14192+ }
14193+
14194++void err_brt(s1,s2,s3,s4) char *s1; char *s2; char *s3; char *s4; {
14195++  pid(); log(s1); log(s2); log(s3); log(s4); log(" by ");
14196++  log(remoteip); log(" (HELO "); log(helohost.s); log(").\n");
14197++}
14198++
14199++void die_attack() {
14200++  pid(); logs(" Too many bad recipients from ",remoteip,", closing connection.\n");
14201++  out("421 service shutting down and closing transmission channel (#4.3.0)\r\n"); flush(); _exit(1);
14202++}
14203++
14204+ int bmfcheck()
14205+ {
14206+   int j;
14207+@@ -208,6 +266,24 @@
14208+   return 0;
14209+ }
14210+
14211++int grtcheck()
14212++{
14213++  int g;
14214++  case_lowerb(addr.s,addr.len);
14215++  if (grtok) {
14216++    if (constmap(&mapgrt,addr.s,addr.len - 1)) return 1;
14217++    g = byte_rchr(addr.s,addr.len,'@');
14218++    if (g < addr.len)
14219++      if (constmap(&mapgrt,addr.s + g,addr.len - g - 1)) return 1; 
14220++  }
14221++  if (fdmgrt != -1) {
14222++    uint32 dlen;
14223++    g = cdb_seek(fdmgrt, addr.s, addr.len - 1, &dlen);
14224++    if (g) return g;
14225++  }
14226++  return 0;
14227++}
14228++
14229+ int addrallowed()
14230+ {
14231+   int r;
14232+@@ -221,6 +297,7 @@
14233+ int flagbarf; /* defined if seenmail */
14234+ stralloc mailfrom = {0};
14235+ stralloc rcptto = {0};
14236++int brtcount;
14237+
14238+ void smtp_helo(arg) char *arg;
14239+ {
14240+@@ -250,7 +327,10 @@
14241+ void smtp_rcpt(arg) char *arg; {
14242+   if (!seenmail) { err_wantmail(); return; }
14243+   if (!addrparse(arg)) { err_syntax(); return; }
14244+-  if (flagbarf) { err_bmf(); return; }
14245++  if (flagbarf) {
14246++    err_brt(" Bad envelope sender ",mailfrom.s," to ",addr.s);
14247++    err_bmf(); return;
14248++  }
14249+   if (relayclient) {
14250+     --addr.len;
14251+     if (!stralloc_cats(&addr,relayclient)) die_nomem();
14252+@@ -257,7 +337,23 @@
14253+     if (!stralloc_0(&addr)) die_nomem();
14254+   }
14255+   else
14256+-    if (!addrallowed()) { err_nogateway(); return; }
14257++    if (!addrallowed()) {
14258++      err_brt(" Bad recipient host ",addr.s," from ",mailfrom.s);
14259++      if (++brtcount == brtlimit) die_attack();
14260++      err_nogateway(); return;
14261++    }
14262++    else
14263++      if (!acceptclient) {
14264++        if (!grtcheck()) {
14265++          if (str_equal(mailfrom.s,"")) {
14266++            err_brt(" Forged recipient user ",addr.s," from ","null");
14267++          }
14268++          else
14269++            err_brt(" Bad recipient user ",addr.s," from ",mailfrom.s);
14270++          if (++brtcount == brtlimit) die_attack();
14271++          out("550 sorry, no mailbox here by that name (#5.1.1)\r\n"); return;
14272++        }
14273++      }
14274+   if (!stralloc_cats(&rcptto,"T")) die_nomem();
14275+   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
14276+   if (!stralloc_0(&rcptto)) die_nomem();
14277diff -ruN ../netqmail-1.06-original/hier.c netqmail-1.06/hier.c
14278--- ../netqmail-1.06-original/hier.c    1998-06-15 12:53:16.000000000 +0200
14279+++ netqmail-1.06/hier.c        2018-01-04 22:33:49.406411066 +0100
14280@@ -32,6 +32,7 @@
14281   h(auto_qmail,auto_uido,auto_gidq,0755);
14282 
14283   d(auto_qmail,"control",auto_uido,auto_gidq,0755);
14284+  d(auto_qmail,"control/cache",89,89,0755);
14285   d(auto_qmail,"users",auto_uido,auto_gidq,0755);
14286   d(auto_qmail,"bin",auto_uido,auto_gidq,0755);
14287   d(auto_qmail,"boot",auto_uido,auto_gidq,0755);
14288@@ -55,6 +56,8 @@
14289   d(auto_qmail,"queue/bounce",auto_uids,auto_gidq,0700);
14290 
14291   dsplit("queue/mess",auto_uidq,0750);
14292+  dsplit("queue/todo",auto_uidq,0750);
14293+  dsplit("queue/intd",auto_uidq,0700);
14294   dsplit("queue/info",auto_uids,0700);
14295   dsplit("queue/local",auto_uids,0700);
14296   dsplit("queue/remote",auto_uids,0700);
14297@@ -89,6 +92,7 @@
14298   c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
14299   c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
14300   c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
14301+  c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644);
14302   c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
14303   c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
14304   c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
14305@@ -104,13 +108,26 @@
14306   c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
14307   c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
14308   c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
14309+  c(auto_qmail,"bin","spawn-filter",auto_uido,auto_gidq,0711);
14310+  c(auto_qmail,"bin","surblfilter",auto_uido,auto_gidq,0711);
14311+  c(auto_qmail,"bin","dk-filter",auto_uido,auto_gidq,0555);
14312+  c(auto_qmail,"bin","dknewkey", auto_uido, auto_gidq, 0711);
14313+  c(auto_qmail,"bin","dktest",auto_uido,auto_gidq,0711);
14314+  c(auto_qmail,"bin","surblqueue",auto_uido,auto_gidq,0555);
14315   c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
14316   c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
14317   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
14318   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
14319+#ifdef EXTERNAL_TODO
14320+  c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711);
14321+#endif
14322+  c(auto_qmail,"bin","qmail-dk",auto_uidq,auto_gidq,0711);
14323+  c(auto_qmail,"bin","qmail-dkim",auto_uidq,auto_gidq,0711);
14324+  c(auto_qmail,"bin","dkim",auto_uidq,auto_gidq,0711);
14325   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
14326   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
14327   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
14328+  c(auto_qmail,"bin","qmail-newmvrt",auto_uido,auto_gidq,0700);
14329   c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
14330   c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
14331   c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
14332@@ -127,6 +144,7 @@
14333   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
14334   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
14335   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
14336+  c(auto_qmail,"bin","srsfilter",auto_uido,auto_gidq,0755);
14337   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
14338   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
14339   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
14340@@ -143,6 +161,9 @@
14341   c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
14342   c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
14343   c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
14344+#ifdef TLS
14345+  c(auto_qmail,"bin","update_tmprsadh",auto_uido,auto_gidq,0755);
14346+#endif
14347 
14348   c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
14349   c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
14350@@ -198,6 +219,18 @@
14351   c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644);
14352 
14353   c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644);
14354+  c(auto_qmail,"man/man8","qmail-dk.8",auto_uido,auto_gidq,0644);
14355+  c(auto_qmail,"man/cat8","qmail-dk.0",auto_uido,auto_gidq,0644);
14356+  c(auto_qmail,"man/man8","dkim.8",auto_uido,auto_gidq,0644);
14357+  c(auto_qmail,"man/man8","qmail-dkim.8",auto_uido,auto_gidq,0644);
14358+  c(auto_qmail,"man/cat8","qmail-dkim.0",auto_uido,auto_gidq,0644);
14359+  c(auto_qmail,"man/man8","dk-filter.8",auto_uido,auto_gidq,0644);
14360+  c(auto_qmail,"man/cat8","dk-filter.0",auto_uido,auto_gidq,0644);
14361+  c(auto_qmail,"man/man8","dktest.8",auto_uido,auto_gidq,0644);
14362+  c(auto_qmail,"man/man8","surblfilter.8",auto_uido,auto_gidq,0644);
14363+  c(auto_qmail,"man/cat8","surblfilter.0",auto_uido,auto_gidq,0644);
14364+  c(auto_qmail,"man/man8","spawn-filter.8",auto_uido,auto_gidq,0644);
14365+  c(auto_qmail,"man/cat8","spawn-filter.0",auto_uido,auto_gidq,0644);
14366   c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644);
14367   c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644);
14368   c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644);
14369diff -ruN ../netqmail-1.06-original/hmac_md5.c netqmail-1.06/hmac_md5.c
14370--- ../netqmail-1.06-original/hmac_md5.c        1970-01-01 01:00:00.000000000 +0100
14371+++ netqmail-1.06/hmac_md5.c    2016-11-22 21:03:57.114528032 +0100
14372@@ -0,0 +1,76 @@
14373+#include "global.h"
14374+#include "md5.h"
14375+
14376+/*
14377+** Function: hmac_md5
14378+*/
14379+
14380+void hmac_md5(text, text_len, key, key_len, digest)
14381+unsigned char*  text;                /* pointer to data stream */
14382+int             text_len;            /* length of data stream */
14383+unsigned char*  key;                 /* pointer to authentication key */
14384+int             key_len;             /* length of authentication key */
14385+unsigned char   *digest;              /* caller digest to be filled in */
14386+
14387+{
14388+        MD5_CTX context;
14389+        unsigned char k_ipad[65];    /* inner padding -
14390+                                      * key XORd with ipad
14391+                                      */
14392+        unsigned char k_opad[65];    /* outer padding -
14393+                                      * key XORd with opad
14394+                                      */
14395+        unsigned char tk[16];
14396+        int i;
14397+        /* if key is longer than 64 bytes reset it to key=MD5(key) */
14398+        if (key_len > 64) {
14399+
14400+                MD5_CTX      tctx;
14401+
14402+                MD5Init(&tctx);
14403+                MD5Update(&tctx, key, key_len);
14404+                MD5Final(tk, &tctx);
14405+
14406+                key = tk;
14407+                key_len = 16;
14408+        }
14409+
14410+        /*
14411+         * the HMAC_MD5 transform looks like:
14412+         *
14413+         * MD5(K XOR opad, MD5(K XOR ipad, text))
14414+         *
14415+         * where K is an n byte key
14416+         * ipad is the byte 0x36 repeated 64 times
14417+         * opad is the byte 0x5c repeated 64 times
14418+         * and text is the data being protected
14419+         */
14420+
14421+        /* start out by storing key in pads */
14422+        bzero( k_ipad, sizeof k_ipad);
14423+        bzero( k_opad, sizeof k_opad);
14424+        bcopy( key, k_ipad, key_len);
14425+        bcopy( key, k_opad, key_len);
14426+
14427+        /* XOR key with ipad and opad values */
14428+        for (i=0; i<64; i++) {
14429+                k_ipad[i] ^= 0x36;
14430+                k_opad[i] ^= 0x5c;
14431+        }
14432+        /*
14433+         * perform inner MD5
14434+         */
14435+        MD5Init(&context);                   /* init context for 1st pass */
14436+        MD5Update(&context, k_ipad, 64);      /* start with inner pad */
14437+        MD5Update(&context, text, text_len); /* then text of datagram */
14438+        MD5Final(digest, &context);          /* finish up 1st pass */
14439+        /*
14440+         * perform outer MD5
14441+         */
14442+        MD5Init(&context);                   /* init context for 2nd
14443+                                              * pass */
14444+        MD5Update(&context, k_opad, 64);     /* start with outer pad */
14445+        MD5Update(&context, digest, 16);     /* then results of 1st
14446+                                              * hash */
14447+        MD5Final(digest, &context);          /* finish up 2nd pass */
14448+}
14449diff -ruN ../netqmail-1.06-original/hmac_md5.h netqmail-1.06/hmac_md5.h
14450--- ../netqmail-1.06-original/hmac_md5.h        1970-01-01 01:00:00.000000000 +0100
14451+++ netqmail-1.06/hmac_md5.h    2016-11-22 21:03:57.114528032 +0100
14452@@ -0,0 +1,11 @@
14453+
14454+/* prototypes */
14455+
14456+void hmac_md5( unsigned char* text, int text_len, unsigned char* key, int key_len, unsigned char* digest);
14457+
14458+/* pointer to data stream */
14459+/* length of data stream */
14460+/* pointer to authentication key */
14461+/* length of authentication key */
14462+/* caller digest to be filled in */
14463+
14464diff -ruN ../netqmail-1.06-original/install-big.c netqmail-1.06/install-big.c
14465--- ../netqmail-1.06-original/install-big.c     1998-06-15 12:53:16.000000000 +0200
14466+++ netqmail-1.06/install-big.c 2016-11-22 21:04:38.822137319 +0100
14467@@ -89,6 +89,7 @@
14468   c(auto_qmail,"doc","TEST.receive",auto_uido,auto_gidq,0644);
14469   c(auto_qmail,"doc","REMOVE.sendmail",auto_uido,auto_gidq,0644);
14470   c(auto_qmail,"doc","REMOVE.binmail",auto_uido,auto_gidq,0644);
14471+  c(auto_qmail,"doc","README.qregex",auto_uido,auto_gidq,0644);
14472   c(auto_qmail,"doc","PIC.local2alias",auto_uido,auto_gidq,0644);
14473   c(auto_qmail,"doc","PIC.local2ext",auto_uido,auto_gidq,0644);
14474   c(auto_qmail,"doc","PIC.local2local",auto_uido,auto_gidq,0644);
14475@@ -108,9 +109,13 @@
14476   c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
14477   c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
14478   c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
14479+#ifdef EXTERNAL_TODO
14480+  c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711);
14481+#endif
14482   c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711);
14483   c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700);
14484   c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700);
14485+  c(auto_qmail,"bin","qmail-newmvrt",auto_uido,auto_gidq,0700);
14486   c(auto_qmail,"bin","qmail-pw2u",auto_uido,auto_gidq,0711);
14487   c(auto_qmail,"bin","qmail-inject",auto_uido,auto_gidq,0755);
14488   c(auto_qmail,"bin","predate",auto_uido,auto_gidq,0755);
14489@@ -133,6 +138,7 @@
14490   c(auto_qmail,"bin","qsmhook",auto_uido,auto_gidq,0755);
14491   c(auto_qmail,"bin","qbiff",auto_uido,auto_gidq,0755);
14492   c(auto_qmail,"bin","forward",auto_uido,auto_gidq,0755);
14493+  c(auto_qmail,"bin","srsfilter",auto_uido,auto_gidq,0755);
14494   c(auto_qmail,"bin","preline",auto_uido,auto_gidq,0755);
14495   c(auto_qmail,"bin","condredirect",auto_uido,auto_gidq,0755);
14496   c(auto_qmail,"bin","bouncesaying",auto_uido,auto_gidq,0755);
14497diff -ruN ../netqmail-1.06-original/ip.h netqmail-1.06/ip.h
14498--- ../netqmail-1.06-original/ip.h      1998-06-15 12:53:16.000000000 +0200
14499+++ netqmail-1.06/ip.h  2016-11-22 21:03:57.115527999 +0100
14500@@ -2,6 +2,7 @@
14501 #define IP_H
14502 
14503 struct ip_address { unsigned char d[4]; } ;
14504+typedef struct  ip_address ip_addr;
14505 
14506 extern unsigned int ip_fmt();
14507 #define IPFMT 19
14508diff -ruN ../netqmail-1.06-original/ipalloc.h netqmail-1.06/ipalloc.h
14509--- ../netqmail-1.06-original/ipalloc.h 1998-06-15 12:53:16.000000000 +0200
14510+++ netqmail-1.06/ipalloc.h     2016-11-22 21:03:57.115527999 +0100
14511@@ -3,7 +3,15 @@
14512 
14513 #include "ip.h"
14514 
14515+#ifdef TLS
14516+# define IX_FQDN 1
14517+#endif
14518+
14519+#ifdef IX_FQDN
14520+struct ip_mx { struct ip_address ip; int pref; char *fqdn; } ;
14521+#else
14522 struct ip_mx { struct ip_address ip; int pref; } ;
14523+#endif
14524 
14525 #include "gen_alloc.h"
14526 
14527diff -ruN ../netqmail-1.06-original/ipme.c netqmail-1.06/ipme.c
14528--- ../netqmail-1.06-original/ipme.c    2007-11-30 21:22:54.000000000 +0100
14529+++ netqmail-1.06/ipme.c        2016-11-22 21:03:57.115527999 +0100
14530@@ -14,23 +14,65 @@
14531 #include "ipalloc.h"
14532 #include "stralloc.h"
14533 #include "ipme.h"
14534+#include "substdio.h"
14535+#include "readwrite.h"
14536 
14537 static int ipmeok = 0;
14538 ipalloc ipme = {0};
14539+ipalloc ipme_mask = {0};
14540+ipalloc notipme = {0};
14541+ipalloc notipme_mask = {0};
14542 
14543 int ipme_is(ip)
14544 struct ip_address *ip;
14545 {
14546-  int i;
14547   if (ipme_init() != 1) return -1;
14548-  for (i = 0;i < ipme.len;++i)
14549-    if (byte_equal(&ipme.ix[i].ip,4,ip))
14550-      return 1;
14551-  return 0;
14552+  return ipme_match(&ipme,&ipme_mask,ip) > ipme_match(&notipme,&notipme_mask,ip);
14553 }
14554 
14555+int ipme_match(ipa, ipa_mask, ip)
14556+struct ipalloc *ipa, *ipa_mask;
14557+struct ip_address *ip;
14558+{
14559+  int i,j;
14560+  struct ip_address masked;
14561+  int masklen, longest_masklen=-1;
14562+
14563+  for(i=0;i < ipa->len;++i)
14564+  {
14565+    masklen = 0;
14566+    for(j=0;j<4;++j)
14567+    {
14568+      switch(ipa_mask->ix[i].ip.d[j])
14569+      {
14570+        case 255:  masklen += 8; break;
14571+        case 254:  masklen += 7; break;
14572+        case 252:  masklen += 6; break;
14573+        case 248:  masklen += 5; break;
14574+        case 240:  masklen += 4; break;
14575+        case 224:  masklen += 3; break;
14576+        case 192:  masklen += 2; break;
14577+        case 128:  masklen += 1; break;
14578+        default:   masklen += 0; break;
14579+      }
14580+      if (ipa->ix[i].ip.d[j] != (ip->d[j] & ipa_mask->ix[i].ip.d[j]))
14581+        break;
14582+    }
14583+    if ( (j == 4) && (masklen > longest_masklen) )
14584+    {
14585+      longest_masklen = masklen;
14586+    }
14587+  }
14588+  return longest_masklen;
14589+}
14590 static stralloc buf = {0};
14591 
14592+#define ipme_init_retclean(ret) { \
14593+  if (moreipme.ix) alloc_free(moreipme.ix); \
14594+  if (moreipme_mask.ix) alloc_free(moreipme_mask.ix); \
14595+  if (buf.s) alloc_free(buf.s); \
14596+  return ret; }
14597+   
14598 int ipme_init()
14599 {
14600   struct ifconf ifc;
14601@@ -39,23 +81,45 @@
14602   struct sockaddr_in *sin;
14603   int len;
14604   int s;
14605-  struct ip_mx ix;
14606-
14607+  struct ip_mx ix, ix_mask;
14608+  ipalloc moreipme = {0};
14609+  ipalloc moreipme_mask = {0};
14610+  int i;
14611+
14612   if (ipmeok) return 1;
14613-  if (!ipalloc_readyplus(&ipme,0)) return 0;
14614+  if (!ipalloc_readyplus(&ipme,0)) ipme_init_retclean(0);
14615+  if (!ipalloc_readyplus(&ipme_mask,0)) ipme_init_retclean(0);
14616+  if (!ipalloc_readyplus(&notipme,0)) ipme_init_retclean(0);
14617+  if (!ipalloc_readyplus(&notipme_mask,0)) ipme_init_retclean(0);
14618+  if (!ipalloc_readyplus(&moreipme,0)) ipme_init_retclean(0);
14619+  if (!ipalloc_readyplus(&moreipme_mask,0)) ipme_init_retclean(0);
14620+
14621   ipme.len = 0;
14622-  ix.pref = 0;
14623-
14624-  /* 0.0.0.0 is a special address which always refers to
14625-   * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.
14626+  ix.pref = ix_mask.pref = 0;
14627+
14628+  if (!ipme_readipfile(&notipme, &notipme_mask, "control/notipme")) ipme_init_retclean(0);
14629+
14630+  /* 127.0.0.0/255.0.0.0 is the localhost network.  Linux will treat
14631+     every address in this range as a local interface, even if it
14632+     isn't explicitly configured.
14633   */
14634+  byte_copy(&ix.ip,4,"\x7f\0\0\0");
14635+  byte_copy(&ix_mask.ip,4,"\xff\0\0\0");
14636+  if (!ipalloc_append(&ipme,&ix)) ipme_init_retclean(0);
14637+  if (!ipalloc_append(&ipme_mask,&ix_mask)) ipme_init_retclean(0);
14638+
14639+  /* 0.0.0.0 is a special address which always refers to
14640+   * "this host, this network", according to RFC 1122, Sec. 3.2.1.3a.  */
14641   byte_copy(&ix.ip,4,"\0\0\0\0");
14642-  if (!ipalloc_append(&ipme,&ix)) { return 0; }
14643-  if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
14644-
14645+  byte_copy(&ix_mask.ip,4,"\xff\xff\xff\xff");
14646+  if (!ipalloc_append(&ipme,&ix)) ipme_init_retclean(0);
14647+  if (!ipalloc_append(&ipme_mask,&ix_mask)) ipme_init_retclean(0);
14648+
14649+  if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) ipme_init_retclean(-1);
14650+
14651   len = 256;
14652   for (;;) {
14653-    if (!stralloc_ready(&buf,len)) { close(s); return 0; }
14654+    if (!stralloc_ready(&buf,len)) { close(s); ipme_init_retclean(0); }
14655     buf.len = 0;
14656     ifc.ifc_buf = buf.s;
14657     ifc.ifc_len = len;
14658@@ -64,7 +128,7 @@
14659         buf.len = ifc.ifc_len;
14660         break;
14661       }
14662-    if (len > 200000) { close(s); return -1; }
14663+    if (len > 200000) { close(s);  ipme_init_retclean(-1); }
14664     len += 100 + (len >> 2);
14665   }
14666   x = buf.s;
14667@@ -79,7 +143,10 @@
14668       byte_copy(&ix.ip,4,&sin->sin_addr);
14669       if (ioctl(s,SIOCGIFFLAGS,x) == 0)
14670         if (ifr->ifr_flags & IFF_UP)
14671-          if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
14672+        {
14673+          if (!ipalloc_append(&ipme,&ix)) { close(s);  ipme_init_retclean(0); }
14674+          if (!ipalloc_append(&ipme_mask,&ix_mask)) { close(s);  ipme_init_retclean(0); }
14675+        }
14676     }
14677 #else
14678     len = sizeof(*ifr);
14679@@ -89,12 +156,60 @@
14680          if (ifr->ifr_addr.sa_family == AF_INET) {
14681            sin = (struct sockaddr_in *) &ifr->ifr_addr;
14682            byte_copy(&ix.ip,4,&sin->sin_addr);
14683-           if (!ipalloc_append(&ipme,&ix)) { close(s); return 0; }
14684+            if (!ipalloc_append(&ipme,&ix)) { close(s);  ipme_init_retclean(0); }
14685+            if (!ipalloc_append(&ipme_mask,&ix_mask)) { close(s);  ipme_init_retclean(0); }
14686          }
14687 #endif
14688     x += len;
14689   }
14690   close(s);
14691+
14692+  if (!ipme_readipfile(&moreipme, &moreipme_mask, "control/moreipme"))  ipme_init_retclean(0);
14693+  for(i = 0;i < moreipme.len;++i)
14694+  {
14695+    if (!ipalloc_append(&ipme,&moreipme.ix[i])) ipme_init_retclean(0);
14696+    if (!ipalloc_append(&ipme_mask,&moreipme_mask.ix[i])) ipme_init_retclean(0);
14697+  }
14698   ipmeok = 1;
14699-  return 1;
14700+  ipme_init_retclean(1);
14701 }
14702+
14703+
14704+int ipme_readipfile(ipa, ipa_mask, fn)
14705+  ipalloc *ipa, *ipa_mask;
14706+  char *fn;
14707+{
14708+  int fd = -1;
14709+  char inbuf[1024];
14710+  substdio ss;
14711+  stralloc l = {0};
14712+  int match;
14713+  struct ip_mx ix, ix_mask;
14714+  int ret = 1;
14715+  int slash = 0;
14716+
14717+  if ( (fd = open_read(fn)) != -1) {
14718+    substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf));
14719+    while ( (getln(&ss,&l,&match,'\n') != -1) && (match || l.len) ) {
14720+      l.len--;
14721+      if (!stralloc_0(&l)) { ret = 0; break; }
14722+      if (l.s[slash=str_chr(l.s,'/')]!='\0')
14723+      {
14724+        l.s[slash]='\0';
14725+        if (!ip_scan(l.s+slash+1,&ix_mask.ip))
14726+          continue;
14727+      }
14728+      else
14729+        if (!ip_scan("255.255.255.255",&ix_mask.ip)) { ret = 0; break; }
14730+
14731+      if (!ip_scan(l.s, &ix.ip)) continue;
14732+      if (!ipalloc_append(ipa,&ix)) { ret = 0; break; }
14733+      if (!ipalloc_append(ipa_mask,&ix_mask.ip)) { ret = 0; break; }
14734+    }
14735+    if (l.s) alloc_free(l.s);
14736+    if ( (fd >= 0) && (close(fd) == -1) )
14737+      ret = 0;
14738+  }
14739+  return ret;
14740+}
14741+
14742diff -ruN ../netqmail-1.06-original/ipme.h netqmail-1.06/ipme.h
14743--- ../netqmail-1.06-original/ipme.h    1998-06-15 12:53:16.000000000 +0200
14744+++ netqmail-1.06/ipme.h        2016-11-22 21:03:57.115527999 +0100
14745@@ -4,7 +4,7 @@
14746 #include "ip.h"
14747 #include "ipalloc.h"
14748 
14749-extern ipalloc ipme;
14750+extern ipalloc ipme, ipme_mask, notipme, notipme_mask;
14751 
14752 extern int ipme_init();
14753 extern int ipme_is();
14754diff -ruN ../netqmail-1.06-original/ipmeprint.c netqmail-1.06/ipmeprint.c
14755--- ../netqmail-1.06-original/ipmeprint.c       1998-06-15 12:53:16.000000000 +0200
14756+++ netqmail-1.06/ipmeprint.c   2016-11-22 21:03:57.115527999 +0100
14757@@ -3,12 +3,15 @@
14758 #include "ip.h"
14759 #include "ipme.h"
14760 #include "exit.h"
14761+#include "auto_qmail.h"
14762 
14763 char temp[IPFMT];
14764 
14765 void main()
14766 {
14767- int j;
14768+ int j,k;
14769+
14770+ chdir(auto_qmail);
14771  switch(ipme_init())
14772   {
14773    case 0: substdio_putsflush(subfderr,"out of memory\n"); _exit(111);
14774@@ -17,8 +20,18 @@
14775  for (j = 0;j < ipme.len;++j)
14776   {
14777    substdio_put(subfdout,temp,ip_fmt(temp,&ipme.ix[j].ip));
14778-   substdio_puts(subfdout,"\n");
14779+   substdio_puts(subfdout,"/");
14780+   substdio_put(subfdout,temp,ip_fmt(temp,&ipme_mask.ix[j].ip));
14781+   substdio_puts(subfdout," is me\n");
14782+  }
14783+ for (j = 0;j < notipme.len;++j)
14784+  {
14785+   substdio_put(subfdout,temp,ip_fmt(temp,&notipme.ix[j].ip));
14786+   substdio_puts(subfdout,"/");
14787+   substdio_put(subfdout,temp,ip_fmt(temp,&notipme_mask.ix[j].ip));
14788+   substdio_puts(subfdout," is not me\n");
14789   }
14790+
14791  substdio_flush(subfdout);
14792  _exit(0);
14793 }
14794diff -ruN ../netqmail-1.06-original/ipmetest.c netqmail-1.06/ipmetest.c
14795--- ../netqmail-1.06-original/ipmetest.c        1970-01-01 01:00:00.000000000 +0100
14796+++ netqmail-1.06/ipmetest.c    2016-11-22 21:03:57.115527999 +0100
14797@@ -0,0 +1,38 @@
14798+#include "subfd.h"
14799+#include "substdio.h"
14800+#include "ip.h"
14801+#include "ipme.h"
14802+#include "exit.h"
14803+#include "auto_qmail.h"
14804+#include "env.h"
14805+
14806+void main(int argc, char *argv[])
14807+{
14808+  struct ip_address ip;
14809+
14810+  if (!env_get("IPMETEST_HERE"))
14811+    chdir(auto_qmail);
14812+
14813+  if (argc < 2)
14814+  {
14815+    substdio_puts(subfdout,"invalid usage\n");
14816+    substdio_flush(subfdout);
14817+    exit(1);
14818+  }
14819+  if (!ip_scan(argv[1],&ip))
14820+  {
14821+    substdio_puts(subfdout,"invalid IP address\n");
14822+    substdio_flush(subfdout);
14823+    exit(1);
14824+  }
14825+  if (ipme_is(&ip))
14826+  {
14827+    substdio_puts(subfdout,"me\n");
14828+  }
14829+  else
14830+  {
14831+    substdio_puts(subfdout,"not me\n");
14832+  }
14833+  substdio_flush(subfdout);
14834+  exit(0);
14835+}
14836diff -ruN ../netqmail-1.06-original/macros.h netqmail-1.06/macros.h
14837--- ../netqmail-1.06-original/macros.h  1970-01-01 01:00:00.000000000 +0100
14838+++ netqmail-1.06/macros.h      2016-11-22 21:03:57.115527999 +0100
14839@@ -0,0 +1,25 @@
14840+/*
14841+ * $Log: macros.h,v $
14842+ * Revision 1.1  2009-03-21 08:50:25+05:30  Cprogrammer
14843+ * Initial revision
14844+ *
14845+ *
14846+ * macros.h:  Useful macros
14847+ *
14848+ * Author:
14849+ *  Dick Porter (dick@ximian.com)
14850+ *
14851+ * (C) 2002 Ximian, Inc.
14852+ */
14853+
14854+#ifndef _WAPI_MACROS_H_
14855+#define _WAPI_MACROS_H_
14856+
14857+#include <sys/types.h>
14858+
14859+#define MAKEWORD(low, high) ((__uint16_t)(((__uint8_t)(low)) | \
14860+                                      ((__uint16_t)((__uint8_t)(high))) << 8))
14861+#define LOBYTE(i16) ((__uint8_t)((i16) & 0xFF))
14862+#define HIBYTE(i16) ((__uint8_t)(((__uint16_t)(i16) >> 8) & 0xFF))
14863+
14864+#endif                                                 /* _WAPI_MACROS_H_ */
14865diff -ruN ../netqmail-1.06-original/maildirflags.c netqmail-1.06/maildirflags.c
14866--- ../netqmail-1.06-original/maildirflags.c    1970-01-01 01:00:00.000000000 +0100
14867+++ netqmail-1.06/maildirflags.c        2016-11-22 21:03:57.116527965 +0100
14868@@ -0,0 +1,23 @@
14869+/*
14870+** Copyright 2000 Double Precision, Inc.
14871+** See COPYING for distribution information.
14872+*/
14873+
14874+#include       <sys/types.h>
14875+#include       <string.h>
14876+
14877+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
14878+
14879+int maildir_hasflag(const char *filename, char flag)
14880+{
14881+       const char *p=strrchr(filename, '/');
14882+
14883+       if (p)
14884+               filename=p+1;
14885+
14886+       p=strrchr(p, ':');
14887+       if (p && strncmp(p, ":2,", 3) == 0 &&
14888+           strchr(p+3, flag))
14889+               return (1);
14890+       return (0);
14891+}
14892diff -ruN ../netqmail-1.06-original/maildirgetquota.c netqmail-1.06/maildirgetquota.c
14893--- ../netqmail-1.06-original/maildirgetquota.c 1970-01-01 01:00:00.000000000 +0100
14894+++ netqmail-1.06/maildirgetquota.c     2016-11-22 21:03:57.116527965 +0100
14895@@ -0,0 +1,50 @@
14896+/*
14897+** Copyright 1998 - 2000 Double Precision, Inc.
14898+** See COPYING for distribution information.
14899+*/
14900+
14901+#include       "maildirgetquota.h"
14902+#include       "maildirmisc.h"
14903+#if    HAVE_UNISTD_H
14904+#include       <unistd.h>
14905+#endif
14906+#include       <stdlib.h>
14907+#include       <string.h>
14908+#include       <fcntl.h>
14909+#include       <sys/types.h>
14910+#include       <sys/stat.h>
14911+
14912+int    maildir_getquota(const char *dir, char buf[QUOTABUFSIZE])
14913+{
14914+char   *p;
14915+struct stat    stat_buf;
14916+int    n;
14917+int    l;
14918+
14919+       p=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
14920+       if (!p) return (-1);
14921+
14922+       strcat(strcpy(p, dir), "/maildirfolder");
14923+       if (stat(p, &stat_buf) == 0)
14924+       {
14925+               strcat(strcpy(p, dir), "/..");
14926+               n=maildir_getquota(p, buf);
14927+               free(p);
14928+               return (n);
14929+       }
14930+
14931+       strcat(strcpy(p, dir), "/maildirsize");
14932+       n=maildir_safeopen(p, O_RDONLY, 0);
14933+       free(p);
14934+       if (n < 0)      return (n);
14935+       if ((l=read(n, buf, QUOTABUFSIZE-1)) < 0)
14936+       {
14937+               close(n);
14938+               return (-1);
14939+       }
14940+       close(n);
14941+       for (n=0; n<l; n++)
14942+               if (buf[n] == '\n')     break;
14943+       buf[n]=0;
14944+       return (0);
14945+}
14946diff -ruN ../netqmail-1.06-original/maildirgetquota.h netqmail-1.06/maildirgetquota.h
14947--- ../netqmail-1.06-original/maildirgetquota.h 1970-01-01 01:00:00.000000000 +0100
14948+++ netqmail-1.06/maildirgetquota.h     2016-11-22 21:03:57.116527965 +0100
14949@@ -0,0 +1,30 @@
14950+#ifndef        maildirgetquota_h
14951+#define        maildirgetquota_h
14952+
14953+/*
14954+** Copyright 1998 - 1999 Double Precision, Inc.
14955+** See COPYING for distribution information.
14956+*/
14957+
14958+#if    HAVE_CONFIG_H
14959+#include       "config.h"
14960+#endif
14961+
14962+#include       <sys/types.h>
14963+#include       <stdio.h>
14964+
14965+#ifdef  __cplusplus
14966+extern "C" {
14967+#endif
14968+
14969+static const char maildirgetquota_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
14970+
14971+#define        QUOTABUFSIZE    256
14972+
14973+int maildir_getquota(const char *, char [QUOTABUFSIZE]);
14974+
14975+#ifdef  __cplusplus
14976+}
14977+#endif
14978+
14979+#endif
14980diff -ruN ../netqmail-1.06-original/maildirmisc.h netqmail-1.06/maildirmisc.h
14981--- ../netqmail-1.06-original/maildirmisc.h     1970-01-01 01:00:00.000000000 +0100
14982+++ netqmail-1.06/maildirmisc.h 2016-11-22 21:03:57.116527965 +0100
14983@@ -0,0 +1,145 @@
14984+#ifndef        maildirmisc_h
14985+#define        maildirmisc_h
14986+
14987+/*
14988+** Copyright 2000 Double Precision, Inc.
14989+** See COPYING for distribution information.
14990+*/
14991+
14992+#if    HAVE_CONFIG_H
14993+#include       "config.h"
14994+#endif
14995+
14996+#ifdef  __cplusplus
14997+extern "C" {
14998+#endif
14999+
15000+static const char maildirmisc_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
15001+
15002+/*
15003+**
15004+** Miscellaneous maildir-related code
15005+**
15006+*/
15007+
15008+/* Some special folders */
15009+
15010+#define        INBOX   "INBOX"
15011+#define        DRAFTS  "Drafts"
15012+#define        SENT    "Sent"
15013+#define        TRASH   "Trash"
15014+
15015+#define        SHAREDSUBDIR    "shared-folders"
15016+
15017+char *maildir_folderdir(const char *,          /* maildir */
15018+       const char *);                          /* folder name */
15019+       /* Returns the directory corresponding to foldername (foldername is
15020+       ** checked to make sure that it's a valid name, else we set errno
15021+       ** to EINVAL, and return (0).
15022+       */
15023+
15024+char *maildir_filename(const char *,           /* maildir */
15025+       const char *,                           /* folder */
15026+       const char *);                          /* filename */
15027+       /*
15028+       ** Builds the filename to this message, suitable for opening.
15029+       ** If the file doesn't appear to be there, search the maildir to
15030+       ** see if someone changed the flags, and return the current filename.
15031+       */
15032+
15033+int maildir_safeopen(const char *,             /* filename */
15034+       int,                            /* mode */
15035+       int);                           /* perm */
15036+
15037+/*
15038+**     Same arguments as open().  When we're accessing a shared maildir,
15039+**     prevent someone from playing cute and dumping a bunch of symlinks
15040+**     in there.  This function will open the indicate file only if the
15041+**     last component is not a symlink.
15042+**     This is implemented by opening the file with O_NONBLOCK (to prevent
15043+**     a DOS attack of someone pointing the symlink to a pipe, causing
15044+**     the open to hang), clearing O_NONBLOCK, then stat-int the file
15045+**     descriptor, lstating the filename, and making sure that dev/ino
15046+**     match.
15047+*/
15048+
15049+int maildir_semisafeopen(const char *, /* filename */
15050+       int,                            /* mode */
15051+       int);                           /* perm */
15052+
15053+/*
15054+** Same thing, except that we allow ONE level of soft link indirection,
15055+** because we're reading from our own maildir, which points to the
15056+** message in the sharable maildir.
15057+*/
15058+
15059+int maildir_mkdir(const char *);       /* directory */
15060+/*
15061+** Create maildir including all subdirectories in the path (like mkdir -p)
15062+*/
15063+
15064+void maildir_purgetmp(const char *);           /* maildir */
15065+       /* purges old stuff out of tmp */
15066+
15067+void maildir_purge(const char *,               /* directory */
15068+       unsigned);                              /* time_t to purge */
15069+
15070+void maildir_getnew(const char *,              /* maildir */
15071+       const char *);                          /* folder */
15072+       /* move messages from new to cur */
15073+
15074+int maildir_deletefolder(const char *,         /* maildir */
15075+       const char *);                          /* folder */
15076+       /* deletes a folder */
15077+
15078+int maildir_mddelete(const char *);    /* delete a maildir folder by path */
15079+
15080+void maildir_list_sharable(const char *,       /* maildir */
15081+       void (*)(const char *, void *),         /* callback function */
15082+       void *);                                /* 2nd arg to callback func */
15083+       /* list sharable folders */
15084+
15085+int maildir_shared_subscribe(const char *,     /* maildir */
15086+               const char *);                  /* folder */
15087+       /* subscribe to a shared folder */
15088+
15089+void maildir_list_shared(const char *,         /* maildir */
15090+       void (*)(const char *, void *),         /* callback function */
15091+       void *);                        /* 2nd arg to the callback func */
15092+       /* list subscribed folders */
15093+
15094+int maildir_shared_unsubscribe(const char *,   /* maildir */
15095+               const char *);                  /* folder */
15096+       /* unsubscribe from a shared folder */
15097+
15098+char *maildir_shareddir(const char *,          /* maildir */
15099+       const char *);                          /* folder */
15100+       /*
15101+       ** Validate and return a path to a shared folder.  folderdir must be
15102+       ** a name of a valid shared folder.
15103+       */
15104+
15105+void maildir_shared_sync(const char *);                /* maildir */
15106+       /* "sync" the shared folder */
15107+
15108+int maildir_sharedisro(const char *);          /* maildir */
15109+       /* maildir is a shared read-only folder */
15110+
15111+int maildir_unlinksharedmsg(const char *);     /* filename */
15112+       /* Remove a message from a shared folder */
15113+
15114+/* Internal function that reads a symlink */
15115+
15116+char *maildir_getlink(const char *);
15117+
15118+       /* Determine whether the maildir filename has a certain flag */
15119+
15120+int maildir_hasflag(const char *filename, char);
15121+
15122+#define        MAILDIR_DELETED(f)      maildir_hasflag((f), 'T')
15123+
15124+#ifdef  __cplusplus
15125+}
15126+#endif
15127+
15128+#endif
15129diff -ruN ../netqmail-1.06-original/maildiropen.c netqmail-1.06/maildiropen.c
15130--- ../netqmail-1.06-original/maildiropen.c     1970-01-01 01:00:00.000000000 +0100
15131+++ netqmail-1.06/maildiropen.c 2016-11-22 21:03:57.116527965 +0100
15132@@ -0,0 +1,133 @@
15133+/*
15134+** Copyright 2000 Double Precision, Inc.
15135+** See COPYING for distribution information.
15136+*/
15137+
15138+#if HAVE_CONFIG_H
15139+#include "config.h"
15140+#endif
15141+
15142+#include       <sys/types.h>
15143+#include       <sys/stat.h>
15144+#include       <string.h>
15145+#include       <stdlib.h>
15146+#include       <time.h>
15147+#if    HAVE_UNISTD_H
15148+#include       <unistd.h>
15149+#endif
15150+#include       <stdio.h>
15151+#include       <ctype.h>
15152+#include       <errno.h>
15153+#include       <fcntl.h>
15154+
15155+#include       "maildirmisc.h"
15156+
15157+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
15158+
15159+char *maildir_getlink(const char *filename)
15160+{
15161+#if     HAVE_READLINK
15162+size_t bufsiz;
15163+char   *buf;
15164+
15165+       bufsiz=0;
15166+       buf=0;
15167+
15168+       for (;;)
15169+       {
15170+       int     n;
15171+
15172+               if (buf)        free(buf);
15173+               bufsiz += 256;
15174+               if ((buf=malloc(bufsiz)) == 0)
15175+               {
15176+                       perror("malloc");
15177+                       return (0);
15178+               }
15179+               if ((n=readlink(filename, buf, bufsiz)) < 0)
15180+               {
15181+                       free(buf);
15182+                       return (0);
15183+               }
15184+               if (n < bufsiz)
15185+               {
15186+                       buf[n]=0;
15187+                       break;
15188+               }
15189+       }
15190+       return (buf);
15191+#else
15192+       return (0);
15193+#endif
15194+}
15195+
15196+int maildir_semisafeopen(const char *path, int mode, int perm)
15197+{
15198+
15199+#if    HAVE_READLINK
15200+
15201+char   *l=maildir_getlink(path);
15202+
15203+       if (l)
15204+       {
15205+       int     f;
15206+
15207+               if (*l != '/')
15208+               {
15209+               char    *q=malloc(strlen(path)+strlen(l)+2);
15210+               char    *s;
15211+
15212+                       if (!q)
15213+                       {
15214+                               free(l);
15215+                               return (-1);
15216+                       }
15217+
15218+                       strcpy(q, path);
15219+                       if ((s=strchr(q, '/')) != 0)
15220+                               s[1]=0;
15221+                       else    *q=0;
15222+                       strcat(q, l);
15223+                       free(l);
15224+                       l=q;
15225+               }
15226+
15227+               f=maildir_safeopen(l, mode, perm);
15228+
15229+               free(l);
15230+               return (f);
15231+       }
15232+#endif
15233+
15234+       return (maildir_safeopen(path, mode, perm));
15235+}
15236+               
15237+int maildir_safeopen(const char *path, int mode, int perm)
15238+{
15239+struct stat    stat1, stat2;
15240+
15241+int    fd=open(path, mode
15242+#ifdef O_NONBLOCK
15243+                       | O_NONBLOCK
15244+#else
15245+                       | O_NDELAY
15246+#endif
15247+                               , perm);
15248+
15249+       if (fd < 0)     return (fd);
15250+       if (fcntl(fd, F_SETFL, (mode & O_APPEND)) || fstat(fd, &stat1)
15251+           || lstat(path, &stat2))
15252+       {
15253+               close(fd);
15254+               return (-1);
15255+       }
15256+
15257+       if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino)
15258+       {
15259+               close(fd);
15260+               errno=ENOENT;
15261+               return (-1);
15262+       }
15263+
15264+       return (fd);
15265+}
15266diff -ruN ../netqmail-1.06-original/maildirparsequota.c netqmail-1.06/maildirparsequota.c
15267--- ../netqmail-1.06-original/maildirparsequota.c       1970-01-01 01:00:00.000000000 +0100
15268+++ netqmail-1.06/maildirparsequota.c   2016-11-22 21:03:57.116527965 +0100
15269@@ -0,0 +1,44 @@
15270+/*
15271+** Copyright 1998 - 1999 Double Precision, Inc.
15272+** See COPYING for distribution information.
15273+*/
15274+
15275+#if HAVE_CONFIG_H
15276+#include "config.h"
15277+#endif
15278+#include       "maildirquota.h"
15279+#include       <stdlib.h>
15280+#include       <string.h>
15281+
15282+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
15283+
15284+int maildir_parsequota(const char *n, unsigned long *s)
15285+{
15286+const char *o;
15287+int    yes;
15288+
15289+       if ((o=strrchr(n, '/')) == 0)   o=n;
15290+
15291+       for (; *o; o++)
15292+               if (*o == ':')  break;
15293+       yes=0;
15294+       for ( ; o >= n; --o)
15295+       {
15296+               if (*o == '/')  break;
15297+
15298+               if (*o == ',' && o[1] == 'S' && o[2] == '=')
15299+               {
15300+                       yes=1;
15301+                       o += 3;
15302+                       break;
15303+               }
15304+       }
15305+       if (yes)
15306+       {
15307+               *s=0;
15308+               while (*o >= '0' && *o <= '9')
15309+                       *s= *s*10 + (*o++ - '0');
15310+               return (0);
15311+       }
15312+       return (-1);
15313+}
15314diff -ruN ../netqmail-1.06-original/maildirquota.c netqmail-1.06/maildirquota.c
15315--- ../netqmail-1.06-original/maildirquota.c    1970-01-01 01:00:00.000000000 +0100
15316+++ netqmail-1.06/maildirquota.c        2016-11-22 21:03:57.116527965 +0100
15317@@ -0,0 +1,685 @@
15318+/*
15319+** Copyright 1998 - 2002 Double Precision, Inc.
15320+** See COPYING for distribution information.
15321+*/
15322+
15323+#if HAVE_CONFIG_H
15324+#include "config.h"
15325+#endif
15326+
15327+#include <sys/types.h>
15328+/* #if HAVE_DIRENT_H */
15329+#include <dirent.h>
15330+#define NAMLEN(dirent) strlen((dirent)->d_name)
15331+/* #else
15332+#define dirent direct
15333+#define NAMLEN(dirent) (dirent)->d_namlen
15334+#if HAVE_SYS_NDIR_H
15335+#include <sys/ndir.h>
15336+#endif
15337+#if HAVE_SYS_DIR_H
15338+#include <sys/dir.h>
15339+#endif
15340+#if HAVE_NDIR_H
15341+#include <ndir.h>
15342+#endif
15343+#endif */
15344+#include       <sys/types.h>
15345+/* #if HAVE_SYS_STAT_H */
15346+#include       <sys/stat.h>
15347+/* #endif */
15348+#include       <sys/uio.h>
15349+
15350+#include       "maildirquota.h"
15351+#include       "maildirmisc.h"
15352+#include       <stdio.h>
15353+#include       <stdlib.h>
15354+#include       <string.h>
15355+#include       <errno.h>
15356+/* #if HAVE_FCNTL_H */
15357+#include       <fcntl.h>
15358+/* #endif */
15359+#if    HAVE_UNISTD_H
15360+#include       <unistd.h>
15361+#endif
15362+#include       <time.h>
15363+#include       "numlib.h"
15364+
15365+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
15366+
15367+/* Read the maildirsize file */
15368+
15369+int maildirsize_read(const char *filename,     /* The filename */
15370+       int *fdptr,     /* Keep the file descriptor open */
15371+       off_t *sizeptr, /* Grand total of maildir size */
15372+       unsigned *cntptr, /* Grand total of message count */
15373+       unsigned *nlines, /* # of lines in maildirsize */
15374+       struct stat *statptr)   /* The stats on maildirsize */
15375+{
15376+char buf[5120];
15377+int f;
15378+char *p;
15379+unsigned l;
15380+int n;
15381+int first;
15382+
15383+       if ((f=maildir_safeopen(filename, O_RDWR|O_APPEND, 0)) < 0)
15384+               return (-1);
15385+       p=buf;
15386+       l=sizeof(buf);
15387+
15388+       while (l)
15389+       {
15390+               n=read(f, p, l);
15391+               if (n < 0)
15392+               {
15393+                       close(f);
15394+                       return (-1);
15395+               }
15396+               if (n == 0)     break;
15397+               p += n;
15398+               l -= n;
15399+       }
15400+       if (l == 0 || fstat(f, statptr))        /* maildir too big */
15401+       {
15402+               close(f);
15403+               return (-1);
15404+       }
15405+
15406+       *sizeptr=0;
15407+       *cntptr=0;
15408+       *nlines=0;
15409+       *p=0;
15410+       p=buf;
15411+       first=1;
15412+       while (*p)
15413+       {
15414+       long n=0;
15415+       int c=0;
15416+       char    *q=p;
15417+
15418+               while (*p)
15419+                       if (*p++ == '\n')
15420+                       {
15421+                               p[-1]=0;
15422+                               break;
15423+                       }
15424+
15425+               if (first)
15426+               {
15427+                       first=0;
15428+                       continue;
15429+               }
15430+               sscanf(q, "%ld %d", &n, &c);
15431+               *sizeptr += n;
15432+               *cntptr += c;
15433+               ++ *nlines;
15434+       }
15435+       *fdptr=f;
15436+       return (0);
15437+}
15438+
15439+static char *makenewmaildirsizename(const char *, int *);
15440+static int countcurnew(const char *, time_t *, off_t *, unsigned *);
15441+static int countsubdir(const char *, const char *,
15442+               time_t *, off_t *, unsigned *);
15443+static int statcurnew(const char *, time_t *);
15444+static int statsubdir(const char *, const char *, time_t *);
15445+
15446+#define        MDQUOTA_SIZE    'S'     /* Total size of all messages in maildir */
15447+#define        MDQUOTA_BLOCKS  'B'     /* Total # of blocks for all messages in
15448+                               maildir -- NOT IMPLEMENTED */
15449+#define        MDQUOTA_COUNT   'C'     /* Total number of messages in maildir */
15450+
15451+static int qcalc(off_t s, unsigned n, const char *quota, int *percentage)
15452+{
15453+off_t i;
15454+int    spercentage=0;
15455+int    npercentage=0;
15456+
15457+       errno=ENOSPC;
15458+       while (quota && *quota)
15459+       {
15460+               int x=1;
15461+
15462+               if (*quota < '0' || *quota > '9')
15463+               {
15464+                       ++quota;
15465+                       continue;
15466+               }
15467+               i=0;
15468+               while (*quota >= '0' && *quota <= '9')
15469+                       i=i*10 + (*quota++ - '0');
15470+               switch (*quota) {
15471+               default:
15472+                       if (i < s)
15473+                       {
15474+                               *percentage=100;
15475+                               return (-1);
15476+                       }
15477+
15478+                       /*
15479+                       ** For huge quotas, over 20mb,
15480+                       ** divide numerator & denominator by 1024 to prevent
15481+                       ** an overflow when multiplying by 100
15482+                       */
15483+
15484+                       x=1;
15485+                       if (i > 20000000) x=1024;
15486+
15487+                       spercentage = i ? (s/x) * 100 / (i/x):100;
15488+                       break;
15489+               case 'C':
15490+
15491+                       if (i < n)
15492+                       {
15493+                               *percentage=100;
15494+                               return (-1);
15495+                       }
15496+
15497+                       /* Ditto */
15498+
15499+                       x=1;
15500+                       if (i > 20000000) x=1024;
15501+
15502+                       npercentage = i ? ((off_t)n/x) * 100 / (i/x):100;
15503+                       break;
15504+               }
15505+       }
15506+       *percentage = spercentage > npercentage ? spercentage:npercentage;
15507+       return (0);
15508+}
15509+
15510+static int     doaddquota(const char *, int, const char *, long, int, int);
15511+
15512+static int docheckquota(const char *dir,
15513+       int *maildirsize_fdptr,
15514+       const char *quota_type,
15515+       long xtra_size,
15516+       int xtra_cnt, int *percentage);
15517+
15518+
15519+int maildir_checkquota(const char *dir,
15520+       int *maildirsize_fdptr,
15521+       const char *quota_type,
15522+       long xtra_size,
15523+       int xtra_cnt)
15524+{
15525+int    dummy;
15526+
15527+       return (docheckquota(dir, maildirsize_fdptr, quota_type,
15528+               xtra_size, xtra_cnt, &dummy));
15529+}
15530+
15531+int maildir_readquota(const char *dir, const char *quota_type)
15532+{
15533+int    percentage=0;
15534+int    fd=-1;
15535+
15536+       (void)docheckquota(dir, &fd, quota_type, 0, 0, &percentage);
15537+       if (fd >= 0)
15538+               close(fd);
15539+       return (percentage);
15540+}
15541+
15542+static int docheckquota(const char *dir,
15543+       int *maildirsize_fdptr,
15544+       const char *quota_type,
15545+       long xtra_size,
15546+       int xtra_cnt,
15547+       int *percentage)
15548+{
15549+char   *checkfolder=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
15550+char   *newmaildirsizename;
15551+struct stat stat_buf;
15552+int    maildirsize_fd;
15553+off_t  maildirsize_size;
15554+unsigned maildirsize_cnt;
15555+unsigned maildirsize_nlines;
15556+int    n;
15557+time_t tm;
15558+time_t maxtime;
15559+DIR    *dirp;
15560+struct dirent *de;
15561+
15562+       if (checkfolder == 0)   return (-1);
15563+       *maildirsize_fdptr= -1;
15564+       strcat(strcpy(checkfolder, dir), "/maildirfolder");
15565+       if (stat(checkfolder, &stat_buf) == 0)  /* Go to parent */
15566+       {
15567+               strcat(strcpy(checkfolder, dir), "/..");
15568+               n=docheckquota(checkfolder, maildirsize_fdptr,
15569+                       quota_type, xtra_size, xtra_cnt, percentage);
15570+               free(checkfolder);
15571+               return (n);
15572+       }
15573+       if (!quota_type || !*quota_type)        return (0);
15574+
15575+       strcat(strcpy(checkfolder, dir), "/maildirsize");
15576+       time(&tm);
15577+       if (maildirsize_read(checkfolder, &maildirsize_fd,
15578+               &maildirsize_size, &maildirsize_cnt,
15579+               &maildirsize_nlines, &stat_buf) == 0)
15580+       {
15581+               n=qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
15582+                       quota_type, percentage);
15583+
15584+               if (n == 0)
15585+               {
15586+                       free(checkfolder);
15587+                       *maildirsize_fdptr=maildirsize_fd;
15588+                       return (0);
15589+               }
15590+               close(maildirsize_fd);
15591+
15592+               if (maildirsize_nlines == 1 && tm < stat_buf.st_mtime + 15*60)
15593+                       return (n);
15594+       }
15595+
15596+       maxtime=0;
15597+       maildirsize_size=0;
15598+       maildirsize_cnt=0;
15599+
15600+       if (countcurnew(dir, &maxtime, &maildirsize_size, &maildirsize_cnt))
15601+       {
15602+               free(checkfolder);
15603+               return (-1);
15604+       }
15605+
15606+       dirp=opendir(dir);
15607+       while (dirp && (de=readdir(dirp)) != 0)
15608+       {
15609+               if (countsubdir(dir, de->d_name, &maxtime, &maildirsize_size,
15610+                       &maildirsize_cnt))
15611+               {
15612+                       free(checkfolder);
15613+                       closedir(dirp);
15614+                       return (-1);
15615+               }
15616+       }
15617+       if (dirp)
15618+       {
15619+#if    CLOSEDIR_VOID
15620+               closedir(dirp);
15621+#else
15622+               if (closedir(dirp))
15623+               {
15624+                       free(checkfolder);
15625+                       return (-1);
15626+               }
15627+#endif
15628+       }
15629+
15630+       newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
15631+       if (!newmaildirsizename)
15632+       {
15633+               free(checkfolder);
15634+               return (-1);
15635+       }
15636+
15637+       *maildirsize_fdptr=maildirsize_fd;
15638+
15639+       if (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
15640+               maildirsize_cnt, 1))
15641+       {
15642+               unlink(newmaildirsizename);
15643+               free(newmaildirsizename);
15644+               close(maildirsize_fd);
15645+               *maildirsize_fdptr= -1;
15646+               free(checkfolder);
15647+               return (-1);
15648+       }
15649+
15650+       strcat(strcpy(checkfolder, dir), "/maildirsize");
15651+
15652+       if (rename(newmaildirsizename, checkfolder))
15653+       {
15654+               /* free(checkfolder); */
15655+               unlink(newmaildirsizename);
15656+               close(maildirsize_fd);
15657+               *maildirsize_fdptr= -1;
15658+       }
15659+       free(checkfolder);
15660+       free(newmaildirsizename);
15661+
15662+       tm=0;
15663+
15664+       if (statcurnew(dir, &tm))
15665+       {
15666+               close(maildirsize_fd);
15667+               *maildirsize_fdptr= -1;
15668+               return (-1);
15669+       }
15670+
15671+       dirp=opendir(dir);
15672+       while (dirp && (de=readdir(dirp)) != 0)
15673+       {
15674+               if (statsubdir(dir, de->d_name, &tm))
15675+               {
15676+                       close(maildirsize_fd);
15677+                       *maildirsize_fdptr= -1;
15678+                       closedir(dirp);
15679+                       return (-1);
15680+               }
15681+       }
15682+       if (dirp)
15683+       {
15684+#if    CLOSEDIR_VOID
15685+               closedir(dirp);
15686+#else
15687+               if (closedir(dirp))
15688+               {
15689+                       close(maildirsize_fd);
15690+                       *maildirsize_fdptr= -1;
15691+                       return (-1);
15692+               }
15693+#endif
15694+       }
15695+
15696+       if (tm != maxtime)      /* Race condition, someone changed something */
15697+       {
15698+               errno=EAGAIN;
15699+               return (-1);
15700+       }
15701+
15702+       return (qcalc(maildirsize_size+xtra_size, maildirsize_cnt+xtra_cnt,
15703+               quota_type, percentage));
15704+}
15705+
15706+int    maildir_addquota(const char *dir, int maildirsize_fd,
15707+       const char *quota_type, long maildirsize_size, int maildirsize_cnt)
15708+{
15709+       if (!quota_type || !*quota_type)        return (0);
15710+       return (doaddquota(dir, maildirsize_fd, quota_type, maildirsize_size,
15711+                       maildirsize_cnt, 0));
15712+}
15713+
15714+static int doaddquota(const char *dir, int maildirsize_fd,
15715+       const char *quota_type, long maildirsize_size, int maildirsize_cnt,
15716+       int isnew)
15717+{
15718+union  {
15719+       char    buf[100];
15720+       struct stat stat_buf;
15721+       } u;                            /* Scrooge */
15722+char   *newname2=0;
15723+char   *newmaildirsizename=0;
15724+struct iovec   iov[3];
15725+int    niov;
15726+struct iovec   *p;
15727+int    n;
15728+
15729+       niov=0;
15730+       if ( maildirsize_fd < 0)
15731+       {
15732+               newname2=(char *)malloc(strlen(dir)+sizeof("/maildirfolder"));
15733+               if (!newname2)  return (-1);
15734+               strcat(strcpy(newname2, dir), "/maildirfolder");
15735+               if (stat(newname2, &u.stat_buf) == 0)
15736+               {
15737+                       strcat(strcpy(newname2, dir), "/..");
15738+                       n=doaddquota(newname2, maildirsize_fd, quota_type,
15739+                                       maildirsize_size, maildirsize_cnt,
15740+                                       isnew);
15741+                       free(newname2);
15742+                       return (n);
15743+               }
15744+
15745+               strcat(strcpy(newname2, dir), "/maildirsize");
15746+
15747+               if ((maildirsize_fd=maildir_safeopen(newname2,
15748+                       O_RDWR|O_APPEND, 0644)) < 0)
15749+               {
15750+                       newmaildirsizename=makenewmaildirsizename(dir, &maildirsize_fd);
15751+                       if (!newmaildirsizename)
15752+                       {
15753+                               free(newname2);
15754+                               return (-1);
15755+                       }
15756+
15757+                       maildirsize_fd=maildir_safeopen(newmaildirsizename,
15758+                               O_CREAT|O_RDWR|O_APPEND, 0644);
15759+
15760+                       if (maildirsize_fd < 0)
15761+                       {
15762+                               free(newname2);
15763+                               return (-1);
15764+                       }
15765+                       isnew=1;
15766+               }
15767+       }
15768+
15769+       if (isnew)
15770+       {
15771+               iov[0].iov_base=(caddr_t)quota_type;
15772+               iov[0].iov_len=strlen(quota_type);
15773+               iov[1].iov_base=(caddr_t)"\n";
15774+               iov[1].iov_len=1;
15775+               niov=2;
15776+       }
15777+
15778+
15779+       sprintf(u.buf, "%ld %d\n", maildirsize_size, maildirsize_cnt);
15780+       iov[niov].iov_base=(caddr_t)u.buf;
15781+       iov[niov].iov_len=strlen(u.buf);
15782+
15783+       p=iov;
15784+       ++niov;
15785+       n=0;
15786+       while (niov)
15787+       {
15788+               if (n)
15789+               {
15790+                       if (n < p->iov_len)
15791+                       {
15792+                               p->iov_base=
15793+                                       (caddr_t)((char *)p->iov_base + n);
15794+                               p->iov_len -= n;
15795+                       }
15796+                       else
15797+                       {
15798+                               n -= p->iov_len;
15799+                               ++p;
15800+                               --niov;
15801+                               continue;
15802+                       }
15803+               }
15804+
15805+               n=writev( maildirsize_fd, p, niov);
15806+
15807+               if (n <= 0)
15808+               {
15809+                       if (newname2)
15810+                       {
15811+                               close(maildirsize_fd);
15812+                               free(newname2);
15813+                       }
15814+                       return (-1);
15815+               }
15816+       }
15817+       if (newname2)
15818+       {
15819+               close(maildirsize_fd);
15820+
15821+               if (newmaildirsizename)
15822+               {
15823+                       rename(newmaildirsizename, newname2);
15824+                       free(newmaildirsizename);
15825+               }
15826+               free(newname2);
15827+       }
15828+       return (0);
15829+}
15830+
15831+/* New maildirsize is built in the tmp subdirectory */
15832+
15833+static char *makenewmaildirsizename(const char *dir, int *fd)
15834+{
15835+char   hostname[256];
15836+struct stat stat_buf;
15837+time_t t;
15838+char   *p;
15839+
15840+       hostname[0]=0;
15841+       hostname[sizeof(hostname)-1]=0;
15842+       gethostname(hostname, sizeof(hostname)-1);
15843+       p=(char *)malloc(strlen(dir)+strlen(hostname)+130);
15844+       if (!p) return (0);
15845+
15846+       for (;;)
15847+       {
15848+       char    tbuf[NUMBUFSIZE];
15849+       char    pbuf[NUMBUFSIZE];
15850+
15851+               time(&t);
15852+               strcat(strcpy(p, dir), "/tmp/");
15853+               sprintf(p+strlen(p), "%s.%s_NeWmAiLdIrSiZe.%s",
15854+                       str_time_t(t, tbuf),
15855+                       str_pid_t(getpid(), pbuf), hostname);
15856+
15857+               if (stat( (const char *)p, &stat_buf) < 0 &&
15858+                       (*fd=maildir_safeopen(p,
15859+                               O_CREAT|O_RDWR|O_APPEND, 0644)) >= 0)
15860+                       break;
15861+               sleep(3);
15862+       }
15863+       return (p);
15864+}
15865+
15866+static int statcurnew(const char *dir, time_t *maxtimestamp)
15867+{
15868+char   *p=(char *)malloc(strlen(dir)+5);
15869+struct stat    stat_buf;
15870+
15871+       if (!p) return (-1);
15872+       strcat(strcpy(p, dir), "/cur");
15873+       if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
15874+               *maxtimestamp=stat_buf.st_mtime;
15875+       strcat(strcpy(p, dir), "/new");
15876+       if ( stat(p, &stat_buf) == 0 && stat_buf.st_mtime > *maxtimestamp)
15877+               *maxtimestamp=stat_buf.st_mtime;
15878+       free(p);
15879+       return (0);
15880+}
15881+
15882+static int statsubdir(const char *dir, const char *subdir, time_t *maxtime)
15883+{
15884+char   *p;
15885+int    n;
15886+
15887+       if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
15888+               strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
15889+               return (0);
15890+
15891+       p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
15892+       if (!p) return (-1);
15893+       strcat(strcat(strcpy(p, dir), "/"), subdir);
15894+       n=statcurnew(p, maxtime);
15895+       free(p);
15896+       return (n);
15897+}
15898+
15899+static int docount(const char *, time_t *, off_t *, unsigned *);
15900+
15901+static int countcurnew(const char *dir, time_t *maxtime,
15902+       off_t *sizep, unsigned *cntp)
15903+{
15904+char   *p=(char *)malloc(strlen(dir)+5);
15905+int    n;
15906+
15907+       if (!p) return (-1);
15908+       strcat(strcpy(p, dir), "/new");
15909+       n=docount(p, maxtime, sizep, cntp);
15910+       if (n == 0)
15911+       {
15912+               strcat(strcpy(p, dir), "/cur");
15913+               n=docount(p, maxtime, sizep, cntp);
15914+       }
15915+       free(p);
15916+       return (n);
15917+}
15918+
15919+static int countsubdir(const char *dir, const char *subdir, time_t *maxtime,
15920+       off_t *sizep, unsigned *cntp)
15921+{
15922+char   *p;
15923+int    n;
15924+
15925+       if ( *subdir != '.' || strcmp(subdir, ".") == 0 ||
15926+               strcmp(subdir, "..") == 0 || strcmp(subdir, "." TRASH) == 0)
15927+               return (0);
15928+
15929+       p=(char *)malloc(strlen(dir)+strlen(subdir)+2);
15930+       if (!p) return (2);
15931+       strcat(strcat(strcpy(p, dir), "/"), subdir);
15932+       n=countcurnew(p, maxtime, sizep, cntp);
15933+       free(p);
15934+       return (n);
15935+}
15936+
15937+static int docount(const char *dir, time_t *dirstamp,
15938+       off_t *sizep, unsigned *cntp)
15939+{
15940+struct stat    stat_buf;
15941+char   *p;
15942+DIR    *dirp;
15943+struct dirent *de;
15944+unsigned long  s;
15945+
15946+       if (stat(dir, &stat_buf))       return (0);     /* Ignore */
15947+       if (stat_buf.st_mtime > *dirstamp)      *dirstamp=stat_buf.st_mtime;
15948+       if ((dirp=opendir(dir)) == 0)   return (0);
15949+       while ((de=readdir(dirp)) != 0)
15950+       {
15951+       const char *n=de->d_name;
15952+
15953+               if (*n == '.')  continue;
15954+
15955+               /* PATCH - do not count msgs marked as deleted */
15956+
15957+               for ( ; *n; n++)
15958+               {
15959+                       if (n[0] != ':' || n[1] != '2' ||
15960+                               n[2] != ',')    continue;
15961+                       n += 3;
15962+                       while (*n >= 'A' && *n <= 'Z')
15963+                       {
15964+                               if (*n == 'T')  break;
15965+                               ++n;
15966+                       }
15967+                       break;
15968+               }
15969+               if (*n == 'T')  continue;
15970+               n=de->d_name;
15971+
15972+
15973+               if (maildir_parsequota(n, &s) == 0)
15974+                       stat_buf.st_size=s;
15975+               else
15976+               {
15977+                       p=(char *)malloc(strlen(dir)+strlen(n)+2);
15978+                       if (!p)
15979+                       {
15980+                               closedir(dirp);
15981+                               return (-1);
15982+                       }
15983+                       strcat(strcat(strcpy(p, dir), "/"), n);
15984+                       if (stat(p, &stat_buf))
15985+                       {
15986+                               free(p);
15987+                               continue;
15988+                       }
15989+                       free(p);
15990+               }
15991+               *sizep += stat_buf.st_size;
15992+               ++*cntp;
15993+       }
15994+
15995+#if    CLOSEDIR_VOID
15996+       closedir(dirp);
15997+#else
15998+       if (closedir(dirp))
15999+               return (-1);
16000+#endif
16001+       return (0);
16002+}
16003diff -ruN ../netqmail-1.06-original/maildirquota.h netqmail-1.06/maildirquota.h
16004--- ../netqmail-1.06-original/maildirquota.h    1970-01-01 01:00:00.000000000 +0100
16005+++ netqmail-1.06/maildirquota.h        2016-11-22 21:03:57.117527932 +0100
16006@@ -0,0 +1,45 @@
16007+#ifndef        maildirquota_h
16008+#define        maildirquota_h
16009+
16010+/*
16011+** Copyright 1998 - 1999 Double Precision, Inc.
16012+** See COPYING for distribution information.
16013+*/
16014+
16015+#if    HAVE_CONFIG_H
16016+#include       "config.h"
16017+#endif
16018+
16019+#include       <sys/types.h>
16020+#include       <stdio.h>
16021+
16022+#ifdef  __cplusplus
16023+extern "C" {
16024+#endif
16025+
16026+static const char maildirquota_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
16027+
16028+int maildir_checkquota(const char *,   /* Pointer to directory */
16029+       int *,  /* Initialized to -1, or opened descriptor for maildirsize */
16030+       const char *,   /* The quota */
16031+       long,           /* Extra bytes planning to add/remove from maildir */
16032+       int);           /* Extra messages planning to add/remove from maildir */
16033+
16034+int maildir_addquota(const char *,     /* Pointer to the maildir */
16035+       int,    /* Must be the int pointed to by 2nd arg to checkquota */
16036+       const char *,   /* The quota */
16037+       long,   /* +/- bytes */
16038+       int);   /* +/- files */
16039+
16040+int maildir_readquota(const char *,    /* Directory */
16041+       const char *);                  /* Quota, from getquota */
16042+
16043+int maildir_parsequota(const char *, unsigned long *);
16044+       /* Attempt to parse file size encoded in filename.  Returns 0 if
16045+       ** parsed, non-zero if we didn't parse. */
16046+
16047+#ifdef  __cplusplus
16048+}
16049+#endif
16050+
16051+#endif
16052diff -ruN ../netqmail-1.06-original/md5.h netqmail-1.06/md5.h
16053--- ../netqmail-1.06-original/md5.h     1970-01-01 01:00:00.000000000 +0100
16054+++ netqmail-1.06/md5.h 2016-11-22 21:03:57.117527932 +0100
16055@@ -0,0 +1,49 @@
16056+/* MD5.H - header file for MD5C.C
16057+ */
16058+
16059+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
16060+   rights reserved.
16061+
16062+   License to copy and use this software is granted provided that it
16063+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
16064+   Algorithm" in all material mentioning or referencing this software
16065+   or this function.
16066+
16067+   License is also granted to make and use derivative works provided
16068+   that such works are identified as "derived from the RSA Data
16069+   Security, Inc. MD5 Message-Digest Algorithm" in all material
16070+   mentioning or referencing the derived work. 
16071+                                                                   
16072+   RSA Data Security, Inc. makes no representations concerning either
16073+   the merchantability of this software or the suitability of this
16074+   software for any particular purpose. It is provided "as is"
16075+   without express or implied warranty of any kind. 
16076+                                                                   
16077+   These notices must be retained in any copies of any part of this
16078+   documentation and/or software. 
16079+ */
16080+
16081+#ifndef _MD5_H_
16082+#define _MD5_H_ 1
16083+
16084+#ifdef __cplusplus
16085+extern "C" {
16086+#endif
16087+
16088+/* MD5 context. */
16089+typedef struct {
16090+  UINT4 state[4];                                   /* state (ABCD) */
16091+  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
16092+  unsigned char buffer[64];                         /* input buffer */
16093+} MD5_CTX;
16094+
16095+void MD5Init PROTO_LIST ((MD5_CTX *));
16096+void MD5Update PROTO_LIST
16097+  ((MD5_CTX *, unsigned char *, unsigned int));
16098+void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
16099+
16100+#ifdef __cplusplus
16101+}
16102+#endif
16103+
16104+#endif
16105diff -ruN ../netqmail-1.06-original/md5c.c netqmail-1.06/md5c.c
16106--- ../netqmail-1.06-original/md5c.c    1970-01-01 01:00:00.000000000 +0100
16107+++ netqmail-1.06/md5c.c        2016-11-22 21:03:57.117527932 +0100
16108@@ -0,0 +1,334 @@
16109+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
16110+ */
16111+
16112+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
16113+   rights reserved.
16114+
16115+   License to copy and use this software is granted provided that it
16116+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
16117+   Algorithm" in all material mentioning or referencing this software
16118+   or this function.
16119+
16120+   License is also granted to make and use derivative works provided
16121+   that such works are identified as "derived from the RSA Data
16122+   Security, Inc. MD5 Message-Digest Algorithm" in all material
16123+   mentioning or referencing the derived work. 
16124+                                                                   
16125+   RSA Data Security, Inc. makes no representations concerning either
16126+   the merchantability of this software or the suitability of this
16127+   software for any particular purpose. It is provided "as is"
16128+   without express or implied warranty of any kind. 
16129+                                                                   
16130+   These notices must be retained in any copies of any part of this
16131+   documentation and/or software. 
16132+ */
16133+
16134+#include "global.h"
16135+#include "md5.h"
16136+
16137+/* Constants for MD5Transform routine.
16138+ */
16139+#define S11 7
16140+#define S12 12
16141+#define S13 17
16142+#define S14 22
16143+#define S21 5
16144+#define S22 9
16145+#define S23 14
16146+#define S24 20
16147+#define S31 4
16148+#define S32 11
16149+#define S33 16
16150+#define S34 23
16151+#define S41 6
16152+#define S42 10
16153+#define S43 15
16154+#define S44 21
16155+
16156+static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
16157+static void Encode PROTO_LIST
16158+  ((unsigned char *, UINT4 *, unsigned int));
16159+static void Decode PROTO_LIST
16160+  ((UINT4 *, unsigned char *, unsigned int));
16161+static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
16162+static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
16163+
16164+static unsigned char PADDING[64] = {
16165+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16166+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16167+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
16168+};
16169+
16170+/* F, G, H and I are basic MD5 functions.
16171+ */
16172+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
16173+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
16174+#define H(x, y, z) ((x) ^ (y) ^ (z))
16175+#define I(x, y, z) ((y) ^ ((x) | (~z)))
16176+
16177+/* ROTATE_LEFT rotates x left n bits.
16178+ */
16179+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
16180+
16181+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
16182+   Rotation is separate from addition to prevent recomputation.
16183+ */
16184+#define FF(a, b, c, d, x, s, ac) { \
16185+    (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
16186+    (a) = ROTATE_LEFT ((a), (s)); \
16187+    (a) += (b); \
16188+  }
16189+#define GG(a, b, c, d, x, s, ac) { \
16190+    (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
16191+    (a) = ROTATE_LEFT ((a), (s)); \
16192+    (a) += (b); \
16193+  }
16194+#define HH(a, b, c, d, x, s, ac) { \
16195+    (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
16196+    (a) = ROTATE_LEFT ((a), (s)); \
16197+    (a) += (b); \
16198+  }
16199+#define II(a, b, c, d, x, s, ac) { \
16200+    (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
16201+    (a) = ROTATE_LEFT ((a), (s)); \
16202+    (a) += (b); \
16203+  }
16204+
16205+/* MD5 initialization. Begins an MD5 operation, writing a new context.
16206+ */
16207+void MD5Init (context)
16208+MD5_CTX *context;                                        /* context */
16209+{
16210+  context->count[0] = context->count[1] = 0;
16211+
16212+  /* Load magic initialization constants.
16213+   */
16214+  context->state[0] = 0x67452301;
16215+  context->state[1] = 0xefcdab89;
16216+  context->state[2] = 0x98badcfe;
16217+  context->state[3] = 0x10325476;
16218+}
16219+
16220+/* MD5 block update operation. Continues an MD5 message-digest
16221+     operation, processing another message block, and updating the
16222+     context.
16223+ */
16224+void MD5Update (context, input, inputLen)
16225+MD5_CTX *context;                                        /* context */
16226+unsigned char *input;                                /* input block */
16227+unsigned int inputLen;                     /* length of input block */
16228+{
16229+  unsigned int i, index, partLen;
16230+
16231+  /* Compute number of bytes mod 64 */
16232+  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
16233+
16234+  /* Update number of bits */
16235+  if ((context->count[0] += ((UINT4)inputLen << 3))
16236+      < ((UINT4)inputLen << 3))
16237+    context->count[1]++;
16238+  context->count[1] += ((UINT4)inputLen >> 29);
16239
16240+  partLen = 64 - index;
16241
16242+  /* Transform as many times as possible.
16243+   */
16244+  if (inputLen >= partLen) {
16245+    MD5_memcpy
16246+      ((POINTER)&context->buffer[index], (POINTER)input, partLen);
16247+    MD5Transform (context->state, context->buffer);
16248
16249+    for (i = partLen; i + 63 < inputLen; i += 64)
16250+      MD5Transform (context->state, &input[i]);
16251+   
16252+    index = 0;
16253+  }
16254+  else
16255+    i = 0;
16256
16257+  /* Buffer remaining input */
16258+  MD5_memcpy
16259+    ((POINTER)&context->buffer[index], (POINTER)&input[i],
16260+     inputLen-i);
16261+}
16262+
16263+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
16264+     the message digest and zeroizing the context.
16265+ */
16266+void MD5Final (digest, context)
16267+unsigned char digest[16];                         /* message digest */
16268+MD5_CTX *context;                                       /* context */
16269+{
16270+  unsigned char bits[8];
16271+  unsigned int index, padLen;
16272+
16273+  /* Save number of bits */
16274+  Encode (bits, context->count, 8);
16275+
16276+  /* Pad out to 56 mod 64.
16277+   */
16278+  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
16279+  padLen = (index < 56) ? (56 - index) : (120 - index);
16280+  MD5Update (context, PADDING, padLen);
16281
16282+  /* Append length (before padding) */
16283+  MD5Update (context, bits, 8);
16284+
16285+  /* Store state in digest */
16286+  Encode (digest, context->state, 16);
16287
16288+  /* Zeroize sensitive information.
16289+   */
16290+  MD5_memset ((POINTER)context, 0, sizeof (*context));
16291+}
16292+
16293+/* MD5 basic transformation. Transforms state based on block.
16294+ */
16295+static void MD5Transform (state, block)
16296+UINT4 state[4];
16297+unsigned char block[64];
16298+{
16299+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
16300
16301+  Decode (x, block, 64);
16302+
16303+  /* Round 1 */
16304+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
16305+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
16306+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
16307+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
16308+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
16309+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
16310+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
16311+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
16312+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
16313+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
16314+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
16315+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
16316+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
16317+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
16318+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
16319+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
16320+
16321+  /* Round 2 */
16322+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
16323+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
16324+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
16325+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
16326+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
16327+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
16328+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
16329+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
16330+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
16331+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
16332+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
16333+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
16334+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
16335+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
16336+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
16337+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
16338+
16339+  /* Round 3 */
16340+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
16341+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
16342+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
16343+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
16344+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
16345+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
16346+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
16347+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
16348+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
16349+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
16350+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
16351+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
16352+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
16353+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
16354+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
16355+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
16356+
16357+  /* Round 4 */
16358+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
16359+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
16360+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
16361+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
16362+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
16363+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
16364+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
16365+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
16366+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
16367+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
16368+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
16369+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
16370+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
16371+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
16372+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
16373+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
16374+
16375+  state[0] += a;
16376+  state[1] += b;
16377+  state[2] += c;
16378+  state[3] += d;
16379
16380+  /* Zeroize sensitive information.
16381+   */
16382+  MD5_memset ((POINTER)x, 0, sizeof (x));
16383+}
16384+
16385+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
16386+     a multiple of 4.
16387+ */
16388+static void Encode (output, input, len)
16389+unsigned char *output;
16390+UINT4 *input;
16391+unsigned int len;
16392+{
16393+  unsigned int i, j;
16394+
16395+  for (i = 0, j = 0; j < len; i++, j += 4) {
16396+    output[j] = (unsigned char)(input[i] & 0xff);
16397+    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
16398+    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
16399+    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
16400+  }
16401+}
16402+
16403+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
16404+     a multiple of 4.
16405+ */
16406+static void Decode (output, input, len)
16407+UINT4 *output;
16408+unsigned char *input;
16409+unsigned int len;
16410+{
16411+  unsigned int i, j;
16412+
16413+  for (i = 0, j = 0; j < len; i++, j += 4)
16414+    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
16415+      (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
16416+}
16417+
16418+/* Note: Replace "for loop" with standard memcpy if possible.
16419+ */
16420+static void MD5_memcpy (output, input, len)
16421+POINTER output;
16422+POINTER input;
16423+unsigned int len;
16424+{
16425+  unsigned int i;
16426
16427+  for (i = 0; i < len; i++)
16428+    output[i] = input[i];
16429+}
16430+
16431+/* Note: Replace "for loop" with standard memset if possible.
16432+ */
16433+static void MD5_memset (output, value, len)
16434+POINTER output;
16435+int value;
16436+unsigned int len;
16437+{
16438+  unsigned int i;
16439
16440+  for (i = 0; i < len; i++)
16441+    ((char *)output)[i] = (char)value;
16442+}
16443diff -ruN ../netqmail-1.06-original/mess822.h netqmail-1.06/mess822.h
16444--- ../netqmail-1.06-original/mess822.h 1970-01-01 01:00:00.000000000 +0100
16445+++ netqmail-1.06/mess822.h     2016-11-22 21:03:57.117527932 +0100
16446@@ -0,0 +1,55 @@
16447+/*
16448+ * $Log: mess822.h,v $
16449+ * Revision 1.3  2004-10-11 13:55:55+05:30  Cprogrammer
16450+ * added function prototypes
16451+ *
16452+ * Revision 1.2  2004-06-18 23:00:58+05:30  Cprogrammer
16453+ * added RCS log
16454+ *
16455+ */
16456+#ifndef MESS822_H
16457+#define MESS822_H
16458+#include "stralloc.h"
16459+#include "caltime.h"
16460+
16461+#define MESS822_HEADER { {0} }
16462+
16463+typedef struct
16464+{
16465+       struct caltime  ct;
16466+       int             known; /*- 0 for ct uninitialized; 1 if ok; 2 if ok and right zone */
16467+}
16468+mess822_time;
16469+
16470+typedef struct
16471+{
16472+       char           *name; /*- 0 means all names */
16473+       int            *flag;
16474+       stralloc       *copy;
16475+       stralloc       *value;
16476+       stralloc       *addr;
16477+       mess822_time   *when;
16478+}
16479+mess822_action;
16480+
16481+typedef struct
16482+{
16483+       stralloc        inprogress;
16484+       mess822_action *action;
16485+}
16486+mess822_header;
16487+
16488+int             mess822_quoteplus(stralloc *, char *, char *);
16489+int             mess822_quote(stralloc *, char *, char *);
16490+int             mess822_quotelist(stralloc *, stralloc *);
16491+int             mess822_fold(stralloc *, stralloc *, char *, int);
16492+int             mess822_date(stralloc *, mess822_time *);
16493+int             mess822_token(stralloc *, char *);
16494+int             mess822_addrlist(stralloc *, char *);
16495+int             mess822_when(mess822_time *, char *);
16496+int             mess822_begin(mess822_header *, mess822_action *);
16497+int             mess822_line(mess822_header *, stralloc *);
16498+int             mess822_end(mess822_header *);
16499+int             mess822_ok(stralloc *);
16500+
16501+#endif
16502diff -ruN ../netqmail-1.06-original/mess822_ok.c netqmail-1.06/mess822_ok.c
16503--- ../netqmail-1.06-original/mess822_ok.c      1970-01-01 01:00:00.000000000 +0100
16504+++ netqmail-1.06/mess822_ok.c  2016-11-22 21:03:57.117527932 +0100
16505@@ -0,0 +1,55 @@
16506+/*
16507+ * $Log: mess822_ok.c,v $
16508+ * Revision 1.2  2004-10-22 20:27:30+05:30  Cprogrammer
16509+ * added RCS id
16510+ *
16511+ * Revision 1.1  2004-01-04 23:17:51+05:30  Cprogrammer
16512+ * Initial revision
16513+ *
16514+ */
16515+#include "mess822.h"
16516+#include "byte.h"
16517+
16518+int
16519+mess822_ok(sa)
16520+       stralloc       *sa;
16521+{
16522+       int             i;
16523+       int             len;
16524+       int             colon;
16525+
16526+       len = sa->len;
16527+       if (len && (sa->s[len - 1] == '\n'))
16528+               --len;
16529+       if (!len)
16530+               return 0;
16531+       /*
16532+        * if input message is 822-compliant, will return 1 after this
16533+        */
16534+       if (sa->s[0] == ' ')
16535+               return 1;
16536+       if (sa->s[0] == '\t')
16537+               return 1;
16538+       colon = byte_chr(sa->s, sa->len, ':');
16539+       if (colon >= sa->len)
16540+               return 0;
16541+       while (colon && ((sa->s[colon - 1] == ' ') || (sa->s[colon - 1] == '\t')))
16542+               --colon;
16543+       if (!colon)
16544+               return 0;
16545+       for (i = 0; i < colon; ++i)
16546+               if (sa->s[i] < 33)
16547+                       return 0;
16548+       for (i = 0; i < colon; ++i)
16549+               if (sa->s[i] > 126)
16550+                       return 0;
16551+       return 1;
16552+}
16553+
16554+void
16555+getversion_mess822_ok_c()
16556+{
16557+       static char    *x = "$Id: mess822_ok.c,v 1.2 2004-10-22 20:27:30+05:30 Cprogrammer Stab mbhangui $";
16558+
16559+       x++;
16560+}
16561diff -ruN ../netqmail-1.06-original/numlib.h netqmail-1.06/numlib.h
16562--- ../netqmail-1.06-original/numlib.h  1970-01-01 01:00:00.000000000 +0100
16563+++ netqmail-1.06/numlib.h      2016-11-22 21:03:57.117527932 +0100
16564@@ -0,0 +1,45 @@
16565+#ifndef        numlib_h
16566+#define        numlib_h
16567+
16568+/*
16569+** Copyright 1998 - 1999 Double Precision, Inc.
16570+** See COPYING for distribution information.
16571+*/
16572+
16573+#ifdef __cplusplus
16574+extern "C" {
16575+#endif
16576+
16577+static const char numlib_h_rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
16578+
16579+#if    HAVE_CONFIG_H
16580+#include       "config.h"
16581+#endif
16582+
16583+#include       <sys/types.h>
16584+#include       <time.h>
16585+
16586+#define        NUMBUFSIZE      60
16587+
16588+/* Convert various system types to decimal */
16589+
16590+char   *str_time_t(time_t, char *);
16591+char   *str_off_t(off_t, char *);
16592+char   *str_pid_t(pid_t, char *);
16593+char   *str_ino_t(ino_t, char *);
16594+char   *str_uid_t(uid_t, char *);
16595+char   *str_gid_t(gid_t, char *);
16596+char   *str_size_t(size_t, char *);
16597+
16598+char   *str_sizekb(unsigned long, char *);     /* X Kb or X Mb */
16599+
16600+/* Convert selected system types to hex */
16601+
16602+char   *strh_time_t(time_t, char *);
16603+char   *strh_pid_t(pid_t, char *);
16604+char   *strh_ino_t(ino_t, char *);
16605+
16606+#ifdef __cplusplus
16607+}
16608+#endif
16609+#endif
16610diff -ruN ../netqmail-1.06-original/overmaildirquota.c netqmail-1.06/overmaildirquota.c
16611--- ../netqmail-1.06-original/overmaildirquota.c        1970-01-01 01:00:00.000000000 +0100
16612+++ netqmail-1.06/overmaildirquota.c    2016-11-22 21:03:57.117527932 +0100
16613@@ -0,0 +1,41 @@
16614+/*
16615+** Copyright 1998 - 1999 Double Precision, Inc.
16616+** See COPYING for distribution information.
16617+*/
16618+
16619+#if HAVE_CONFIG_H
16620+#include "config.h"
16621+#endif
16622+#include        "maildirquota.h"
16623+#include        <stdlib.h>
16624+#include        <string.h>
16625+#include        <errno.h>
16626+#include        <sys/stat.h>
16627+
16628+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
16629+
16630+int user_over_maildirquota( const char *dir, const char *q)
16631+{
16632+struct  stat    stat_buf;
16633+int     quotafd;
16634+int     ret_value;
16635+
16636+        if (fstat(0, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) &&
16637+                stat_buf.st_size > 0 && *q)
16638+        {
16639+                if (maildir_checkquota(dir, &quotafd, q, stat_buf.st_size, 1)
16640+                        && errno != EAGAIN)
16641+                {
16642+                        if (quotafd >= 0)       close(quotafd);
16643+                        ret_value = 1;
16644+                } else {
16645+                        maildir_addquota(dir, quotafd, q, stat_buf.st_size, 1);
16646+                        if (quotafd >= 0)       close(quotafd);
16647+                        ret_value = 0;
16648+                }
16649+        } else {
16650+                ret_value = 0;
16651+        }
16652+
16653+        return(ret_value);
16654+}
16655diff -ruN ../netqmail-1.06-original/policy.c netqmail-1.06/policy.c
16656--- ../netqmail-1.06-original/policy.c  1970-01-01 01:00:00.000000000 +0100
16657+++ netqmail-1.06/policy.c      2016-11-22 21:03:57.118527899 +0100
16658@@ -0,0 +1,1210 @@
16659+/*
16660+ * Copyright (C) 2005 Inter7 Internet Technologies, Inc.
16661+ *
16662+ * This program is free software; you can redistribute it and/or modify
16663+ * it under the terms of the GNU General Public License as published by
16664+ * the Free Software Foundation; either version 2 of the License, or
16665+ * (at your option) any later version.
16666+ *
16667+ * This program is distributed in the hope that it will be useful,
16668+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16669+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16670+ * GNU General Public License for more details.
16671+ *
16672+ * You should have received a copy of the GNU General Public License
16673+ * along with this program; if not, write to the Free Software
16674+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
16675+ *
16676+ * <matt@inter7.com>
16677+ * eMail Messaging Policy Framework
16678+ * http://www.inter7.com/?page=empf
16679+ *
16680+ */
16681+
16682+#include <stdio.h>
16683+#include <string.h>
16684+#include <errno.h>
16685+#ifndef _GNU_SOURCE
16686+   #define _GNU_SOURCE
16687+#endif
16688+#ifndef FNM_CASEFOLD
16689+   #include <ctype.h>
16690+#endif
16691+#include <fnmatch.h>
16692+#include "stralloc.h"
16693+#include "rcpthosts.h"
16694+#include "policy.h"
16695+
16696+/*
16697+   Policy delivery flags
16698+*/
16699+
16700+#define POLICY_F_NONE 0
16701+#define POLICY_F_ALLOW 1               /* Allow this delivery type   */
16702+#define POLICY_F_DISALLOW 2            /* Do not allow this delivery */
16703+#define POLICY_F_LOCAL 4               /* Local -> local delivery    */
16704+#define POLICY_F_REMOTE 8              /* Local -> remote delivery   */
16705+#define POLICY_F_EXTERNAL 16   /* Remote -> local delivery   */
16706+#define POLICY_F_INTERNAL 32    /* Local -> local delivery    */
16707+
16708+/*
16709+   Policy
16710+*/
16711+
16712+typedef struct __policy_ {
16713+   int flags,                          /* Allow or disallow */
16714+          numargs;                             /* Arguments read    */
16715+   char **args;                                /* Argument array    */
16716+
16717+   struct __policy_ *next;
16718+} policy_t;
16719+
16720+/*
16721+   Local delivery name
16722+*/
16723+
16724+typedef struct __local_ {
16725+   char *local;                                                /* Local name */
16726+   policy_t *policy;                           /* Policy         */
16727+
16728+   struct __local_ *next;
16729+} local_t;
16730+
16731+/*
16732+   Domain name
16733+*/
16734+
16735+typedef struct __domain_ {
16736+   char *domain;                                       /* Domain name */
16737+   policy_t *policy;                           /* Policy          */
16738+   local_t *locals;                                    /* Locals      */
16739+
16740+   struct __domain_ *next;
16741+} domain_t;
16742+
16743+extern char *remoteinfo;
16744+extern stralloc mailfrom;
16745+extern stralloc addr;
16746+extern void out(char *);
16747+
16748+static int policy_load(const char *);
16749+static int policy_parse(const char *, char *);
16750+static policy_t *policy_construct(char *);
16751+static int policy_construct_parse_arguments(char **, const int, char *);
16752+static int policy_match(const char *, const char *);
16753+static int policy_applies_to(const policy_t *, const char *);
16754+static int policy_flags(const char);
16755+static policy_t *policy_find(const policy_t *, const int);
16756+static int policy_forbids(void);
16757+static domain_t *domain_find(const char *);
16758+static local_t *local_find(const domain_t *, const char *);
16759+static void policy_free(policy_t *);
16760+static void local_free(local_t *);
16761+static void domains_free(void);
16762+
16763+static domain_t *domains = NULL, *s_domain = NULL, *r_domain = NULL;
16764+
16765+/*
16766+   Check policy for delivery restrictions
16767+*/
16768+
16769+int policy_check(void)
16770+{
16771+   int ret = 0;
16772+   const char *p = NULL;
16773+   
16774+   s_domain = r_domain = NULL;
16775+
16776+#ifdef POLICY_DEALLOCATE
16777+   domains_free();
16778+#endif
16779+
16780+   if (remoteinfo)
16781+         p = remoteinfo;
16782+   else
16783+         p = mailfrom.s;
16784+
16785+   fprintf(stderr, "policy_check: %s %s -> %s %s (%s)\n",
16786+                rcpthosts(p, strlen(p)) ? "local" : "remote", p,
16787+                rcpthosts(addr.s, strlen(addr.s)) ? "local" : "remote", addr.s,
16788+                remoteinfo ? "AUTHENTICATED SENDER" : "UNAUTHENTICATED SENDER");
16789+
16790+   /*
16791+         Load sender-related policy
16792+   */
16793+
16794+   for (; ((p) && (*p)); p++) {
16795+         if (*p == '@')
16796+                break;
16797+   }
16798+
16799+   if (*p) {
16800+         if (rcpthosts(remoteinfo ? remoteinfo : mailfrom.s,
16801+                          remoteinfo ? strlen(remoteinfo) : strlen(mailfrom.s))) {
16802+                ret = policy_load(p + 1);
16803+                if (!ret) {
16804+                       fprintf(stderr, "policy_check: policy_load failed\n");
16805+                       return -1;
16806+                }
16807+
16808+                s_domain = domain_find(p + 1);
16809+
16810+#ifdef POLICY_ENFORCE_AUTHENTICATION
16811+                /*
16812+                   This check is done here in the event that there is
16813+                       no policy for a domain.  In that event, we do not
16814+                       wish to enforce policy rules
16815+                */
16816+
16817+                if ((s_domain) && (remoteinfo == NULL)) {
16818+                       fprintf(stderr, "policy_check: sender not authenticated\n");
16819+                       return 0;
16820+                }
16821+#endif
16822+         }
16823+
16824+#ifdef DEBUG
16825+         else
16826+                fprintf(stderr, "policy_check: %s is not local\n", p + 1);
16827+#endif
16828+   }
16829+
16830+   /*
16831+         Load recipient-related policy
16832+   */
16833+
16834+   for (p = addr.s; ((p) && (*p)); p++) {
16835+         if (*p == '@')
16836+                break;
16837+   }
16838+
16839+   if (*p) {
16840+         if (rcpthosts(addr.s, strlen(addr.s))) {
16841+                ret = policy_load(p + 1);
16842+                if (!ret) {
16843+                       fprintf(stderr, "policy_check: policy_load failed\n");
16844+                       return -1;
16845+                }
16846+
16847+                r_domain = domain_find(p + 1);
16848+         }
16849+
16850+#ifdef DEBUG
16851+         else
16852+                fprintf(stderr, "policy_check: %s is not local\n", p + 1);
16853+#endif
16854+   }
16855+
16856+   /*
16857+         Policy enforcement
16858+   */
16859+
16860+   ret = policy_forbids();
16861+   if (ret == 1) {
16862+         fprintf(stderr, "policy_check: policy forbids transmission\n");
16863+         return 0;
16864+   }
16865+
16866+   else if (ret == 0) {
16867+         fprintf(stderr, "policy_check: policy allows transmission\n");
16868+         return 1;
16869+   }
16870+
16871+   fprintf(stderr, "policy_check: policy_forbids failed\n");
16872+   return -1;
16873+}
16874+
16875+/*
16876+   Load policy from policy file
16877+*/
16878+
16879+static int policy_load(const char *domain)
16880+{
16881+   size_t line = 0;
16882+   FILE *stream = NULL;
16883+   char b[4096] = { 0 };
16884+   int locald = 0, ret = 0;
16885+
16886+   if (domain == NULL)
16887+         return 1;
16888+
16889+   /*
16890+         See if we've already loaded this policy
16891+   */
16892+
16893+   if (domain_find(domain)) {
16894+#ifdef DEBUG
16895+         fprintf(stderr, "policy_load(%s): already loaded\n", domain);
16896+#endif
16897+         return 1;
16898+   }
16899+
16900+   stream = fopen(POLICY_FILENAME, "r");
16901+
16902+   /*
16903+         If policy file doesnt exist, allow all messaging
16904+         Otherwise trigger error
16905+   */
16906+
16907+   if (stream == NULL) {
16908+         if (errno != ENOENT) {
16909+                fprintf(stderr, "policy_load(%s): cannot read policy\n", domain);
16910+                return 0;
16911+         }
16912+
16913+#ifdef DEBUG
16914+         fprintf(stderr, "policy_load(%s): no policy\n", domain);
16915+#endif
16916+         return 1;
16917+   }
16918+
16919+   /*
16920+         Run through policy line-by-line
16921+   */
16922+
16923+   line = 1;
16924+
16925+   while(!(feof(stream))) {
16926+         memset(b, 0, sizeof(b));
16927+         fgets(b, sizeof(b), stream);
16928+
16929+         if ((*b == '#') || (*b == ';') || (*b == '\r') || (*b == '\n') || (*b == '\0'))
16930+                continue;
16931+
16932+         ret = policy_parse(domain, b);
16933+         if (ret == -1) {
16934+                fprintf(stderr, "policy_load(%s): policy_parse failed (line %d)\n", domain, line);
16935+                fclose(stream);
16936+                return 0;
16937+         }
16938+
16939+         else if (ret == 1)
16940+                break;
16941+
16942+         line++;
16943+   }
16944+
16945+   fclose(stream);
16946+
16947+#ifdef DEBUG
16948+   fprintf(stderr, "policy_load(%s): loaded\n", domain);
16949+#endif
16950+
16951+   return 1;
16952+}
16953+
16954+/*
16955+   Parse policy data
16956+*/
16957+
16958+static int policy_parse(const char *domain, char *data)
16959+{
16960+   int ia = 0;
16961+   domain_t *d = NULL;
16962+   policy_t *p = NULL, *lp = NULL;
16963+   local_t *l_list = NULL, *l = NULL;
16964+   char *h = NULL, *t = NULL, *pp = NULL;
16965+
16966+   if ((domain == NULL) || (data == NULL))
16967+         return -1;
16968+
16969+   /*
16970+         Seperate out domain part
16971+   */
16972+
16973+   for (h = t = data; *h; h++) {
16974+         if (*h == ':')
16975+                break;
16976+   }
16977+
16978+   if (*h != ':') {
16979+         fprintf(stderr, "policy_parse: syntax error: no domain seperator\n");
16980+         return -1;
16981+   }
16982+
16983+   *h = '\0';
16984+
16985+   /*
16986+         Determine if this is the correct policy
16987+   */
16988+
16989+   if (strcasecmp(domain, t))
16990+         return 0;
16991+
16992+   /*
16993+         Seperate domain policy
16994+   */
16995+
16996+   for (ia = 0, t = ++h; *h; h++) {
16997+         if ((*h == '(') && (ia == 0))
16998+                ia = 1;
16999+
17000+         else if ((*h == ')') && (ia == 1))
17001+                ia = 0;
17002+
17003+         else if ((*h == ',') && (ia == 0))
17004+                break;
17005+   }
17006+
17007+   if (*h != ',') {
17008+         fprintf(stderr, "policy_parse: syntax error: no domain policy seperator\n");
17009+         return -1;
17010+   }
17011+
17012+   *h = '\0';
17013+
17014+   p = policy_construct(t);
17015+   if (p == NULL) {
17016+         fprintf(stderr, "policy_parse: policy_construct failed\n");
17017+         return -1;
17018+   }
17019+
17020+   /*
17021+         Parse locals
17022+   */
17023+
17024+   h++;
17025+   l_list = l = NULL;
17026+
17027+   while(*h) {
17028+         if ((*h == '\n') || (*h == '\r'))
17029+                break;
17030+
17031+         for (ia = 0, t = h; *h; h++) {
17032+                if ((*h == '(') && (ia == 0))
17033+                       ia = 1;
17034+
17035+                else if ((*h == ')') && (ia == 1))
17036+                       ia = 0;
17037+
17038+                else if ((*h == ',') && (ia == 0))
17039+                       break;
17040+         }
17041+
17042+         if (*h != ',') {
17043+                policy_free(p);
17044+                fprintf(stderr, "policy_parse: syntax error: no local policy seperator\n");
17045+                return -1;
17046+         }
17047+
17048+         *h = '\0';
17049+
17050+         /*
17051+            Seperate local name from local policy
17052+         */
17053+
17054+         for (pp = t; *pp; pp++) {
17055+                if (*pp == ':')
17056+                       break;
17057+         }
17058+
17059+         if (*pp != ':') {
17060+                policy_free(p);
17061+                fprintf(stderr, "policy_parse: syntax error: no local name, policy seperator\n");
17062+                return -1;
17063+         }
17064+
17065+         *pp++ = '\0';
17066+
17067+         if (!(*t)) {
17068+                policy_free(p);
17069+                fprintf(stderr, "policy_parse: syntax error: empty local name\n");
17070+                return -1;
17071+         }
17072+
17073+         if ((!(*pp)) || (*pp == ',')) {
17074+                policy_free(p);
17075+                fprintf(stderr, "policy_parse: syntax error: empty local policy\n");
17076+                return -1;
17077+         }
17078+
17079+         /*
17080+            Load local policy
17081+         */
17082+
17083+         lp = policy_construct(pp);
17084+         if (lp == NULL) {
17085+                policy_free(p);
17086+                fprintf(stderr, "policy_parse: policy_construct failed\n");
17087+                return -1;
17088+         }
17089+
17090+         l = (local_t *)malloc(sizeof(local_t));
17091+         if (l == NULL) {
17092+                policy_free(p);
17093+                policy_free(lp);
17094+                fprintf(stderr, "policy_parse: malloc failed\n");
17095+                return -1;
17096+         }
17097+
17098+         memset(l, 0, sizeof(local_t));
17099+
17100+         ia = strlen(t);
17101+
17102+         l->local = (char *)malloc(ia + 1 + strlen(domain) + 1);
17103+         if (l->local == NULL) {
17104+                policy_free(p);
17105+                policy_free(lp);
17106+                free(l);
17107+                fprintf(stderr, "policy_parse: malloc failed\n");
17108+                return -1;
17109+         }
17110+
17111+         memset(l->local, 0, ia + 1 + strlen(domain) + 1);
17112+         memcpy(l->local, t, ia);
17113+         memcpy(l->local + ia, "@", 1);
17114+         memcpy(l->local + ia + 1, domain, strlen(domain));
17115+
17116+         l->policy = lp;
17117+         l->next = NULL;
17118+
17119+         l->next = l_list;
17120+         l_list = l;
17121+
17122+         t = ++h;
17123+   }
17124+
17125+   /*
17126+         Allocate and fill domain structure
17127+   */
17128+
17129+   d = (domain_t *)malloc(sizeof(domain_t));
17130+   if (d == NULL) {
17131+         policy_free(p);
17132+         local_free(l_list);
17133+         fprintf(stderr, "policy_parse: malloc failed\n");
17134+         return -1;
17135+   }
17136+
17137+   memset(d, 0, sizeof(domain_t));
17138+
17139+   ia = strlen(domain);
17140+
17141+   d->domain = (char *)malloc(ia + 1);
17142+   if (d->domain == NULL) {
17143+         policy_free(p);
17144+         local_free(l_list);
17145+         free(d);
17146+         fprintf(stderr, "policy_parse: malloc failed\n");
17147+         return -1;
17148+   }
17149+
17150+   memset(d->domain, 0, ia + 1);
17151+   memcpy(d->domain, domain, ia);
17152+
17153+   d->locals = l_list;
17154+   d->policy = p;
17155+
17156+   d->next = domains;
17157+   domains = d;
17158+
17159+   return 1;
17160+}
17161+
17162+/*
17163+   Parse policy data into a policy structure
17164+*/
17165+
17166+static policy_t *policy_construct(char *data)
17167+{
17168+   char pc = 0, **args = NULL;
17169+   policy_t *p_list = NULL, *p = NULL;
17170+   char *h = NULL, *t = NULL, *hp = NULL;
17171+   int flags = POLICY_F_NONE, numargs = 0, i = 0, ret = 0;
17172+
17173+   if (data == NULL)
17174+         return 0;
17175+
17176+   pc = 0;
17177+
17178+   for (h = data; *h; h++) {
17179+         pc = *h;
17180+
17181+         flags = policy_flags(*h);
17182+         if (flags == POLICY_F_NONE) {
17183+                fprintf(stderr, "policy_construct: unknown identifier\n");
17184+                return NULL;
17185+         }
17186+
17187+         args = NULL;
17188+
17189+         /*
17190+            Count, parse and allocate addresses
17191+         */
17192+
17193+         if (*(h + 1) == '(') {
17194+                t = (h + 2);
17195+
17196+                for (h += 2; *h; h++) {
17197+                       if (*h == ')')
17198+                          break;
17199+                }
17200+
17201+                if (*h != ')') {
17202+                       fprintf(stderr, "policy_construct: no terminating ')'\n");
17203+                       return 0;
17204+                }
17205+
17206+                numargs = 1;
17207+
17208+                for (hp = t; hp < h; hp++) {
17209+                       if (*hp == ',')
17210+                          numargs++;
17211+                }
17212+
17213+                /*
17214+                   No arguments
17215+                */
17216+
17217+                if (hp == t) {
17218+                       fprintf(stderr, "policy_construct: empty argument\n");
17219+                       return NULL;
17220+                }
17221+
17222+                args = (char **)malloc(sizeof(char *) * numargs);
17223+                if (args == NULL) {
17224+                       fprintf(stderr, "policy_construct: malloc failed\n");
17225+                       return NULL;
17226+                }
17227+
17228+                for (i = 0; i < numargs; i++)
17229+                       args[i] = NULL;
17230+
17231+                *h = '\0';
17232+
17233+                ret = policy_construct_parse_arguments(args, numargs, t);
17234+                if (!ret) {
17235+                       fprintf(stderr, "policy_construct: policy_construct_parse_arguments failed\n");
17236+                       free(args);
17237+                       return NULL;
17238+                }
17239+         }
17240+
17241+         /*
17242+            Allocate policy structure,
17243+                add to linked list
17244+         */
17245+
17246+         p = (policy_t *)malloc(sizeof(policy_t));
17247+         if (p == NULL) {
17248+                for (i = 0; i < numargs; i++)
17249+                       free(args[i]);
17250+
17251+                free(args);
17252+
17253+                fprintf(stderr, "policy_construct: malloc failed\n");
17254+                return NULL;
17255+         }
17256+
17257+         memset(p, 0, sizeof(policy_t));
17258+
17259+         p->numargs = numargs;
17260+         p->args = args;
17261+         p->flags = flags;
17262+         p->next = NULL;
17263+
17264+         p->next = p_list;
17265+         p_list = p;
17266+   }
17267+
17268+   return p_list;
17269+}
17270+
17271+/*
17272+   Parse policy arguments,
17273+   fill array
17274+*/
17275+
17276+static int policy_construct_parse_arguments(char **args, const int numargs, char *data)
17277+{
17278+   int len = 0, i = 0;
17279+   char *h = NULL, *t = NULL;
17280+
17281+   i = 0;
17282+
17283+   for (i = 0, h = t = data;;h++) {
17284+        if ((*h == ',') || (*h == '\0')) {
17285+               len = (h - t);
17286+
17287+                if (*h == '\0')
17288+                       h = NULL;
17289+                else
17290+                       *h = '\0';
17291+
17292+                if (!(*t)) {
17293+                       for (; i >= 0; i--)
17294+                          free(args[i]);
17295+
17296+                       fprintf(stderr, "policy_construct_parse_arguments: empty argument value\n");
17297+                       return 0;
17298+                }
17299+
17300+                if (i >= numargs) {
17301+                       for (; i >= 0; i--)
17302+                          free(args[i]);
17303+
17304+                       fprintf(stderr, "policy_construct_parse_arguments: too many arguments\n");
17305+                       return 0;
17306+                }
17307+
17308+                args[i] = (char *)malloc(len + 1);
17309+                if (args[i] == NULL) {
17310+                       for (; i >= 0; i--)
17311+                          free(args[i]);
17312+
17313+                       fprintf(stderr, "policy_construct_parse_arguments: malloc failed\n");
17314+                       return 0;
17315+                }
17316+
17317+                memset(args[i], 0, len + 1);
17318+                memcpy(args[i], t, len);
17319+
17320+                i++;
17321+
17322+                if (!h)
17323+                       break;
17324+
17325+                t = (h + 1);
17326+         }
17327+   }
17328+
17329+   if (i != numargs) {
17330+         fprintf(stderr, "policy_construct_parse_arguments: post argument count failed (%d/%d)\n",
17331+                       i, numargs);
17332+
17333+         for (; i >= 0; i--)
17334+                free(args[i]);
17335+
17336+         return 0;
17337+   }
17338+
17339+   return 1;
17340+}
17341+
17342+/*
17343+   Match a filter against an address string
17344+*/
17345+
17346+static int policy_match(const char *filter, const char *address)
17347+{
17348+   int ret = 0;
17349+
17350+   if ((filter == NULL) || (address == NULL))
17351+         return 0;
17352+
17353+#ifndef FNM_CASEFOLD
17354+   int len = 0, flags = 0;
17355+   char filt[POLICY_MAX_FILTER] = { 0 }, addr[POLICY_MAX_ADDRESS] = { 0 },
17356+               *p = NULL;
17357+
17358+   memset(filt, 0, sizeof(filt));
17359+
17360+   len = strlen(filter);
17361+   if (len >= POLICY_MAX_FILTER)
17362+         len = (POLICY_MAX_FILTER - 1);
17363+
17364+   memcpy(filt, filter, len);
17365+   
17366+   for (p = filt; *p; p++)
17367+         *p = tolower(*p);
17368+   
17369+   memset(addr, 0, sizeof(addr));
17370+
17371+   len = strlen(address);
17372+   if (len >= POLICY_MAX_ADDRESS)
17373+         len = (POLICY_MAX_ADDRESS - 1);
17374+
17375+   memcpy(addr, address, len);
17376+   
17377+   for (p = addr; *p; p++)
17378+         *p = tolower(*p);
17379+
17380+   filter = filt;
17381+   address = addr;
17382+#else
17383+   int flags = FNM_CASEFOLD;
17384+#endif
17385+   
17386+   ret = fnmatch(filter, address, flags);
17387+   if (ret == 0)
17388+         return 1;
17389+
17390+   return 0;
17391+}
17392+
17393+/*
17394+   See if a given policy applies to a particular
17395+   address
17396+*/
17397+
17398+static int policy_applies_to(const policy_t *p, const char *addr)
17399+{
17400+   int i = 0;
17401+
17402+   if ((p == NULL) || (addr == NULL))
17403+         return 0;
17404+
17405+   if (p->numargs == 0) {
17406+#ifdef DEBUG
17407+         fprintf(stderr, "policy_applies_to: no arguments (yes)\n");
17408+#endif
17409+         return 1;
17410+   }
17411+
17412+   if (p->args == NULL) {
17413+#ifdef DEBUG
17414+         fprintf(stderr, "policy_applies_to: broken arguments (no)\n");
17415+#endif
17416+         return 0;
17417+   }
17418+
17419+   for (i = 0; i < p->numargs; i++) {
17420+         if (policy_match(p->args[i], addr)) {
17421+#ifdef DEBUG
17422+                fprintf(stderr, "policy_applies_to: match (yes)\n");
17423+#endif
17424+                return 1;
17425+         }
17426+   }
17427+
17428+#ifdef DEBUG
17429+   fprintf(stderr, "policy_applies_to: no match (no)\n");
17430+#endif
17431+   return 0;
17432+}
17433+
17434+/*
17435+   Return flags for policy identifier
17436+*/
17437+
17438+static int policy_flags(const char c)
17439+{
17440+   int flags = POLICY_F_NONE;
17441+
17442+   switch(c) {
17443+         case 'l':
17444+                flags = (POLICY_F_LOCAL|POLICY_F_DISALLOW);
17445+                break;
17446+
17447+         case 'r':
17448+                flags = (POLICY_F_REMOTE|POLICY_F_DISALLOW);
17449+                break;
17450+
17451+         case 'e':
17452+                flags = (POLICY_F_EXTERNAL|POLICY_F_DISALLOW);
17453+                break;
17454+
17455+         case 'i':
17456+                flags = (POLICY_F_INTERNAL|POLICY_F_DISALLOW);
17457+                break;
17458+
17459+         case 'L':
17460+                flags = (POLICY_F_LOCAL|POLICY_F_ALLOW);
17461+                break;
17462+
17463+         case 'R':
17464+                flags = (POLICY_F_REMOTE|POLICY_F_ALLOW);
17465+                break;
17466+
17467+         case 'E':
17468+                flags = (POLICY_F_EXTERNAL|POLICY_F_ALLOW);
17469+                break;
17470+
17471+         case 'I':
17472+                flags = (POLICY_F_INTERNAL|POLICY_F_ALLOW);
17473+                break;
17474+   
17475+         default:
17476+                break;
17477+   };
17478+
17479+   return flags;
17480+}
17481+
17482+/*
17483+   Find a policy definition
17484+*/
17485+
17486+static policy_t *policy_find(const policy_t *sp, const int flag)
17487+{
17488+   for (; sp; sp = sp->next) {
17489+         if (sp->flags & flag)
17490+                return (policy_t *)sp;
17491+   }
17492+
17493+   return NULL;
17494+}
17495+
17496+/*
17497+   Compare policies and determine
17498+   if messaging is forbidden
17499+*/
17500+
17501+static int policy_forbids(void)
17502+{
17503+   policy_t *pl = NULL, *p = NULL;
17504+   local_t *s_local = NULL, *r_local = NULL;
17505+   int dtype = 0, s_forbid = 0, r_forbid = 0;
17506+
17507+   /*
17508+         Find local policy if any
17509+   */
17510+
17511+   if (s_domain)
17512+         s_local = local_find(s_domain, remoteinfo ? remoteinfo : mailfrom.s);
17513+
17514+   if (r_domain)
17515+         r_local = local_find(r_domain, addr.s);
17516+
17517+   /*
17518+         Determine type of delivery
17519+         (local, remote, external)
17520+   */
17521+
17522+   if ((s_domain) && (r_domain) && (s_domain == r_domain))
17523+         dtype = POLICY_F_LOCAL;
17524+
17525+   else if ((s_domain == NULL) && (r_domain))
17526+         dtype = POLICY_F_EXTERNAL;
17527+
17528+   else if ((s_domain) && (r_domain == NULL))
17529+         dtype = POLICY_F_REMOTE;
17530+
17531+   else if ((s_domain) && (r_domain) && (s_domain != r_domain))
17532+         dtype = POLICY_F_REMOTE;
17533+
17534+   else if ((s_domain == NULL) && (r_domain == NULL)) {
17535+#ifdef DEBUG
17536+         fprintf(stderr, "policy_forbids: no policies for this delivery\n");
17537+#endif
17538+         return 0;
17539+   }
17540+
17541+   else {
17542+         fprintf(stderr, "policy_forbids: unknown delivery type\n");
17543+         return -1;
17544+   }
17545+
17546+   p = NULL;
17547+   s_forbid = r_forbid = 0;
17548+
17549+   /*
17550+         If there is a local rule for sender, use that to
17551+         determine if able to send.  If not, check domain
17552+   */
17553+
17554+   if (s_local) {
17555+         p = policy_find(s_local->policy, dtype);
17556+         if (p) {
17557+                /*
17558+                   See if policy matches
17559+                */
17560+
17561+                if (p->flags & POLICY_F_DISALLOW) {
17562+                       if (policy_applies_to(p, addr.s)) {
17563+#ifdef DEBUG
17564+                          fprintf(stderr, "*** sender local policy disallows\n");
17565+#endif
17566+                          s_forbid = 1;
17567+                       }
17568+
17569+#ifdef DEBUG
17570+                       else
17571+                          fprintf(stderr, "*** sender local policy allows\n");
17572+#endif
17573+                }
17574+
17575+                else {
17576+#ifdef DEBUG
17577+                       if (policy_applies_to(p, addr.s))
17578+                          fprintf(stderr, "*** sender local policy allows\n");
17579+
17580+                       else {
17581+                          fprintf(stderr, "*** sender local policy denies\n");
17582+                          s_forbid = 1;
17583+                       }
17584+#else
17585+                       if (!(policy_applies_to(p, addr.s)))
17586+                                 s_forbid = 1;
17587+#endif
17588+                }
17589+         }
17590+
17591+#ifdef DEBUG
17592+         else
17593+                fprintf(stderr, "*** no sender local policy\n");
17594+#endif
17595+   }
17596+
17597+   if ((p == NULL) && (s_domain)) {
17598+         p = policy_find(s_domain->policy, dtype);
17599+         if (p) {
17600+                if (p->flags & POLICY_F_DISALLOW) {
17601+                       s_forbid = 1;
17602+#ifdef DEBUG
17603+                       fprintf(stderr, "--- sender domain policy disallows\n");
17604+#endif
17605+                }
17606+         }
17607+
17608+#ifdef DEBUG
17609+         else
17610+                fprintf(stderr, "--- no sender domain policy\n");
17611+#endif
17612+   }
17613+
17614+   /*
17615+         Deny messaging
17616+   */
17617+
17618+   if (s_forbid) {
17619+#ifdef DEBUG
17620+         fprintf(stderr, "policy_forbids: sender policy denies messaging\n");
17621+#endif
17622+         return 1;
17623+   }
17624+
17625+   /*
17626+         Reverse delivery type, and check same above
17627+         for recipient unless the recipient is of the
17628+         same domain
17629+   */
17630+
17631+   /*
17632+         A local user to local user delivery is an 'internal'
17633+         delivery for the recipient user.
17634+   */
17635+
17636+   if (dtype == POLICY_F_LOCAL)
17637+         dtype = POLICY_F_INTERNAL;
17638+
17639+   /*
17640+         Sender on same system, different domain.
17641+         This is considered an 'external' delivery
17642+         to the recipient
17643+   */
17644+
17645+   else if (dtype == POLICY_F_REMOTE)
17646+         dtype = POLICY_F_EXTERNAL;
17647+
17648+   /*
17649+         Sender from off-system remains
17650+         as external
17651+   */
17652+
17653+   else if (dtype != POLICY_F_EXTERNAL) {
17654+         fprintf(stderr, "policy_forbids: unknown recipient delivery type\n");
17655+         return -1;
17656+   }
17657+
17658+   p = NULL;
17659+   r_forbid = 0;
17660+
17661+   if (r_local) {
17662+         p = policy_find(r_local->policy, dtype);
17663+         if (p) {
17664+                /*
17665+                   See if policy matches
17666+                */
17667+
17668+                if (p->flags & POLICY_F_DISALLOW) {
17669+                       if (policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s)) {
17670+#ifdef DEBUG
17671+                          fprintf(stderr, "*** recipient local policy disallows\n");
17672+#endif
17673+                          r_forbid = 1;
17674+                       }
17675+
17676+#ifdef DEBUG
17677+                       else
17678+                          fprintf(stderr, "*** recipient local policy allows\n");
17679+#endif
17680+                }
17681+
17682+                else {
17683+#ifdef DEBUG
17684+                       if (policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s))
17685+                          fprintf(stderr, "*** recipient local policy allows\n");
17686+
17687+                       else {
17688+                          fprintf(stderr, "*** recipient local policy denies\n");
17689+                          r_forbid = 1;
17690+                       }
17691+#else
17692+                       if (!(policy_applies_to(p, remoteinfo ? remoteinfo : mailfrom.s)))
17693+                                 r_forbid = 1;
17694+#endif
17695+                }
17696+         }
17697+
17698+#ifdef DEBUG
17699+         else
17700+                fprintf(stderr, "*** no recipient local policy\n");
17701+#endif
17702+   }
17703+
17704+   if ((p == NULL) && (r_domain)) {
17705+         p = policy_find(r_domain->policy, dtype);
17706+         if (p) {
17707+                if (p->flags & POLICY_F_DISALLOW) {
17708+                       r_forbid = 1;
17709+#ifdef DEBUG
17710+                       fprintf(stderr, "--- recipient domain policy disallows\n");
17711+#endif
17712+                }
17713+         }
17714+
17715+#ifdef DEBUG
17716+         else
17717+                fprintf(stderr, "--- no recipient domain policy\n");
17718+#endif
17719+
17720+   }
17721+
17722+   /*
17723+         Deny messaging
17724+   */
17725+
17726+   if (r_forbid) {
17727+#ifdef DEBUG
17728+         fprintf(stderr, "policy_forbids: recipient policy denies messaging\n");
17729+#endif
17730+         return 1;
17731+   }
17732+
17733+   /*
17734+         Accept message
17735+   */
17736+
17737+   return 0;
17738+}
17739+
17740+/*
17741+   Search for a domain in the linked list of domains
17742+*/
17743+
17744+static domain_t *domain_find(const char *domain)
17745+{
17746+   domain_t *d = NULL;
17747+
17748+   if (domain == NULL)
17749+         return NULL;
17750+
17751+   for (d = domains; d; d = d->next) {
17752+         if (!(strcasecmp(d->domain, domain)))
17753+                return d;
17754+   }
17755+
17756+   return NULL;
17757+}
17758+
17759+/*
17760+   Search for a local under a domain in the locals linked list
17761+*/
17762+
17763+static local_t *local_find(const domain_t *d, const char *local)
17764+{
17765+   local_t *l = NULL;
17766+
17767+   if ((d == NULL) || (local == NULL)) {
17768+         fprintf(stderr, "local_find: null argument\n");
17769+         return NULL;
17770+   }
17771+
17772+   for (l = d->locals; l; l = l->next) {
17773+         if (policy_match(l->local, local))
17774+                return l;
17775+   }
17776+
17777+   return NULL;
17778+}
17779+
17780+/*
17781+   Deallocate a policy
17782+*/
17783+
17784+static void policy_free(policy_t *policy)
17785+{
17786+   int i = 0;
17787+   policy_t *p = NULL, *op = NULL;
17788+
17789+   if (policy == NULL)
17790+         return;
17791+
17792+   p = policy;
17793+
17794+   while(p) {
17795+         op = p;
17796+         p = p->next;
17797+
17798+         if (op->args) {
17799+                if (op->numargs) {
17800+                       for (i = 0; i < op->numargs; i++)
17801+                          free(op->args[i]);
17802+
17803+                       free(op->args);
17804+                }
17805+
17806+                else
17807+                       fprintf(stderr, "policy_free: no argument count\n");
17808+         }
17809+
17810+         free(op);
17811+   }
17812+}
17813+
17814+/*
17815+   Deallocate a local
17816+*/
17817+
17818+static void local_free(local_t *local)
17819+{
17820+   local_t *l = NULL, *ol = NULL;
17821+
17822+   if (local == NULL)
17823+         return;
17824+
17825+   l = local;
17826+
17827+   while(l) {
17828+         ol = l;
17829+         l = l->next;
17830+
17831+         if (ol->policy)
17832+                policy_free(ol->policy);
17833+
17834+         if (ol->local)
17835+                free(ol->local);
17836+
17837+         free(ol);
17838+   }
17839+}
17840+
17841+/*
17842+   Deallocate all domains
17843+*/
17844+
17845+static void domains_free(void)
17846+{
17847+   domain_t *d = NULL, *od = NULL;
17848+
17849+   d = domains;
17850+
17851+   while(d) {
17852+         od = d;
17853+         d = d->next;
17854+
17855+         if (od->policy)
17856+                policy_free(od->policy);
17857+
17858+         if (od->locals)
17859+                local_free(od->locals);
17860+
17861+         if (od->domain)
17862+                free(od->domain);
17863+
17864+         free(od);
17865+   }
17866+
17867+   domains = NULL;
17868+}
17869diff -ruN ../netqmail-1.06-original/policy.h netqmail-1.06/policy.h
17870--- ../netqmail-1.06-original/policy.h  1970-01-01 01:00:00.000000000 +0100
17871+++ netqmail-1.06/policy.h      2016-11-22 21:03:57.118527899 +0100
17872@@ -0,0 +1,6 @@
17873+#ifndef __POLICY_H_
17874+   #define __POLICY_H_
17875+
17876+int policy_check(void);
17877+
17878+#endif
17879diff -ruN ../netqmail-1.06-original/qmail-clean.c netqmail-1.06/qmail-clean.c
17880--- ../netqmail-1.06-original/qmail-clean.c     1998-06-15 12:53:16.000000000 +0200
17881+++ netqmail-1.06/qmail-clean.c 2016-11-22 21:03:57.118527899 +0100
17882@@ -73,22 +73,26 @@
17883    if (line.len < 7) { respond("x"); continue; }
17884    if (line.len > 100) { respond("x"); continue; }
17885    if (line.s[line.len - 1]) { respond("x"); continue; } /* impossible */
17886-   for (i = 5;i < line.len - 1;++i)
17887+   for (i = line.len - 2;i > 4;--i)
17888+    {
17889+     if (line.s[i] == '/') break;
17890      if ((unsigned char) (line.s[i] - '0') > 9)
17891       { respond("x"); continue; }
17892-   if (!scan_ulong(line.s + 5,&id)) { respond("x"); continue; }
17893+    }
17894+   if (line.s[i] == '/')
17895+     if (!scan_ulong(line.s + i + 1,&id)) { respond("x"); continue; }
17896    if (byte_equal(line.s,5,"foop/"))
17897     {
17898 #define U(prefix,flag) fmtqfn(fnbuf,prefix,id,flag); \
17899 if (unlink(fnbuf) == -1) if (errno != error_noent) { respond("!"); continue; }
17900-     U("intd/",0)
17901+     U("intd/",1)
17902      U("mess/",1)
17903      respond("+");
17904     }
17905    else if (byte_equal(line.s,4,"todo/"))
17906     {
17907-     U("intd/",0)
17908-     U("todo/",0)
17909+     U("intd/",1)
17910+     U("todo/",1)
17911      respond("+");
17912     }
17913    else
17914diff -ruN ../netqmail-1.06-original/qmail-control.9 netqmail-1.06/qmail-control.9
17915--- ../netqmail-1.06-original/qmail-control.9   1998-06-15 12:53:16.000000000 +0200
17916+++ netqmail-1.06/qmail-control.9       2016-11-28 17:36:57.350660496 +0100
17917@@ -20,7 +20,11 @@
17918 
17919 Comments are allowed
17920 in
17921+.IR badhelo ,
17922 .IR badmailfrom ,
17923+.IR badmailfromnorelay ,
17924+.IR badmailto ,
17925+.IR badmailtonorelay ,
17926 .IR locals ,
17927 .IR percenthack ,
17928 .IR qmqpservers ,
17929@@ -40,14 +44,25 @@
17930 .ta 5c 10c
17931 control        default used by
17932 
17933+.I authsender  \fR(none)       \fRqmail-remote
17934+.I badhelo     \fR(none)       \fRqmail-smtpd
17935+.I badhelonorelay     \fR(none)       \fRqmail-smtpd
17936 .I badmailfrom \fR(none)       \fRqmail-smtpd
17937+.I badmailfromnorelay  \fR(none)       \fRqmail-smtpd
17938+.I badmailto   \fR(none)       \fRqmail-smtpd
17939+.I badmailtonorelay    \fR(none)       \fRqmail-smtpd
17940 .I bouncefrom  \fRMAILER-DAEMON        \fRqmail-send
17941 .I bouncehost  \fIme   \fRqmail-send
17942+.I brtlimit    \fR0    \fRqmail-smtpd
17943+.I clientca.pem        \fR(none)       \fRqmail-smtpd
17944+.I clientcert.pem      \fR(none)       \fRqmail-remote
17945 .I concurrencylocal    \fR10   \fRqmail-send
17946 .I concurrencyremote   \fR20   \fRqmail-send
17947 .I defaultdomain       \fIme   \fRqmail-inject
17948 .I defaulthost \fIme   \fRqmail-inject
17949 .I databytes   \fR0    \fRqmail-smtpd
17950+.I dnsbllist   \fR(none)       \fRqmail-smtpd
17951+.I dh2048.pem  \fR(none)       \fRqmail-smtpd
17952 .I doublebouncehost    \fIme   \fRqmail-send
17953 .I doublebounceto      \fRpostmaster   \fRqmail-send
17954 .I envnoathost \fIme   \fRqmail-send
17955@@ -56,22 +71,35 @@
17956 .I localiphost \fIme   \fRqmail-smtpd
17957 .I locals      \fIme   \fRqmail-send
17958 .I morercpthosts       \fR(none)       \fRqmail-smtpd
17959+.I outgoingip  \fR0.0.0.0      \fRqmail-remote
17960 .I percenthack \fR(none)       \fRqmail-send
17961 .I plusdomain  \fIme   \fRqmail-inject
17962 .I qmqpservers \fR(none)       \fRqmail-qmqpc
17963 .I queuelifetime       \fR604800       \fRqmail-send
17964 .I rcpthosts   \fR(none)       \fRqmail-smtpd
17965+.I rsa2048.pem \fR(none)       \fRqmail-smtpd
17966+.I servercert.pem      \fR(none)       \fRqmail-smtpd
17967 .I smtpgreeting        \fIme   \fRqmail-smtpd
17968 .I smtproutes  \fR(none)       \fRqmail-remote
17969+.I spfbehavior \fR0    \fRqmail-smtpd
17970+.I spfexp      \fR(default)    \fRqmail-smtpd
17971+.I spfguess    \fR(none)       \fRqmail-smtpd
17972+.I spfrules    \fR(none)       \fRqmail-smtpd
17973+.I taps        \fR(none)       \fRqmail-queue
17974 .I timeoutconnect      \fR60   \fRqmail-remote
17975 .I timeoutremote       \fR1200 \fRqmail-remote
17976 .I timeoutsmtpd        \fR1200 \fRqmail-smtpd
17977+.I tlsclients  \fR(none)       \fRqmail-smtpd
17978+.I tlsclientciphers    \fR(none)       \fRqmail-remote
17979+.I tlshosts/FQDN.pem   \fR(none)       \fRqmail-remote
17980+.I tlsserverciphers    \fR(none)       \fRqmail-smtpd
17981 .I virtualdomains      \fR(none)       \fRqmail-send
17982 .fi
17983 .RE
17984 .SH "SEE ALSO"
17985 qmail-inject(8),
17986 qmail-qmqpc(8),
17987+qmail-queue(8),
17988 qmail-remote(8),
17989 qmail-send(8),
17990 qmail-showctl(8),
17991diff -ruN ../netqmail-1.06-original/qmail-dk.9 netqmail-1.06/qmail-dk.9
17992--- ../netqmail-1.06-original/qmail-dk.9        1970-01-01 01:00:00.000000000 +0100
17993+++ netqmail-1.06/qmail-dk.9    2018-04-03 14:46:51.371411632 +0200
17994@@ -0,0 +1,164 @@
17995+.TH qmail-dk 8
17996+.SH NAME
17997+qmail-dk \- sign/verify and queue a mail message for delivery
17998+.SH SYNOPSIS
17999+.B qmail-dk
18000+.SH DESCRIPTION
18001+.B qmail-dk
18002+has the same interface as
18003+.B qmail-queue
18004+except that it inserts an appropriate DomainKeys header before it
18005+queues the message.  To invoke
18006+.BR qmail-dk ,
18007+set QMAILQUEUE to point to qmail-dk in the environment when
18008+you send or receive email. qmail-dk will call
18009+.BR qmail-multi .
18010+To invoke an executable other than
18011+.B qmail-multi
18012+set DKQUEUE=bin/qmail-queue for example.
18013+
18014+.B qmail-dk
18015+supports DomainKey signing and verification.  It uses the libdomainkey
18016+and OpenSSL libraries.  To sign a message, set the
18017+.B DKSIGN
18018+environment variable to the pathname to the private key that will be
18019+used to sign the message.  If there is a % character in the environment
18020+variable, it is removed and replaced by the domain name in the From: header.
18021+If, after substituting the %, that file does not exist, the % will be removed.
18022+If the private key file does not exist and does not have a % character, the message will
18023+be rejected with error 32. The selector will be taken from the basename of the file.
18024+The private key should be created by
18025+.BR dknewkey(8) .
18026+
18027+In the absense of DKSIGN and DKVERIFY environment variable, qmail-dk will sign the
18028+message if RELAYCLIENT environment variable is set and verify the message if RELAYCLIENT
18029+environment variable is not set. If DKVERIFY is set, you can disable dk verification, if
18030+RELAYCLIENT is set by setting RELAYCLIENT_NODKVERIFY environment variable.
18031+
18032+You can set various DK options in getopt style, by setting the environment variable DKSIGNOPTIONS
18033+ b <advice_length>    Length of Advice
18034+ c <canonicalization> simple, nofws
18035+ s <privkeyfile>
18036+ h                    include h= tag
18037+ r                    Skip Duplicate Headers
18038+.EX
18039+ DKSIGNOPTIONS="-h -r -c nofws"
18040+ sets the h= tag, skips duplicate headers and sets nofws canonicalization
18041+.EE
18042+
18043+To verify a message, set the
18044+.B DKVERIFY
18045+environment variable to a desired set of letters.  Precisely, if you
18046+want a libdomainkey return status to generate an error, include that
18047+letter, where A is the first return status (DK_STAT_OK), B is the
18048+second (DK_STAT_BADSIG), etc.  The letter should be uppercase if you
18049+want a permanent error to be returned and lowercase if you want a temporary error
18050+to be returned.
18051+
18052+The complete set of letters with the corresponding return status is given below
18053+
18054+ A - DK_STAT_OK           - Function completed successfully
18055+ B - DK_STAT_BADSIG       - Signature was available but failed to verify against
18056+                            domain specified key
18057+ C - DK_STAT_NOSIG        - No signature available in message
18058+ D - DK_STAT_NOKEY        - No public key available (permanent failure)
18059+ E - DK_STAT_BADKEY       - Unusable key, public if verifying, private if signing
18060+ F - DK_STAT_CANTVRFY     - Cannot get domain key to verify signature
18061+                            (temporary failure)
18062+ G - DK_STAT_SYNTAX       - Message is not valid syntax. Signature could not be
18063+                            created/checked
18064+ H - DK_STAT_NORESOURCE   - Could not get critical resource (temporary failure)
18065+ I - DK_STAT_ARGS         - Arguments are not usable.
18066+ J - DK_STAT_REVOKED      - Key has been revoked.
18067+ K - DK_STAT_INTERNAL     - cannot call this routine in this context. Internal error.
18068+ L - DK_STAT_GRANULARITY  - Granularity mismatch: sender doesn't match g= option.
18069+ M - DK_STAT_DUPLICATE    - duplicate Domainkey-Signature in message.
18070+
18071+For example, if you want to permanently reject messages that have a
18072+signature that has been revoked, include the letter 'K' in the
18073+.B DKVERIFY
18074+environment variable.  A conservative set of letters is
18075+.BR DEGIJKfh .
18076+Reject permanently BADSIG, NOKEY, BADKEY, SYNTAX, ARGS, REVOKED, and
18077+INTERNAL errors, and temporarily CANTVRFY and NORESOURCE. Add in
18078+.B B
18079+if you want to reject messages that have a signature that doesn't
18080+verify (presumably because the message is a forgery or has been
18081+damaged in transit.  Note that
18082+.B qmail-dk
18083+always inserts the
18084+.B DomainKey-Status
18085+header, so that messages can be
18086+rejected at delivery time, or in the mail reader.
18087+
18088+Typically, you would sign messages generated on-host by setting
18089+.B DKSIGN
18090+in the environment before running an email program.  DKSIGN will be carried
18091+through qmail's sendmail emulation through
18092+.B qmail-inject
18093+to
18094+.BR qmail-dk .
18095+You would also set it for
18096+.B qmail-smtpd
18097+at the same time
18098+.B RELAYCLIENT
18099+is set, most often in the tcpserver cdb file.  If a host is authorized
18100+to relay, you probably want to sign messages sent by that host.
18101+.B DKVERIFY
18102+should be set for all other hosts.
18103+
18104+If neither
18105+.B DKSIGN
18106+nor
18107+.B DKVERIFY
18108+are set, then
18109+.B DKSIGN
18110+will be set to QMAILHOME/control/domainkeys/%/default. The % will be replaced by the domain in the
18111+From: header. If such a file does not exist, then it will be set to
18112+QMAILHOME/control/domainkeys/default. If such a private key exists, it will be used to sign the
18113+domain. You can also set \fBDKKEY\fR to chose a key different from
18114+QMAILHOME/control/domainkeys/%/default. \fBDKKEY\fR can also have % character that will be
18115+replaced by the domain in the From: header.
18116+
18117+By default
18118+.B qmail-dk
18119+will use all of the headers when signing a message.
18120+The environment variable
18121+.B DKEXCLUDEHEADERS
18122+may be set to a colon-separated list of headers that are to be excluded from signing.
18123+
18124+.B qmail-dk
18125+will ordinarily spawn
18126+.BR qmail-multi ,
18127+but if DKQUEUE is set in the environment,
18128+the program that it points to will be executed instead.
18129+
18130+.SH "EXIT CODES"
18131+.B qmail-dk
18132+returns the same exit codes as qmail-queue with these additions:
18133+.TP 5
18134+.B 35
18135+The private key file does not exist.
18136+.TP 5
18137+.B 57
18138+Trouble waiting for qmail-queue to exit.
18139+.TP 5
18140+.B 58
18141+Unable to vfork.
18142+.TP 5
18143+.B 59
18144+Unable to create a pipe to qmail-queue.
18145+.SH "SEE ALSO"
18146+addresses(5),
18147+envelopes(5),
18148+qmail-header(5),
18149+dknewkey(8),
18150+dktest(8),
18151+qmail-inject(8),
18152+qmail-qmqpc(8),
18153+qmail-queue(8),
18154+qmail-send(8),
18155+qmail-smtpd(8),
18156+qmail-dkim(8),
18157+domain-keys(5)
18158+
18159diff -ruN ../netqmail-1.06-original/qmail-dk.c netqmail-1.06/qmail-dk.c
18160--- ../netqmail-1.06-original/qmail-dk.c        1970-01-01 01:00:00.000000000 +0100
18161+++ netqmail-1.06/qmail-dk.c    2018-04-03 14:46:51.372411636 +0200
18162@@ -0,0 +1,866 @@
18163+/*
18164+ * $Log: qmail-dk.c,v $
18165+ * Revision 1.37  2013-01-24 22:42:07+05:30  Cprogrammer
18166+ * alternate code for chosing DKSIGN selector filename
18167+ *
18168+ * Revision 1.36  2011-11-10 14:31:42+05:30  Cprogrammer
18169+ * BUG ssout to be assigned only after pidopen()
18170+ *
18171+ * Revision 1.35  2011-11-07 09:35:25+05:30  Cprogrammer
18172+ * set ssout, sserr, ssin before executing other functions
18173+ *
18174+ * Revision 1.34  2011-07-29 09:28:48+05:30  Cprogrammer
18175+ * fixed key file name
18176+ *
18177+ * Revision 1.33  2011-07-28 19:34:45+05:30  Cprogrammer
18178+ * BUG - fixed opening of private key with absolute path
18179+ *
18180+ * Revision 1.32  2011-07-22 19:29:06+05:30  Cprogrammer
18181+ * fixed compilation error
18182+ *
18183+ * Revision 1.31  2011-07-22 14:39:53+05:30  Cprogrammer
18184+ * added DKDOMAIN feature to set d= tag
18185+ *
18186+ * Revision 1.30  2011-06-04 17:44:16+05:30  Cprogrammer
18187+ * remove % sign from private key if file not found
18188+ *
18189+ * Revision 1.29  2010-07-21 08:59:14+05:30  Cprogrammer
18190+ * use CONTROLDIR environment variable instead of a hardcoded control directory
18191+ *
18192+ * Revision 1.28  2009-08-13 18:36:39+05:30  Cprogrammer
18193+ * for verification continue in case of DK_SYNTAX errors
18194+ *
18195+ * Revision 1.27  2009-04-22 13:42:47+05:30  Cprogrammer
18196+ * made fd for custom error configurable through env variable ERROR_FD
18197+ *
18198+ * Revision 1.26  2009-04-21 09:05:14+05:30  Cprogrammer
18199+ * return relevant error message for reading private key
18200+ *
18201+ * Revision 1.25  2009-04-21 08:56:03+05:30  Cprogrammer
18202+ * return temp errors for temporary failures
18203+ *
18204+ * Revision 1.24  2009-04-21 08:15:39+05:30  Cprogrammer
18205+ * moved stralloc variable outside block
18206+ *
18207+ * Revision 1.23  2009-04-20 22:20:40+05:30  Cprogrammer
18208+ * added DKSIGNOPTIONS
18209+ *
18210+ * Revision 1.22  2009-04-07 11:38:02+05:30  Cprogrammer
18211+ * use TMPDIR env variable for tmp directory
18212+ *
18213+ * Revision 1.21  2009-04-05 12:52:10+05:30  Cprogrammer
18214+ * added preprocessor warning
18215+ *
18216+ * Revision 1.20  2009-03-31 08:21:12+05:30  Cprogrammer
18217+ * set dksign when RELAYCLIENT is defined when both dksign, dkverify are undefined
18218+ *
18219+ * Revision 1.19  2009-03-28 22:26:35+05:30  Cprogrammer
18220+ * use DKSIGN,DKVERIFY env variables if RELAYCLIENT is not set
18221+ *
18222+ * Revision 1.18  2009-03-28 22:02:32+05:30  Cprogrammer
18223+ * removed extra white space
18224+ *
18225+ * Revision 1.17  2009-03-28 11:34:51+05:30  Cprogrammer
18226+ * BUG fix. corrected setting of dksign, dkverify variables
18227+ *
18228+ * Revision 1.16  2009-03-22 16:58:13+05:30  Cprogrammer
18229+ * report custom errors to qmail-queue through custom error interface
18230+ *
18231+ * Revision 1.15  2009-03-21 15:15:56+05:30  Cprogrammer
18232+ * improved logic
18233+ *
18234+ * Revision 1.14  2009-03-20 22:35:24+05:30  Cprogrammer
18235+ * fix for multi-line headers
18236+ *
18237+ * Revision 1.13  2009-03-19 08:28:12+05:30  Cprogrammer
18238+ * added EXCLUDEHEADERS
18239+ *
18240+ * Revision 1.12  2009-03-14 17:11:32+05:30  Cprogrammer
18241+ * added DK_STAT_GRANULARITY
18242+ *
18243+ * Revision 1.11  2009-03-14 08:52:54+05:30  Cprogrammer
18244+ * look for domainkey in control/domainkeys
18245+ *
18246+ * Revision 1.10  2005-08-23 17:33:37+05:30  Cprogrammer
18247+ * gcc 4 compliance
18248+ *
18249+ * Revision 1.9  2005-04-01 21:42:04+05:30  Cprogrammer
18250+ * added DK_STAT_SYNTAX
18251+ * changed error codes
18252+ *
18253+ * Revision 1.8  2004-11-02 09:15:53+05:30  Cprogrammer
18254+ * commented out writing of Comments: header
18255+ *
18256+ * Revision 1.7  2004-10-24 21:32:00+05:30  Cprogrammer
18257+ * removed unecessary header files
18258+ *
18259+ * Revision 1.6  2004-10-22 20:28:18+05:30  Cprogrammer
18260+ * added RCS id
18261+ *
18262+ * Revision 1.5  2004-10-22 15:36:45+05:30  Cprogrammer
18263+ * removed readwrite.h
18264+ *
18265+ * Revision 1.4  2004-10-22 14:44:26+05:30  Cprogrammer
18266+ * use control_readnativefile to avoid skipping signure with '#' char
18267+ *
18268+ * Revision 1.3  2004-10-20 20:08:53+05:30  Cprogrammer
18269+ * libdomainkeys-0.62
18270+ *
18271+ * Revision 1.2  2004-09-23 22:55:32+05:30  Cprogrammer
18272+ * removed uneccessary header files
18273+ *
18274+ * Revision 1.1  2004-08-28 01:02:16+05:30  Cprogrammer
18275+ * Initial revision
18276+ *
18277+ */
18278+#include <unistd.h>
18279+#include <stdlib.h>
18280+#include <sys/types.h>
18281+#include <sys/stat.h>
18282+#include "sgetopt.h"
18283+#include "substdio.h"
18284+#include "open.h"
18285+#include "qmail.h"
18286+#include "sig.h"
18287+#include "fmt.h"
18288+#include "fd.h"
18289+#include "alloc.h"
18290+#include "str.h"
18291+#include "getln.h"
18292+#include "case.h"
18293+#include "stralloc.h"
18294+#include "datetime.h"
18295+#include "now.h"
18296+#include "wait.h"
18297+#include "auto_qmail.h"
18298+#include "env.h"
18299+#include "scan.h"
18300+#include "mess822.h"
18301+#include "control.h"
18302+#include "domainkeys.h"
18303+
18304+#define DEATH 86400    /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
18305+#define ADDR 1003
18306+#define ADVICE_BUF 2048
18307+
18308+char            inbuf[2048];
18309+struct substdio ssin;
18310+char            outbuf[256];
18311+struct substdio ssout;
18312+struct substdio sserr;
18313+char            errbuf[256];
18314+
18315+datetime_sec    starttime;
18316+struct datetime dt;
18317+unsigned long   mypid;
18318+unsigned long   uid;
18319+char           *pidfn;
18320+int             messfd;
18321+int             readfd;
18322+char           *dksign = 0;
18323+char           *dkverify = 0;
18324+
18325+char          **MakeArgs(char *);
18326+void            FreeMakeArgs(char **);
18327+
18328+void
18329+die(e)
18330+       int             e;
18331+{
18332+       _exit(e);
18333+}
18334+
18335+void
18336+die_write()
18337+{
18338+       die(53);
18339+}
18340+
18341+void
18342+die_read()
18343+{
18344+       die(54);
18345+}
18346+
18347+void
18348+sigalrm()
18349+{
18350+       /*- thou shalt not clean up here */
18351+       die(52);
18352+}
18353+
18354+void
18355+sigbug()
18356+{
18357+       die(81);
18358+}
18359+
18360+void
18361+custom_error(char *flag, char *status, char *code)
18362+{
18363+       char           *c;
18364+
18365+       if (substdio_put(&sserr, flag, 1) == -1)
18366+               die_write();
18367+       if (substdio_put(&sserr, "qmail-dk: ", 10) == -1)
18368+               die_write();
18369+       if (substdio_puts(&sserr, status) == -1)
18370+               die_write();
18371+       if (code)
18372+       {
18373+               if (substdio_put(&sserr, " (#", 3) == -1)
18374+                       die_write();
18375+               c = (*flag == 'Z') ? "4" : "5";
18376+               if (substdio_put(&sserr, c, 1) == -1)
18377+                       die_write();
18378+               if (substdio_put(&sserr, code + 1, 4) == -1)
18379+                       die_write();
18380+               if (substdio_put(&sserr, ")", 1) == -1)
18381+                       die_write();
18382+       }
18383+       if (substdio_flush(&sserr) == -1)
18384+               die_write();
18385+       return;
18386+}
18387+
18388+void
18389+maybe_die_dk(e)
18390+       DK_STAT         e;
18391+{
18392+       switch (e)
18393+       {
18394+       case DK_STAT_NORESOURCE:
18395+               _exit(51);
18396+       case DK_STAT_INTERNAL:
18397+               _exit(81);
18398+       case DK_STAT_ARGS:
18399+               custom_error("Z", "Arguments are not usable. (#4.3.5)", 0);
18400+               _exit(88);
18401+       case DK_STAT_SYNTAX:
18402+               if (!dksign && dkverify)
18403+                       return;
18404+               custom_error("Z", "Message is not valid syntax. Signature could not be created/checked (#4.6.0)", 0);
18405+               _exit(88);
18406+       case DK_STAT_CANTVRFY:
18407+               custom_error("Z", "Cannot get domainkeys to verify signature (#5.7.5)", 0);
18408+               _exit(88);
18409+       case DK_STAT_BADKEY:
18410+               if (env_get("DKVERIFY"))
18411+                       custom_error("Z", "Unusable public key for verifying (#5.7.5)", 0);
18412+               else
18413+                       custom_error("Z", "Unusable private key for signing (#5.7.15", 0);
18414+               _exit(88);
18415+       default:
18416+               return;
18417+       }
18418+}
18419+
18420+unsigned int
18421+pidfmt(s, seq)
18422+       char           *s;
18423+       unsigned long   seq;
18424+{
18425+       unsigned int    i;
18426+       unsigned int    len;
18427+       char           *tmpdir;
18428+
18429+       if (!(tmpdir = env_get("TMPDIR")))
18430+               tmpdir = "/tmp";
18431+       len = 0;
18432+       i = fmt_str(s, tmpdir);
18433+       len += i;
18434+       if (s)
18435+               s += i;
18436+       i = fmt_str(s, "/qmail-dk.");
18437+       len += i;
18438+       if (s)
18439+               s += i;
18440+       i = fmt_ulong(s, mypid);
18441+       len += i;
18442+       if (s)
18443+               s += i;
18444+       i = fmt_str(s, ".");
18445+       len += i;
18446+       if (s)
18447+               s += i;
18448+       i = fmt_ulong(s, starttime);
18449+       len += i;
18450+       if (s)
18451+               s += i;
18452+       i = fmt_str(s, ".");
18453+       len += i;
18454+       if (s)
18455+               s += i;
18456+       i = fmt_ulong(s, seq);
18457+       len += i;
18458+       if (s)
18459+               s += i;
18460+       ++len;
18461+       if (s)
18462+               *s++ = 0;
18463+
18464+       return len;
18465+}
18466+
18467+void
18468+pidopen()
18469+{
18470+       unsigned int    len;
18471+       unsigned long   seq;
18472+
18473+       seq = 1;
18474+       len = pidfmt((char *) 0, seq);
18475+       if (!(pidfn = alloc(len)))
18476+               die(51);
18477+       for (seq = 1; seq < 10; ++seq)
18478+       {
18479+               if (pidfmt((char *) 0, seq) > len)
18480+                       die(81); /*- paranoia */
18481+               pidfmt(pidfn, seq);
18482+               if ((messfd = open_excl(pidfn)) != -1)
18483+                       return;
18484+       }
18485+       die(63);
18486+}
18487+
18488+char            tmp[FMT_ULONG];
18489+DK_LIB         *dklib;
18490+DK             *dk;
18491+DK_STAT         st;
18492+stralloc        dkoutput = { 0 };    /*- Domainkey-Signature */
18493+stralloc        dksignature = { 0 }; /*- content of private signature */
18494+stralloc        dkopts = { 0 };
18495+char           *dkqueue = 0;
18496+char           *dkexcludeheaders;
18497+
18498+static void
18499+write_signature(DK *dk, char *dk_selector, char *keyfn,
18500+       int advicelen, int opth, char *canon)
18501+{
18502+       unsigned char   advice[ADVICE_BUF];
18503+       char           *selector, *from, *tmp;
18504+       static stralloc keyfnfrom = { 0 };
18505+       int             i;
18506+
18507+       from = dk_from(dk);
18508+       if (keyfn[0] != '/')
18509+       {
18510+               if (!stralloc_copys(&keyfnfrom, "control/"))
18511+                       die(51, 1);
18512+       }
18513+       i = str_chr(keyfn, '%');
18514+       if (keyfn[i])
18515+       {
18516+               if (keyfn[0] == '/')
18517+               {
18518+                       if (!stralloc_copyb(&keyfnfrom, keyfn, i))
18519+                               die(51, 1);
18520+               } else
18521+               if (!stralloc_catb(&keyfnfrom, keyfn, i))
18522+                       die(51, 1);
18523+               if (!stralloc_cats(&keyfnfrom, from))
18524+                       die(51);
18525+               if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
18526+                       die(51);
18527+               if (!stralloc_0(&keyfnfrom))
18528+                       die(51);
18529+               if (access(keyfnfrom.s, F_OK))
18530+               {
18531+                       /*- since file is not found remove '%' sign */
18532+                       keyfnfrom.len = 8;
18533+                       if (keyfn[0] == '/')
18534+                       {
18535+                               if (!stralloc_copyb(&keyfnfrom, keyfn, i))
18536+                                       die(51, 1);
18537+                       } else
18538+                       if (!stralloc_catb(&keyfnfrom, keyfn, i))
18539+                               die(51, 1);
18540+                       if ((i - 1) > 0 && keyfn[i - 1] == '/' && keyfn[i + 1] == '/')
18541+                               i++;
18542+                       if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
18543+                               die(51);
18544+                       if (!stralloc_0(&keyfnfrom))
18545+                               die(51);
18546+               }
18547+       } else
18548+       {
18549+               if (keyfn[0] == '/')
18550+               {
18551+                       if (!stralloc_copys(&keyfnfrom, keyfn))
18552+                               die(51, 1);
18553+               } else
18554+               if (!stralloc_cats(&keyfnfrom, keyfn))
18555+                       die(51, 1);
18556+               if (!stralloc_0(&keyfnfrom))
18557+                       die(51);
18558+       }
18559+       switch (control_readnativefile(&dksignature, keyfnfrom.s, 1))
18560+       {
18561+       case 0: /*- file not present */
18562+               /*
18563+                * You may have multiple domains, but may chose to sign
18564+                * only for few domains which have the key present. Do not
18565+                * treat domains with missing key as an error.
18566+                */
18567+               if (keyfn[i])
18568+                       return;
18569+               die(32);
18570+       case 1:
18571+               break;
18572+       default:
18573+               custom_error("Z", "Unable to read private key. (#4.3.0)", 0);
18574+               _exit(88);
18575+       }
18576+       for (i = 0; i < dksignature.len; i++)
18577+       {
18578+               if (dksignature.s[i] == '\0')
18579+                       dksignature.s[i] = '\n';
18580+       }
18581+       if (!stralloc_0(&dksignature))
18582+               die(51);
18583+       st = dk_getsig(dk, dksignature.s, advice, advicelen);
18584+       maybe_die_dk(st);
18585+       if (!dk_selector)
18586+       {
18587+               selector = keyfn;
18588+               while (*keyfn)
18589+               {
18590+                       if (*keyfn == '/')
18591+                               selector = keyfn + 1;
18592+                       keyfn++;
18593+               }
18594+       } else
18595+               selector = dk_selector;
18596+       if (!stralloc_cats(&dkoutput,
18597+#if 0
18598+               "Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n"
18599+#endif
18600+               "DomainKey-Signature: a=rsa-sha1; q=dns; c="))
18601+               die(51);
18602+       if (!stralloc_cats(&dkoutput, canon))
18603+               die(51);
18604+       if (!stralloc_cats(&dkoutput, ";\n"))
18605+               die(51);
18606+       if (!stralloc_cats(&dkoutput, "    s="))
18607+               die(51);
18608+       if (!stralloc_cats(&dkoutput, selector))
18609+               die(51);
18610+       if (!stralloc_cats(&dkoutput, "; d="))
18611+               die(51);
18612+       tmp = env_get("DKDOMAIN");
18613+       if (from || tmp)
18614+       {
18615+               if (!stralloc_cats(&dkoutput, tmp ? tmp : from))
18616+                       die(51);
18617+       } else
18618+       if (!stralloc_cats(&dkoutput, "unknown"))
18619+               die(51);
18620+       if (!stralloc_cats(&dkoutput, ";\n"))
18621+               die(51);
18622+       if (!stralloc_cats(&dkoutput, "    b="))
18623+               die(51);
18624+       if (!stralloc_cats(&dkoutput, (char *) advice))
18625+               die(51);
18626+       if (dkexcludeheaders || opth)
18627+       {
18628+               if ((i = dk_headers(dk, NULL)) > 0)
18629+               {
18630+                       if (!(tmp = alloc(i)))
18631+                               die(51);
18632+                       if (!dk_headers(dk, tmp))
18633+                               die(51);
18634+                       if (!stralloc_cats(&dkoutput, ";\n    h="))
18635+                               die(51);
18636+                       if (!stralloc_cats(&dkoutput, tmp))
18637+                               die(51);
18638+                       alloc_free(tmp);
18639+               }
18640+       }
18641+       if (!stralloc_cats(&dkoutput, ";\n"))
18642+               die(51);
18643+}
18644+
18645+int
18646+find_header(stralloc *line)
18647+{
18648+       static stralloc headers = { 0 };
18649+       int             n, i, j;
18650+
18651+       for (n = 0; n < line->len; ++n)
18652+       {
18653+               if (line->s[n] == ':')
18654+                       break;
18655+       }
18656+       if (n == line->len)
18657+               return -1;
18658+       if (!headers.len)
18659+       {
18660+               if (!stralloc_copys(&headers, ""))
18661+                       die(51);
18662+               if (dkexcludeheaders)
18663+               {
18664+                       if (!stralloc_cats(&headers, dkexcludeheaders))
18665+                               die(51);
18666+                       if (!stralloc_append(&headers, ":"))
18667+                               die(51);
18668+               }
18669+       }
18670+       if (!headers.len)
18671+               return 0;
18672+       for (i = j = 0; i < headers.len; ++i)
18673+       {
18674+               if (headers.s[i] != ':')
18675+                       continue;
18676+               if (i - j == n && !case_diffb(headers.s + j, n, line->s))
18677+                       return 1;
18678+               j = i + 1;
18679+       }
18680+       return 0;
18681+}
18682+
18683+int
18684+dk_setoptions(char **selector, int *advicelen, int *opth, int *optr, int *optc,
18685+       char **canon, char *signOptions)
18686+{
18687+       char          **argv;
18688+       int             ch, argc;
18689+
18690+       *opth = 0;
18691+       *optr = 0;
18692+       *optc = DK_CANON_NOFWS;
18693+       *canon = "nofws";
18694+       *selector = 0;
18695+       if (!signOptions)
18696+               return (0);
18697+       if (!stralloc_copys(&dkopts, "qmail-dk "))
18698+               die(51);
18699+       if (!stralloc_cats(&dkopts, signOptions))
18700+               die(51);
18701+       if (!stralloc_0(&dkopts))
18702+               die(51);
18703+       if (!(argv = MakeArgs(dkopts.s)))
18704+               die(51);
18705+       for (argc = 0;argv[argc];argc++);
18706+       while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone)
18707+       {
18708+               switch (ch)
18709+               {
18710+               case 'h':
18711+                       *opth = 1;
18712+                       break;
18713+               case 'r':
18714+                       *optr = 1;
18715+                       *opth = 1;
18716+                       break;
18717+               case 'b':
18718+                       *advicelen = atoi(optarg);
18719+                       if (*advicelen > ADVICE_BUF);
18720+                               *advicelen = ADVICE_BUF;
18721+                       break;
18722+               case 'c':
18723+                       if (!str_diffn("simple\0", optarg, 7))
18724+                       {
18725+                               *optc = DK_CANON_SIMPLE;
18726+                               *canon = "simple";
18727+                       }
18728+                       break;
18729+               case 's':
18730+                       *selector = optarg;
18731+                       break;
18732+               default:
18733+                       FreeMakeArgs(argv);
18734+                       return (1);
18735+               }
18736+       } /*- while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone) */
18737+       FreeMakeArgs(argv);
18738+       return (0);
18739+}
18740+
18741+static char    *binqqargs[2] = { "bin/qmail-queue", 0 };
18742+
18743+int
18744+main(int argc, char *argv[])
18745+{
18746+       int             errfd, pim[2];
18747+       int             wstat, match, opth = 0, optr = 0, optc = DK_CANON_NOFWS,
18748+                                       advicelen = ADVICE_BUF;
18749+       char           *x, *relayclient, *canon = "nofws", *selector = 0;
18750+       stralloc        line = {0}, dkfn = {0};
18751+       unsigned long   pid;
18752+
18753+       sig_blocknone();
18754+       umask(033);
18755+       if (!(x = env_get("ERROR_FD")))
18756+               errfd = CUSTOM_ERR_FD;
18757+       else
18758+               scan_int(x, &errfd);
18759+       substdio_fdbuf(&sserr, write, errfd, errbuf, sizeof(errbuf));
18760+       if (chdir(auto_qmail) == -1)
18761+               die(61);
18762+       dkqueue = env_get("DKQUEUE");
18763+       if (dkqueue && *dkqueue)
18764+               binqqargs[0] = dkqueue;
18765+       dksign = env_get("DKSIGN");
18766+       dkverify = env_get("DKVERIFY");
18767+       relayclient = env_get("RELAYCLIENT");
18768+       if (dkverify && relayclient && env_get("RELAYCLIENT_NODKVERIFY")) {
18769+               execv(*binqqargs, binqqargs);
18770+               die(120, 0);
18771+       }
18772+       if (!dksign && !dkverify && relayclient)
18773+       {
18774+               if (!(dksign = env_get("DKKEY")))
18775+               {
18776+                       if (!stralloc_copys(&dkfn, "domainkeys/%/default"))
18777+                               die(51);
18778+                       if (!stralloc_0(&dkfn))
18779+                               die(51);
18780+                       dksign = dkfn.s;
18781+               }
18782+       }
18783+       if (dksign || dkverify)
18784+       {
18785+               if (!(dklib = dk_init(&st)))
18786+               {
18787+                       maybe_die_dk(st);
18788+                       custom_error("Z", "dk initialization failed (#4.3.0)", 0);
18789+                       _exit(88);
18790+               }
18791+       }
18792+       /*- Initialization */
18793+       if (dksign)
18794+       {
18795+               if (dk_setoptions(&selector, &advicelen, &opth, &optr, &optc, &canon, env_get("DKSIGNOPTIONS")))
18796+               {
18797+                       custom_error("Z", "Invalid DKSIGNOPTIONS (#4.3.0)", 0);
18798+                       _exit(88);
18799+               }
18800+               if (!(dk = dk_sign(dklib, &st, optc)))
18801+               {
18802+                       maybe_die_dk(st);
18803+                       custom_error("Z", "dk_sign failed (#4.3.0)", 0);
18804+                       _exit(88);
18805+               }
18806+               if (optr && dk_setopts(dk, DKOPT_RDUPE) != DK_STAT_OK)
18807+               {
18808+                       custom_error("Z", "DKOPT_RDUPE failed (#4.3.0)", 0);
18809+                       _exit(88);
18810+               }
18811+       } else
18812+       if (dkverify)
18813+       {
18814+               if (!(dk = dk_verify(dklib, &st)))
18815+               {
18816+                       maybe_die_dk(st);
18817+                       custom_error("Z", "dk_verify failed (#4.3.0)", 0);
18818+                       _exit(88);
18819+               }
18820+       }
18821+       mypid = getpid();
18822+       uid = getuid();
18823+       starttime = now();
18824+       datetime_tai(&dt, starttime);
18825+       sig_pipeignore();
18826+       sig_miscignore();
18827+       sig_alarmcatch(sigalrm);
18828+       sig_bugcatch(sigbug);
18829+       alarm(DEATH);
18830+       pidopen();
18831+       if ((readfd = open_read(pidfn)) == -1)
18832+               die(63);
18833+       if (unlink(pidfn) == -1)
18834+               die(63);
18835+       substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf));
18836+       substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf));
18837+       dkexcludeheaders = env_get("DKEXCLUDEHEADERS");
18838+       if (dkexcludeheaders)
18839+       {
18840+               int             hdr_continue, in_header = 1;
18841+
18842+               hdr_continue = 0;
18843+               for (;;)
18844+               {
18845+       
18846+                       if (getln(&ssin, &line, &match, '\n') == -1)
18847+                               die_read();
18848+                       if (!match && line.len == 0)
18849+                               break;
18850+                       if (substdio_put(&ssout, line.s, line.len) == -1)
18851+                               die_write();
18852+                       if (!dksign && !dkverify)
18853+                               continue;
18854+                       if (in_header && !mess822_ok(&line))
18855+                               in_header = 0;
18856+                       if (in_header)
18857+                       {
18858+                               if (line.s[0] == ' ' || line.s[0] == '\t')
18859+                               {
18860+                                       if (hdr_continue)
18861+                                               continue;
18862+                               } else
18863+                               if (find_header(&line) == 1) {
18864+                                       hdr_continue = 1;
18865+                                       continue;
18866+                               } else
18867+                                       hdr_continue = 0;
18868+                       }
18869+                       if (match)
18870+                       {
18871+                               st = dk_message(dk, (unsigned char *) line.s, line.len - 1);
18872+                               maybe_die_dk(st);
18873+                               st = dk_message(dk, (unsigned char *) "\r\n", 2);
18874+                       } else
18875+                               st = dk_message(dk, (unsigned char *) line.s, line.len);
18876+                       maybe_die_dk(st);
18877+               }
18878+       } else
18879+       for (;;)
18880+       {
18881+               register int    n;
18882+               register char  *x;
18883+               int             i;
18884+
18885+               if ((n = substdio_feed(&ssin)) < 0)
18886+                       die_read();
18887+               if (!n)
18888+                       break;
18889+               x = substdio_PEEK(&ssin);
18890+               if (dksign || dkverify)
18891+               {
18892+                       for (i = 0; i < n; i++)
18893+                       {
18894+                               if (x[i] == '\n')
18895+                                       st = dk_message(dk, (unsigned char *) "\r\n", 2);
18896+                               else
18897+                                       st = dk_message(dk, (unsigned char *) x + i, 1);
18898+                               maybe_die_dk(st);
18899+                       }
18900+               }
18901+               if (substdio_put(&ssout, x, n) == -1)
18902+                       die_write();
18903+               substdio_SEEK(&ssin, n);
18904+       }
18905+       if (substdio_flush(&ssout) == -1)
18906+               die_write();
18907+       if (dksign || dkverify)
18908+       {
18909+               st = dk_eom(dk, (void *) 0);
18910+               maybe_die_dk(st);
18911+               if (dksign)
18912+                       write_signature(dk, selector, dksign, advicelen, opth, canon);
18913+               else
18914+               if (dkverify)
18915+               {
18916+                       char           *status = 0, *code = 0;
18917+
18918+                       if (!stralloc_copys(&dkoutput, "DomainKey-Status: "))
18919+                               die(51);
18920+                       switch (st)
18921+                       {
18922+                       case DK_STAT_OK:
18923+                               status = "good        ";
18924+                               break;
18925+                       case DK_STAT_BADSIG:
18926+                               status = "bad         ";
18927+                               code = "X.7.5";
18928+                               break;
18929+                       case DK_STAT_NOSIG:
18930+                               status = "no signature";
18931+                               code = "X.7.5";
18932+                               break;
18933+                       case DK_STAT_NOKEY:
18934+                       case DK_STAT_CANTVRFY:
18935+                               status = "no key      ";
18936+                               code = "X.7.0";
18937+                               break;
18938+                       case DK_STAT_BADKEY:
18939+                               status = "bad key     ";
18940+                               code = "X.7.5";
18941+                               break;
18942+                       case DK_STAT_INTERNAL:
18943+                               status = "bad format  ";
18944+                               code = "X.3.0";
18945+                               break;
18946+                       case DK_STAT_ARGS:
18947+                               status = "bad format  ";
18948+                               code = "X.3.5";
18949+                               break;
18950+                       case DK_STAT_SYNTAX:
18951+                               status = "bad format  ";
18952+                               code = "X.6.0";
18953+                               break;
18954+                       case DK_STAT_NORESOURCE:
18955+                               status = "no resources";
18956+                               code = "X.3.0";
18957+                               break;
18958+                       case DK_STAT_REVOKED:
18959+                               status = "revoked     ";
18960+                               code = "X.7.5";
18961+                               break;
18962+                       case DK_STAT_GRANULARITY:
18963+                               status = "bad sender  ";
18964+                               code = "X.7.5";
18965+                               break;
18966+                       }
18967+                       if (!stralloc_cats(&dkoutput, status))
18968+                               die(51);
18969+                       if (!stralloc_cats(&dkoutput, "\n"))
18970+                               die(51);
18971+                       if (dkverify[str_chr(dkverify, 'A' + st)])
18972+                       {
18973+                               custom_error("D", status, code); /*- return permanent error */
18974+                               die(88);
18975+                       }
18976+                       if (dkverify[str_chr(dkverify, 'a' + st)])
18977+                       {
18978+                               custom_error("Z", status, code); /*- return temporary error */
18979+                               die(88);
18980+                       }
18981+               }
18982+       }
18983+       if (pipe(pim) == -1)
18984+               die(59);
18985+       switch (pid = vfork())
18986+       {
18987+       case -1:
18988+               close(pim[0]);
18989+               close(pim[1]);
18990+               die(58);
18991+       case 0:
18992+               close(pim[1]);
18993+               if (fd_move(0, pim[0]) == -1)
18994+                       die(120);
18995+               execv(*binqqargs, binqqargs);
18996+               die(120);
18997+       }
18998+       close(pim[0]);
18999+       substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf));
19000+       substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf));
19001+       if (substdio_bput(&ssout, dkoutput.s, dkoutput.len) == -1) /*- write DK signature */
19002+               die_write();
19003+       switch (substdio_copy(&ssout, &ssin))
19004+       {
19005+       case -2:
19006+               die_read();
19007+       case -3:
19008+               die_write();
19009+       }
19010+       if (substdio_flush(&ssout) == -1)
19011+               die_write();
19012+       close(pim[1]);
19013+       if (wait_pid(&wstat, pid) != pid)
19014+               die(57);
19015+       if (wait_crashed(wstat))
19016+               die(57);
19017+       die(wait_exitcode(wstat));
19018+       /*- Not Reached */
19019+       exit(0);
19020+}
19021+
19022+void
19023+getversion_qmail_dk_c()
19024+{
19025+       static char    *x = "$Id: qmail-dk.c,v 1.37 2013-01-24 22:42:07+05:30 Cprogrammer Exp mbhangui $";
19026+
19027+       x++;
19028+}
19029diff -ruN ../netqmail-1.06-original/qmail-dkim.9 netqmail-1.06/qmail-dkim.9
19030--- ../netqmail-1.06-original/qmail-dkim.9      1970-01-01 01:00:00.000000000 +0100
19031+++ netqmail-1.06/qmail-dkim.9  2018-04-03 14:46:51.372411636 +0200
19032@@ -0,0 +1,321 @@
19033+.TH qmail-dkim 8
19034+.SH NAME
19035+qmail-dkim \- sign/verify using DKIM (SSP/ADSP optionally) and queue a mail message for delivery
19036+.SH SYNOPSIS
19037+.B qmail-dkim
19038+.SH DESCRIPTION
19039+.B qmail-dkim
19040+has the same interface as
19041+.B qmail-queue
19042+except that it inserts an appropriate DKIM header (rfc4871) before it
19043+queues the message.  To invoke
19044+.BR qmail-dkim ,
19045+set QMAILQUEUE to point to qmail-dkim in the environment when
19046+you send or receive email. qmail-dkim will call
19047+.BR qmail-queue .
19048+To invoke an executable other than
19049+.B qmail-queue
19050+set DKIMQUEUE=bin/qmail-dk for example.
19051+
19052+.B qmail-dkim
19053+supports DKIM signing and verification and can optionally use
19054+.B Sender Signing Practice (SSP)
19055+or
19056+.B Author Domain Signing Practice.
19057+It uses the libdkim and OpenSSL libraries.  To sign a message, set the
19058+.B DKIMSIGN
19059+environment variable to the pathname of the private key that will be
19060+used to sign the message. If there is a % character in the environment
19061+variable, it is removed and replaced by the domain name in the From: header.
19062+If, after substituting the %, that file does not exist, the % character will be
19063+removed. If the private key file does not exist and does not have a % character,
19064+the message will be rejected with error 35. The selector (s=) will be taken from
19065+the basename of the file. The private key should be created by
19066+.BR dknewkey(8) .
19067+
19068+In the absense of DKSIGN and DKVERIFY environment variable, qmail-dkim will sign the
19069+message if RELAYCLIENT or AUTHINFO environment variable is set. It will verify the message
19070+if RELAYCLIENT or AUTHINFO environment variable is not set. Even if DKVERIFY is set, you can disable dkim
19071+verification, if RELAYCLIENT or AUTHINFO is set, by setting RELAYCLIENT_NODKVERIFY environment variable.
19072+
19073+You can set various DKIM options in getopt style, by setting the environment variable
19074+.B DKIMSIGNOPTIONS
19075+
19076+ b <standard>         1 - allman, 2 - ietf or 3 - both
19077+ c <canonicalization> r for relaxed [DEFAULT], s - simple,
19078+                      t relaxed/simple, u - simple/relaxed
19079+ l                    include body length tag
19080+ q                    include query method tag;
19081+ t                    include a timestamp tag
19082+ h                    include copied headers
19083+ i <identity>         the identity, if not provided it will not be included
19084+ x <expire_time>      the expire time in seconds since epoch
19085+                      ( DEFAULT = current time + 604800)
19086+                      if set to - then it will not be included
19087+ z <hash>             1 for sha1, 2 for sha256, 3 for both
19088+
19089+.EX
19090+ DKIMSIGNOPTIONS="-b 1 -c r -q"
19091+ sets allman standard, with relaxed canonicalization and include query method tag
19092+.EE
19093+
19094+Apart from setting
19095+.BR DKIMSIGNOPTIONS ,
19096+you can set the identity and the expire time by setting
19097+.B DKIMIDENTITY
19098+and
19099+.B DKIMEXPIRE
19100+respectively.
19101+.B DKIMIDENTITY
19102+takes precedence over -i option specified in
19103+.BR DKIMSIGNOPTIONS.
19104+Similarly,
19105+.B DKIMEXPIRE
19106+takes precedence over -x option specifed in
19107+.BR DKIMSIGNOPTIONS.
19108+.B qmail-dkim
19109+uses the domain found in the Sender: header to set the domain tag. If not it uses the
19110+From: header. You can override this by setting
19111+.B DKIMDOMAIN
19112+environment variable.
19113+.B DKIMDOMAIN
19114+can be set to an email address or a domain (without the at sign).
19115+
19116+To verify a message, set the
19117+.B DKIMVERIFY
19118+environment variable to a desired set of letters.  Precisely, if you
19119+want a libdkim return status to generate an error, include that
19120+letter, where A is the first return status (DKIM_SUCCESS), B is the
19121+second (DKIM_FINISHED_BODY), etc.  The letter should be uppercase if you
19122+want a permanent error to be returned, and lowercase if
19123+you want a temporary error to be returned (exit code 88). If you omit the letter,
19124+\fBqmail-dkim\fR will not issue any error inspite of DKIM verification failure. It
19125+will return success and the email will get delivered. The complete set of letters
19126+with the corresponding return status is given below
19127+
19128+ A - DKIM_SUCCESS                        - Function executed successfully
19129+ B - DKIM_FINISHED_BODY                  - process result: no more message
19130+                                           body is needed
19131+ C - DKIM_PARTIAL_SUCCESS                - verify result: at least one
19132+                                           but not all signatures verified
19133+ D - DKIM_NEUTRAL                        - verify result: no signatures
19134+                                           verified but message is
19135+                                           not suspicious
19136+ E - DKIM_SUCCESS_BUT_EXTRA              - signature result: signature
19137+                                           verified but it did not
19138+                                           include all of the body
19139+ F - DKIM_3PS_SIGNATURE                  - 3rd-party signature
19140+ G - DKIM_FAIL                           - Function failed to execute
19141+ H - DKIM_BAD_SYNTAX                     - signature error: DKIM-Signature
19142+                                           could not parse or has bad
19143+                                           tags/values
19144+ I - DKIM_SIGNATURE_BAD                  - signature error: RSA verify
19145+                                           failed
19146+ J - DKIM_SIGNATURE_BAD_BUT_TESTING      - signature error: RSA verify
19147+                                           failed but testing
19148+ K - DKIM_SIGNATURE_EXPIRED              - signature error: x= is old
19149+ L - DKIM_SELECTOR_INVALID               - signature error: selector doesn't
19150+                                           parse or contains invalid values
19151+ M - DKIM_SELECTOR_GRANULARITY_MISMATCH  - signature error: selector
19152+                                           g= doesn't match i=
19153+ N - DKIM_SELECTOR_KEY_REVOKED           - signature error: selector
19154+                                           p= empty
19155+ O - DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG  - signature error: selector domain
19156+                                           name too long to request
19157+ P - DKIM_SELECTOR_DNS_TEMP_FAILURE      - signature error: temporary dns
19158+                                           failure requesting selector
19159+ Q - DKIM_SELECTOR_DNS_PERM_FAILURE      - signature error: permanent dns
19160+                                           failure requesting selector
19161+ R - DKIM_SELECTOR_PUBLIC_KEY_INVALID    - signature error: selector
19162+                                           p= value invalid or wrong format
19163+ S - DKIM_NO_SIGNATURES                  - no signatures
19164+ T - DKIM_NO_VALID_SIGNATURES            - no valid signatures
19165+ U - DKIM_BODY_HASH_MISMATCH             - sigature verify error: message
19166+                                           body does not hash to bh value
19167+ V - DKIM_SELECTOR_ALGORITHM_MISMATCH    - signature error: selector
19168+                                           h= doesn't match signature a=
19169+ W - DKIM_STAT_INCOMPAT                  - signature error: incompatible v=
19170+ X - DKIM_UNSIGNED_FROM                  - signature error: not all message's
19171+                                           From headers in signature
19172+
19173+For example, if you want to permanently reject messages that have a
19174+signature that is expired, include the letter 'K' in the
19175+.B DKIMVERIFY
19176+environment variable.  A conservative set of letters is
19177+.BR FGHIKLMNOQRTUVWjp .
19178+Reject permanently 3PS, FAILURE, SYNTAX, SIGNATURE_BAD, SIGNATURE_EXPIRED, SELECTOR_INVALID,
19179+GRANULARITY_MISMATCH, SELECTOR_KEY_REVOKED, DOMAIN_NAME_TOO_LONG, SELECTOR_PUBLIC_KEY_INVALID,
19180+NO_VALID_SIGNATURES and BODY_HASH_MISMATCH errors, and temporarily SIGNATURE_BAD_BUT_TESTING and DNS_TEMP_FAILURE .
19181+Add in
19182+.B S
19183+if you want to reject messages that do not have a DKIM signature. You can use the control files
19184+signaturedomains and nosignature domains (See Below) to further fine tune the action to be
19185+taken when a mail arrives with no DKIM signature.  Note that
19186+.B qmail-dkim
19187+always inserts the
19188+.B DKIM-Status
19189+header, so that messages can be
19190+rejected later at delivery time, or in the mail reader. In that case you may set
19191+.B DKIMVERIFY
19192+to an empty string. If you want to check all message's From header in signature set the
19193+\fBUNSIGNED_FROM\fR environment variable to an empty string.
19194+
19195+qmail-dkim supports signing practice which can be additonall checked when a signature
19196+verifcation fails -
19197+
19198+.BR "SSP - Sender Signing Practice"
19199+
19200+and
19201+
19202+.BR "ADSP - Author Domain Signing Practice" .
19203+
19204+When a signature fails to verify for a message, you can use SSP/ADSP to determine if the message is suspicious or not.
19205+To verify a message against SSP/ADSP, set the
19206+.B DKIMPRACTICE
19207+environment variable to the desired set of letters allowed for DKIMVERIFY environment variable.
19208+SSP/ADSP should be used only when signature verification fails. SSP/ADSP should be invoked only when libdkim returns the
19209+error codes (F,G,H,I,J,K,L,M,N,P,Q,R,S,T,U,V,W,X) for signature verification. In case you want to test
19210+against SSP/ADSP only for DKIM_NO_SIGNATURE and DKIM_NO_VALID_SIGNATURE
19211+set the environment variable
19212+.BR DKIMPRACTICE="ST" .
19213+If you want automatic behaviour, set DKIMADSPERROR to an empty string. In this case ADSP/SSP will be used when return code
19214+matches "FGHIJKLMNPQRSTUVWX".
19215+.B qmail-dkim
19216+uses ADSP as the default signing practice. You can override this by setting the SIGN_PRACTICE to ssp, adsp, local (lowercase).
19217+if you set SIGN_PRACTICE to \fIlocal\fB,
19218+.B qmail-dkim
19219+will check the domain against the control file
19220+.I signaturedomains
19221+(See Below). If the domain is found listed in
19222+.I signaturedomains
19223+.B qmail-dkim
19224+will bypass ADSP/SSP and return DKIM_FAIL if signature fails to verify. Setting SIGN_PRACTICE
19225+to anything else will cause
19226+.B qmail-dkim
19227+to disable Signing Practice.
19228+
19229+If ADSP or SSP is checked,
19230+.B qmail-dkim
19231+will insert the
19232+.B X-DKIM-ADSP
19233+or
19234+.B X-DKIM-SSP
19235+header as given below
19236+
19237+ A - DKIM_SUCCESS             - Message passes ADSP test
19238+ B - DKIM_ADSP_UNKNOWN        - some messages may be signed
19239+ C - DKIM_ADSP_ALL            - All message are signed with author signature
19240+ D - DKIM_ADSP_DISCARDABLE    - messages which fail verification are Discardable
19241+ E - DKIM_ADSP_SCOPE          - domain is out of scope
19242+ F - DKIM_ADSP_TEMPFAIL       - Temporary Error
19243+
19244+ or
19245+
19246+ A - DKIM_SUCCESS            - Message passes ADSP test
19247+ B - DKIM_SSP_UNKNOWN        - some messages may be signed
19248+ C - DKIM_SSP_ALL            - All message are signed with author signature
19249+ D - DKIM_SSP_STRICT         - messages which fail verification are Discardable
19250+ E - DKIM_SSP_SCOPE          - domain is out of scope
19251+ F - DKIM_SSP_TEMPFAIL       - Temporary Error
19252+
19253+You can have a control file
19254+.I signaturedomains
19255+containing a list of domains which you know are sure to sign messages using DKIM. If a message comes
19256+from a domain listed in
19257+.IR signaturedomains ,
19258+and the signature fails verification (any of DKIM failure status),
19259+.B qmail-dkim
19260+will bypass ADSP/SSP checks and return DKIM_FAIL. The name of this control file can be
19261+overriden by the environment variable
19262+.BR SIGNATUREDOMAINS .
19263+
19264+You can have a control file
19265+.I nosignaturedomains
19266+containing a list of domains which you know are sure not to sign messages using DKIM.
19267+If a message comes from a domain listed in
19268+.IR signaturedomains ,
19269+and does not have a DKIM-Signature header,
19270+.B qmail-dkim
19271+will bypass ADSP/SSP checks and return DKIM_NEUTRAL. The wildcard entry '*' in this file, will
19272+result in all mails which do not have a signature to pass DKIM test (unless the domain is listed
19273+in the control file
19274+.BR signaturedomains ).
19275+The name of this control file can be overriden by the environment variable
19276+.BR NOSIGNATUREDOMAINS .
19277+
19278+Typically, you would sign messages generated on-host by setting
19279+.B DKIMSIGN
19280+in the environment before running an email program.  DKIMSIGN will be carried
19281+through qmail's sendmail emulation through
19282+.B qmail-inject
19283+to
19284+.BR qmail-dkim .
19285+You would also set it for
19286+.B qmail-smtpd
19287+at the same time
19288+.B RELAYCLIENT
19289+is set, most often in the tcpserver cdb file.  If a host is authorized
19290+to relay, you probably want to sign messages sent by that host.
19291+.B DKIMVERIFY
19292+should be set for all other hosts.
19293+
19294+If neither
19295+.B DKIMSIGN
19296+nor
19297+.B DKIMVERIFY
19298+are set, then
19299+.B DKIMSIGN
19300+will be set to QMAILHOME/control/domainkeys/%/default. The % will be replaced by the domain in the
19301+From: header. If such a file does not exist, then it will be set to
19302+QMAILHOME/control/domainkeys/default. If such a private key exists, it will be used to sign the
19303+domain. You can also set \fBDKIMKEY\fR to chose a key different from
19304+QMAILHOME/control/domainkeys/%/default. \fBDKIMKEY\fR can also have % character that will be
19305+replaced by the domain in the From: header. If the private key does not exist, qmail-dkim
19306+will exit with return code 32.
19307+
19308+By default
19309+.B qmail-dkim
19310+will use all of the headers when signing a message.
19311+
19312+.B qmail-dkim
19313+will ordinarily spawn
19314+.BR qmail-queue ,
19315+but if DKIMQUEUE is set in the environment,
19316+the program that it points to will be executed instead.
19317+
19318+.SH "EXIT CODES"
19319+.B qmail-dkim
19320+returns the same exit codes as qmail-queue with these additions:
19321+.TP 5
19322+.B 32
19323+The private key file does not exist.
19324+.TP 5
19325+.B 57
19326+Trouble waiting for qmail-queue to exit.
19327+.TP 5
19328+.B 58
19329+Unable to vfork.
19330+.TP 5
19331+.B 59
19332+Unable to create a pipe to qmail-queue.
19333+.SH "SEE ALSO"
19334+addresses(5),
19335+envelopes(5),
19336+qmail-header(5),
19337+dknewkey(8),
19338+dktest(8),
19339+qmail-inject(8),
19340+qmail-qmqpc(8),
19341+qmail-queue(8),
19342+qmail-send(8),
19343+qmail-smtpd(8),
19344+qmail-dk(8),
19345+domain-keys(5)
19346+
19347+.SH "AUTHORS"
19348+
19349+Manvendra Bhangui.
19350+.SH PROBLEMS
19351+Problems with
19352+.B qmail-dkim
19353+should be forwarded to "Manvendra Bhangui" <mbhangui@gmail.com>
19354diff -ruN ../netqmail-1.06-original/qmail-dkim.c netqmail-1.06/qmail-dkim.c
19355--- ../netqmail-1.06-original/qmail-dkim.c      1970-01-01 01:00:00.000000000 +0100
19356+++ netqmail-1.06/qmail-dkim.c  2018-04-03 14:46:51.373411639 +0200
19357@@ -0,0 +1,1388 @@
19358+/*
19359+ * $Log: qmail-dkim.c,v $
19360+ * Revision 1.47  2016-06-03 09:57:59+05:30  Cprogrammer
19361+ * moved qmail-multi to sbin
19362+ *
19363+ * Revision 1.46  2016-05-17 19:44:58+05:30  Cprogrammer
19364+ * use auto_control, set by conf-control to set control directory
19365+ *
19366+ * Revision 1.45  2016-03-01 18:48:02+05:30  Cprogrammer
19367+ * added env variable UNSIGNED_SUBJECT to verify dkim without subject field
19368+ *
19369+ * Revision 1.44  2015-12-15 16:05:58+05:30  Cprogrammer
19370+ * increased buffer size for long header issue
19371+ *
19372+ * Revision 1.43  2014-01-22 22:45:01+05:30  Cprogrammer
19373+ * treat AUTHINFO environment like RELAYCLIENT environment variable
19374+ *
19375+ * Revision 1.42  2013-10-01 17:11:24+05:30  Cprogrammer
19376+ * fixed QMAILQUEUE recursion
19377+ *
19378+ * Revision 1.41  2013-09-16 22:16:35+05:30  Cprogrammer
19379+ * corrected logic for RELAYCLIENT_NODKIMVERIFY
19380+ *
19381+ * Revision 1.40  2013-09-13 16:34:35+05:30  Cprogrammer
19382+ * turn off verification if RELAYCLIENT, DKIMVERIFY and RELAYCLIENT_NODKIMVERIFY is set
19383+ *
19384+ * Revision 1.39  2013-08-18 15:53:30+05:30  Cprogrammer
19385+ * revert back to default verification mode if both dksign, dkverify are not set
19386+ *
19387+ * Revision 1.38  2013-08-17 15:00:33+05:30  Cprogrammer
19388+ * BUG - corrected location of private key when % sign is removed
19389+ *
19390+ * Revision 1.37  2013-01-24 22:37:22+05:30  Cprogrammer
19391+ * BUG (fix by Piotr Gronek) - DKIM_FREE(results) called before call to ParseTagValues()
19392+ * alternate code for DKIMSIGN selector file name
19393+ *
19394+ * Revision 1.36  2012-08-16 08:01:46+05:30  Cprogrammer
19395+ * do not skip X-Mailer headers
19396+ *
19397+ * Revision 1.35  2011-11-10 14:32:08+05:30  Cprogrammer
19398+ * BUG ssout to be assigned only after pidopen()
19399+ *
19400+ * Revision 1.34  2011-11-07 09:35:59+05:30  Cprogrammer
19401+ * set ssout, sserr, ssin before executing other functions
19402+ *
19403+ * Revision 1.33  2011-07-29 09:29:17+05:30  Cprogrammer
19404+ * fixed key file name
19405+ *
19406+ * Revision 1.32  2011-07-28 19:36:36+05:30  Cprogrammer
19407+ * BUG - fixed opening of private key with absolute path
19408+ *
19409+ * Revision 1.31  2011-07-22 14:40:05+05:30  Cprogrammer
19410+ * fixed checking of private key file
19411+ *
19412+ * Revision 1.30  2011-06-04 14:49:48+05:30  Cprogrammer
19413+ * remove '%' sign from private key if key not found
19414+ *
19415+ * Revision 1.29  2011-06-04 14:22:29+05:30  Cprogrammer
19416+ * added DKIM_UNSIGNED_FROM error code for dkimadspverify
19417+ *
19418+ * Revision 1.28  2011-06-04 14:07:41+05:30  Cprogrammer
19419+ * added DKIM_UNSIGNED_FROM
19420+ *
19421+ * Revision 1.27  2011-02-10 23:39:59+05:30  Cprogrammer
19422+ * use DKIMKEY to override defult control/domainkeys/%/default
19423+ *
19424+ * Revision 1.26  2011-02-06 10:13:50+05:30  Cprogrammer
19425+ * BUG - signature was wrongly freed before being accessed.
19426+ *
19427+ * Revision 1.25  2011-02-05 09:47:47+05:30  Cprogrammer
19428+ * fixed SIGSEGV occuring for messages without body
19429+ *
19430+ * Revision 1.24  2010-11-02 18:45:14+05:30  Cprogrammer
19431+ * Improve DKIM signing/verification speed
19432+ *
19433+ * Revision 1.23  2010-07-21 08:59:57+05:30  Cprogrammer
19434+ * use CONTROLDIR environment variable instead of a hardcoded control directory
19435+ *
19436+ * Revision 1.22  2009-04-22 13:42:51+05:30  Cprogrammer
19437+ * made fd for custom error configurable through env variable ERROR_FD
19438+ *
19439+ * Revision 1.21  2009-04-21 09:05:48+05:30  Cprogrammer
19440+ * return relevant error message for reading private key
19441+ *
19442+ * Revision 1.20  2009-04-21 08:55:41+05:30  Cprogrammer
19443+ * return temporary error for temp failures
19444+ *
19445+ * Revision 1.19  2009-04-20 22:19:01+05:30  Cprogrammer
19446+ * made dkimopts global
19447+ *
19448+ * Revision 1.18  2009-04-16 13:48:32+05:30  Cprogrammer
19449+ * added dkim_setoptions() to set all DKIM options
19450+ *
19451+ * Revision 1.17  2009-04-07 11:36:56+05:30  Cprogrammer
19452+ * use TMPDIR env variable for tmp directory
19453+ *
19454+ * Revision 1.16  2009-04-05 12:52:17+05:30  Cprogrammer
19455+ * added preprocessor warning
19456+ *
19457+ * Revision 1.15  2009-04-04 00:33:44+05:30  Cprogrammer
19458+ * removed dk_strdup()
19459+ *
19460+ * Revision 1.14  2009-03-31 08:21:58+05:30  Cprogrammer
19461+ * set dkimsign when RELAYCLIENT is defined when both dkimsign and dkimverify are undefined
19462+ *
19463+ * Revision 1.13  2009-03-30 22:25:54+05:30  Cprogrammer
19464+ * made DKIM messages friendlier
19465+ *
19466+ * Revision 1.12  2009-03-30 14:47:59+05:30  Cprogrammer
19467+ * added descriptive text for original dkim error
19468+ *
19469+ * Revision 1.11  2009-03-29 19:20:43+05:30  Cprogrammer
19470+ * added nosignaturedomains
19471+ *
19472+ * Revision 1.10  2009-03-28 22:27:02+05:30  Cprogrammer
19473+ * use DKIMSIGN, DKIMVERIFY if RELAYCLIENT is not set
19474+ *
19475+ * Revision 1.9  2009-03-28 22:03:05+05:30  Cprogrammer
19476+ * fixed DKIM return codes
19477+ *
19478+ * Revision 1.8  2009-03-28 13:37:37+05:30  Cprogrammer
19479+ * call DKIMVerifyGetDetails() always
19480+ *
19481+ * Revision 1.7  2009-03-28 11:39:23+05:30  Cprogrammer
19482+ * set automatic setting of dkimsign, dkimverify variables based on RELAYCLIENT
19483+ *
19484+ * Revision 1.6  2009-03-28 11:35:58+05:30  Cprogrammer
19485+ * added ADSP/SSP
19486+ *
19487+ * Revision 1.5  2009-03-22 17:39:38+05:30  Cprogrammer
19488+ * set identity using basename of signature or environment variable DKIMIDENTITY
19489+ *
19490+ * Revision 1.4  2009-03-22 16:58:38+05:30  Cprogrammer
19491+ * fixed bug with verification
19492+ * report custom errors to qmail-queue through custom error interface
19493+ *
19494+ * Revision 1.3  2009-03-21 12:34:38+05:30  Cprogrammer
19495+ * use hasdkim.h for conditional compilation of dkim
19496+ *
19497+ * Revision 1.2  2009-03-20 22:35:57+05:30  Cprogrammer
19498+ * set error to DKIM_NO_SIGNATURE when DKIM-Signature is not present
19499+ *
19500+ * Revision 1.1  2009-03-18 13:54:49+05:30  Cprogrammer
19501+ * Initial revision
19502+ *
19503+ */
19504+#include <unistd.h>
19505+#include <stdlib.h>
19506+#include <ctype.h>
19507+#include <sys/types.h>
19508+#include <sys/stat.h>
19509+#include "sgetopt.h"
19510+#include "substdio.h"
19511+#include "open.h"
19512+#include "qmail.h"
19513+#include "sig.h"
19514+#include "scan.h"
19515+#include "case.h"
19516+#include "fmt.h"
19517+#include "fd.h"
19518+#include "alloc.h"
19519+#include "str.h"
19520+#include "stralloc.h"
19521+#include "datetime.h"
19522+#include "now.h"
19523+#include "wait.h"
19524+#include "auto_qmail.h"
19525+#include "env.h"
19526+#include "control.h"
19527+#ifdef HAVE_CONFIG_H
19528+#include "config.h"
19529+#endif
19530+#include "dkim.h"
19531+
19532+#define DEATH 86400    /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
19533+#define ADDR 1003
19534+#define HAVE_EVP_SHA256
19535+#define strncasecmp(x,y,z) case_diffb((x), (z), (y))
19536+#define strcasecmp(x,y)    case_diffs((x), (y))
19537+
19538+char            inbuf[4096];
19539+char            outbuf[256];
19540+char            errbuf[256];
19541+struct substdio ssin;
19542+struct substdio ssout;
19543+struct substdio sserr;
19544+
19545+datetime_sec    starttime;
19546+struct datetime dt;
19547+unsigned long   mypid;
19548+unsigned long   uid;
19549+char           *pidfn;
19550+int             messfd;
19551+int             readfd;
19552+DKIMContext     ctxt;
19553+
19554+char          **MakeArgs(char *);
19555+void            FreeMakeArgs(char **);
19556+
19557+
19558+void
19559+die(int e, int what)
19560+{
19561+       if (!what)
19562+               _exit(e);
19563+       (what == 1 ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
19564+       _exit(e);
19565+}
19566+
19567+void
19568+die_write()
19569+{
19570+       die(53, 0);
19571+}
19572+
19573+void
19574+die_read()
19575+{
19576+       die(54, 0);
19577+}
19578+
19579+void
19580+sigalrm()
19581+{
19582+       /*- thou shalt not clean up here */
19583+       die(52, 0);
19584+}
19585+
19586+void
19587+sigbug()
19588+{
19589+       die(81, 0);
19590+}
19591+
19592+void
19593+custom_error(char *flag, char *status, char *code)
19594+{
19595+       char           *c;
19596+
19597+       if (substdio_put(&sserr, flag, 1) == -1)
19598+               die_write();
19599+       if (substdio_put(&sserr, "qmail-dkim: ", 12) == -1)
19600+               die_write();
19601+       if (substdio_puts(&sserr, status) == -1)
19602+               die_write();
19603+       if (code) {
19604+               if (substdio_put(&sserr, " (#", 3) == -1)
19605+                       die_write();
19606+               c = (*flag == 'Z') ? "4" : "5";
19607+               if (substdio_put(&sserr, c, 1) == -1)
19608+                       die_write();
19609+               if (substdio_put(&sserr, code + 1, 4) == -1)
19610+                       die_write();
19611+               if (substdio_put(&sserr, ")", 1) == -1)
19612+                       die_write();
19613+       }
19614+       if (substdio_flush(&sserr) == -1)
19615+               die_write();
19616+       return;
19617+}
19618+
19619+int DKIM_CALL
19620+SignThisHeader(const char *szHeader)
19621+{
19622+       if ((!strncasecmp((char *) szHeader, "X-", 2)
19623+                       && strncasecmp((char *) szHeader, "X-Mailer", 8))
19624+               || strncasecmp((char *) szHeader, "Received:", 9) == 0
19625+               || strncasecmp((char *) szHeader, "Authentication-Results:", 23) == 0
19626+               || strncasecmp((char *) szHeader, "Return-Path:", 12) == 0) {
19627+               return 0;
19628+       }
19629+       return 1;
19630+}
19631+
19632+void
19633+maybe_die_dkim(e)
19634+       int             e;
19635+{
19636+       switch (e)
19637+       {
19638+       case DKIM_OUT_OF_MEMORY:
19639+       case DKIM_BUFFER_TOO_SMALL:
19640+               _exit (51);
19641+       case DKIM_INVALID_CONTEXT:
19642+               custom_error("Z", "DKIMContext structure invalid for this operation (#4.3.0)", 0);
19643+               _exit(88);
19644+       case DKIM_NO_SENDER:
19645+               custom_error("Z", "Could not find From: or Sender: header in message (#5.1.7)", 0);
19646+               _exit(88);
19647+       case DKIM_BAD_PRIVATE_KEY:
19648+               custom_error("D", "Could not parse private key (#5.7.5)", 0);
19649+               _exit(88);
19650+       default:
19651+               return;
19652+       }
19653+}
19654+
19655+unsigned int
19656+pidfmt(s, seq)
19657+       char           *s;
19658+       unsigned long   seq;
19659+{
19660+       unsigned int    i;
19661+       unsigned int    len;
19662+       char           *tmpdir;
19663+
19664+       if (!(tmpdir = env_get("TMPDIR")))
19665+               tmpdir = "/tmp";
19666+       len = 0;
19667+       i = fmt_str(s, tmpdir);
19668+       len += i;
19669+       if (s)
19670+               s += i;
19671+       i = fmt_str(s, "/qmail-dkim.");
19672+       len += i;
19673+       if (s)
19674+               s += i;
19675+       i = fmt_ulong(s, mypid);
19676+       len += i;
19677+       if (s)
19678+               s += i;
19679+       i = fmt_str(s, ".");
19680+       len += i;
19681+       if (s)
19682+               s += i;
19683+       i = fmt_ulong(s, starttime);
19684+       len += i;
19685+       if (s)
19686+               s += i;
19687+       i = fmt_str(s, ".");
19688+       len += i;
19689+       if (s)
19690+               s += i;
19691+       i = fmt_ulong(s, seq);
19692+       len += i;
19693+       if (s)
19694+               s += i;
19695+       ++len;
19696+       if (s)
19697+               *s++ = 0;
19698+
19699+       return len;
19700+}
19701+
19702+void
19703+pidopen()
19704+{
19705+       unsigned int    len;
19706+       unsigned long   seq;
19707+
19708+       seq = 1;
19709+       len = pidfmt((char *) 0, seq);
19710+       if (!(pidfn = alloc(len)))
19711+               die(51, 0);
19712+       for (seq = 1; seq < 10; ++seq) {
19713+               if (pidfmt((char *) 0, seq) > len)
19714+                       die(81, 0); /*- paranoia */
19715+               pidfmt(pidfn, seq);
19716+               if ((messfd = open_excl(pidfn)) != -1)
19717+                       return;
19718+       }
19719+       die(63, 0);
19720+}
19721+
19722+char            tmp[FMT_ULONG];
19723+char           *dkimsign = 0;
19724+char           *dkimverify = 0;
19725+char           *dkimadspverify = 0, *dkimpractice =  "FGHIJKLMNPQRSTUVWX";
19726+stralloc        dkimoutput = { 0 };  /*- DKIM-Signature */
19727+stralloc        dksignature = { 0 }; /*- content of private signature */
19728+stralloc        sigdomains = { 0 };  /*- domains which must have signatures */
19729+stralloc        nsigdomains = { 0 }; /*- domains which do not have signatures */
19730+stralloc        dkimopts = { 0 };
19731+char           *dkimqueue = 0;
19732+
19733+static void
19734+write_signature(char *domain, char *keyfn)
19735+{
19736+       char           *pSig;
19737+       int             i;
19738+       static stralloc keyfnfrom = { 0 };
19739+
19740+       if (keyfn[0] != '/') {
19741+               if (!stralloc_copys(&keyfnfrom, "control/"))
19742+                       die(51, 1);
19743+       }
19744+       i = str_chr(keyfn, '%');
19745+       if (keyfn[i]) {
19746+               if (keyfn[0] == '/') {
19747+                       if (!stralloc_copyb(&keyfnfrom, keyfn, i))
19748+                               die(51, 1);
19749+               } else
19750+               if (!stralloc_catb(&keyfnfrom, keyfn, i))
19751+                       die(51, 1);
19752+               if (!stralloc_cats(&keyfnfrom, domain))
19753+                       die(51, 1);
19754+               if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
19755+                       die(51, 1);
19756+               if (!stralloc_0(&keyfnfrom))
19757+                       die(51, 1);
19758+               if (access(keyfnfrom.s, F_OK)) {
19759+                       /*- since file does not exists remove '%' sign */
19760+                       keyfnfrom.len = 8;
19761+                       if (keyfn[0] == '/') {
19762+                               if (!stralloc_copyb(&keyfnfrom, keyfn, i))
19763+                                       die(51, 1);
19764+                       } else
19765+                       if (!stralloc_catb(&keyfnfrom, keyfn, i))
19766+                               die(51, 1);
19767+                       if ((i - 1) > 0 && keyfn[i - 1] == '/' && keyfn[i + 1] == '/')
19768+                               i++;
19769+                       if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
19770+                               die(51, 1);
19771+                       if (!stralloc_0(&keyfnfrom))
19772+                               die(51, 1);
19773+               }
19774+       } else {
19775+               if (keyfn[0] == '/') {
19776+                       if (!stralloc_copys(&keyfnfrom, keyfn))
19777+                               die(51, 1);
19778+               } else
19779+               if (!stralloc_cats(&keyfnfrom, keyfn))
19780+                       die(51, 1);
19781+               if (!stralloc_0(&keyfnfrom))
19782+                       die(51, 1);
19783+       }
19784+       switch (control_readnativefile(&dksignature, keyfnfrom.s, 1))
19785+       {
19786+       case 0: /*- missing signature file */
19787+               DKIMSignFree(&ctxt);
19788+               /*
19789+                * You may have multiple domains, but may chose to sign
19790+                * only for few domains which have the key present. Do not
19791+                * treat domains with missing key as an error.
19792+                */
19793+               if (keyfn[i])
19794+                       return;
19795+               die(32, 0);
19796+       case 1:
19797+               break;
19798+       default:
19799+               custom_error("Z", "Unable to read private key. (#4.3.0)", 0);
19800+               DKIMSignFree(&ctxt);
19801+               _exit(88);
19802+       }
19803+       for (i = 0; i < dksignature.len; i++) {
19804+               if (dksignature.s[i] == '\0')
19805+                       dksignature.s[i] = '\n';
19806+       }
19807+       if (!stralloc_0(&dksignature))
19808+               die(51, 1);
19809+       i = DKIMSignGetSig2(&ctxt, dksignature.s, &pSig);
19810+       maybe_die_dkim(i);
19811+       if (pSig) {
19812+               if (!stralloc_catb(&dkimoutput, pSig, str_len(pSig)))
19813+                       die(51, 1);
19814+               if (!stralloc_cats(&dkimoutput, "\n"))
19815+                       die(51, 1);
19816+       }
19817+       DKIMSignFree(&ctxt);
19818+}
19819+
19820+#include <openssl/evp.h>
19821+#define DKIM_MALLOC(s)     OPENSSL_malloc(s)
19822+#define DKIM_MFREE(s)      OPENSSL_free(s); s = NULL;
19823+char           *dns_text(char *);
19824+
19825+int
19826+ParseTagValues(char *list, char *letters[], char *values[])
19827+{
19828+       char           *tmp, *ptr, *key;
19829+       int             i;
19830+
19831+       /*- start with all args unset */
19832+       for (i = 0; letters[i]; i++)
19833+               values[i] = 0;
19834+       key = 0;
19835+       for(ptr = list;*ptr;) {
19836+               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */
19837+                       *ptr++ = 0;
19838+               if (!key)
19839+                       key = ptr;
19840+               if (*ptr == '=') {
19841+                       *ptr = 0;
19842+                       for (i = 0;letters[i];i++) {
19843+                               if (!str_diff(letters[i], key)) {
19844+                                       ptr++;
19845+                                       for (;*ptr;) {
19846+                                               if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) {
19847+                                                       ptr++;
19848+                                                       continue;
19849+                                               }
19850+                                               break;
19851+                                       }
19852+                                       values[i] = ptr;
19853+                                       for(;*ptr && *ptr != ';';ptr++);
19854+                                       tmp = ptr;
19855+                                       if (*ptr)
19856+                                               *ptr++ = 0;
19857+                                       for(;tmp != values[i];tmp--) { /*- RFC 4871 3.2 */
19858+                                               if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n')) {
19859+                                                       *tmp = 0;
19860+                                                       continue;
19861+                                               }
19862+                                               break;
19863+                                       }
19864+                                       key = 0;
19865+                                       break;
19866+                               }
19867+                       }
19868+               } else
19869+                       ptr++;
19870+       }
19871+       return (0);
19872+}
19873+
19874+int
19875+checkSSP(char *domain, int *bTesting)
19876+{
19877+       char           *query, *results;
19878+       char           *tags[] = { "dkim", "t", 0};
19879+       char           *values[2];
19880+       int             bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN;
19881+
19882+       *bTesting = 0;
19883+       if (!(query = DKIM_MALLOC(str_len("_ssp._domainkey.") + str_len(domain) + 1)))
19884+               die(51, 0);
19885+       sprintf(query, "_ssp._domainkey.%s", domain);
19886+       results = dns_text(query);
19887+       DKIM_MFREE(query);
19888+       if (!str_diff(results, "e=temp;")) {
19889+               DKIM_MFREE(results);
19890+               return DKIM_SSP_TEMPFAIL;
19891+       } else
19892+       if (!str_diff(results, "e=perm;")) {
19893+               results = dns_text(domain);
19894+               if (!str_diff(results, "e=temp;")) {
19895+                       DKIM_MFREE(results);
19896+                       return DKIM_SSP_TEMPFAIL;
19897+               } else
19898+               if (!str_diff(results, "e=perm;")) {
19899+                       DKIM_MFREE(results);
19900+                       return DKIM_SSP_SCOPE;
19901+               }
19902+               bIsParentSSP = 1;
19903+       }
19904+       /*
19905+        * PG.1 2013-01-03
19906+        * Bug fix by Piotr Gronek, Faculy of Physics & Applied Computer Science, Poland 2013-01-03
19907+        * Deallocating storage for 'results' here is premature - moved beyond last reference to it.
19908+        *
19909+        */
19910+       if (!ParseTagValues(results, tags, values)) {
19911+               DKIM_MFREE(results);
19912+               return DKIM_SSP_UNKNOWN;
19913+       }
19914+       DKIM_MFREE(results);
19915+       if (values[0] != NULL) {
19916+               if (strcasecmp(values[0], "all") == 0)
19917+                       iSSP = DKIM_SSP_ALL;
19918+               else
19919+               if (strcasecmp(values[0], "strict") == 0)
19920+                       iSSP = DKIM_SSP_STRICT;
19921+       }
19922+       // flags
19923+       if (values[1] != NULL) {
19924+               char           *s, *p;
19925+               for (p = values[1], s = values[1]; *p; p++) {
19926+                       if (*p == '|')
19927+                               *p = 0;
19928+                       else
19929+                               continue;
19930+                       if (!str_diff(s, "y"))
19931+                               *bTesting = 1;
19932+                       else
19933+                       if (!str_diff(s, "s")) {
19934+                               if (bIsParentSSP) {
19935+                                       /*
19936+                                        * this is a parent's SSP record that should not apply to subdomains
19937+                                        * the message is non-suspicious
19938+                                        */
19939+                                       *bTesting = 0;
19940+                                       return (DKIM_SSP_UNKNOWN);
19941+                               }
19942+                       }
19943+                       s = p + 1;
19944+               }
19945+       }
19946+       return iSSP;
19947+}
19948+
19949+int
19950+checkADSP(char *domain)
19951+{
19952+       char           *query, *results;
19953+       char           *tags[] = { "dkim", 0};
19954+       char           *values[1];
19955+
19956+       results = dns_text(domain);
19957+       if (!str_diff(results, "e=perm;")) {
19958+               DKIM_MFREE(results);
19959+               return DKIM_ADSP_SCOPE;
19960+       }
19961+       else
19962+       if (!str_diff(results, "e=temp;")) {
19963+               DKIM_MFREE(results);
19964+               return DKIM_ADSP_TEMPFAIL;
19965+       }
19966+       if (!(query = DKIM_MALLOC(str_len("_adsp._domainkey.") + str_len(domain) + 1)))
19967+               die(51, 0);
19968+       sprintf(query, "_adsp._domainkey.%s", domain);
19969+       results = dns_text(query);
19970+       DKIM_MFREE(query);
19971+       if (!str_diff(results, "e=perm;")) {
19972+               DKIM_MFREE(results);
19973+               return DKIM_ADSP_SCOPE;
19974+       } else
19975+       if (!str_diff(results, "e=temp;")) {
19976+               DKIM_MFREE(results);
19977+               return DKIM_ADSP_TEMPFAIL;
19978+       }
19979+       /*
19980+        * PG.1 2013-01-03
19981+        * Bug fix by Piotr Gronek, Faculy of Physics & Applied Computer Science, Poland 2013-01-03
19982+        *
19983+        * Deallocating storage for 'results' here is premature - moved beyond last reference to it.
19984+        *
19985+        */
19986+       if (!ParseTagValues(results, tags, values)) {
19987+               DKIM_MFREE(results);
19988+               return DKIM_SSP_UNKNOWN;
19989+       }
19990+       DKIM_MFREE(results);
19991+       if (values[0] != NULL) {
19992+               if (strcasecmp(values[0], "all") == 0)
19993+                       return (DKIM_ADSP_ALL);
19994+               else
19995+               if (strcasecmp(values[0], "discardable") == 0)
19996+                       return (DKIM_ADSP_DISCARDABLE);
19997+       }
19998+       return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */
19999+}
20000+
20001+void
20002+dkimverify_exit(int dkimRet, char *status, char *code)
20003+{
20004+       if (dkimRet < 0) {
20005+               if (dkimverify[str_chr(dkimverify, 'F' - dkimRet)]) {
20006+                       custom_error("D", status, code);
20007+                       die(88, 0);
20008+               }
20009+               if (dkimverify[str_chr(dkimverify, 'f' - dkimRet)]) {
20010+                       custom_error("Z", status, code);
20011+                       die(88, 0);
20012+               }
20013+       } else {
20014+               if (dkimverify[str_chr(dkimverify, 'A' + dkimRet)]) {
20015+                       custom_error("D", status, code);
20016+                       die(88, 0);
20017+               }
20018+               if (dkimverify[str_chr(dkimverify, 'a' + dkimRet)]) {
20019+                       custom_error("Z", status, code);
20020+                       die(88, 0);
20021+               }
20022+       }
20023+}
20024+
20025+void
20026+writeHeaderNexit(int ret, int origRet, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP)
20027+{
20028+       char           *dkimStatus = 0, *sspStatus = 0, *adspStatus = 0, *code = 0, *orig = 0;
20029+       char            strnum[FMT_ULONG];
20030+
20031+       switch (ret)
20032+       {
20033+       case DKIM_SUCCESS:                      /*- 0 */ /*- A */
20034+               dkimStatus = "good        ";
20035+               code = "X.7.0";
20036+               break;
20037+       case DKIM_FINISHED_BODY:        /*- 1 process result: no more message body is needed */
20038+               dkimStatus = "process result: no more message body is needed";
20039+               code = "X.7.0";
20040+               break;
20041+       case DKIM_PARTIAL_SUCCESS:      /*- 2 verify result: at least one but not all signatures verified */
20042+               dkimStatus = "verify result: at least none but not all signatures verified";
20043+               code = "X.7.0";
20044+               break;
20045+       case DKIM_NEUTRAL:                      /*- 3 verify result: no signatures verified but message is not suspicious */
20046+               dkimStatus = "verify result: no signatures verified but message is not suspicious";
20047+               code = "X.7.0";
20048+               break;
20049+       case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
20050+               dkimStatus = "signature result: signature verified but it did not include all of the body";
20051+               code = "X.7.0";
20052+               break;
20053+       case DKIM_FAIL:                         /*- -1 */ /*- F */
20054+               dkimStatus = "DKIM Signature verification failed";
20055+               code = "X.7.0";
20056+               break;
20057+       case DKIM_BAD_SYNTAX:           /*- -2 */ /*- G */
20058+               dkimStatus = "signature error: DKIM-Signature could not parse or has bad tags/values";
20059+               code = "X.7.5";
20060+               break;
20061+       case DKIM_SIGNATURE_BAD:        /*- -3 */
20062+               dkimStatus = "signature error: RSA verify failed";
20063+               code = "X.7.5";
20064+               break;
20065+       case DKIM_SIGNATURE_BAD_BUT_TESTING:
20066+               dkimStatus = "signature error: RSA verify failed but testing";
20067+               code = "X.7.5";
20068+               break;
20069+       case DKIM_SIGNATURE_EXPIRED:
20070+               dkimStatus = "signature error: x= is old";
20071+               code = "X.7.5";
20072+               break;
20073+       case DKIM_SELECTOR_INVALID:
20074+               dkimStatus = "signature error: selector doesn't parse or contains invalid values";
20075+               code = "X.7.5";
20076+               break;
20077+       case DKIM_SELECTOR_GRANULARITY_MISMATCH:
20078+               dkimStatus = "signature error: selector g= doesn't match i=";
20079+               code = "X.7.5";
20080+               break;
20081+       case DKIM_SELECTOR_KEY_REVOKED:
20082+               dkimStatus = "signature error: selector p= empty";
20083+               code = "X.7.5";
20084+               break;
20085+       case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
20086+               dkimStatus = "signature error: selector domain name too long to request";
20087+               code = "X.7.0";
20088+               break;
20089+       case DKIM_SELECTOR_DNS_TEMP_FAILURE:
20090+               dkimStatus = "signature error: temporary dns failure requesting selector";
20091+               code = "X.7.0";
20092+               break;
20093+       case DKIM_SELECTOR_DNS_PERM_FAILURE:
20094+               dkimStatus = "signature error: permanent dns failure requesting selector";
20095+               code = "X.7.0";
20096+               break;
20097+       case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
20098+               dkimStatus = "signature error: selector p= value invalid or wrong format";
20099+               code = "X.7.5";
20100+               break;
20101+       case DKIM_NO_SIGNATURES:
20102+               dkimStatus = "no signatures";
20103+               code = "X.7.5";
20104+               break;
20105+       case DKIM_NO_VALID_SIGNATURES:
20106+               dkimStatus = "no valid signatures";
20107+               code = "X.7.5";
20108+               break;
20109+       case DKIM_BODY_HASH_MISMATCH:
20110+               dkimStatus = "signature verify error: message body does not hash to bh value";
20111+               code = "X.7.7";
20112+               break;
20113+       case DKIM_SELECTOR_ALGORITHM_MISMATCH:
20114+               dkimStatus = "signature error: selector h= doesn't match signature a=";
20115+               code = "X.7.7";
20116+               break;
20117+       case DKIM_STAT_INCOMPAT:
20118+               dkimStatus = "signature error: incompatible v=";
20119+               code = "X.7.6";
20120+               break;
20121+       case DKIM_UNSIGNED_FROM:
20122+               dkimStatus = "signature error: not all message's From headers in signature";
20123+               code = "X.7.7";
20124+               break;
20125+       default:
20126+               dkimStatus = "error";
20127+               code = "X.3.0";
20128+               break;
20129+       }
20130+       if (useSSP && resDKIMSSP != -1) {
20131+               switch(resDKIMSSP)
20132+               {
20133+                       case DKIM_SSP_ALL:
20134+                               sspStatus = (char *) "all;";
20135+                               break;
20136+                       case DKIM_SSP_STRICT:
20137+                               sspStatus = (char *) "strict;";
20138+                               break;
20139+                       case DKIM_SSP_SCOPE:
20140+                               sspStatus = (char *) "out of scope;";
20141+                               break;
20142+                       case DKIM_SSP_TEMPFAIL:
20143+                               sspStatus = (char *) "temporary failure;";
20144+                               break;
20145+                       case DKIM_SSP_UNKNOWN:
20146+                       default:
20147+                               sspStatus = (char *) "unknown;";
20148+                               break;
20149+               }
20150+       }
20151+       if (useADSP && resDKIMADSP != -1) {
20152+               switch(resDKIMADSP)
20153+               {
20154+                       case DKIM_ADSP_ALL:
20155+                               adspStatus = (char *) "all;";
20156+                               break;
20157+                       case DKIM_ADSP_DISCARDABLE:
20158+                               adspStatus = (char *) "discardable;";
20159+                               break;
20160+                       case DKIM_ADSP_SCOPE:
20161+                               adspStatus = (char *) "out of scope;";
20162+                               break;
20163+                       case DKIM_ADSP_TEMPFAIL:
20164+                               adspStatus = (char *) "temporary failure;";
20165+                               break;
20166+                       case DKIM_ADSP_UNKNOWN:
20167+                       default:
20168+                               adspStatus = (char *) "unknown ;";
20169+                               break;
20170+               }
20171+       }
20172+       if (!stralloc_copys(&dkimoutput, "DKIM-Status: "))
20173+               die(51, 0);
20174+       if (!stralloc_cats(&dkimoutput, dkimStatus))
20175+               die(51, 0);
20176+       if (origRet != DKIM_MAX_ERROR && ret != origRet) {
20177+               if (!stralloc_cats(&dkimoutput, "\n\t(old="))
20178+                       die(51, 0);
20179+               switch (origRet)
20180+               {
20181+               case DKIM_SUCCESS:                      /*- 0 */ /*- A */
20182+                       orig = "SUCCESS";
20183+                       break;
20184+               case DKIM_FINISHED_BODY:        /*- 1 process result: no more message body is needed */
20185+                       orig = "FINISHED BODY";
20186+                       break;
20187+               case DKIM_PARTIAL_SUCCESS:      /*- 2 verify result: at least one but not all signatures verified */
20188+                       orig = "PARTIAL SUCCESS";
20189+                       break;
20190+               case DKIM_NEUTRAL:                      /*- 3 verify result: no signatures verified but message is not suspicious */
20191+                       orig = "NEUTRAL";
20192+                       break;
20193+               case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
20194+                       orig = "SUCCESS(BUT EXTRA)";
20195+                       break;
20196+               case DKIM_FAIL:                         /*- -1 */ /*- F */
20197+                       orig = "FAIL";
20198+                       break;
20199+               case DKIM_BAD_SYNTAX:           /*- -2 */ /*- G */
20200+                       orig = "BAD SYNTAX";
20201+                       break;
20202+               case DKIM_SIGNATURE_BAD:        /*- -3 */
20203+                       orig = "SIGNATURE BAD";
20204+                       break;
20205+               case DKIM_SIGNATURE_BAD_BUT_TESTING:
20206+                       orig = "SIGNATURE BAD (TESTING)";
20207+                       break;
20208+               case DKIM_SIGNATURE_EXPIRED:
20209+                       orig = "SIGNATURE EXPIRED";
20210+                       break;
20211+               case DKIM_SELECTOR_INVALID:
20212+                       orig = "SELECTOR INVALID";
20213+                       break;
20214+               case DKIM_SELECTOR_GRANULARITY_MISMATCH:
20215+                       orig = "SELECTOR GRANULARITY MISMATCH";
20216+                       break;
20217+               case DKIM_SELECTOR_KEY_REVOKED:
20218+                       orig = "SELECTOR KEY REVOKED";
20219+                       break;
20220+               case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
20221+                       orig = "DOMAIN NAME TOO LONG";
20222+                       break;
20223+               case DKIM_SELECTOR_DNS_TEMP_FAILURE:
20224+                       orig = "DNS TEMP FAILURE";
20225+                       break;
20226+               case DKIM_SELECTOR_DNS_PERM_FAILURE:
20227+                       orig = "DNS PERM FAILURE";
20228+                       break;
20229+               case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
20230+                       orig = "PUBLIC KEY INVALID";
20231+                       break;
20232+               case DKIM_NO_SIGNATURES:
20233+                       orig = "NO SIGNATURES";
20234+                       break;
20235+               case DKIM_NO_VALID_SIGNATURES:
20236+                       orig = "NO VALID SIGNATURES";
20237+                       break;
20238+               case DKIM_BODY_HASH_MISMATCH:
20239+                       orig = "BODY HASH MISMATCH";
20240+                       break;
20241+               case DKIM_SELECTOR_ALGORITHM_MISMATCH:
20242+                       orig = "ALGORITHM MISMATCH";
20243+                       break;
20244+               case DKIM_STAT_INCOMPAT:
20245+                       orig = "STAT INCOMPAT";
20246+                       break;
20247+               case DKIM_UNSIGNED_FROM:
20248+                       orig = "UNSIGNED FROM";
20249+                       break;
20250+               default:
20251+                       orig = "Unkown error";
20252+                       break;
20253+               }
20254+               if (!stralloc_cats(&dkimoutput, orig))
20255+                       die(51, 0);
20256+               if (!stralloc_cats(&dkimoutput, ":"))
20257+                       die(51, 0);
20258+               if (origRet < 0) {
20259+                       if (!stralloc_cats(&dkimoutput, "-"))
20260+                               die(51, 0);
20261+                       strnum[fmt_ulong(strnum, 0 - origRet)] = 0;
20262+               } else
20263+                       strnum[fmt_ulong(strnum, origRet)] = 0;
20264+               if (!stralloc_cats(&dkimoutput, strnum))
20265+                       die(51, 0);
20266+               if (!stralloc_cats(&dkimoutput, ")"))
20267+                       die(51, 0);
20268+       }
20269+       if (!stralloc_cats(&dkimoutput, "\n"))
20270+               die(51, 0);
20271+       if (useSSP && sspStatus) {
20272+               if (!stralloc_cats(&dkimoutput, "X-DKIM-SSP: "))
20273+                       die(51, 0);
20274+               if (!stralloc_cats(&dkimoutput, sspStatus))
20275+                       die(51, 0);
20276+               if (!stralloc_cats(&dkimoutput, "\n"))
20277+                       die(51, 0);
20278+       }
20279+       if (useADSP && adspStatus) {
20280+               if (!stralloc_cats(&dkimoutput, "X-DKIM-ADSP: "))
20281+                       die(51, 0);
20282+               if (!stralloc_cats(&dkimoutput, adspStatus))
20283+                       die(51, 0);
20284+               if (!stralloc_cats(&dkimoutput, "\n"))
20285+                       die(51, 0);
20286+       }
20287+       dkimverify_exit(ret, dkimStatus, code);
20288+       return;
20289+}
20290+
20291+int
20292+checkPractice(int dkimRet)
20293+{
20294+       char           *ptr;
20295+
20296+       if (!(ptr = env_get("DKIMPRACTICE")))
20297+               return (0);
20298+       else
20299+               dkimpractice = ptr;
20300+       if (!*ptr) {
20301+               if (dkimRet < 0 || dkimRet == DKIM_3PS_SIGNATURE)
20302+                       return (1);
20303+               return (0);
20304+       }
20305+       if (dkimRet < 0) {
20306+               if (dkimpractice[str_chr(dkimpractice, 'F' - dkimRet)])
20307+                       return (1);
20308+               if (dkimpractice[str_chr(dkimpractice, 'f' - dkimRet)])
20309+                       return (1);
20310+       } else {
20311+               if (dkimpractice[str_chr(dkimpractice, 'A' + dkimRet)])
20312+                       return (1);
20313+               if (dkimpractice[str_chr(dkimpractice, 'a' + dkimRet)])
20314+                       return (1);
20315+       }
20316+       return (0);
20317+}
20318+
20319+static char    *binqqargs[2] = { "bin/qmail-queue", 0 };
20320+
20321+int
20322+dkim_setoptions(DKIMSignOptions *opts, char *signOptions)
20323+{
20324+       int             ch, argc;
20325+       char          **argv;
20326+
20327+       opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
20328+       opts->nCanon = DKIM_SIGN_RELAXED;                                       /*- c */
20329+       opts->nIncludeBodyLengthTag = 0;                                        /*- l */
20330+       opts->nIncludeQueryMethod = 0;                                          /*- q */
20331+       opts->nIncludeTimeStamp = 0;                                            /*- t */
20332+       opts->nIncludeCopiedHeaders = 0;                                        /*- h */
20333+       opts->szIdentity[0] = '\0';
20334+       opts->expireTime = starttime + 604800;  // expires in 1 week
20335+       opts->nHash = DKIM_HASH_SHA1;
20336+       str_copy(opts->szRequiredHeaders, "NonExistent");
20337+       if (!signOptions)
20338+               return (0);
20339+       if (!stralloc_copys(&dkimopts, "dkim "))
20340+               die(51, 0);
20341+       if (!stralloc_cats(&dkimopts, signOptions))
20342+               die(51, 0);
20343+       if (!stralloc_0(&dkimopts))
20344+               die(51, 0);
20345+       if (!(argv = MakeArgs(dkimopts.s)))
20346+               die(51, 0);
20347+       for (argc = 0;argv[argc];argc++);
20348+#ifdef HAVE_EVP_SHA256
20349+       while ((ch = sgopt(argc, argv, "b:c:li:qthx:z:")) != sgoptdone) {
20350+#else
20351+       while ((ch = sgopt(argc, argv, "b:c:li:qthx:")) != sgoptdone) {
20352+#endif
20353+               switch (ch)
20354+               {
20355+               case 'b':
20356+                       switch (*optarg)
20357+                       {
20358+                       case '1':
20359+                               opts->nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1;
20360+                               break;
20361+                       case '2':
20362+                               opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
20363+                               break;
20364+                       case '3':
20365+                               opts->nIncludeBodyHash = DKIM_BODYHASH_BOTH;
20366+                               break;
20367+                       default:
20368+                               FreeMakeArgs(argv);
20369+                               return (1);
20370+                       }
20371+                       break;
20372+               case 'c':
20373+                       switch (*optarg)
20374+                       {
20375+                       case 'r':
20376+                               opts->nCanon = DKIM_SIGN_RELAXED;
20377+                               break;
20378+                       case 's':
20379+                               opts->nCanon = DKIM_SIGN_SIMPLE;
20380+                               break;
20381+                       case 't':
20382+                               opts->nCanon = DKIM_SIGN_RELAXED_SIMPLE;
20383+                               break;
20384+                       case 'u':
20385+                               opts->nCanon = DKIM_SIGN_SIMPLE_RELAXED;
20386+                               break;
20387+                       default:
20388+                               FreeMakeArgs(argv);
20389+                               return (1);
20390+                       }
20391+                       break;
20392+               case 'l': /*- body length tag */
20393+                       opts->nIncludeBodyLengthTag = 1;
20394+                       break;
20395+               case 'q': /*- query method tag */
20396+                       opts->nIncludeQueryMethod = 1;
20397+                       break;
20398+               case 't': /*- timestamp tag */
20399+                       opts->nIncludeTimeStamp = 1;
20400+                       break;
20401+               case 'h':
20402+                       opts->nIncludeCopiedHeaders = 1;
20403+                       break;
20404+               case 'i':       /*- identity */
20405+                       if (*optarg == '-') /* do not use i= tag */
20406+                               opts->szIdentity[0] = '\0';
20407+                       else
20408+                               str_copyb(opts->szIdentity, optarg, sizeof(opts->szIdentity) - 1);
20409+                       break;
20410+               case 'x': /*- expire time */
20411+                       if (*optarg == '-')
20412+                               opts->expireTime = 0;
20413+                       else
20414+                               opts->expireTime = starttime + atoi(optarg);
20415+                       break;
20416+#ifdef HAVE_EVP_SHA256
20417+               case 'z': /*- sign w/ sha1, sha256 or both */
20418+                       switch (*optarg)
20419+                       {
20420+                       case '1':
20421+                               opts->nHash = DKIM_HASH_SHA1;
20422+                               break;
20423+                       case '2':
20424+                               opts->nHash = DKIM_HASH_SHA256;
20425+                               break;
20426+                       case '3':
20427+                               opts->nHash = DKIM_HASH_SHA1_AND_256;
20428+                               break;
20429+                       default:
20430+                               FreeMakeArgs(argv);
20431+                               return (1);
20432+                       }
20433+                       break;
20434+#endif
20435+               default:
20436+                       FreeMakeArgs(argv);
20437+                       return (1);
20438+               } /*- switch (ch) */
20439+       } /*- while (1) */
20440+       FreeMakeArgs(argv);
20441+       return (0);
20442+}
20443+
20444+int
20445+main(int argc, char *argv[])
20446+{
20447+       int             errfd, pim[2];
20448+       int             wstat;
20449+       int             resDKIMSSP = -1, resDKIMADSP = -1, useSSP = 0, useADSP = 0, accept3ps = 0;
20450+       int             sCount = 0, sSize = 0;
20451+       int             ret = 0, origRet = DKIM_MAX_ERROR, i, nSigCount = 0, len, token_len;
20452+       unsigned long   pid;
20453+       char           *selector, *p;
20454+       stralloc        dkimfn = {0};
20455+       DKIMSignOptions opts = { 0 };
20456+       DKIMVerifyDetails *pDetails;
20457+       DKIMVerifyOptions vopts = { 0 };
20458+
20459+       starttime = now();
20460+       sig_blocknone();
20461+       umask(033);
20462+       if (!(p = env_get("ERROR_FD")))
20463+               errfd = CUSTOM_ERR_FD;
20464+       else
20465+               scan_int(p, &errfd);
20466+       substdio_fdbuf(&sserr, write, errfd, errbuf, sizeof(errbuf));
20467+       if (chdir(auto_qmail) == -1)
20468+               die(61, 0);
20469+       dkimqueue = env_get("DKIMQUEUE");
20470+       if (dkimqueue && *dkimqueue)
20471+               binqqargs[0] = dkimqueue;
20472+       dkimsign = env_get("DKIMSIGN");
20473+       dkimverify = env_get("DKIMVERIFY");
20474+       p = (env_get("RELAYCLIENT") || env_get("AUTHINFO")) ? "" : 0;
20475+       if (dkimverify && p && env_get("RELAYCLIENT_NODKIMVERIFY")) {
20476+               execv(*binqqargs, binqqargs);
20477+               die(120, 0);
20478+       }
20479+       if (!dkimsign && !dkimverify && p) {
20480+               if (!(dkimsign = env_get("DKIMKEY"))) {
20481+                       if (!stralloc_copys(&dkimfn, "domainkeys/%/default"))
20482+                               die(51, 0);
20483+                       if (!stralloc_0(&dkimfn))
20484+                               die(51, 0);
20485+                       dkimsign = dkimfn.s;
20486+               }
20487+       }
20488+       if (dkimsign) {
20489+               /* selector */
20490+               p = dkimsign;
20491+               selector = p;
20492+               while (*p) {
20493+                       if (*p == '/' && *(p + 1))
20494+                               selector = p + 1;
20495+                       p++;
20496+               }
20497+               str_copyb(opts.szSelector, selector, sizeof(opts.szSelector) - 1);
20498+
20499+               if (dkim_setoptions(&opts, env_get("DKIMSIGNOPTIONS"))) {
20500+                       custom_error("Z", "Invalid DKIMSIGNOPTIONS (#4.3.0)", 0);
20501+                       _exit(88);
20502+               }
20503+               p = env_get("DKIMIDENTITY");
20504+               if (p && *p)
20505+                       str_copyb(opts.szIdentity, p, sizeof(opts.szIdentity) - 1);
20506+               p = env_get("DKIMEXPIRE");
20507+               if (p && *p)
20508+                       opts.expireTime = starttime + atol(p);
20509+               else
20510+               if (p)
20511+                       opts.expireTime = 0;
20512+               opts.pfnHeaderCallback = SignThisHeader;
20513+               if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS) { /*- failed to initialize signature */
20514+                       custom_error("Z", "dkim initialization failed (#4.3.0)", 0);
20515+                       _exit(88);
20516+               }
20517+       } else {
20518+               char           *x;
20519+
20520+               if (!dkimverify)
20521+                       dkimverify = "";
20522+               if (!(x = env_get("SIGN_PRACTICE")))
20523+                       x = "adsp";
20524+               if (!str_diffn("adsp", x, 4)) {
20525+                       useADSP = 1;
20526+                       accept3ps = 1;
20527+               }
20528+               else
20529+               if (!str_diffn("ssp", x, 3)) {
20530+                       useSSP = 1;
20531+                       accept3ps = 1;
20532+               } else
20533+               if (!str_diffn("local", x, 5)) {
20534+                       useSSP = 0;
20535+                       useADSP = 0;
20536+                       accept3ps = 1;
20537+               }
20538+               if (useADSP)
20539+                       vopts.nCheckPractices = useADSP;
20540+               else
20541+               if (useSSP)
20542+                       vopts.nCheckPractices = useSSP;
20543+               else
20544+                       vopts.nCheckPractices = 0;
20545+               vopts.nAccept3ps = accept3ps;
20546+               vopts.pfnSelectorCallback = NULL;       /*- SelectorCallback; */
20547+               if (env_get("UNSIGNED_FROM"))
20548+                       vopts.nAllowUnsignedFromHeaders = 1;
20549+               vopts.nSubjectRequired = env_get("UNSIGNED_SUBJECT") ? 0 : 1;
20550+               DKIMVerifyInit(&ctxt, &vopts);          /*- this is always successful */
20551+       }
20552+       /*- Initialization */
20553+       mypid = getpid();
20554+       uid = getuid();
20555+       datetime_tai(&dt, starttime);
20556+       sig_pipeignore();
20557+       sig_miscignore();
20558+       sig_alarmcatch(sigalrm);
20559+       sig_bugcatch(sigbug);
20560+       alarm(DEATH);
20561+       pidopen(); /*- fd = messfd */
20562+       if ((readfd = open_read(pidfn)) == -1)
20563+               die(63, dkimsign ? 1 : 2);
20564+       if (unlink(pidfn) == -1)
20565+               die(63, dkimsign ? 1 : 2);
20566+       substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf));
20567+       substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf));
20568+       for (ret = 0;;) {
20569+               register int    n;
20570+               register char  *x;
20571+
20572+               if ((n = substdio_feed(&ssin)) < 0) {
20573+                       (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20574+                       die_read();
20575+               }
20576+               if (!n)
20577+                       break;
20578+               x = substdio_PEEK(&ssin);
20579+               if (!ret) {
20580+                       if ((ret = (dkimsign ? DKIMSignProcess : DKIMVerifyProcess) (&ctxt, x, n)) == DKIM_INVALID_CONTEXT)
20581+                               (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20582+                       maybe_die_dkim(ret);
20583+               }
20584+               if (substdio_put(&ssout, x, n) == -1) {
20585+                       (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20586+                       die_write();
20587+               }
20588+               substdio_SEEK(&ssin, n);
20589+       }
20590+       if (substdio_flush(&ssout) == -1) {
20591+               (dkimsign ? DKIMSignFree : DKIMVerifyFree) (&ctxt);
20592+               die_write();
20593+       }
20594+       if (dkimsign || dkimverify) {
20595+               if (dkimsign) {
20596+                       char           *p;
20597+
20598+                       if (!(p = DKIMSignGetDomain(&ctxt))) {
20599+                               DKIMSignFree(&ctxt);
20600+                               maybe_die_dkim(DKIM_INVALID_CONTEXT);
20601+                       }
20602+                       write_signature(p, dkimsign); /*- calls DKIMSignFree(&ctxt) */
20603+               } else
20604+               if (dkimverify) {
20605+                       char            szPolicy[512];
20606+
20607+                       if (!ret) {
20608+                               if ((ret = DKIMVerifyResults(&ctxt, &sCount, &sSize)) != DKIM_SUCCESS)
20609+                                       maybe_die_dkim(ret);
20610+                               if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS)
20611+                                       maybe_die_dkim(ret);
20612+                               else
20613+                               for (ret = DKIM_SUCCESS,i = 0; i < nSigCount; i++) {
20614+                                       if (pDetails[i].nResult < 0) {
20615+                                               ret = pDetails[i].nResult;
20616+                                               break; /*- don't know if it is right to break */
20617+                                       }
20618+                               }
20619+                               if (!nSigCount)
20620+                                       ret = DKIM_NO_SIGNATURES;
20621+                       }
20622+                       /*- what to do if DKIM Verification fails */
20623+                       if (checkPractice(ret)) {
20624+                               char           *domain;
20625+
20626+                               origRet = ret;
20627+                               if ((domain = DKIMVerifyGetDomain(&ctxt))) {
20628+                                       if (!(p = env_get("SIGNATUREDOMAINS"))) {
20629+                                               if (control_readfile(&sigdomains, "signaturedomains", 0) == -1)
20630+                                                       die(55, 2);
20631+                                       } else
20632+                                       if (!stralloc_copys(&sigdomains, p))
20633+                                               die(51, 2);
20634+                                       for (len = 0, p = sigdomains.s;len < sigdomains.len;) {
20635+                                               len += ((token_len = str_len(p)) + 1); /*- next domain */
20636+                                               if (!case_diffb(p, token_len, domain)) {
20637+                                                       ret = DKIM_FAIL;
20638+                                                       useADSP = 0;
20639+                                                       useSSP = 0;
20640+                                                       break;
20641+                                               }
20642+                                               p = sigdomains.s + len;
20643+                                       }
20644+                                       if (!(p = env_get("NOSIGNATUREDOMAINS"))) {
20645+                                               if (control_readfile(&nsigdomains, "nosignaturedomains", 0) == -1)
20646+                                                       die(55, 2);
20647+                                       } else
20648+                                       if (!stralloc_copys(&nsigdomains, p))
20649+                                               die(51, 2);
20650+                                       for (len = 0, p = nsigdomains.s;len < nsigdomains.len;) {
20651+                                               len += ((token_len = str_len(p)) + 1); /*- next domain */
20652+                                               if (*p == '*' || !case_diffb(p, token_len, domain)) {
20653+                                                       ret = DKIM_NEUTRAL;
20654+                                                       useADSP = 0;
20655+                                                       useSSP = 0;
20656+                                                       break;
20657+                                               }
20658+                                               p = nsigdomains.s + len;
20659+                                       }
20660+                               }
20661+                               if (!domain || !*domain)
20662+                                       ; /*- do nothing ? */
20663+                               else
20664+                               if (useADSP) {
20665+                                       resDKIMADSP = checkADSP(domain);
20666+                                       if (sCount > 0) {
20667+                                               if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL)
20668+                                                       ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
20669+                                       }
20670+                                       /* if the message should be signed, return fail */
20671+                                       if (resDKIMADSP == DKIM_ADSP_DISCARDABLE)
20672+                                               ret = DKIM_FAIL;
20673+                                       else
20674+                                               ret = DKIM_NEUTRAL;
20675+                               } else
20676+                               if (useSSP) {
20677+                                       int             bTestingPractices = 0;
20678+                                       char           *domain;
20679+       
20680+                                       if ((domain = DKIMVerifyGetDomain(&ctxt)))
20681+                                               resDKIMSSP = checkSSP(domain, &bTestingPractices);
20682+                                       if (sCount > 0) {
20683+                                               if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL))
20684+                                                       ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
20685+                                       }
20686+                                       // if the SSP is testing, return neutral
20687+                                       if (bTestingPractices)
20688+                                               ret = DKIM_NEUTRAL;
20689+                                       /* if the message should be signed, return fail */
20690+                                       if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT)
20691+                                               ret = DKIM_FAIL;
20692+                                       else
20693+                                               ret = DKIM_NEUTRAL;
20694+                               }
20695+                       }
20696+                       DKIMVerifyFree(&ctxt);
20697+                       writeHeaderNexit(ret, origRet, resDKIMSSP, resDKIMADSP, useSSP, useADSP);
20698+               } /*- if (dkimverify) */
20699+       }
20700+       if (pipe(pim) == -1)
20701+               die(59, 0);
20702+       switch (pid = vfork())
20703+       {
20704+       case -1:
20705+               close(pim[0]);
20706+               close(pim[1]);
20707+               die(58, 0);
20708+       case 0:
20709+               close(pim[1]);
20710+               if (fd_move(0, pim[0]) == -1)
20711+                       die(120, 0);
20712+               execv(*binqqargs, binqqargs);
20713+               die(120, 0);
20714+       }
20715+       close(pim[0]);
20716+       substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf));
20717+       substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf));
20718+       if (substdio_bput(&ssout, dkimoutput.s, dkimoutput.len) == -1) /*- write DKIM signature */
20719+               die_write();
20720+       switch (substdio_copy(&ssout, &ssin))
20721+       {
20722+       case -2:
20723+               die_read();
20724+       case -3:
20725+               die_write();
20726+       }
20727+       if (substdio_flush(&ssout) == -1)
20728+               die_write();
20729+       close(pim[1]);
20730+       if (wait_pid(&wstat, pid) != pid)
20731+               die(57, 0);
20732+       if (wait_crashed(wstat))
20733+               die(57, 0);
20734+       die(wait_exitcode(wstat), 0);
20735+       /*- Not Reached */
20736+       exit(0);
20737+}
20738+
20739+void
20740+getversion_qmail_dkim_c()
20741+{
20742+       static char    *x = "$Id: qmail-dkim.c,v 1.37 2013-01-24 22:37:22+05:30 Cprogrammer Exp mbhangui $";
20743+
20744+       x++;
20745+}
20746diff -ruN ../netqmail-1.06-original/qmail-inject.c netqmail-1.06/qmail-inject.c
20747--- ../netqmail-1.06-original/qmail-inject.c    1998-06-15 12:53:16.000000000 +0200
20748+++ netqmail-1.06/qmail-inject.c        2016-11-22 21:03:57.120527832 +0100
20749@@ -22,6 +22,7 @@
20750 #include "auto_qmail.h"
20751 #include "newfield.h"
20752 #include "constmap.h"
20753+#include "srs.h"
20754 
20755 #define LINELEN 80
20756 
20757@@ -61,6 +62,11 @@
20758 void temp() { _exit(111); }
20759 void die_nomem() {
20760  substdio_putsflush(subfderr,"qmail-inject: fatal: out of memory\n"); temp(); }
20761+void die_srs() {
20762+ substdio_puts("qmail-inject: fatal: ");
20763+ substdio_puts(subfderr,srs_error.s);
20764+ substdio_putsflush(subfderr,"\n");
20765+ perm(); }
20766 void die_invalid(sa) stralloc *sa; {
20767  substdio_putsflush(subfderr,"qmail-inject: fatal: invalid header field: ");
20768  substdio_putflush(subfderr,sa->s,sa->len); perm(); }
20769@@ -99,6 +105,17 @@
20770    int i;
20771 
20772    if (!stralloc_0(&sender)) die_nomem();
20773+   
20774+   if (!env_get("QMAILINJECT_SKIP_SRS") && (env_get("QMAILINJECT_FORCE_SRS") || (env_get("EXT") && env_get("HOST")))) {
20775+     switch(srsforward(sender.s)) {
20776+       case -3: die_srs(); break;
20777+       case -2: die_nomem(); break;
20778+       case -1: die_read(); break;
20779+       case 0: break;
20780+       case 1: if (!stralloc_copy(&sender,&srs_result)) die_nomem(); break;
20781+     }
20782+   }
20783+   
20784    qmail_from(&qqt,sender.s);
20785 
20786    for (i = 0;i < reciplist.len;++i)
20787@@ -269,6 +286,10 @@
20788 token822_alloc *addr;
20789 {
20790  if (!addr->len) return; /* don't rewrite <> */
20791+ if (addr->len == 1 && str_equal(addr->t[0].s,"<>")) {
20792+ addr->len = 0;
20793+ return;
20794+ }
20795  if (addr->len >= 2)
20796    if (addr->t[1].type == TOKEN822_AT)
20797      if (addr->t[0].type == TOKEN822_LITERAL)
20798diff -ruN ../netqmail-1.06-original/qmail-local.c netqmail-1.06/qmail-local.c
20799--- ../netqmail-1.06-original/qmail-local.c     2007-11-30 21:22:54.000000000 +0100
20800+++ netqmail-1.06/qmail-local.c 2018-01-01 12:10:21.981102868 +0100
20801@@ -28,6 +28,7 @@
20802 #include "myctime.h"
20803 #include "gfrom.h"
20804 #include "auto_patrn.h"
20805+#include "srs.h"
20806 
20807 void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); }
20808 
20809@@ -66,6 +67,15 @@
20810 
20811 char buf[1024];
20812 char outbuf[1024];
20813+#define QUOTABUFSIZE    256
20814+
20815+void die_control() { strerr_die1x(111,"Unable to read controls (#4.3.0)"); }
20816+void die_srs() {
20817+  if (!stralloc_copys(&foo,srs_error.s)) temp_nomem();
20818+  if (!stralloc_cats(&foo," (#4.3.0)")) temp_nomem();
20819+  if (!stralloc_0(&foo)) temp_nomem();
20820+  strerr_die1x(111,foo.s);
20821+}
20822 
20823 /* child process */
20824 
20825@@ -86,9 +96,15 @@
20826  int fd;
20827  substdio ss;
20828  substdio ssout;
20829+ char quotabuf[QUOTABUFSIZE];
20830 
20831  sig_alarmcatch(sigalrm);
20832  if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
20833+ if (maildir_getquota(dir, quotabuf) == 0) {
20834+  if (user_over_maildirquota(dir,quotabuf)==1) {
20835+   _exit(1);
20836+  }
20837+ }
20838  pid = getpid();
20839  host[0] = 0;
20840  gethostname(host,sizeof(host));
20841@@ -99,7 +115,10 @@
20842    s += fmt_str(s,"tmp/");
20843    s += fmt_ulong(s,time); *s++ = '.';
20844    s += fmt_ulong(s,pid); *s++ = '.';
20845-   s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
20846+   s += fmt_strn(s,host,sizeof(host));
20847+   s += fmt_strn(s,",S=",sizeof(",S="));
20848+   if (fstat(0,&st) == -1) if (errno == error_noent) break;
20849+   s += fmt_ulong(s,st.st_size+rpline.len+dtline.len); *s++ = 0;
20850    if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
20851    /* really should never get to this point */
20852    if (loop == 2) _exit(1);
20853@@ -159,6 +178,7 @@
20854  switch(wait_exitcode(wstat))
20855   {
20856    case 0: break;
20857+   case 1: strerr_die1x(1, "User over quota. (#5.1.1)");
20858    case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
20859    case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
20860    case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
20861@@ -282,6 +302,15 @@
20862    qmail_put(&qqt,messline.s,messline.len);
20863   }
20864  while (match);
20865+
20866+ switch(srsforward(ueo.s)) {
20867+   case -3: die_srs(); break;
20868+   case -2: temp_nomem(); break;
20869+   case -1: die_control(); break;
20870+   case 0: break;
20871+   case 1: if (!stralloc_copy(&ueo,&srs_result)) temp_nomem(); break;
20872+ }
20873+
20874  qmail_from(&qqt,ueo.s);
20875  while (*recips) qmail_to(&qqt,*recips++);
20876  qqx = qmail_close(&qqt);
20877diff -ruN ../netqmail-1.06-original/qmail-lspawn.c netqmail-1.06/qmail-lspawn.c
20878--- ../netqmail-1.06-original/qmail-lspawn.c    1998-06-15 12:53:16.000000000 +0200
20879+++ netqmail-1.06/qmail-lspawn.c        2016-11-22 21:03:57.120527832 +0100
20880@@ -1,4 +1,5 @@
20881 #include "fd.h"
20882+#include "env.h"
20883 #include "wait.h"
20884 #include "prot.h"
20885 #include "substdio.h"
20886@@ -170,6 +171,7 @@
20887 char *s; char *r; int at;
20888 {
20889  int f;
20890+ char *ptr;
20891 
20892  if (!(f = fork()))
20893   {
20894@@ -226,7 +228,10 @@
20895    if (prot_uid(uid) == -1) _exit(QLX_USAGE);
20896    if (!getuid()) _exit(QLX_ROOT);
20897 
20898-   execv(*args,args);
20899+       if(!(ptr = env_get("QMAILLOCAL")))
20900+               execv(*args, args);
20901+       else
20902+               execv(ptr, args);
20903    if (error_temp(errno)) _exit(QLX_EXECSOFT);
20904    _exit(QLX_EXECHARD);
20905   }
20906diff -ruN ../netqmail-1.06-original/qmail-newmvrt.c netqmail-1.06/qmail-newmvrt.c
20907--- ../netqmail-1.06-original/qmail-newmvrt.c   1970-01-01 01:00:00.000000000 +0100
20908+++ netqmail-1.06/qmail-newmvrt.c       2016-11-22 21:04:20.041763530 +0100
20909@@ -0,0 +1,70 @@
20910+#include "strerr.h"
20911+#include "stralloc.h"
20912+#include "substdio.h"
20913+#include "getln.h"
20914+#include "exit.h"
20915+#include "readwrite.h"
20916+#include "open.h"
20917+#include "auto_qmail.h"
20918+#include "cdbmss.h"
20919+
20920+#define FATAL "qmail-newmvrt: fatal: "
20921+
20922+void die_read()
20923+{
20924+  strerr_die2sys(111,FATAL,"unable to read control/morevalidrcptto: ");
20925+}
20926+void die_write()
20927+{
20928+  strerr_die2sys(111,FATAL,"unable to write to control/morevalidrcptto.tmp: ");
20929+}
20930+
20931+char inbuf[1024];
20932+substdio ssin;
20933+
20934+int fd;
20935+int fdtemp;
20936+
20937+struct cdbmss cdbmss;
20938+stralloc line = {0};
20939+int match;
20940+
20941+void main()
20942+{
20943+  umask(033);
20944+  if (chdir(auto_qmail) == -1)
20945+    strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
20946+
20947+  fd = open_read("control/morevalidrcptto");
20948+  if (fd == -1) die_read();
20949+
20950+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
20951+
20952+  fdtemp = open_trunc("control/morevalidrcptto.tmp");
20953+  if (fdtemp == -1) die_write();
20954+
20955+  if (cdbmss_start(&cdbmss,fdtemp) == -1) die_write();
20956+
20957+  for (;;) {
20958+    if (getln(&ssin,&line,&match,'\n') != 0) die_read();
20959+    case_lowerb(line.s,line.len);
20960+    while (line.len) {
20961+      if (line.s[line.len - 1] == ' ') { --line.len; continue; }
20962+      if (line.s[line.len - 1] == '\n') { --line.len; continue; }
20963+      if (line.s[line.len - 1] == '\t') { --line.len; continue; }
20964+      if (line.s[0] != '#')
20965+       if (cdbmss_add(&cdbmss,line.s,line.len,"",0) == -1)
20966+         die_write();
20967+      break;
20968+    }
20969+    if (!match) break;
20970+  }
20971+
20972+  if (cdbmss_finish(&cdbmss) == -1) die_write();
20973+  if (fsync(fdtemp) == -1) die_write();
20974+  if (close(fdtemp) == -1) die_write(); /* NFS stupidity */
20975+  if (rename("control/morevalidrcptto.tmp","control/morevalidrcptto.cdb") == -1)
20976+    strerr_die2sys(111,FATAL,"unable to move control/morevalidrcptto.tmp to control/morevalidrcptto.cdb");
20977+
20978+  _exit(0);
20979+}
20980diff -ruN ../netqmail-1.06-original/qmail-pop3d.c netqmail-1.06/qmail-pop3d.c
20981--- ../netqmail-1.06-original/qmail-pop3d.c     2007-11-30 21:22:54.000000000 +0100
20982+++ netqmail-1.06/qmail-pop3d.c 2016-11-22 21:03:57.121527799 +0100
20983@@ -16,6 +16,11 @@
20984 #include "readwrite.h"
20985 #include "timeoutread.h"
20986 #include "timeoutwrite.h"
20987+#include <errno.h>
20988+#include "maildirquota.h"
20989+#include "maildirmisc.h"
20990+
20991+#define QUOTABUFSIZE 256
20992 
20993 void die() { _exit(0); }
20994 
20995@@ -45,19 +50,15 @@
20996 {
20997   substdio_put(&ssout,buf,len);
20998 }
20999-void puts(s) char *s;
21000-{
21001-  substdio_puts(&ssout,s);
21002-}
21003 void flush()
21004 {
21005   substdio_flush(&ssout);
21006 }
21007 void err(s) char *s;
21008 {
21009-  puts("-ERR ");
21010-  puts(s);
21011-  puts("\r\n");
21012+  substdio_puts(&ssout,"-ERR ");
21013+  substdio_puts(&ssout,s);
21014+  substdio_puts(&ssout,"\r\n");
21015   flush();
21016 }
21017 
21018@@ -73,7 +74,7 @@
21019 void err_nosuch() { err("unable to open that message"); }
21020 void err_nounlink() { err("unable to unlink all deleted messages"); }
21021 
21022-void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
21023+void okay() { substdio_puts(&ssout,"+OK \r\n"); flush(); }
21024 
21025 void printfn(fn) char *fn;
21026 {
21027@@ -153,11 +154,11 @@
21028 
21029   total = 0;
21030   for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
21031-  puts("+OK ");
21032+  substdio_puts(&ssout,"+OK ");
21033   put(strnum,fmt_uint(strnum,numm));
21034-  puts(" ");
21035+  substdio_puts(&ssout," ");
21036   put(strnum,fmt_ulong(strnum,total));
21037-  puts("\r\n");
21038+  substdio_puts(&ssout,"\r\n");
21039   flush();
21040 }
21041 
21042@@ -171,18 +172,41 @@
21043 
21044 void pop3_last(arg) char *arg;
21045 {
21046-  puts("+OK ");
21047+  substdio_puts(&ssout,"+OK ");
21048   put(strnum,fmt_uint(strnum,last));
21049-  puts("\r\n");
21050+  substdio_puts(&ssout,"\r\n");
21051   flush();
21052 }
21053 
21054 void pop3_quit(arg) char *arg;
21055 {
21056   int i;
21057+  char quotabuf[QUOTABUFSIZE];
21058+  int has_quota=maildir_getquota(".", quotabuf);
21059+
21060+  long deleted_bytes=0;
21061+  long deleted_messages=0;
21062+
21063   for (i = 0;i < numm;++i)
21064     if (m[i].flagdeleted) {
21065-      if (unlink(m[i].fn) == -1) err_nounlink();
21066+      unsigned long un=0;
21067+      const char *filename=m[i].fn;
21068+      if (has_quota == 0 && !MAILDIR_DELETED(filename)) {
21069+          if (maildir_parsequota(filename, &un)) {
21070+              struct stat stat_buf;
21071+
21072+              if (stat(filename, &stat_buf) == 0)
21073+                  un=stat_buf.st_size;
21074+          }
21075+      }
21076+      if (unlink(m[i].fn) == -1) {
21077+          err_nounlink();
21078+          un=0;
21079+      }
21080+      if (un) {
21081+          deleted_bytes -= un;
21082+          deleted_messages -= 1;
21083+      }
21084     }
21085     else
21086       if (str_start(m[i].fn,"new/")) {
21087@@ -192,6 +216,21 @@
21088        if (!stralloc_0(&line)) die_nomem();
21089        rename(m[i].fn,line.s); /* if it fails, bummer */
21090       }
21091+
21092+    if (deleted_messages < 0) {
21093+        int quotafd;
21094+
21095+        if (maildir_checkquota(".", &quotafd, quotabuf, deleted_bytes,
21096+                               deleted_messages) && errno != EAGAIN &&
21097+                               deleted_bytes >= 0)
21098+            {
21099+                if (quotafd >= 0) close (quotafd);
21100+            } else {
21101+                 maildir_addquota(".", quotafd, quotabuf,
21102+                                 deleted_bytes, deleted_messages);
21103+                 if (quotafd >= 0) close(quotafd);
21104+            }
21105+        }
21106   okay(0);
21107   die();
21108 }
21109@@ -222,10 +261,10 @@
21110 int flaguidl;
21111 {
21112   put(strnum,fmt_uint(strnum,i + 1));
21113-  puts(" ");
21114+  substdio_puts(&ssout," ");
21115   if (flaguidl) printfn(m[i].fn);
21116   else put(strnum,fmt_ulong(strnum,m[i].size));
21117-  puts("\r\n");
21118+  substdio_puts(&ssout,"\r\n");
21119 }
21120 
21121 void dolisting(arg,flaguidl) char *arg; int flaguidl;
21122@@ -234,7 +273,7 @@
21123   if (*arg) {
21124     i = msgno(arg);
21125     if (i == -1) return;
21126-    puts("+OK ");
21127+    substdio_puts(&ssout,"+OK ");
21128     list(i,flaguidl);
21129   }
21130   else {
21131@@ -242,7 +281,7 @@
21132     for (i = 0;i < numm;++i)
21133       if (!m[i].flagdeleted)
21134        list(i,flaguidl);
21135-    puts(".\r\n");
21136+    substdio_puts(&ssout,".\r\n");
21137   }
21138   flush();
21139 }
21140diff -ruN ../netqmail-1.06-original/qmail-pw2u.c netqmail-1.06/qmail-pw2u.c
21141--- ../netqmail-1.06-original/qmail-pw2u.c      1998-06-15 12:53:16.000000000 +0200
21142+++ netqmail-1.06/qmail-pw2u.c  2016-11-22 21:03:57.121527799 +0100
21143@@ -1,3 +1,4 @@
21144+#include <unistd.h>
21145 #include <sys/types.h>
21146 #include <sys/stat.h>
21147 #include "substdio.h"
21148diff -ruN ../netqmail-1.06-original/qmail-qmqpc.c netqmail-1.06/qmail-qmqpc.c
21149--- ../netqmail-1.06-original/qmail-qmqpc.c     1998-06-15 12:53:16.000000000 +0200
21150+++ netqmail-1.06/qmail-qmqpc.c 2016-11-22 21:03:57.121527799 +0100
21151@@ -102,6 +102,8 @@
21152 char *server;
21153 {
21154   struct ip_address ip;
21155+  struct ip_address outip;
21156+  outip.d[0]=outip.d[1]=outip.d[2]=outip.d[3]=(unsigned char) 0;
21157   char ch;
21158 
21159   if (!ip_scan(server,&ip)) return;
21160@@ -109,7 +111,7 @@
21161   qmqpfd = socket(AF_INET,SOCK_STREAM,0);
21162   if (qmqpfd == -1) die_socket();
21163 
21164-  if (timeoutconn(qmqpfd,&ip,PORT_QMQP,10) != 0) {
21165+  if (timeoutconn(qmqpfd,&ip,&outip,PORT_QMQP,10) != 0) {
21166     lasterror = 73;
21167     if (errno == error_timeout) lasterror = 72;
21168     close(qmqpfd);
21169diff -ruN ../netqmail-1.06-original/qmail-qmtpd.c netqmail-1.06/qmail-qmtpd.c
21170--- ../netqmail-1.06-original/qmail-qmtpd.c     1998-06-15 12:53:16.000000000 +0200
21171+++ netqmail-1.06/qmail-qmtpd.c 2016-11-22 21:03:57.121527799 +0100
21172@@ -1,3 +1,5 @@
21173+#include <unistd.h>
21174+#include <unistd.h>
21175 #include "stralloc.h"
21176 #include "substdio.h"
21177 #include "qmail.h"
21178diff -ruN ../netqmail-1.06-original/qmail-qstat.sh netqmail-1.06/qmail-qstat.sh
21179--- ../netqmail-1.06-original/qmail-qstat.sh    1998-06-15 12:53:16.000000000 +0200
21180+++ netqmail-1.06/qmail-qstat.sh        2016-11-22 21:03:57.121527799 +0100
21181@@ -1,7 +1,7 @@
21182 cd QMAIL
21183 messdirs=`echo queue/mess/* | wc -w`
21184 messfiles=`find queue/mess/* -print | wc -w`
21185-tododirs=`echo queue/todo | wc -w`
21186-todofiles=`find queue/todo -print | wc -w`
21187+tododirs=`echo queue/todo/* | wc -w`
21188+todofiles=`find queue/todo/* -print | wc -w`
21189 echo messages in queue: `expr $messfiles - $messdirs`
21190 echo messages in queue but not yet preprocessed: `expr $todofiles - $tododirs`
21191diff -ruN ../netqmail-1.06-original/qmail-queue.8 netqmail-1.06/qmail-queue.8
21192--- ../netqmail-1.06-original/qmail-queue.8     2007-11-30 21:22:54.000000000 +0100
21193+++ netqmail-1.06/qmail-queue.8 2016-12-05 19:24:19.597680897 +0100
21194@@ -46,6 +46,13 @@
21195 will invoke the contents of
21196 .B $QMAILQUEUE
21197 instead, if that environment variable is set.
21198+.SH "CONTROL FILES"
21199+.TP 5
21200+.I taps
21201+Should contain source address (T for To,F for From,
21202+A for Any), regex syntax of email addresses to tap
21203+and the associated email address to send the copy to.
21204+The fields should be separated by colon.
21205 .SH "FILESYSTEM RESTRICTIONS"
21206 .B qmail-queue
21207 imposes two constraints on the queue structure:
21208diff -ruN ../netqmail-1.06-original/qmail-queue.c netqmail-1.06/qmail-queue.c
21209--- ../netqmail-1.06-original/qmail-queue.c     1998-06-15 12:53:16.000000000 +0200
21210+++ netqmail-1.06/qmail-queue.c 2016-12-05 19:24:19.597680897 +0100
21211@@ -16,6 +16,8 @@
21212 #include "auto_uids.h"
21213 #include "date822fmt.h"
21214 #include "fmtqfn.h"
21215+#include "stralloc.h"
21216+#include "constmap.h"
21217 
21218 #define DEATH 86400 /* 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
21219 #define ADDR 1003
21220@@ -25,6 +27,14 @@
21221 char outbuf[256];
21222 struct substdio ssout;
21223 
21224+int tapok = 0;
21225+stralloc tap = {0};
21226+struct constmap maptap;
21227+stralloc chkaddr = {0};
21228+int tapped;
21229+stralloc tapaddr = {0};
21230+stralloc controlfile = {0};
21231+
21232 datetime_sec starttime;
21233 struct datetime dt;
21234 unsigned long mypid;
21235@@ -175,13 +185,20 @@
21236 
21237  alarm(DEATH);
21238 
21239+ stralloc_copys( &controlfile, auto_qmail);
21240+ stralloc_cats( &controlfile, "/control/taps");
21241+ stralloc_0( &controlfile);
21242+ tapok = control_readfile(&tap,controlfile.s,0);
21243+ if (tapok == -1) die(65);
21244+ if (!constmap_init(&maptap,tap.s,tap.len,0)) die(65);
21245+
21246  pidopen();
21247  if (fstat(messfd,&pidst) == -1) die(63);
21248 
21249  messnum = pidst.st_ino;
21250  messfn = fnnum("mess/",1);
21251- todofn = fnnum("todo/",0);
21252- intdfn = fnnum("intd/",0);
21253+ todofn = fnnum("todo/",1);
21254+ intdfn = fnnum("intd/",1);
21255 
21256  if (link(pidfn,messfn) == -1) die(64);
21257  if (unlink(pidfn) == -1) die(63);
21258@@ -219,14 +236,28 @@
21259  if (substdio_get(&ssin,&ch,1) < 1) die_read();
21260  if (ch != 'F') die(91);
21261  if (substdio_bput(&ssout,&ch,1) == -1) die_write();
21262+ stralloc_0(&chkaddr);
21263  for (len = 0;len < ADDR;++len)
21264   {
21265+   if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
21266+   else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
21267    if (substdio_get(&ssin,&ch,1) < 1) die_read();
21268    if (substdio_put(&ssout,&ch,1) == -1) die_write();
21269    if (!ch) break;
21270   }
21271  if (len >= ADDR) die(11);
21272 
21273+ /* check the from address */
21274+ stralloc_0(&chkaddr);
21275+ if (tapped == 0 && tapcheck('F')==1 ) {
21276+   tapped = 1;
21277+   if ( tapaddr.len > 0 ) {
21278+     if (substdio_bput(&ssout,"T",1) == -1) die_write();
21279+     if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
21280+     if (substdio_bput(&ssout,"",1) == -1) die_write();
21281+   }
21282+ }
21283+
21284  if (substdio_bput(&ssout,QUEUE_EXTRA,QUEUE_EXTRALEN) == -1) die_write();
21285 
21286  for (;;)
21287@@ -237,10 +268,24 @@
21288    if (substdio_bput(&ssout,&ch,1) == -1) die_write();
21289    for (len = 0;len < ADDR;++len)
21290     {
21291+     if ( len == 1 ) stralloc_copyb(&chkaddr, &ch,1);
21292+     else if ( len > 1 ) stralloc_catb(&chkaddr, &ch,1);
21293      if (substdio_get(&ssin,&ch,1) < 1) die_read();
21294      if (substdio_bput(&ssout,&ch,1) == -1) die_write();
21295      if (!ch) break;
21296     }
21297+
21298+    /* check the to address */
21299+    stralloc_0(&chkaddr);
21300+    if (tapped == 0 && tapcheck('T')==1 ) {
21301+      tapped = 1;
21302+      if ( tapaddr.len > 0 ) {
21303+        if (substdio_bput(&ssout,"T",1) == -1) die_write();
21304+        if (substdio_bput(&ssout,tapaddr.s,tapaddr.len) == -1) die_write();
21305+        if (substdio_bput(&ssout,"",1) == -1) die_write();
21306+       }
21307+     }
21308+
21309    if (len >= ADDR) die(11);
21310   }
21311 
21312@@ -252,3 +297,47 @@
21313  triggerpull();
21314  die(0);
21315 }
21316+
21317+int tapcheck(t)
21318+char t;
21319+{
21320+  int i = 0;
21321+  int j = 0;
21322+  int x = 0;
21323+  int negate = 0;
21324+  stralloc curregex = {0};
21325+  char tmpbuf[200];
21326+
21327+  while (j < tap.len) {
21328+    i = j;
21329+    if ( tap.s[i]==t || tap.s[i]=='A'){
21330+            while ((tap.s[i] != ':') && (i < tap.len)) i++;
21331+            i++;
21332+            j=i;
21333+            while ((tap.s[i] != ':') && (i < tap.len)) i++;
21334+            if (tap.s[j] == '!') {
21335+              negate = 1;
21336+              j++;
21337+            }
21338+            stralloc_copys(&tapaddr, &tap.s[i+1]);
21339+
21340+            stralloc_copyb(&curregex,tap.s + j,(i - j));
21341+            stralloc_0(&curregex);
21342+            x = matchregex(chkaddr.s, curregex.s, tmpbuf);
21343+
21344+
21345+            if ((negate) && (x == 0)) {
21346+              return 1;
21347+            }
21348+            if (!(negate) && (x > 0)) {
21349+              return 1;
21350+            }
21351+    }
21352+    while ((tap.s[i] != '\0') && (i < tap.len)) i++;
21353+    j = i + 1;
21354+    negate = 0;
21355+
21356+
21357+  }
21358+  return 0;
21359+}
21360diff -ruN ../netqmail-1.06-original/qmail-remote.8 netqmail-1.06/qmail-remote.8
21361--- ../netqmail-1.06-original/qmail-remote.8    1998-06-15 12:53:16.000000000 +0200
21362+++ netqmail-1.06/qmail-remote.8        2016-11-22 21:03:57.122527765 +0100
21363@@ -100,6 +100,73 @@
21364 After this letter comes a human-readable description of
21365 what happened.
21366 
21367+.B qmail-remote
21368+may use SMTP Authenticaton of type CRAM-MD5, PLAIN, or LOGIN
21369+(in this order) to connect to remote hosts.
21370+The following reports are provided:
21371+.TP 5
21372+K
21373+no supported AUTH method found, continuing without authentication.
21374+.TP 5
21375+Z
21376+Connected to
21377+.I host
21378+but password expired.
21379+.TP 5
21380+Z
21381+Connected to
21382+.I host
21383+but authentication was rejected (AUTH PLAIN).
21384+.TP 5
21385+Z
21386+Connected to
21387+.I host
21388+but unable to base64encode (plain).
21389+.TP 5
21390+Z
21391+Connected to
21392+.I host
21393+but authentication was rejected (plain)."
21394+.TP 5
21395+Z
21396+Connected to
21397+.I host
21398+but authentication was rejected (AUTH LOGIN).
21399+.TP 5
21400+Z
21401+Connected to
21402+.I host
21403+but unable to base64encode user.
21404+.TP 5
21405+Z
21406+Connected to
21407+.I host
21408+but authentication was rejected (username).
21409+.TP 5
21410+Z
21411+Connected to
21412+.I host
21413+but unable to base64encode pass.
21414+.TP 5
21415+Z
21416+Connected to
21417+.I host
21418+but authentication was rejected (AUTH CRAM-MD5).
21419+Z
21420+Connected to
21421+.I host
21422+but unable to base64decode challenge.
21423+.TP 5
21424+Z
21425+Connected to
21426+.I host
21427+but unable to base64encode username+digest.
21428+.TP 5
21429+Z
21430+Connected to
21431+.I host
21432+but authentication was rejected (username+digest).
21433+.PP
21434 The recipient reports will always be printed in the same order as
21435 .BR qmail-remote 's
21436 .I recip
21437@@ -114,6 +181,55 @@
21438 always exits zero.
21439 .SH "CONTROL FILES"
21440 .TP 5
21441+.I authsenders
21442+Authenticated sender.
21443+For each
21444+.I sender
21445+included in
21446+.IR authsenders :
21447+.I sender\fB:\fIrelay\fB:\fIport\fB|\fIuser\fB|\fIpassword
21448+.B qmail-remote
21449+will try SMTP Authentication
21450+of type CRAM-MD5, LOGIN, or PLAIN
21451+with the provided user name
21452+.I user
21453+and password
21454+.I password
21455+(the authentication information)
21456+and eventually relay the
21457+mail through
21458+.I relay
21459+on port
21460+.IR port .
21461+The use of
21462+.I relay
21463+and
21464+.I port
21465+follows the same rules as for
21466+.IR smtproutes
21467+Note: In case
21468+.I sender
21469+is empty,
21470+.B qmail-remote
21471+will try to deliver each outgoing mail
21472+SMTP authenticated. If the authentication
21473+information is missing, the mail is
21474+delivered none-authenticated.
21475+.I authsenders
21476+can be constructed as follows:
21477+
21478+.EX
21479+   @example.com|generic|passwd
21480+   .subdomain.example.com|other|otherpw
21481+   mail@example.com|test|testpass
21482+   info@example.com:smtp.example.com:26|other|otherpw
21483+   :mailrelay.example.com:587|e=mc2|testpass
21484+.EE
21485+.TP 5
21486+.I clientcert.pem
21487+SSL certificate that is used to authenticate with the remote server
21488+during a TLS session.
21489+.TP 5
21490 .I helohost
21491 Current host name,
21492 for use solely in saying hello to the remote SMTP server.
21493@@ -123,12 +239,31 @@
21494 otherwise
21495 .B qmail-remote
21496 refuses to run.
21497+
21498+.TP 5
21499+.I notlshosts/<FQDN>
21500+.B qmail-remote
21501+will not try TLS on servers for which this file exists
21502+.RB ( <FQDN>
21503+is the fully-qualified domain name of the server).
21504+.IR (tlshosts/<FQDN>.pem
21505+takes precedence over this file however).
21506+
21507+.TP 5
21508+.I outgoingip
21509+IP address to be used on outgoing connections.
21510+Default: system-defined.
21511+The value
21512+.IR 0.0.0.0
21513+is equivalent to the system default.
21514 .TP 5
21515 .I smtproutes
21516 Artificial SMTP routes.
21517 Each route has the form
21518 .IR domain\fB:\fIrelay ,
21519-without any extra spaces.
21520+or
21521+.IR domain\fB:\fIrelay\fB|\fIuser\fB|\fIpassword
21522+in case of authenticated routes without any extra spaces.
21523 If
21524 .I domain
21525 matches
21526@@ -149,6 +284,7 @@
21527 
21528 .EX
21529    inside.af.mil:firewall.af.mil:26
21530+  :submission.myrelay.com:587|myuserid|mypasswd
21531 .EE
21532 
21533 .I relay
21534@@ -156,6 +292,8 @@
21535 this tells
21536 .B qmail-remote
21537 to look up MX records as usual.
21538+.I port
21539+value of 465 (deprecated smtps port) causes TLS session to be started.
21540 .I smtproutes
21541 may include wildcards:
21542 
21543@@ -182,6 +320,10 @@
21544 you are always safe using
21545 .I smtproutes
21546 if you do not accept mail from the network.
21547+Note:
21548+.I authsender
21549+routes have precedence over
21550+.IR smtproutes .
21551 .TP 5
21552 .I timeoutconnect
21553 Number of seconds
21554@@ -195,6 +337,88 @@
21555 .B qmail-remote
21556 will wait for each response from the remote SMTP server.
21557 Default: 1200.
21558+
21559+.TP 5
21560+.I tlsclientciphers
21561+A set of OpenSSL client cipher strings. Multiple ciphers
21562+contained in a string should be separated by a colon.
21563+
21564+.TP 5
21565+.I tlshosts/<FQDN>.pem
21566+.B qmail-remote
21567+requires TLS authentication from servers for which this file exists
21568+.RB ( <FQDN>
21569+is the fully-qualified domain name of the server). One of the
21570+.I dNSName
21571+or the
21572+.I CommonName
21573+attributes have to match. The file contains the trusted CA certificates.
21574+
21575+.B WARNING:
21576+this option may cause mail to be delayed, bounced, doublebounced, or lost.
21577+
21578+.TP 5
21579+.I tlshosts/exhaustivelist
21580+if this file exists
21581+no TLS will be tried on hosts other than those for which a file
21582+.B tlshosts/<FQDN>.pem
21583+exists.
21584+
21585+.SH "ENVIRONMENT VARIABLES READ"
21586+Environment variables may be defined globally in the
21587+.B qmail-smtpd
21588+startup script and/or individually as part of the
21589+.B tcpserver's
21590+cdb database.
21591+The environment variables may be quoted ("variable", or 'variable') and
21592+in case of global use, have to be exported.
21593+.B qmail-smtpd
21594+supports the following legacy environment variables, typically
21595+provided by
21596+.B tcpserver
21597+or
21598+.B sslserver
21599+or
21600+.BR tcp-env :
21601+.IR TCPREMOTEIP ,
21602+.IR TCPREMOTEHOST
21603+.IR TCPREMOTEINFO
21604+and
21605+.IR TCPLOCALPORT
21606+as well as
21607+.IR RELAYCLIENT .
21608+
21609+.B qmail-smtpd
21610+may use the following environment variables for SMTP authentication:
21611+.TP 5
21612+.IR SMTPAUTH
21613+is used to enable SMTP Authentication for the AUTH types
21614+LOGIN and PLAIN.
21615+In case
21616+.TP 5
21617+.IR SMTPAUTH='+cram'
21618+is defined,
21619+.B qmail-smtpd
21620+honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication.
21621+Simply
21622+.TP 5
21623+.IR SMTPAUTH='cram'
21624+restricts authentication just to CRAM-MD5.
21625+If however
21626+.TP 5
21627+.IR SMTPAUTH='!'
21628+starts with an exclamation mark, AUTH is required. In particular,
21629+.TP 5
21630+.IR SMTPAUTH='!cram'
21631+may be useful.
21632+In opposite, if
21633+.TP 5
21634+.IR SMTPAUTH='-'
21635+starts with a dash, AUTH is disabled for particular
21636+connections.
21637+
21638+Note: The use of 'cram' requires a CRAM-MD5 enabled PAM.
21639+
21640 .SH "SEE ALSO"
21641 addresses(5),
21642 envelopes(5),
21643diff -ruN ../netqmail-1.06-original/qmail-remote.c netqmail-1.06/qmail-remote.c
21644--- ../netqmail-1.06-original/qmail-remote.c    1998-06-15 12:53:16.000000000 +0200
21645+++ netqmail-1.06/qmail-remote.c        2017-08-28 17:51:47.068287985 +0200
21646@@ -28,6 +28,7 @@
21647 #include "timeoutconn.h"
21648 #include "timeoutread.h"
21649 #include "timeoutwrite.h"
21650+#include "base64.h"
21651 
21652 #define HUGESMTPTEXT 5000
21653 
21654@@ -39,14 +40,37 @@
21655 static stralloc sauninit = {0};
21656 
21657 stralloc helohost = {0};
21658+stralloc outgoingip = {0};
21659 stralloc routes = {0};
21660 struct constmap maproutes;
21661 stralloc host = {0};
21662 stralloc sender = {0};
21663 
21664+stralloc authsenders = {0};
21665+struct constmap mapauthsenders;
21666+stralloc user = {0};
21667+stralloc pass = {0};
21668+stralloc auth = {0};
21669+stralloc plain = {0};
21670+stralloc chal  = {0};
21671+stralloc slop  = {0};
21672+char *authsender;
21673+
21674 saa reciplist = {0};
21675 
21676 struct ip_address partner;
21677+struct ip_address outip;
21678+
21679+#ifdef TLS
21680+# include <sys/stat.h>
21681+# include "tls.h"
21682+# include "ssl_timeoutio.h"
21683+# include <openssl/x509v3.h>
21684+# define EHLO 1
21685+
21686+int tls_init();
21687+const char *ssl_err_str = 0;
21688+#endif
21689 
21690 void out(s) char *s; { if (substdio_puts(subfdoutsmall,s) == -1) _exit(0); }
21691 void zero() { if (substdio_put(subfdoutsmall,"\0",1) == -1) _exit(0); }
21692@@ -56,6 +80,7 @@
21693 ch = sa->s[i]; if (ch < 33) ch = '?'; if (ch > 126) ch = '?';
21694 if (substdio_put(subfdoutsmall,&ch,1) == -1) _exit(0); } }
21695 
21696+void temp_noip() { out("Zinvalid ipaddr in control/outgoingip (#4.3.0)\n"); zerodie(); }
21697 void temp_nomem() { out("ZOut of memory. (#4.3.0)\n"); zerodie(); }
21698 void temp_oserr() { out("Z\
21699 System resources temporarily unavailable. (#4.3.0)\n"); zerodie(); }
21700@@ -86,6 +111,12 @@
21701 it isn't in my control/locals file, so I don't treat it as local. (#5.4.6)\n");
21702 zerodie(); }
21703 
21704+void err_authprot() {
21705+  out("Kno supported AUTH method found, continuing without authentication.\n");
21706+  zero();
21707+  substdio_flush(subfdoutsmall);
21708+}
21709+
21710 void outhost()
21711 {
21712   char x[IPFMT];
21713@@ -99,6 +130,9 @@
21714   outhost();
21715   out(" but connection died. ");
21716   if (flagcritical) out("Possible duplicate! ");
21717+#ifdef TLS
21718+  if (ssl_err_str) { out(ssl_err_str); out(" "); }
21719+#endif
21720   out("(#4.4.2)\n");
21721   zerodie();
21722 }
21723@@ -110,6 +144,12 @@
21724 int saferead(fd,buf,len) int fd; char *buf; int len;
21725 {
21726   int r;
21727+#ifdef TLS
21728+  if (ssl) {
21729+    r = ssl_timeoutread(timeout, smtpfd, smtpfd, ssl, buf, len);
21730+    if (r < 0) ssl_err_str = ssl_error_str();
21731+  } else
21732+#endif
21733   r = timeoutread(timeout,smtpfd,buf,len);
21734   if (r <= 0) dropped();
21735   return r;
21736@@ -117,6 +157,12 @@
21737 int safewrite(fd,buf,len) int fd; char *buf; int len;
21738 {
21739   int r;
21740+#ifdef TLS
21741+  if (ssl) {
21742+    r = ssl_timeoutwrite(timeout, smtpfd, smtpfd, ssl, buf, len);
21743+    if (r < 0) ssl_err_str = ssl_error_str();
21744+  } else
21745+#endif
21746   r = timeoutwrite(timeout,smtpfd,buf,len);
21747   if (r <= 0) dropped();
21748   return r;
21749@@ -163,6 +209,65 @@
21750   return code;
21751 }
21752 
21753+#ifdef EHLO
21754+saa ehlokw = {0}; /* list of EHLO keywords and parameters */
21755+int maxehlokwlen = 0;
21756+
21757+unsigned long ehlo()
21758+{
21759+  stralloc *sa;
21760+  char *s, *e, *p;
21761+  unsigned long code;
21762+
21763+  if (ehlokw.len > maxehlokwlen) maxehlokwlen = ehlokw.len;
21764+  ehlokw.len = 0;
21765+
21766+# ifdef MXPS
21767+  if (type == 's') return 0;
21768+# endif
21769+
21770+  substdio_puts(&smtpto, "EHLO ");
21771+  substdio_put(&smtpto, helohost.s, helohost.len);
21772+  substdio_puts(&smtpto, "\r\n");
21773+  substdio_flush(&smtpto);
21774+
21775+  code = smtpcode();
21776+  if (code != 250) return code;
21777+
21778+  s = smtptext.s;
21779+  while (*s++ != '\n') ; /* skip the first line: contains the domain */
21780+
21781+  e = smtptext.s + smtptext.len - 6; /* 250-?\n */
21782+  while (s <= e)
21783+  {
21784+    int wasspace = 0;
21785+
21786+    if (!saa_readyplus(&ehlokw, 1)) temp_nomem();
21787+    sa = ehlokw.sa + ehlokw.len++;
21788+    if (ehlokw.len > maxehlokwlen) *sa = sauninit; else sa->len = 0;
21789+
21790+     /* smtptext is known to end in a '\n' */
21791+     for (p = (s += 4); ; ++p)
21792+       if (*p == '\n' || *p == ' ' || *p == '\t') {
21793+         if (!wasspace)
21794+           if (!stralloc_catb(sa, s, p - s) || !stralloc_0(sa)) temp_nomem();
21795+         if (*p == '\n') break;
21796+         wasspace = 1;
21797+       } else if (wasspace == 1) {
21798+         wasspace = 0;
21799+         s = p;
21800+       }
21801+    s = ++p;
21802+
21803+    /* keyword should consist of alpha-num and '-'
21804+     * broken AUTH might use '=' instead of space */
21805+    for (p = sa->s; *p; ++p) if (*p == '=') { *p = 0; break; }
21806+  }
21807+
21808+  return 250;
21809+}
21810+#endif
21811+
21812 void outsmtptext()
21813 {
21814   int i;
21815@@ -179,6 +284,11 @@
21816 char *prepend;
21817 char *append;
21818 {
21819+#ifdef TLS
21820+  /* shouldn't talk to the client unless in an appropriate state */
21821+  int state = ssl ? ssl->state : SSL_ST_BEFORE;
21822+  if (state & SSL_ST_OK || (!smtps && state & SSL_ST_BEFORE))
21823+#endif
21824   substdio_putsflush(&smtpto,"QUIT\r\n");
21825   /* waiting for remote side is just too ridiculous */
21826   out(prepend);
21827@@ -186,6 +296,30 @@
21828   out(append);
21829   out(".\n");
21830   outsmtptext();
21831+
21832+#if defined(TLS) && defined(DEBUG)
21833+  if (ssl) {
21834+    X509 *peercert;
21835+
21836+    out("STARTTLS proto="); out(SSL_get_version(ssl));
21837+    out("; cipher="); out(SSL_get_cipher(ssl));
21838+
21839+    /* we want certificate details */
21840+    if (peercert = SSL_get_peer_certificate(ssl)) {
21841+      char *str;
21842+
21843+      str = X509_NAME_oneline(X509_get_subject_name(peercert), NULL, 0);
21844+      out("; subject="); out(str); OPENSSL_free(str);
21845+
21846+      str = X509_NAME_oneline(X509_get_issuer_name(peercert), NULL, 0);
21847+      out("; issuer="); out(str); OPENSSL_free(str);
21848+
21849+      X509_free(peercert);
21850+    }
21851+    out(";\n");
21852+  }
21853+#endif
21854+
21855   zerodie();
21856 }
21857 
21858@@ -201,6 +335,16 @@
21859     if (ch == '.')
21860       substdio_put(&smtpto,".",1);
21861     while (ch != '\n') {
21862+      if (ch == '\r') {
21863+       r = substdio_get(&ssin, &ch, 1);
21864+       if (r == 0)
21865+               break;
21866+       if (r == -1) temp_read();
21867+       if (ch != '\n') {
21868+               substdio_put(&smtpto, "\r\n", 2);
21869+       } else
21870+               break;
21871+      }
21872       substdio_put(&smtpto,&ch,1);
21873       r = substdio_get(&ssin,&ch,1);
21874       if (r == 0) perm_partialline();
21875@@ -214,30 +358,432 @@
21876   substdio_flush(&smtpto);
21877 }
21878 
21879-stralloc recip = {0};
21880+#ifdef TLS
21881+char *partner_fqdn = 0;
21882 
21883-void smtp()
21884+# define TLS_QUIT quit(ssl ? "; connected to " : "; connecting to ", "")
21885+void tls_quit(const char *s1, const char *s2)
21886+{
21887+  out(s1); if (s2) { out(": "); out(s2); } TLS_QUIT;
21888+}
21889+# define tls_quit_error(s) tls_quit(s, ssl_error())
21890+
21891+int match_partner(const char *s, int len)
21892+{
21893+  if (!case_diffb(partner_fqdn, len, s) && !partner_fqdn[len]) return 1;
21894+  /* we also match if the name is *.domainname */
21895+  if (*s == '*') {
21896+    const char *domain = partner_fqdn + str_chr(partner_fqdn, '.');
21897+    if (!case_diffb(domain, --len, ++s) && !domain[len]) return 1;
21898+  }
21899+  return 0;
21900+}
21901+
21902+/* don't want to fail handshake if certificate can't be verified */
21903+int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
21904+
21905+int tls_init()
21906 {
21907-  unsigned long code;
21908-  int flagbother;
21909   int i;
21910+  SSL *myssl;
21911+  SSL_CTX *ctx;
21912+  stralloc saciphers = {0};
21913+  const char *ciphers, *servercert = 0;
21914+
21915+  if (partner_fqdn) {
21916+    struct stat st;
21917+    stralloc tmp = {0};
21918+    if (!stralloc_copys(&tmp, "control/tlshosts/")
21919+      || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn))
21920+      || !stralloc_catb(&tmp, ".pem", 5)) temp_nomem();
21921+    if (stat(tmp.s, &st) == 0)
21922+      servercert = tmp.s;
21923+    else {
21924+      if (!stralloc_copys(&tmp, "control/notlshosts/")
21925+        || !stralloc_catb(&tmp, partner_fqdn, str_len(partner_fqdn)+1))
21926+        temp_nomem();
21927+      if ((stat("control/tlshosts/exhaustivelist", &st) == 0) ||
21928+         (stat(tmp.s, &st) == 0)) {
21929+         alloc_free(tmp.s);
21930+         return 0;
21931+      }
21932+      alloc_free(tmp.s);
21933+    }
21934+  }
21935 
21936-  if (smtpcode() != 220) quit("ZConnected to "," but greeting failed");
21937-
21938-  substdio_puts(&smtpto,"HELO ");
21939-  substdio_put(&smtpto,helohost.s,helohost.len);
21940-  substdio_puts(&smtpto,"\r\n");
21941-  substdio_flush(&smtpto);
21942-  if (smtpcode() != 250) quit("ZConnected to "," but my name was rejected");
21943-
21944+  if (!smtps) {
21945+    stralloc *sa = ehlokw.sa;
21946+    unsigned int len = ehlokw.len;
21947+    /* look for STARTTLS among EHLO keywords */
21948+    for ( ; len && case_diffs(sa->s, "STARTTLS"); ++sa, --len) ;
21949+    if (!len) {
21950+      if (!servercert) return 0;
21951+      out("ZNo TLS achieved while "); out(servercert);
21952+      out(" exists"); smtptext.len = 0; TLS_QUIT;
21953+    }
21954+  }
21955+
21956+  SSL_library_init();
21957+  ctx = SSL_CTX_new(SSLv23_client_method());
21958+  if (!ctx) {
21959+    if (!smtps && !servercert) return 0;
21960+    smtptext.len = 0;
21961+    tls_quit_error("ZTLS error initializing ctx");
21962+  }
21963+
21964+  /* POODLE vulnerability */
21965+  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
21966+
21967+  if (servercert) {
21968+    if (!SSL_CTX_load_verify_locations(ctx, servercert, NULL)) {
21969+      SSL_CTX_free(ctx);
21970+      smtptext.len = 0;
21971+      out("ZTLS unable to load "); tls_quit_error(servercert);
21972+    }
21973+    /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
21974+    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
21975+  }
21976+
21977+  /* let the other side complain if it needs a cert and we don't have one */
21978+# define CLIENTCERT "control/clientcert.pem"
21979+  if (SSL_CTX_use_certificate_chain_file(ctx, CLIENTCERT))
21980+    SSL_CTX_use_RSAPrivateKey_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM);
21981+# undef CLIENTCERT
21982+
21983+  myssl = SSL_new(ctx);
21984+  SSL_CTX_free(ctx);
21985+  if (!myssl) {
21986+    if (!smtps && !servercert) return 0;
21987+    smtptext.len = 0;
21988+    tls_quit_error("ZTLS error initializing ssl");
21989+  }
21990+
21991+  if (!smtps) substdio_putsflush(&smtpto, "STARTTLS\r\n");
21992+
21993+  /* while the server is preparing a responce, do something else */
21994+  if (control_readfile(&saciphers, "control/tlsclientciphers", 0) == -1)
21995+    { SSL_free(myssl); temp_control(); }
21996+  if (saciphers.len) {
21997+    for (i = 0; i < saciphers.len - 1; ++i)
21998+      if (!saciphers.s[i]) saciphers.s[i] = ':';
21999+    ciphers = saciphers.s;
22000+  }
22001+  else ciphers = "DEFAULT";
22002+  SSL_set_cipher_list(myssl, ciphers);
22003+  alloc_free(saciphers.s);
22004+
22005+  SSL_set_fd(myssl, smtpfd);
22006+
22007+  /* read the response to STARTTLS */
22008+  if (!smtps) {
22009+    if (smtpcode() != 220) {
22010+      SSL_free(myssl);
22011+      if (!servercert) return 0;
22012+      out("ZSTARTTLS rejected while ");
22013+      out(servercert); out(" exists"); TLS_QUIT;
22014+    }
22015+    smtptext.len = 0;
22016+  }
22017+
22018+  ssl = myssl;
22019+  if (ssl_timeoutconn(timeout, smtpfd, smtpfd, ssl) <= 0)
22020+    tls_quit("ZTLS connect failed", ssl_error_str());
22021+
22022+  if (servercert) {
22023+    X509 *peercert;
22024+    STACK_OF(GENERAL_NAME) *gens;
22025+    int found_gen_dns = 0;
22026+    int matched_gen_dns = 0;
22027+
22028+    int r = SSL_get_verify_result(ssl);
22029+    if (r != X509_V_OK) {
22030+      out("ZTLS unable to verify server with ");
22031+      tls_quit(servercert, X509_verify_cert_error_string(r));
22032+    }
22033+    alloc_free(servercert);
22034+
22035+    peercert = SSL_get_peer_certificate(ssl);
22036+    if (!peercert) {
22037+      out("ZTLS unable to verify server ");
22038+      tls_quit(partner_fqdn, "no certificate provided");
22039+    }
22040+
22041+    /* RFC 2595 section 2.4: find a matching name
22042+     * first find a match among alternative names */
22043+    gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
22044+    if (gens) {
22045+      for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i)
22046+      {
22047+        const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
22048+        if (gn->type == GEN_DNS){
22049+          found_gen_dns = 1;
22050+          if (match_partner(gn->d.ia5->data, gn->d.ia5->length)){
22051+            matched_gen_dns = 1;
22052+            break;
22053+          }
22054+        }
22055+      }
22056+      sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
22057+    }
22058+
22059+    /* no SubjectAltName of type DNS found, look up commonName */
22060+    if (!found_gen_dns) {
22061+      stralloc peer = {0};
22062+      X509_NAME *subj = X509_get_subject_name(peercert);
22063+      i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
22064+      if (i >= 0) {
22065+        const ASN1_STRING *s = X509_NAME_get_entry(subj, i)->value;
22066+        if (s) { peer.len = s->length; peer.s = s->data; }
22067+      }
22068+      if (peer.len <= 0) {
22069+        out("ZTLS unable to verify server ");
22070+        tls_quit(partner_fqdn, "certificate contains no valid commonName");
22071+      }
22072+      if (!match_partner(peer.s, peer.len)) {
22073+        out("ZTLS unable to verify server "); out(partner_fqdn);
22074+        out(": received certificate for "); outsafe(&peer); TLS_QUIT;
22075+      }
22076+    } else if (!matched_gen_dns) {
22077+      out("ZTLS unable to verify server ");
22078+      tls_quit(partner_fqdn, "certificate contains no matching dNSNnames");
22079+    }
22080+
22081+    X509_free(peercert);
22082+  }
22083+
22084+  if (smtps) if (smtpcode() != 220)
22085+    quit("ZTLS Connected to "," but greeting failed");
22086+
22087+  return 1;
22088+}
22089+#endif
22090+
22091+stralloc recip = {0};
22092+
22093+void mailfrom()
22094+{
22095   substdio_puts(&smtpto,"MAIL FROM:<");
22096   substdio_put(&smtpto,sender.s,sender.len);
22097   substdio_puts(&smtpto,">\r\n");
22098   substdio_flush(&smtpto);
22099+}
22100+
22101+stralloc xuser = {0};
22102+
22103+int xtext(sa,s,len)
22104+stralloc *sa;
22105+char *s;
22106+int len;
22107+{
22108+  int i;
22109+
22110+  if(!stralloc_copys(sa,"")) temp_nomem();
22111
22112+  for (i = 0; i < len; i++) {
22113+    if (s[i] == '=') {
22114+      if (!stralloc_cats(sa,"+3D")) temp_nomem();
22115+    } else if (s[i] == '+') { 
22116+        if (!stralloc_cats(sa,"+2B")) temp_nomem();
22117+    } else if ((int) s[i] < 33 || (int) s[i] > 126) {
22118+        if (!stralloc_cats(sa,"+3F")) temp_nomem(); /* ok. not correct */
22119+    } else if (!stralloc_catb(sa,s+i,1)) {
22120+        temp_nomem();
22121+    }
22122+  }
22123+
22124+  return sa->len;
22125+}
22126+
22127+void mailfrom_xtext()
22128+{
22129+  if (!xtext(&xuser,user.s,user.len)) temp_nomem();
22130+  substdio_puts(&smtpto,"MAIL FROM:<");
22131+  substdio_put(&smtpto,sender.s,sender.len);
22132+  substdio_puts(&smtpto,"> AUTH=");
22133+  substdio_put(&smtpto,xuser.s,xuser.len);
22134+  substdio_puts(&smtpto,"\r\n");
22135+  substdio_flush(&smtpto);
22136+}
22137+
22138+int mailfrom_plain()
22139+{
22140+  substdio_puts(&smtpto,"AUTH PLAIN\r\n");
22141+  substdio_flush(&smtpto);
22142+  if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH PLAIN)."); return -1; }
22143+  if (!stralloc_cat(&plain,&user)) temp_nomem(); /* <authorization-id> */
22144+  if (!stralloc_0(&plain)) temp_nomem();
22145+  if (!stralloc_cat(&plain,&user)) temp_nomem(); /* <authentication-id> */
22146+  if (!stralloc_0(&plain)) temp_nomem();
22147+  if (!stralloc_cat(&plain,&pass)) temp_nomem(); /* password */
22148+  if (b64encode(&plain,&auth)) quit("ZConnected to "," but unable to base64encode (plain).");
22149+  substdio_put(&smtpto,auth.s,auth.len);
22150+  substdio_puts(&smtpto,"\r\n");
22151+  substdio_flush(&smtpto);
22152+  if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
22153+  else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
22154+  else { quit("ZConnected to "," but authentication was rejected (plain)."); return 1; }
22155+
22156+  return 0;
22157+}
22158+
22159+int mailfrom_login()
22160+{
22161+  substdio_puts(&smtpto,"AUTH LOGIN\r\n");
22162+  substdio_flush(&smtpto);
22163+  if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH LOGIN)."); return -1; }
22164+
22165+  if (!stralloc_copys(&auth,"")) temp_nomem();
22166+  if (b64encode(&user,&auth)) quit("ZConnected to "," but unable to base64encode user.");
22167+  substdio_put(&smtpto,auth.s,auth.len);
22168+  substdio_puts(&smtpto,"\r\n");
22169+  substdio_flush(&smtpto);
22170+  if (smtpcode() != 334) quit("ZConnected to "," but authentication was rejected (username).");
22171+
22172+  if (!stralloc_copys(&auth,"")) temp_nomem();
22173+  if (b64encode(&pass,&auth)) quit("ZConnected to "," but unable to base64encode pass.");
22174+  substdio_put(&smtpto,auth.s,auth.len);
22175+  substdio_puts(&smtpto,"\r\n");
22176+  substdio_flush(&smtpto);
22177+  if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
22178+  else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
22179+  else { quit("ZConnected to "," but authentication was rejected (password)."); return 1; }
22180+}
22181+
22182+int mailfrom_cram()
22183+{
22184+  int j;
22185+  unsigned char h;
22186+  unsigned char digest[16];
22187+  unsigned char digascii[33];
22188+  static char hextab[]="0123456789abcdef";
22189+
22190+  substdio_puts(&smtpto,"AUTH CRAM-MD5\r\n");
22191+  substdio_flush(&smtpto);
22192+  if (smtpcode() != 334) { quit("ZConnected to "," but authentication was rejected (AUTH CRAM-MD5)."); return -1; }
22193+
22194+  if (str_chr(smtptext.s+4,' ')) {                     /* Challenge */
22195+    if(!stralloc_copys(&slop,"")) temp_nomem();
22196+    if (!stralloc_copyb(&slop,smtptext.s+4,smtptext.len-5)) temp_nomem();
22197+    if (b64decode(slop.s,slop.len,&chal)) quit("ZConnected to "," but unable to base64decode challenge.");
22198+  }
22199+   
22200+  hmac_md5(chal.s,chal.len,pass.s,pass.len,digest);
22201+
22202+  for (j = 0;j < 16;j++)                               /* HEX => ASCII */
22203+  {
22204+    digascii[2*j] = hextab[digest[j] >> 4]; 
22205+    digascii[2*j+1] = hextab[digest[j] & 0xf];
22206+  }
22207+  digascii[32]=0;
22208+
22209+  slop.len = 0;
22210+  if (!stralloc_copys(&slop,"")) temp_nomem();
22211+  if (!stralloc_cat(&slop,&user)) temp_nomem();                 /* user-id */
22212+  if (!stralloc_cats(&slop," ")) temp_nomem();
22213+  if (!stralloc_catb(&slop,digascii,32)) temp_nomem();   /* digest */
22214+
22215+  if (!stralloc_copys(&auth,"")) temp_nomem();
22216+  if (b64encode(&slop,&auth)) quit("ZConnected to "," but unable to base64encode username+digest.");
22217+  substdio_put(&smtpto,auth.s,auth.len);
22218+  substdio_puts(&smtpto,"\r\n");
22219+  substdio_flush(&smtpto);
22220+  if (smtpcode() == 235) { mailfrom_xtext(); return 0; }
22221+  else if (smtpcode() == 432) { quit("ZConnected to "," but password expired."); return 1; }
22222+  else { quit("ZConnected to "," but authentication was rejected (username+digest)."); return 1; }
22223+}
22224+
22225+void smtp_auth()
22226+{
22227+  int i, j;
22228+
22229+  for (i = 0; i + 8 < smtptext.len; i += str_chr(smtptext.s+i,'\n')+1)
22230+    if (!str_diffn(smtptext.s+i+4,"AUTH",4)) { 
22231+      if (j = str_chr(smtptext.s+i+8,'C') > 0)
22232+        if (case_starts(smtptext.s+i+8+j,"CRAM"))
22233+          if (mailfrom_cram() >= 0) return;
22234+
22235+      if (j = str_chr(smtptext.s+i+8,'P') > 0)
22236+        if (case_starts(smtptext.s+i+8+j,"PLAIN"))
22237+          if (mailfrom_plain() >= 0) return;
22238+
22239+      if (j = str_chr(smtptext.s+i+8,'L') > 0)
22240+        if (case_starts(smtptext.s+i+8+j,"LOGIN"))
22241+          if (mailfrom_login() >= 0) return;
22242+
22243+      err_authprot();
22244+      mailfrom();
22245+    }
22246+}
22247+
22248+void smtp()
22249+{
22250+  unsigned long code;
22251+  int flagbother;
22252+  int i;
22253+
22254+  #ifndef PORT_SMTP
22255+    /* the qmtpc patch uses smtp_port and undefines PORT_SMTP */
22256+  # define port smtp_port
22257+  #endif
22258+
22259+  #ifdef TLS
22260+  # ifdef MXPS
22261+    if (type == 'S') smtps = 1;
22262+    else if (type != 's')
22263+  # endif
22264+      if (port == 465) smtps = 1;
22265+    if (!smtps)
22266+  #endif
22267+
22268+  code = smtpcode();
22269+  if (code >= 500 && code < 600) quit("DConnected to "," but greeting failed");
22270+  if (code >= 400 && code < 500) return; /* try next MX, see RFC-2821 */
22271+  if (code != 220) quit("ZConnected to "," but greeting failed");
22272+
22273+#ifdef EHLO
22274+# ifdef TLS
22275+  if (!smtps)
22276+# endif
22277+  code = ehlo();
22278+
22279+# ifdef TLS
22280+  if (tls_init())
22281+    /* RFC2487 says we should issue EHLO (even if we might not need
22282+     * extensions); at the same time, it does not prohibit a server
22283+     * to reject the EHLO and make us fallback to HELO */
22284+    code = ehlo();
22285+# endif
22286+
22287+  if (code == 250) {
22288+    /* add EHLO response checks here */
22289+
22290+    /* and if EHLO failed, use HELO */
22291+  } else {
22292+#endif
22293+
22294+/*  if (smtpcode() != 250) { */
22295+    substdio_puts(&smtpto,"HELO ");
22296+    substdio_put(&smtpto,helohost.s,helohost.len);
22297+    substdio_puts(&smtpto,"\r\n");
22298+    substdio_flush(&smtpto);
22299+    code = smtpcode();
22300+    if (code >= 500) quit("DConnected to "," but my name was rejected");
22301+    if (code != 250) quit("ZConnected to "," but my name was rejected");
22302+/*  } */
22303+
22304+#ifdef EHLO
22305+  }
22306+#endif
22307+
22308+  if (user.len && pass.len)
22309+    smtp_auth();
22310+  else
22311+    mailfrom();
22312+
22313   code = smtpcode();
22314   if (code >= 500) quit("DConnected to "," but sender was rejected");
22315   if (code >= 400) quit("ZConnected to "," but sender was rejected");
22316-
22317+
22318   flagbother = 0;
22319   for (i = 0;i < reciplist.len;++i) {
22320     substdio_puts(&smtpto,"RCPT TO:<");
22321@@ -297,19 +843,14 @@
22322   if (!stralloc_cats(saout,"@")) temp_nomem();
22323 
22324   if (!stralloc_copys(&canonhost,s + j + 1)) temp_nomem();
22325-  if (flagcname)
22326-    switch(dns_cname(&canonhost)) {
22327-      case 0: *flagalias = 0; break;
22328-      case DNS_MEM: temp_nomem();
22329-      case DNS_SOFT: temp_dnscanon();
22330-      case DNS_HARD: ; /* alias loop, not our problem */
22331-    }
22332+  if (flagcname) *flagalias = 0;
22333 
22334   if (!stralloc_cat(saout,&canonhost)) temp_nomem();
22335 }
22336 
22337 void getcontrols()
22338 {
22339+  int r;
22340   if (control_init() == -1) temp_control();
22341   if (control_readint(&timeout,"control/timeoutremote") == -1) temp_control();
22342   if (control_readint(&timeoutconnect,"control/timeoutconnect") == -1)
22343@@ -324,48 +865,108 @@
22344     case 1:
22345       if (!constmap_init(&maproutes,routes.s,routes.len,1)) temp_nomem(); break;
22346   }
22347
22348+  switch(control_readfile(&authsenders,"control/authsenders",0)) {
22349+    case -1:
22350+       temp_control();
22351+    case 0:
22352+      if (!constmap_init(&mapauthsenders,"",0,1)) temp_nomem(); break;
22353+    case 1:
22354+      if (!constmap_init(&mapauthsenders,authsenders.s,authsenders.len,1)) temp_nomem(); break;
22355+  }
22356+ r = control_readline(&outgoingip,"control/outgoingip");
22357+ if (-1 == r) { if (errno == error_nomem) temp_nomem(); temp_control(); }
22358+ if (0 == r && !stralloc_copys(&outgoingip, "0.0.0.0")) temp_nomem();
22359+ if (str_equal(outgoingip.s, "0.0.0.0"))
22360+   { outip.d[0]=outip.d[1]=outip.d[2]=outip.d[3]=(unsigned long) 0; }
22361+ else if (!ip_scan(outgoingip.s, &outip)) temp_noip();
22362 }
22363 
22364-void main(argc,argv)
22365+int main(argc,argv)
22366 int argc;
22367 char **argv;
22368 {
22369   static ipalloc ip = {0};
22370-  int i;
22371+  int i, j;
22372   unsigned long random;
22373   char **recips;
22374   unsigned long prefme;
22375   int flagallaliases;
22376   int flagalias;
22377   char *relayhost;
22378-
22379+   
22380   sig_pipeignore();
22381   if (argc < 4) perm_usage();
22382   if (chdir(auto_qmail) == -1) temp_chdir();
22383   getcontrols();
22384 
22385-
22386   if (!stralloc_copys(&host,argv[1])) temp_nomem();
22387-
22388+
22389+  authsender = 0;
22390   relayhost = 0;
22391-  for (i = 0;i <= host.len;++i)
22392-    if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
22393-      if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
22394+
22395+  addrmangle(&sender,argv[2],&flagalias,0);
22396+
22397+  for (i = 0;i <= sender.len;++i)
22398+    if ((i == 0) || (i == sender.len) || (sender.s[i] == '.') || (sender.s[i] == '@'))
22399+      if (authsender = constmap(&mapauthsenders,sender.s + i,sender.len - i))
22400         break;
22401-  if (relayhost && !*relayhost) relayhost = 0;
22402-
22403-  if (relayhost) {
22404-    i = str_chr(relayhost,':');
22405-    if (relayhost[i]) {
22406-      scan_ulong(relayhost + i + 1,&port);
22407-      relayhost[i] = 0;
22408+
22409+  if (authsender && !*authsender) authsender = 0;
22410+
22411+  if (authsender) {
22412+    i = str_chr(authsender,'|');
22413+    if (authsender[i]) {
22414+      j = str_chr(authsender + i + 1,'|');
22415+      if (authsender[j]) {
22416+        authsender[i] = 0;
22417+        authsender[i + j + 1] = 0;
22418+        if (!stralloc_copys(&user,"")) temp_nomem();
22419+        if (!stralloc_copys(&user,authsender + i + 1)) temp_nomem();
22420+        if (!stralloc_copys(&pass,"")) temp_nomem();
22421+        if (!stralloc_copys(&pass,authsender + i + j + 2)) temp_nomem();
22422+      }
22423+    }
22424+    i = str_chr(authsender,':');
22425+    if (authsender[i]) {
22426+      scan_ulong(authsender + i + 1,&port);
22427+      authsender[i] = 0;
22428     }
22429-    if (!stralloc_copys(&host,relayhost)) temp_nomem();
22430-  }
22431 
22432+    if (!stralloc_copys(&relayhost,authsender)) temp_nomem();
22433+    if (!stralloc_copys(&host,authsender)) temp_nomem();
22434+
22435+  }
22436+  else {                                       /* default smtproutes -- authenticated */
22437+    for (i = 0;i <= host.len;++i)
22438+      if ((i == 0) || (i == host.len) || (host.s[i] == '.'))
22439+        if (relayhost = constmap(&maproutes,host.s + i,host.len - i))
22440+          break;
22441+
22442+    if (relayhost && !*relayhost) relayhost = 0;
22443+
22444+    if (relayhost) {
22445+      i = str_chr(relayhost,'|');
22446+      if (relayhost[i]) {
22447+        j = str_chr(relayhost + i + 1,'|');
22448+        if (relayhost[j]) {
22449+          relayhost[i] = 0;
22450+          relayhost[i + j + 1] = 0;
22451+          if (!stralloc_copys(&user,"")) temp_nomem();
22452+          if (!stralloc_copys(&user,relayhost + i + 1)) temp_nomem();
22453+          if (!stralloc_copys(&pass,"")) temp_nomem();
22454+          if (!stralloc_copys(&pass,relayhost + i + j + 2)) temp_nomem();
22455+        }
22456+      }
22457+      i = str_chr(relayhost,':');
22458+      if (relayhost[i]) {
22459+        scan_ulong(relayhost + i + 1,&port);
22460+        relayhost[i] = 0;
22461+      }
22462+      if (!stralloc_copys(&host,relayhost)) temp_nomem();
22463+    }
22464+  }
22465 
22466-  addrmangle(&sender,argv[2],&flagalias,0);
22467-
22468   if (!saa_readyplus(&reciplist,0)) temp_nomem();
22469   if (ipme_init() != 1) temp_oserr();
22470 
22471@@ -414,10 +1015,13 @@
22472     smtpfd = socket(AF_INET,SOCK_STREAM,0);
22473     if (smtpfd == -1) temp_oserr();
22474 
22475-    if (timeoutconn(smtpfd,&ip.ix[i].ip,(unsigned int) port,timeoutconnect) == 0) {
22476+    if (timeoutconn(smtpfd,&ip.ix[i].ip,&outip,(unsigned int) port,timeoutconnect) == 0) {
22477       tcpto_err(&ip.ix[i].ip,0);
22478       partner = ip.ix[i].ip;
22479-      smtp(); /* does not return */
22480+#ifdef TLS
22481+      partner_fqdn = ip.ix[i].fqdn;
22482+#endif
22483+      smtp(); /* only returns when the next MX is to be tried */
22484     }
22485     tcpto_err(&ip.ix[i].ip,errno == error_timeout);
22486     close(smtpfd);
22487diff -ruN ../netqmail-1.06-original/qmail-rspawn.c netqmail-1.06/qmail-rspawn.c
22488--- ../netqmail-1.06-original/qmail-rspawn.c    1998-06-15 12:53:16.000000000 +0200
22489+++ netqmail-1.06/qmail-rspawn.c        2016-11-22 21:03:57.123527732 +0100
22490@@ -1,3 +1,4 @@
22491+#include "env.h"
22492 #include "fd.h"
22493 #include "wait.h"
22494 #include "substdio.h"
22495@@ -82,7 +83,7 @@
22496 char *s; char *r; int at;
22497 {
22498  int f;
22499- char *(args[5]);
22500+ char *ptr, *(args[5]);
22501 
22502  args[0] = "qmail-remote";
22503  args[1] = r + at + 1;
22504@@ -95,7 +96,10 @@
22505    if (fd_move(0,fdmess) == -1) _exit(111);
22506    if (fd_move(1,fdout) == -1) _exit(111);
22507    if (fd_copy(2,1) == -1) _exit(111);
22508-   execvp(*args,args);
22509+   if(!(ptr = env_get("QMAILREMOTE")))
22510+      execvp(*args, args);
22511+   else
22512+      execvp(ptr, args);
22513    if (error_temp(errno)) _exit(111);
22514    _exit(100);
22515   }
22516diff -ruN ../netqmail-1.06-original/qmail-send.9 netqmail-1.06/qmail-send.9
22517--- ../netqmail-1.06-original/qmail-send.9      1998-06-15 12:53:16.000000000 +0200
22518+++ netqmail-1.06/qmail-send.9  2016-11-22 21:03:57.123527732 +0100
22519@@ -51,7 +51,9 @@
22520 .B qmail-send
22521 receives a HUP signal,
22522 it will reread
22523-.I locals
22524+.IR concurrencylocal ,
22525+.IR concurrencyremote ,
22526+.IR locals
22527 and
22528 .IR virtualdomains .
22529 .TP 5
22530@@ -115,6 +117,10 @@
22531 (If that bounces,
22532 .B qmail-send
22533 gives up.)
22534+As a special case, if the first line of
22535+.IR doublebounceto
22536+is blank (contains a single linefeed), qmail-send will not queue
22537+the double-bounce at all.
22538 .TP 5
22539 .I envnoathost
22540 Presumed domain name for addresses without @ signs.
22541diff -ruN ../netqmail-1.06-original/qmail-send.c netqmail-1.06/qmail-send.c
22542--- ../netqmail-1.06-original/qmail-send.c      1998-06-15 12:53:16.000000000 +0200
22543+++ netqmail-1.06/qmail-send.c  2016-11-22 21:03:57.124527699 +0100
22544@@ -31,6 +31,7 @@
22545 #include "constmap.h"
22546 #include "fmtqfn.h"
22547 #include "readsubdir.h"
22548+#include "srs.h"
22549 
22550 /* critical timing feature #1: if not triggered, do not busy-loop */
22551 /* critical timing feature #2: if triggered, respond within fixed time */
22552@@ -44,6 +45,8 @@
22553 
22554 int lifetime = 604800;
22555 
22556+int bouncemaxbytes = 50000;
22557+
22558 stralloc percenthack = {0};
22559 struct constmap mappercenthack;
22560 stralloc locals = {0};
22561@@ -55,6 +58,7 @@
22562 stralloc bouncehost = {0};
22563 stralloc doublebounceto = {0};
22564 stralloc doublebouncehost = {0};
22565+stralloc srs_domain = {0};
22566 
22567 char strnum2[FMT_ULONG];
22568 char strnum3[FMT_ULONG];
22569@@ -82,9 +86,6 @@
22570 
22571 datetime_sec recent;
22572 
22573-
22574-/* this file is too long ----------------------------------------- FILENAMES */
22575-
22576 stralloc fn = {0};
22577 stralloc fn2 = {0};
22578 char fnmake_strnum[FMT_ULONG];
22579@@ -96,7 +97,7 @@
22580 }
22581 
22582 void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); }
22583-void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); }
22584+void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,1); }
22585 void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); }
22586 void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); }
22587 void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); }
22588@@ -262,6 +263,8 @@
22589  while (!stralloc_copys(&comm_buf[c],"")) nomem();
22590  ch = delnum;
22591  while (!stralloc_append(&comm_buf[c],&ch)) nomem();
22592+ ch = delnum >> 8;
22593+ while (!stralloc_append(&comm_buf[c],&ch)) nomem();
22594  fnmake_split(id);
22595  while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
22596  while (!stralloc_0(&comm_buf[c])) nomem();
22597@@ -683,15 +686,39 @@
22598   }
22599  if (str_equal(sender.s,"#@[]"))
22600    log3("triple bounce: discarding ",fn2.s,"\n");
22601+ else if (!*sender.s && *doublebounceto.s == '@')
22602+   log3("double bounce: discarding ",fn2.s,"\n");
22603  else
22604   {
22605    if (qmail_open(&qqt) == -1)
22606     { log1("warning: unable to start qmail-queue, will try later\n"); return 0; }
22607    qp = qmail_qp(&qqt);
22608 
22609-   if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
22610-   else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }
22611-
22612+   if (*sender.s) {
22613+     if (srs_domain.len) {
22614+       int j = 0;
22615+       j = byte_rchr(sender.s, sender.len, '@');
22616+       if (j < sender.len) {
22617+         if (srs_domain.len == sender.len - j - 1 && stralloc_starts(&srs_domain, sender.s + j + 1)) {
22618+           switch(srsreverse(sender.s)) {
22619+             case -3: log1(srs_error.s); log1("\n"); _exit(111); break;
22620+             case -2: nomem(); break;
22621+             case -1: log1("alert: unable to read controls\n"); _exit(111); break;
22622+             case 0: break;
22623+             case 1: if (!stralloc_copy(&sender,&srs_result)) nomem(); break;
22624+           }
22625+           if (chdir(auto_qmail) == -1) { log1("alert: unable to switch to home directory\n"); _exit(111); }
22626+           if (chdir("queue") == -1) { log1("alert: unable to switch to queue directory\n"); _exit(111); }
22627+         }
22628+       }
22629+     }
22630+     bouncesender = "";
22631+     bouncerecip = sender.s;
22632+   } else {
22633+     bouncesender = "#@[]";
22634+     bouncerecip = doublebounceto.s;
22635+   }
22636+   
22637    while (!newfield_datemake(now())) nomem();
22638    qmail_put(&qqt,newfield_date.s,newfield_date.len);
22639    qmail_puts(&qqt,"From: ");
22640@@ -740,9 +767,17 @@
22641      qmail_fail(&qqt);
22642    else
22643     {
22644+     int bytestogo = bouncemaxbytes;
22645+     int bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
22646      substdio_fdbuf(&ssread,read,fd,inbuf,sizeof(inbuf));
22647-     while ((r = substdio_get(&ssread,buf,sizeof(buf))) > 0)
22648+     while (bytestoget > 0 && (r = substdio_get(&ssread,buf,bytestoget)) > 0) {
22649        qmail_put(&qqt,buf,r);
22650+       bytestogo -= bytestoget;
22651+       bytestoget = (bytestogo < sizeof buf) ? bytestogo : sizeof buf;
22652+     }
22653+     if (r > 0) {
22654+       qmail_puts(&qqt,"\n\n--- End of message stripped.\n");
22655+     }
22656      close(fd);
22657      if (r == -1)
22658        qmail_fail(&qqt);
22659@@ -906,41 +941,42 @@
22660      dline[c].len = REPORTMAX;
22661      /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
22662      /* but from a security point of view, we don't trust rspawn */
22663-   if (!ch && (dline[c].len > 1))
22664+   if (!ch && (dline[c].len > 2))
22665     {
22666      delnum = (unsigned int) (unsigned char) dline[c].s[0];
22667+     delnum += (unsigned int) ((unsigned int) dline[c].s[1]) << 8;
22668      if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
22669        log1("warning: internal error: delivery report out of range\n");
22670      else
22671       {
22672        strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
22673-       if (dline[c].s[1] == 'Z')
22674+       if (dline[c].s[2] == 'Z')
22675         if (jo[d[c][delnum].j].flagdying)
22676          {
22677-          dline[c].s[1] = 'D';
22678+          dline[c].s[2] = 'D';
22679           --dline[c].len;
22680           while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
22681           while (!stralloc_0(&dline[c])) nomem();
22682          }
22683-       switch(dline[c].s[1])
22684+       switch(dline[c].s[2])
22685        {
22686         case 'K':
22687           log3("delivery ",strnum3,": success: ");
22688-          logsafe(dline[c].s + 2);
22689+          logsafe(dline[c].s + 3);
22690           log1("\n");
22691           markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
22692           --jo[d[c][delnum].j].numtodo;
22693           break;
22694         case 'Z':
22695           log3("delivery ",strnum3,": deferral: ");
22696-          logsafe(dline[c].s + 2);
22697+          logsafe(dline[c].s + 3);
22698           log1("\n");
22699           break;
22700         case 'D':
22701           log3("delivery ",strnum3,": failure: ");
22702-          logsafe(dline[c].s + 2);
22703+          logsafe(dline[c].s + 3);
22704           log1("\n");
22705-          addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
22706+          addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 3);
22707           markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
22708           --jo[d[c][delnum].j].numtodo;
22709           break;
22710@@ -1215,8 +1251,10 @@
22711 
22712 /* this file is too long ---------------------------------------------- TODO */
22713 
22714+#ifndef EXTERNAL_TODO
22715 datetime_sec nexttodorun;
22716-DIR *tododir; /* if 0, have to opendir again */
22717+int flagtododir = 0; /* if 0, have to readsubdir_init again */
22718+readsubdir todosubdir;
22719 stralloc todoline = {0};
22720 char todobuf[SUBSTDIO_INSIZE];
22721 char todobufinfo[512];
22722@@ -1224,7 +1262,7 @@
22723 
22724 void todo_init()
22725 {
22726- tododir = 0;
22727+ flagtododir = 0;
22728  nexttodorun = now();
22729  trigger_set();
22730 }
22731@@ -1236,7 +1274,7 @@
22732 {
22733  if (flagexitasap) return;
22734  trigger_selprep(nfds,rfds);
22735- if (tododir) *wakeup = 0;
22736+ if (flagtododir) *wakeup = 0;
22737  if (*wakeup > nexttodorun) *wakeup = nexttodorun;
22738 }
22739 
22740@@ -1253,8 +1291,7 @@
22741  char ch;
22742  int match;
22743  unsigned long id;
22744- unsigned int len;
22745- direntry *d;
22746+ int z;
22747  int c;
22748  unsigned long uid;
22749  unsigned long pid;
22750@@ -1265,32 +1302,26 @@
22751 
22752  if (flagexitasap) return;
22753 
22754- if (!tododir)
22755+ if (!flagtododir)
22756   {
22757    if (!trigger_pulled(rfds))
22758      if (recent < nexttodorun)
22759        return;
22760    trigger_set();
22761-   tododir = opendir("todo");
22762-   if (!tododir)
22763-    {
22764-     pausedir("todo");
22765-     return;
22766-    }
22767+   readsubdir_init(&todosubdir, "todo", pausedir);
22768+   flagtododir = 1;
22769    nexttodorun = recent + SLEEP_TODO;
22770   }
22771 
22772- d = readdir(tododir);
22773- if (!d)
22774+ switch(readsubdir_next(&todosubdir, &id))
22775   {
22776-   closedir(tododir);
22777-   tododir = 0;
22778+    case 1:
22779+      break;
22780+    case 0:
22781+      flagtododir = 0;
22782+    default:
22783    return;
22784   }
22785- if (str_equal(d->d_name,".")) return;
22786- if (str_equal(d->d_name,"..")) return;
22787- len = scan_ulong(d->d_name,&id);
22788- if (!len || d->d_name[len]) return;
22789 
22790  fnmake_todo(id);
22791 
22792@@ -1438,10 +1469,148 @@
22793    if (fdchan[c] != -1) close(fdchan[c]);
22794 }
22795 
22796+#endif
22797+
22798+/* this file is too long ------------------------------------- EXTERNAL TODO */
22799+
22800+#ifdef EXTERNAL_TODO
22801+stralloc todoline = {0};
22802+char todobuf[2048];
22803+int todofdin;
22804+int todofdout;
22805+int flagtodoalive;
22806+
22807+void tododied() { log1("alert: oh no! lost qmail-todo connection! dying...\n");
22808+ flagexitasap = 1; flagtodoalive = 0; }
22809+
22810+void todo_init()
22811+{
22812+  todofdout = 7;
22813+  todofdin = 8;
22814+  flagtodoalive = 1;
22815+  /* sync with external todo */
22816+  if (write(todofdout, "S", 1) != 1) tododied();
22817
22818+  return;
22819+}
22820+
22821+void todo_selprep(nfds,rfds,wakeup)
22822+int *nfds;
22823+fd_set *rfds;
22824+datetime_sec *wakeup;
22825+{
22826+  if (flagexitasap) {
22827+    if (flagtodoalive) {
22828+      write(todofdout, "X", 1);
22829+    }
22830+  }
22831+  if (flagtodoalive) {
22832+    FD_SET(todofdin,rfds);
22833+    if (*nfds <= todofdin)
22834+      *nfds = todofdin + 1;
22835+  }
22836+}
22837+
22838+void todo_del(char* s)
22839+{
22840+ int flagchan[CHANNELS];
22841+ struct prioq_elt pe;
22842+ unsigned long id;
22843+ unsigned int len;
22844+ int c;
22845+
22846+ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
22847+ switch(*s++) {
22848+  case 'L':
22849+    flagchan[0] = 1;
22850+    break;
22851+  case 'R':
22852+    flagchan[1] = 1;
22853+    break;
22854+  case 'B':
22855+    flagchan[0] = 1;
22856+    flagchan[1] = 1;
22857+    break;
22858+  case 'X':
22859+    break;
22860+  default:
22861+    log1("warning: qmail-send unable to understand qmail-todo\n");
22862+    return;
22863+ }
22864+
22865+ len = scan_ulong(s,&id);
22866+ if (!len || s[len]) {
22867+  log1("warning: qmail-send unable to understand qmail-todo\n");
22868+  return;
22869+ }
22870+
22871+ pe.id = id; pe.dt = now();
22872+ for (c = 0;c < CHANNELS;++c)
22873+   if (flagchan[c])
22874+     while (!prioq_insert(&pqchan[c],&pe)) nomem();
22875+
22876+ for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break;
22877+ if (c == CHANNELS)
22878+   while (!prioq_insert(&pqdone,&pe)) nomem();
22879+
22880+ return;
22881+}
22882+
22883+void todo_do(rfds)
22884+fd_set *rfds;
22885+{
22886+  int r;
22887+  char ch;
22888+  int i;
22889
22890+  if (!flagtodoalive) return;
22891+  if (!FD_ISSET(todofdin,rfds)) return;
22892+
22893+  r = read(todofdin,todobuf,sizeof(todobuf));
22894+  if (r == -1) return;
22895+  if (r == 0) {
22896+    if (flagexitasap)
22897+      flagtodoalive = 0;
22898+    else
22899+      tododied();
22900+    return;
22901+  }
22902+  for (i = 0;i < r;++i) {
22903+    ch = todobuf[i];
22904+    while (!stralloc_append(&todoline,&ch)) nomem();
22905+    if (todoline.len > REPORTMAX)
22906+      todoline.len = REPORTMAX;
22907+      /* qmail-todo is responsible for keeping it short */
22908+    if (!ch && (todoline.len > 1)) {
22909+      switch (todoline.s[0]) {
22910+       case 'D':
22911+         if (flagexitasap) break;
22912+         todo_del(todoline.s + 1);
22913+         break;
22914+       case 'L':
22915+         log1(todoline.s + 1);
22916+         break;
22917+       case 'X':
22918+         if (flagexitasap)
22919+           flagtodoalive = 0;
22920+         else
22921+           tododied();
22922+         break;
22923+       default:
22924+         log1("warning: qmail-send unable to understand qmail-todo: report mangled\n");
22925+         break;
22926+      }
22927+      todoline.len = 0;
22928+    }
22929+  }
22930+}
22931+
22932+#endif
22933 
22934 /* this file is too long ---------------------------------------------- MAIN */
22935 
22936 int getcontrols() { if (control_init() == -1) return 0;
22937+ if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0;   
22938  if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
22939  if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
22940  if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
22941@@ -1449,6 +1618,8 @@
22942  if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0;
22943  if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0;
22944  if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0;
22945+ if (control_readline(&srs_domain,"control/srs_domain") == -1) return 0;
22946+ if (srs_domain.len && !stralloc_0(&srs_domain)) return 0;
22947  if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0;
22948  if (!stralloc_cats(&doublebounceto,"@")) return 0;
22949  if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0;
22950@@ -1478,6 +1649,10 @@
22951 
22952  if (control_readfile(&newlocals,"control/locals",1) != 1)
22953   { log1("alert: unable to reread control/locals\n"); return; }
22954+ if (control_readint(&concurrency[0],"control/concurrencylocal") == -1)
22955+  { log1("alert: unable to reread control/concurrencylocal\n",0); return; }
22956+ if (control_readint(&concurrency[1],"control/concurrencyremote") == -1)
22957+  { log1("alert: unable to reread control/concurrencyremote\n",0); return; }
22958  r = control_readfile(&newvdoms,"control/virtualdomains",0);
22959  if (r == -1)
22960   { log1("alert: unable to reread control/virtualdomains\n"); return; }
22961@@ -1504,6 +1679,9 @@
22962    log1("alert: unable to reread controls: unable to switch to home directory\n");
22963    return;
22964   }
22965+#ifdef EXTERNAL_TODO
22966+ write(todofdout, "H", 1);
22967+#endif
22968  regetcontrols();
22969  while (chdir("queue") == -1)
22970   {
22971@@ -1544,7 +1722,7 @@
22972  numjobs = 0;
22973  for (c = 0;c < CHANNELS;++c)
22974   {
22975-   char ch;
22976+   char ch, ch1;
22977    int u;
22978    int r;
22979    do
22980@@ -1552,7 +1730,13 @@
22981    while ((r == -1) && (errno == error_intr));
22982    if (r < 1)
22983     { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
22984+   do
22985+     r = read(chanfdin[c],&ch1,1);
22986+   while ((r == -1) && (errno == error_intr));
22987+   if (r < 1)
22988+    { log1("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
22989    u = (unsigned int) (unsigned char) ch;
22990+   u += (unsigned int) ((unsigned char) ch1) << 8;
22991    if (concurrency[c] > u) concurrency[c] = u;
22992    numjobs += concurrency[c];
22993   }
22994@@ -1568,7 +1752,11 @@
22995  todo_init();
22996  cleanup_init();
22997 
22998+#ifdef EXTERNAL_TODO
22999+ while (!flagexitasap || !del_canexit() || flagtodoalive)
23000+#else
23001  while (!flagexitasap || !del_canexit())
23002+#endif
23003   {
23004    recent = now();
23005 
23006diff -ruN ../netqmail-1.06-original/qmail-showctl.c netqmail-1.06/qmail-showctl.c
23007--- ../netqmail-1.06-original/qmail-showctl.c   1998-06-15 12:53:16.000000000 +0200
23008+++ netqmail-1.06/qmail-showctl.c       2016-11-28 19:39:45.341600337 +0100
23009@@ -15,6 +15,7 @@
23010 #include "auto_patrn.h"
23011 #include "auto_spawn.h"
23012 #include "auto_split.h"
23013+#include "spf.h"
23014 
23015 stralloc me = {0};
23016 int meok;
23017@@ -112,7 +113,7 @@
23018   substdio_puts(subfdout,"\n");
23019   substdio_puts(subfdout,fn);
23020   substdio_puts(subfdout,": ");
23021-  switch(control_readfile(&line,fn)) {
23022+  switch(control_readfile(&line,fn, 0)) {
23023     case 0:
23024       substdio_puts(subfdout,"(Default.) ");
23025       substdio_puts(subfdout,def);
23026@@ -214,14 +215,22 @@
23027     _exit(111);
23028   }
23029 
23030-  do_lst("badmailfrom","Any MAIL FROM is allowed.",""," not accepted in MAIL FROM.");
23031+  do_lst("authsenders","No authenticated SMTP sender.","Authenicated SMTP sender: ","");
23032+  do_lst("badhelo","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern.");
23033+  do_lst("badhelonorelay","Any HELO host name is allowed.",""," HELO host name denied if it matches this pattern and RELAYCLIENT is not set.");
23034+  do_lst("badmailfrom","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern.");
23035+  do_lst("badmailfromnorelay","Any MAIL FROM is allowed.",""," MAIL FROM denied if it matches this pattern and RELAYCLIENT is not set.");
23036+  do_lst("badmailto","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern.");
23037+  do_lst("badmailtonorelay","No RCPT TO are specifically denied.",""," RCPT TO denied if it matches this pattern and RELAYCLIENT is not set.");
23038   do_str("bouncefrom",0,"MAILER-DAEMON","Bounce user name is ");
23039   do_str("bouncehost",1,"bouncehost","Bounce host name is ");
23040+  do_int("brtlimit","0","The brtlimit is ","");
23041   do_int("concurrencylocal","10","Local concurrency is ","");
23042   do_int("concurrencyremote","20","Remote concurrency is ","");
23043   do_int("databytes","0","SMTP DATA limit is "," bytes");
23044   do_str("defaultdomain",1,"defaultdomain","Default domain name is ");
23045   do_str("defaulthost",1,"defaulthost","Default host name is ");
23046+  do_lst("dnsbllist","No dnsbl list configured.","List at "," configured for dnsbl check.");
23047   do_str("doublebouncehost",1,"doublebouncehost","2B recipient host: ");
23048   do_str("doublebounceto",0,"postmaster","2B recipient user: ");
23049   do_str("envnoathost",1,"envnoathost","Presumed domain name is ");
23050@@ -230,6 +239,9 @@
23051   do_str("localiphost",1,"localiphost","Local IP address becomes ");
23052   do_lst("locals","Messages for me are delivered locally.","Messages for "," are delivered locally.");
23053   do_str("me",0,"undefined! Uh-oh","My name is ");
23054+  do_lst("moreipme","No additional IP addresses are me.","IP address "," is me.");
23055+  do_lst("notipme","All of my IP addresses are me.","IP address "," is not me.");
23056+  do_str("outgoingip",0,"0.0.0.0","Outgoing IP address is ");
23057   do_lst("percenthack","The percent hack is not allowed.","The percent hack is allowed for user%host@",".");
23058   do_str("plusdomain",1,"plusdomain","Plus domain name is ");
23059   do_lst("qmqpservers","No QMQP servers.","QMQP server: ",".");
23060@@ -257,6 +269,15 @@
23061 
23062   do_str("smtpgreeting",1,"smtpgreeting","SMTP greeting: 220 ");
23063   do_lst("smtproutes","No artificial SMTP routes.","SMTP route: ","");
23064+  do_int("spfbehavior","0","The SPF behavior is ","");
23065+  do_str("spfexp",0,SPF_DEFEXP,"The SPF default explanation is: 550 ");
23066+  do_str("spfguess",0,"","The guess SPF rules are: ");
23067+  do_str("spfrules",0,"","The local SPF rules are: ");
23068+  do_str("srs_domain",0,"","SRS domain name is ");
23069+  do_lst("srs_secrets","No secrets","","");
23070+  do_int("srs_maxage","21","SRS maxage is ","");
23071+  do_int("srs_hashlength","4","SRS hashlength is ","");
23072+  do_int("srs_hashmin","4","SRS hashmin is ","");
23073   do_int("timeoutconnect","60","SMTP client connection timeout is "," seconds");
23074   do_int("timeoutremote","1200","SMTP client data timeout is "," seconds");
23075   do_int("timeoutsmtpd","1200","SMTP server data timeout is "," seconds");
23076@@ -265,9 +286,12 @@
23077   while (d = readdir(dir)) {
23078     if (str_equal(d->d_name,".")) continue;
23079     if (str_equal(d->d_name,"..")) continue;
23080-    if (str_equal(d->d_name,"bouncefrom")) continue;
23081-    if (str_equal(d->d_name,"bouncehost")) continue;
23082+    if (str_equal(d->d_name,"authsenders")) continue;
23083     if (str_equal(d->d_name,"badmailfrom")) continue;
23084+    if (str_equal(d->d_name,"badhelo")) continue;
23085+    if (str_equal(d->d_name,"badmailfromnorelay")) continue;
23086+    if (str_equal(d->d_name,"badmailto")) continue;
23087+    if (str_equal(d->d_name,"badmailtonorelay")) continue;
23088     if (str_equal(d->d_name,"bouncefrom")) continue;
23089     if (str_equal(d->d_name,"bouncehost")) continue;
23090     if (str_equal(d->d_name,"concurrencylocal")) continue;
23091@@ -275,6 +299,7 @@
23092     if (str_equal(d->d_name,"databytes")) continue;
23093     if (str_equal(d->d_name,"defaultdomain")) continue;
23094     if (str_equal(d->d_name,"defaulthost")) continue;
23095+    if (str_equal(d->d_name,"dnsbllist")) continue;
23096     if (str_equal(d->d_name,"doublebouncehost")) continue;
23097     if (str_equal(d->d_name,"doublebounceto")) continue;
23098     if (str_equal(d->d_name,"envnoathost")) continue;
23099@@ -283,8 +308,10 @@
23100     if (str_equal(d->d_name,"localiphost")) continue;
23101     if (str_equal(d->d_name,"locals")) continue;
23102     if (str_equal(d->d_name,"me")) continue;
23103+    if (str_equal(d->d_name,"moreipme")) continue;
23104     if (str_equal(d->d_name,"morercpthosts")) continue;
23105     if (str_equal(d->d_name,"morercpthosts.cdb")) continue;
23106+    if (str_equal(d->d_name,"notipme")) continue;
23107     if (str_equal(d->d_name,"percenthack")) continue;
23108     if (str_equal(d->d_name,"plusdomain")) continue;
23109     if (str_equal(d->d_name,"qmqpservers")) continue;
23110@@ -292,6 +319,15 @@
23111     if (str_equal(d->d_name,"rcpthosts")) continue;
23112     if (str_equal(d->d_name,"smtpgreeting")) continue;
23113     if (str_equal(d->d_name,"smtproutes")) continue;
23114+    if (str_equal(d->d_name,"spfbehavior")) continue;
23115+    if (str_equal(d->d_name,"spfexp")) continue;
23116+    if (str_equal(d->d_name,"spfguess")) continue;
23117+    if (str_equal(d->d_name,"spfrules")) continue;
23118+    if (str_equal(d->d_name,"srs_domain")) continue;
23119+    if (str_equal(d->d_name,"srs_secrets")) continue;
23120+    if (str_equal(d->d_name,"srs_maxage")) continue;
23121+    if (str_equal(d->d_name,"srs_hashlength")) continue;
23122+    if (str_equal(d->d_name,"srs_hashmin")) continue;
23123     if (str_equal(d->d_name,"timeoutconnect")) continue;
23124     if (str_equal(d->d_name,"timeoutremote")) continue;
23125     if (str_equal(d->d_name,"timeoutsmtpd")) continue;
23126diff -ruN ../netqmail-1.06-original/qmail-smtpd.8 netqmail-1.06/qmail-smtpd.8
23127--- ../netqmail-1.06-original/qmail-smtpd.8     1998-06-15 12:53:16.000000000 +0200
23128+++ netqmail-1.06/qmail-smtpd.8 2016-12-18 19:08:22.496459855 +0100
23129@@ -14,6 +14,15 @@
23130 see
23131 .BR tcp-environ(5) .
23132 
23133+If the environment variable
23134+.B SMTPS
23135+is non-empty,
23136+.B qmail-smtpd
23137+starts a TLS session (to support the deprecated SMTPS protocol,
23138+normally on port 465). Otherwise,
23139+.B qmail-smtpd
23140+offers the STARTTLS extension to ESMTP.
23141+
23142 .B qmail-smtpd
23143 is responsible for counting hops.
23144 It rejects any message with 100 or more
23145@@ -23,7 +32,30 @@
23146 header fields.
23147 
23148 .B qmail-smtpd
23149-supports ESMTP, including the 8BITMIME and PIPELINING options.
23150+supports ESMTP, including the 8BITMIME, DATA, PIPELINING, SIZE, and AUTH options.
23151+.B qmail-smtpd
23152+includes a \'MAIL FROM:\' parameter parser and obeys \'Auth\' and \'Size\' advertisements.
23153+.B qmail-smtpd
23154+can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types. It invokes
23155+.IR checkprogram ,
23156+which reads on file descriptor 3 the username, a 0 byte, the password
23157+or CRAM-MD5 digest/response derived from the SMTP client,
23158+another 0 byte, a CRAM-MD5 challenge (if applicable to the AUTH type),
23159+and a final 0 byte.
23160+.I checkprogram
23161+invokes
23162+.I subprogram
23163+upon successful authentication, which should in turn return 0 to
23164+.BR qmail-smtpd ,
23165+effectively setting the environment variables $RELAYCLIENT and $TCPREMOTEINFO
23166+(any supplied value replaced with the authenticated username).
23167+.B qmail-smtpd
23168+will reject the authentication attempt if it receives a nonzero return
23169+value from
23170+.I checkprogram
23171+or
23172+.IR subprogram .
23173+
23174 .SH TRANSPARENCY
23175 .B qmail-smtpd
23176 converts the SMTP newline convention into the UNIX newline convention
23177@@ -37,11 +69,34 @@
23178 even though such messages violate the SMTP protocol.
23179 .SH "CONTROL FILES"
23180 .TP 5
23181+.I badhelo
23182+Unacceptable HELO/EHLO host names.
23183+.B qmail-smtpd
23184+will reject every recipient address for a message if
23185+the host name is listed in,
23186+or matches a POSIX regular expression pattern listed in,
23187+.IR badhelo .
23188+If the
23189+.B NOBADHELO
23190+environment variable is set, then the contents of
23191+.IR badhelo
23192+will be ignored.
23193+For more information, please have a look at doc/README.qregex.
23194+.TP 5
23195+.I badhelonorelay
23196+Functions the same as the
23197+.IR badhelo
23198+control file but is read only if the
23199+.B RELAYCLIENT
23200+environment variable is not set.
23201+For more information, please have a look at doc/README.qregex.
23202+.TP 5
23203 .I badmailfrom
23204 Unacceptable envelope sender addresses.
23205 .B qmail-smtpd
23206 will reject every recipient address for a message
23207-if the envelope sender address is listed in
23208+if the envelope sender address is listed in, , or matches a POSIX regular expression
23209+pattern listed in,
23210 .IR badmailfrom .
23211 A line in
23212 .I badmailfrom
23213@@ -49,6 +104,61 @@
23214 .BR @\fIhost ,
23215 meaning every address at
23216 .IR host .
23217+For more information, please have a look at doc/README.qregex.
23218+.TP 5
23219+.I badmailfromnorelay
23220+Functions the same as the
23221+.IR badmailfrom
23222+control file but is read only if the
23223+.B RELAYCLIENT
23224+environment variable is not set.
23225+For more information, please have a look at doc/README.qregex.
23226+.TP 5
23227+.I badmailtonorelay
23228+Functions the same as the
23229+.IR badmailto
23230+control file but is read only if the
23231+.B RELAYCLIENT
23232+environment variable is not set.
23233+For more information, please have a look at doc/README.qregex.
23234+.TP 5
23235+.I badrcptto
23236+Unacceptable envelope recipient addresses.
23237+.B qmail-smtpd
23238+will reject every recipient address for a message if the recipient address
23239+is listed in,
23240+or matches a POSIX regular expression pattern listed in,
23241+.IR badrcptto .
23242+For more information, please have a look at doc/README.qregex.
23243+
23244+.TP 5
23245+.I brtlimit
23246+Number of bad recipients before closing the transmission channel.
23247+.B qmail-smtpd
23248+will close the transmission channel after
23249+reaching the number of bad recipients in
23250+.IR brtlimit .
23251+Both badrcptto, chkuser and validrcptto failures are counted.
23252+
23253+If the environment variable
23254+.B BRTLIMIT
23255+is set, it overrides
23256+.IR brtlimit .
23257+
23258+Default and minimum: 0.
23259+
23260+.TP 5
23261+.I clientca.pem
23262+A list of Certifying Authority (CA) certificates that are used to verify
23263+the client-presented certificates during a TLS-encrypted session.
23264+
23265+.TP 5
23266+.I clientcrl.pem
23267+A list of Certificate Revocation Lists (CRLs). If present it
23268+should contain the CRLs of the CAs in
23269+.I clientca.pem
23270+and client certs will be checked for revocation.
23271+
23272 .TP 5
23273 .I databytes
23274 Maximum number of bytes allowed in a message,
23275@@ -76,6 +186,34 @@
23276 .B DATABYTES
23277 is set, it overrides
23278 .IR databytes .
23279+
23280+.TP 5
23281+.I dh2048.pem
23282+If these 2048 bit DH parameters are provided,
23283+.B qmail-smtpd
23284+will use them for TLS sessions instead of generating one on-the-fly
23285+(which is very timeconsuming).
23286+
23287+.TP 5
23288+.I dnsbllist
23289+A list of dnsbl providers that
23290+.B qmail-smtpd
23291+checks to identify blacklisted ip addresses.
23292+
23293+Exception:
23294+If the environment variable
23295+.B DNSBLSKIP
23296+is set,
23297+.B qmail-smtpd
23298+ignores
23299+.IR dnsbllist ,
23300+and the dnsbl check is not performed.
23301+The check is skipped even if some other authentication method succedeed
23302+and authorized the client to relay (smtp-auth or tls client certificate),
23303+or if
23304+.B RELAYCLIENT
23305+enviromnent variable is set.
23306+
23307 .TP 5
23308 .I localiphost
23309 Replacement host name for local IP addresses.
23310@@ -151,6 +289,19 @@
23311 
23312 Envelope recipient addresses without @ signs are
23313 always allowed through.
23314+
23315+.TP 5
23316+.I rsa2048.pem
23317+If this 2048 bit RSA key is provided,
23318+.B qmail-smtpd
23319+will use it for TLS sessions instead of generating one on-the-fly.
23320+
23321+.TP 5
23322+.I servercert.pem
23323+SSL certificate to be presented to clients in TLS-encrypted sessions.
23324+Should contain both the certificate and the private key. Certifying Authority
23325+(CA) and intermediate certificates can be added at the end of the file.
23326+
23327 .TP 5
23328 .I smtpgreeting
23329 SMTP greeting message.
23330@@ -169,6 +320,119 @@
23331 .B qmail-smtpd
23332 will wait for each new buffer of data from the remote SMTP client.
23333 Default: 1200.
23334+
23335+.SH "ENVIRONMENT VARIABLES READ"
23336+Environment variables may be defined globally in the
23337+.B qmail-smtpd
23338+startup script and/or individually as part of the
23339+.B tcpserver's
23340+cdb database.
23341+The environment variables may be quoted ("variable", or 'variable') and
23342+in case of global use, have to be exported.
23343+.B qmail-smtpd
23344+supports the following legacy environment variables, typically
23345+provided by
23346+.B tcpserver
23347+or
23348+.B sslserver
23349+or
23350+.BR tcp-env :
23351+.IR TCPREMOTEIP ,
23352+.IR TCPREMOTEHOST
23353+.IR TCPREMOTEINFO
23354+and
23355+.IR TCPLOCALPORT
23356+as well as
23357+.IR RELAYCLIENT .
23358+
23359+.B qmail-smtpd
23360+may use the following environment variables for SMTP authentication:
23361+.TP 5
23362+.IR SMTPAUTH
23363+is used to enable SMTP Authentication for the AUTH types
23364+LOGIN and PLAIN.
23365+In case
23366+.TP 5
23367+.IR SMTPAUTH='+cram'
23368+is defined,
23369+.B qmail-smtpd
23370+honors LOGIN, PLAIN, and additionally CRAM-MD5 authentication.
23371+Simply
23372+.TP 5
23373+.IR SMTPAUTH='cram'
23374+restricts authentication just to CRAM-MD5.
23375+If however
23376+.TP 5
23377+.IR SMTPAUTH='!'
23378+starts with an exclamation mark, AUTH is required.
23379+You can enforce 'Submission' using this option
23380+and binding
23381+.B qmail-smtpd
23382+to the SUBMISSION port \'587'\.
23383+In particular,
23384+.TP 5
23385+.IR SMTPAUTH='!cram'
23386+may be useful.
23387+In opposite, if
23388+.TP 5
23389+.IR SMTPAUTH='-'
23390+starts with a dash, AUTH is disabled for particular
23391+connections.
23392+
23393+Note: The use of 'cram' requires a CRAM-MD5 enabled PAM.
23394+
23395+.TP 5
23396+.I tlsclients
23397+A list of email addresses. When relay rules would reject an incoming message,
23398+.B qmail-smtpd
23399+can allow it if the client presents a certificate that can be verified against
23400+the CA list in
23401+.I clientca.pem
23402+and the certificate email address is in
23403+.IR tlsclients .
23404+
23405+.TP 5
23406+.I tlsserverciphers
23407+A set of OpenSSL cipher strings. Multiple ciphers contained in a
23408+string should be separated by a colon. If the environment variable
23409+.B TLSCIPHERS
23410+is set to such a string, it takes precedence.
23411+
23412+.TP 5
23413+.I spfbehavior
23414+Set to a value between 1 and 6 to enable SPF checks; 0 to disable.
23415+1 selects 'annotate-only' mode, where
23416+.B qmail-smtpd
23417+will annotate incoming email with
23418+.B Received-SPF
23419+fields, but will not reject any messages.  2 will produce temporary
23420+failures on DNS lookup problems so you can make sure you always have
23421+meaningful Received-SPF headers.  3 selects 'reject' mode,
23422+where incoming mail will be rejected if the SPF record says 'fail'.  4
23423+selects a more stricter rejection mode, which is like 'reject' mode,
23424+except that incoming mail will also be rejected when the SPF record
23425+says 'softfail'.  5 will also reject when the SPF record says 'neutral',
23426+and 6 if no SPF records are available at all (or a syntax error was
23427+encountered). The contents of this file are overridden by the value of
23428+the
23429+.B SPFBEHAVIOR
23430+environment variable, if set.
23431+Default: 0.
23432+.TP 5
23433+.I spfexp
23434+You can add a line with a an SPF explanation that will be shown to the
23435+sender in case of a reject. It will override the default one. You can
23436+use SPF macro expansion.
23437+.TP 5
23438+.I spfguess
23439+You can add a line with SPF rules that will be checked if a sender
23440+domain doesn't have a SPF record. The local rules will also be used
23441+in this case.
23442+.TP 5
23443+.I spfrules
23444+You can add a line with SPF rules that will be checked before other SPF
23445+rules would fail.  This can be used to always allow certain machines to
23446+send certain mails.
23447 .SH "SEE ALSO"
23448 tcp-env(1),
23449 tcp-environ(5),
23450diff -ruN ../netqmail-1.06-original/qmail-smtpd.c netqmail-1.06/qmail-smtpd.c
23451--- ../netqmail-1.06-original/qmail-smtpd.c     2007-11-30 21:22:54.000000000 +0100
23452+++ netqmail-1.06/qmail-smtpd.c 2017-11-07 17:45:04.599607652 +0100
23453@@ -12,6 +12,9 @@
23454 #include "ip.h"
23455 #include "qmail.h"
23456 #include "str.h"
23457+#include "strerr.h"
23458+#include "qregex.h"
23459+#include "cdb.h"
23460 #include "fmt.h"
23461 #include "scan.h"
23462 #include "byte.h"
23463@@ -23,44 +26,178 @@
23464 #include "timeoutread.h"
23465 #include "timeoutwrite.h"
23466 #include "commands.h"
23467+#include "dns.h"
23468+#include "wait.h"
23469+/* start chkuser code */
23470+#include "chkuser.h"
23471+/* end chkuser code */
23472+#include "spf.h"
23473+/* rbl: start */
23474+#include "strsalloc.h"
23475+/* rbl: end */
23476+
23477+#define AUTHSLEEP 5
23478 
23479 #define MAXHOPS 100
23480+
23481+#define BMCHECK_BMF 0
23482+#define BMCHECK_BMFNR 1
23483+#define BMCHECK_BMT 2
23484+#define BMCHECK_BMTNR 3
23485+#define BMCHECK_BHELO 4
23486+#define BMCHECK_BHELONR 5
23487+
23488+static char strnum[FMT_ULONG];
23489 unsigned int databytes = 0;
23490+unsigned int greetdelay = 0;
23491+unsigned int drop_pre_greet = 0;
23492 int timeout = 1200;
23493+int maxrcpt = -1;
23494+unsigned int spfbehavior = 0;
23495+
23496+/* rejectrelaytest: start */
23497+unsigned int rejectrelaytest = 0;
23498+/* rejecrelayttest: end */
23499+/* rejectnullsenders: start */
23500+unsigned int rejnsmf = 0;
23501+/* rejectnullsenders: end */
23502+
23503+const char *protocol = "SMTP";
23504+
23505+/* spf ipv6 fix */
23506+char *remoteip4;
23507+/* end spf ipv6 fix */
23508+
23509+#ifdef TLS
23510+#include <sys/stat.h>
23511+#include "tls.h"
23512+#include "ssl_timeoutio.h"
23513+void tls_init();
23514+int tls_verify();
23515+void tls_nogateway();
23516+int ssl_rfd = -1, ssl_wfd = -1; /* SSL_get_Xfd() are broken */
23517+int forcetls = 1;
23518+#endif
23519 
23520 int safewrite(fd,buf,len) int fd; char *buf; int len;
23521 {
23522   int r;
23523+#ifdef TLS
23524+  if (ssl && fd == ssl_wfd)
23525+    r = ssl_timeoutwrite(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
23526+  else
23527+#endif
23528   r = timeoutwrite(timeout,fd,buf,len);
23529   if (r <= 0) _exit(1);
23530   return r;
23531 }
23532 
23533 char ssoutbuf[512];
23534+char sslogbuf[512];
23535 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
23536+substdio sslog = SUBSTDIO_FDBUF(safewrite,2,sslogbuf,sizeof sslogbuf);
23537+char sserrbuf[512];
23538+substdio sserr = SUBSTDIO_FDBUF(safewrite,2,sserrbuf,sizeof sserrbuf);
23539+
23540+int addrinrcpthosts = 0;
23541+int envelopepos = 0; // 1: ehlo/helo, 2: mailfrom, 3: rcptto: 4: data
23542+void qsmtpdlog(const char *head, const char *result, const char *reason, const char *detail, const char *statuscode);
23543+void qlogenvelope(char *result, char *reason, char *detail, char *statuscode) { qsmtpdlog("qlogenvelope",result,reason,detail,statuscode); }
23544+void qlogreceived(char *result, char *reason, char *detail, char *statuscode) { qsmtpdlog("qlogreceived",result,reason,detail,statuscode); }
23545 
23546+void logit(const char* message);
23547+void logit2(const char* message, const char* reason);
23548 void flush() { substdio_flush(&ssout); }
23549 void out(s) char *s; { substdio_puts(&ssout,s); }
23550 
23551-void die_read() { _exit(1); }
23552-void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
23553-void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
23554-void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
23555-void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
23556-void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
23557+void die_read() { logit("read failed"); _exit(1); }
23558+void die_alarm() { qlogenvelope("rejected","alarmtimeout","","451"); logit("timeout"); out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }
23559+void die_nomem() { qlogenvelope("rejected","outofmemory","","421"); out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }
23560+void die_control() { qlogenvelope("rejected","cannotreadcontrols","","421"); logit("unable to read controls"); out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }
23561+void die_ipme() { qlogenvelope("rejected","unknownipme","","553"); logit("unable to figure out my IP addresses"); out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }
23562+/* rbl: start */
23563+/*
23564+void die_dnsbl(arg)
23565+char *arg;
23566+{
23567+  out("421 your ip is currently blacklisted, try to auth first ("); out(arg); out(")\r\n");
23568+  logit2("message rejected (qmail-dnsbl)", arg);
23569+  flush();
23570+  _exit(1);
23571+}
23572+*/
23573+/* rbl: end */
23574+void err_maxrcpt()
23575+{
23576+  out("553 max rcpt limit exceeded (#5.7.1)\r\n");
23577+  logit("max rcpt limit exceeded (qmail-maxrcpt)");
23578+  qlogenvelope("rejected","maxrcpt","","553");
23579+  flush();
23580+}
23581+void straynewline() { qlogenvelope("rejected","badnewlines","","451"); logit("bad newlines"); out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }
23582+void die_pre_greet() { qlogenvelope("rejected","pregreet","","554"); out("554 SMTP protocol violation\r\n"); flush(); _exit(1); }
23583 
23584-void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
23585-void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
23586+void err_size() { qlogreceived("rejected","size","","552"); out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); }
23587+#ifndef TLS
23588+void err_nogateway() { qlogenvelope("rejected","notinrcpthosts","","553"); out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }
23589+#else
23590+void err_nogateway()
23591+{
23592+  qlogenvelope("rejected","notinrcpthosts","","553"); out("553 sorry, that domain isn't in my list of allowed rcpthosts");
23593+  tls_nogateway();
23594+  out(" (#5.7.1)\r\n");
23595+}
23596+#endif
23597 void err_unimpl(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); }
23598+void err_unrecog() { out("500 unrecognised (#5.5.2)\r\n"); }
23599 void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }
23600 void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }
23601 void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }
23602 void err_noop(arg) char *arg; { out("250 ok\r\n"); }
23603 void err_vrfy(arg) char *arg; { out("252 send some mail, i'll try my best\r\n"); }
23604-void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
23605+void err_qqt() { qlogenvelope("rejected","qqtfailure","","451"); out("451 qqt failure (#4.3.0)\r\n"); }
23606 
23607+int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
23608+int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
23609+int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
23610+int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
23611+void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
23612+void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
23613+int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }
23614+int err_authabrt() { out("501 auth exchange canceled (#5.0.0)\r\n"); return -1; }
23615+int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
23616+void err_authfail() { qlogenvelope("rejected","authfailed","","535"); out("535 authentication failed (#5.7.1)\r\n"); }
23617+void err_submission() { qlogenvelope("rejected","authrequired","","530"); out("530 Authorization required (#5.7.1) \r\n"); }
23618+void err_vrt() { qlogenvelope("rejected","validrcptto","","553"); out("553 sorry, this recipient is not in my validrcptto list (#5.7.1)\r\n"); }
23619+void die_brtlimit() { qlogenvelope("rejected","brtlimit","","421"); out("421 too many invalid addresses, goodbye (#4.3.0)\r\n"); flush(); _exit(1); }
23620+void err_rcpt() { qlogenvelope("rejected","nomailbox","","550"); out("550 sorry, no mailbox here by that name (#5.1.1)\r\n"); }
23621+/* rcptcheck: start */
23622+void die_fork() { qlogenvelope("rejected","rcptcheck","cannotfork","421"); out("421 unable to fork (#4.3.0)\r\n"); flush(); _exit(1); }
23623+void die_rcpt() { qlogenvelope("rejected","rcptcheck","cannotverify","421"); out("421 unable to verify recipient (#4.3.0)\r\n"); flush(); _exit(1); }
23624+void die_rcpt2() { qlogenvelope("rejected","rcptcheck","cannotexecute","421"); out("421 unable to execute recipient check (#4.3.0)\r\n"); flush(); _exit(1); }
23625+/* rcptcheck: end */
23626+/* qregex: start */
23627+/*
23628+void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }
23629+*/
23630+void err_bmf() { out("553 sorry, your envelope sender has been denied (#5.7.1)\r\n"); }
23631+void err_bmt() { out("553 sorry, your envelope recipient has been denied (#5.7.1)\r\n"); }
23632+void err_bhelo() { out("553 sorry, your HELO host name has been denied (#5.7.1)\r\n"); }
23633+/* qregex: end */
23634+/* rejectnullsenders: start */
23635+void die_nullsender() { qlogenvelope("rejected","nullsenderdenied","","421"); out("421 null senders temporarily denied (#4.3.0)\r\n"); flush(); _exit(1); }
23636+/* rejectnullsenders: end */
23637+/* rejectrelaytest: start */
23638+void err_relay() { qlogenvelope("rejected","dontrelay","","553"); out("553 we don't relay (#5.7.1)\r\n"); }
23639+/* rejectrelaytest: end */
23640+/* authtlsvariables: start */
23641+void err_authmismatch() { qlogenvelope("rejected","authnotmailfrom","","503"); out("503 from and auth not the same (#5.5.1)\r\n"); }
23642+/* authtlsvariables: end */
23643 
23644 stralloc greeting = {0};
23645+stralloc spflocal = {0};
23646+stralloc spfguess = {0};
23647+stralloc spfexp = {0};
23648 
23649 void smtp_greet(code) char *code;
23650 {
23651@@ -76,11 +213,33 @@
23652   smtp_greet("221 "); out("\r\n"); flush(); _exit(0);
23653 }
23654 
23655+/* char *protocol; */
23656 char *remoteip;
23657 char *remotehost;
23658 char *remoteinfo;
23659 char *local;
23660+char *localport;
23661+char *submission;
23662 char *relayclient;
23663+char *dnsblskip;
23664+char *auth;
23665+/* authtlsvariables: start */
23666+int flagtls = 0;
23667+int forceauthmailfrom = 0;
23668+int disabletls = 0;
23669+/* authtlsvariables: end */
23670+
23671+char unique[FMT_ULONG + FMT_ULONG + 3];
23672+static stralloc authin = {0};   /* input from SMTP client */
23673+static stralloc user = {0};     /* authorization user-id */
23674+static stralloc pass = {0};     /* plain passwd or digest */
23675+static stralloc resp = {0};     /* b64 response */
23676+static stralloc chal = {0};     /* plain challenge */
23677+static stralloc slop = {0};     /* b64 challenge */
23678+
23679+char **childargs;
23680+char ssauthbuf[512];
23681+substdio ssauth = SUBSTDIO_FDBUF(safewrite,3,ssauthbuf,sizeof(ssauthbuf));
23682 
23683 stralloc helohost = {0};
23684 char *fakehelo; /* pointer into helohost, or 0 */
23685@@ -91,11 +250,101 @@
23686   fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;
23687 }
23688 
23689+int smtpauth = 0;
23690 int liphostok = 0;
23691 stralloc liphost = {0};
23692 int bmfok = 0;
23693 stralloc bmf = {0};
23694-struct constmap mapbmf;
23695+
23696+/* rbl: start */
23697+int flagrbldns = 0;
23698+int flagrbldelay = 0;
23699+int flagrblfailclosed = 0;
23700+int flagmustnotbounce = 0;
23701+static stralloc rbldnslist = {0};
23702+static stralloc rblhost = {0};
23703+int rblhosterror = 0;
23704+int rbllistok = 0;
23705+int rblok = 0;
23706+char *ip_env;
23707+static stralloc ip_reverse;
23708+int rbldecision = 0; /* 0 undecided, 1 accept, 2 reject (451), 3 bounce (553) */
23709+static stralloc rbltext = {0}; /* defined if rbldecision is 2 or 3 */
23710+static stralloc rblmessage = {0};
23711+static stralloc rblserver = {0};
23712+
23713+void err_rblreject() {
23714+  if (env_get("RBLSMTPD")) {
23715+    qlogenvelope("rejected","rblreject","rblsmtpd","553");
23716+  }
23717+  else {
23718+    if (rblserver.len) qlogenvelope("rejected","rblreject",rblserver.s,"553");
23719+    else qlogenvelope("rejected","rblreject","","553");
23720+  }
23721+  substdio_put(&ssout,rblmessage.s,rblmessage.len);
23722+  flush();
23723+}
23724+
23725+void die_rbldelay() {
23726+  if (env_get("RBLSMTPD")) {
23727+    qlogenvelope("rejected","rbldelay","rblsmtpd","451");
23728+  }
23729+  else {
23730+    if (rblserver.len) qlogenvelope("rejected","rbldelay",rblserver.s,"451");
23731+    else qlogenvelope("rejected","rbldelay","","451");
23732+  }
23733+  substdio_put(&ssout,rblmessage.s,rblmessage.len); flush();
23734+  _exit(1);
23735+}
23736+/* rbl: end */
23737+
23738+/* qregex: start */
23739+/*
23740+ struct constmap mapbmf;
23741+*/
23742+int bmfnrok = 0;
23743+stralloc bmfnr = {0};
23744+
23745+int bmtok = 0;
23746+stralloc bmt = {0};
23747+
23748+int bmtnrok = 0;
23749+stralloc bmtnr = {0};
23750+
23751+int bhelook = 0;
23752+stralloc bhelo = {0};
23753+
23754+int bhelonrok = 0;
23755+stralloc bhelonr = {0};
23756+
23757+int logregex = 0;
23758+stralloc matchedregex = {0};
23759+/* qregex: end */
23760+
23761+/* validrcptto.cdb: start */
23762+int vrtok = 0;
23763+stralloc vrt = {0};
23764+struct constmap mapvrt;
23765+
23766+int vrtfd = -1;
23767+int vrtcount = 0;
23768+int vrtlog_do = 0;
23769+
23770+stralloc title = {0};
23771+char pid_buf[FMT_ULONG];
23772+/* validrcptto.cdb: end */
23773+
23774+/* realbadrcpt: start */
23775+int brtlimit = 0;
23776+static char strnumpid[FMT_ULONG];
23777+static char strnumqp[FMT_ULONG];
23778+/* realbadrcpt: end */
23779+
23780+/* rcptcheck: start */
23781+static char *rcptcheck[2] = { 0, 0 };
23782+char rcptcheck_err[1024];
23783+int rcptcheckrelayclient = 0;
23784+/* rcptcheck: end */
23785 
23786 void setup()
23787 {
23788@@ -109,21 +358,110 @@
23789   if (liphostok == -1) die_control();
23790   if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();
23791   if (timeout <= 0) timeout = 1;
23792+  if (control_readint(&maxrcpt,"control/maxrcpt") == -1) die_control();
23793 
23794   if (rcpthosts_init() == -1) die_control();
23795 
23796   bmfok = control_readfile(&bmf,"control/badmailfrom",0);
23797   if (bmfok == -1) die_control();
23798+/* qregex: start */
23799+/*
23800   if (bmfok)
23801     if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();
23802+*/
23803+  strnumpid[fmt_uint(strnumpid,(unsigned int) getpid())] = 0;
23804+
23805+  bmfnrok = control_readfile(&bmfnr,"control/badmailfromnorelay",0);
23806+  if (bmfnrok == -1) die_control();
23807+
23808+  bmtok = control_readfile(&bmt,"control/badrcptto",0);
23809+  if (bmtok == -1) die_control();
23810+
23811+  bmtnrok = control_readfile(&bmtnr,"control/badrcpttonorelay",0);
23812+  if (bmtnrok == -1) die_control();
23813+
23814+  bhelook = control_readfile(&bhelo, "control/badhelo",0);
23815+  if (bhelook == -1) die_control();
23816+
23817+  bhelonrok = control_readfile(&bhelonr, "control/badhelonorelay",0);
23818+  if (bhelonrok == -1) die_control();
23819+
23820+  if (env_get("LOGREGEX")) logregex = 1;
23821+/* qregex: end */
23822+
23823+/* validrcptto.cdb: start */
23824+  x = env_get("VALIDRCPTTO");
23825+  if (x) { vrtok = control_readfile(&vrt,x,0); }
23826+  else vrtok = control_readfile(&vrt,"control/validrcptto",0);
23827+  if (vrtok == -1) die_control();
23828+  if (vrtok)
23829+  if (!constmap_init(&mapvrt,vrt.s,vrt.len,0)) die_nomem();
23830+
23831+  x = env_get("MOREVALIDRCPTTO_CDB");
23832+  if (x) { vrtfd = open_read(x); }
23833+  else vrtfd = open_read("control/morevalidrcptto.cdb");
23834+  if (-1 == vrtfd) if (errno != error_noent) die_control();
23835+
23836+  x = env_get("VALIDRCPTTO_LOG");
23837+  if(x) { scan_ulong(x,&u); vrtlog_do = (int) u; }
23838+/* validrcptto.cdb: end */
23839+
23840+/* realbadrcpt: start */
23841+  if (control_readint(&brtlimit,"control/brtlimit") == -1) die_control();
23842+  x = env_get("BRTLIMIT");
23843+  if (x) { scan_ulong(x,&u); brtlimit = u; };
23844+  /* Disable limits check, defaults to 0 */
23845+/*  if (brtlimit <= 1) brtlimit = 2; */
23846+/* realbadrcpt: end */
23847+
23848+/* rcptcheck: start */
23849+  rcptcheck[0] = env_get("RCPTCHECK");
23850+
23851+  x = env_get("RCPTCHECKRELAYCLIENT");
23852+  if (x) { scan_ulong(x,&u); rcptcheckrelayclient = u; };
23853+/* rcptcheck: end */
23854+
23855+/* rejectrelaytest: start */
23856+  if (control_readint(&rejectrelaytest,"control/rejectrelaytest") == -1) die_control();
23857+/* rejectrelaytest: end */
23858+
23859+/* rejectnullsenders: start */
23860+  if (control_readint(&rejnsmf,"control/rejectnullsenders") == -1) die_control();
23861+/* rejectnullsenders: end */
23862 
23863   if (control_readint(&databytes,"control/databytes") == -1) die_control();
23864   x = env_get("DATABYTES");
23865   if (x) { scan_ulong(x,&u); databytes = u; }
23866   if (!(databytes + 1)) --databytes;
23867-
23868+
23869+  x = env_get("SMTPD_GREETDELAY");
23870+  if (x) { scan_ulong(x, &u); greetdelay = u; }
23871+  x = env_get("DROP_PRE_GREET");
23872+  if (x) { scan_ulong(x, &u); drop_pre_greet = u; }
23873+
23874+  protocol = "SMTP";
23875+
23876+  if (control_readint(&spfbehavior,"control/spfbehavior") == -1)
23877+    die_control();
23878+  x = env_get("SPFBEHAVIOR");
23879+  if (x) { scan_ulong(x,&u); spfbehavior = u; }
23880+
23881+  if (control_readline(&spflocal,"control/spfrules") == -1) die_control();
23882+  if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
23883+  if (control_readline(&spfguess,"control/spfguess") == -1) die_control();
23884+  if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
23885+  if (control_rldef(&spfexp,"control/spfexp",0,SPF_DEFEXP) == -1)
23886+    die_control();
23887+  if (!stralloc_0(&spfexp)) die_nomem();
23888+
23889+  /* spf ipv6 fix */
23890+  if (!(remoteip4 = env_get("TCPREMOTEIP")))
23891+      remoteip4 = "unknown";
23892+  /* end spf ipv6 fix */
23893   remoteip = env_get("TCPREMOTEIP");
23894   if (!remoteip) remoteip = "unknown";
23895+  localport = env_get("TCPLOCALPORT");
23896+  if (!localport) localport = "0";
23897   local = env_get("TCPLOCALHOST");
23898   if (!local) local = env_get("TCPLOCALIP");
23899   if (!local) local = "unknown";
23900@@ -131,10 +469,67 @@
23901   if (!remotehost) remotehost = "unknown";
23902   remoteinfo = env_get("TCPREMOTEINFO");
23903   relayclient = env_get("RELAYCLIENT");
23904+  dnsblskip = env_get("DNSBLSKIP");
23905+/* rbl: start */
23906+  x = env_get("DNSBLLIST");
23907+  if (x) {
23908+    rbllistok = control_readfile(&rbldnslist,x,0);
23909+    if (rbllistok == -1) die_control();
23910+    if (rbllistok) rblok = 1;
23911+  }
23912+  else {
23913+    rbllistok = control_readfile(&rbldnslist,"control/dnsbllist",0);
23914+    if (rbllistok == -1) die_control();
23915+    if (rbllistok) rblok = 1;
23916+  }
23917+
23918+  /* from rblsmtpd.c, if RBLSMTPD is defined and empty then accept mail, if defined and string begins with '-' then
23919+     block mail using error code 553 + string without hyphen, else (if defined, not null and not beginning with '-')
23920+     reject mail using error code 451 + string  */
23921+  x = env_get("RBLSMTPD");
23922+  if (x) {
23923+    if (!*x)
23924+      rbldecision = 1;
23925+    else if (*x == '-') {
23926+      if (!stralloc_copys(&rbltext,x + 1)) die_nomem();
23927+      rbldecision = 3;
23928+    }
23929+    else {
23930+      if (!stralloc_copys(&rbltext,x)) die_nomem();
23931+      rbldecision = 2;
23932+    }
23933+    rblok = 1;
23934+  }
23935
23936+  if (control_readint(&flagrblfailclosed,"control/dnsblfailclosed") == -1) die_control();
23937+  x = env_get("DNSBLFAILCLOSED");
23938+  if (x) { scan_ulong(x,&u); flagrblfailclosed = u; }
23939+/* rbl: end */
23940+  auth = env_get("SMTPAUTH");
23941+  if (auth) {
23942+    smtpauth = 1;
23943+    case_lowers(auth);
23944+    if (!case_diffs(auth,"-") || !case_diffs(auth,"0")) smtpauth = 0;
23945+    if (!case_diffs(auth,"!")) smtpauth = 11;
23946+    if (case_starts(auth,"cram")) smtpauth = 2;
23947+    if (case_starts(auth,"+cram")) smtpauth = 3;
23948+    if (case_starts(auth,"!cram")) smtpauth = 12;
23949+    if (case_starts(auth,"!+cram")) smtpauth = 13;
23950+  }
23951+/* authtlsvariables: start */
23952+  x = env_get("FORCEAUTHMAILFROM"); if (x) if (!str_diff(x,"1")) { forceauthmailfrom = 1; }
23953+  #ifdef TLS
23954+  x = env_get("DISABLETLS"); if (x) if (!str_diff(x,"1")) { disabletls = 1; }
23955+  #endif
23956+/* authtlsvariables: end */
23957+  #ifdef TLS
23958+  x = env_get("FORCETLS"); if (x) if (!str_diff(x, "0")) forcetls = 0;
23959+  if (env_get("SMTPS")) { smtps = 1; tls_init(); }
23960+  else
23961+  #endif
23962   dohelo(remotehost);
23963 }
23964 
23965-
23966 stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */
23967 
23968 int addrparse(arg)
23969@@ -155,6 +550,7 @@
23970     terminator = ' ';
23971     arg += str_chr(arg,':');
23972     if (*arg == ':') ++arg;
23973+    if (*arg == '\0') return 0;
23974     while (*arg == ' ') ++arg;
23975   }
23976 
23977@@ -197,6 +593,8 @@
23978   return 1;
23979 }
23980 
23981+/* qregex: start */
23982+/*
23983 int bmfcheck()
23984 {
23985   int j;
23986@@ -207,68 +605,1102 @@
23987     if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;
23988   return 0;
23989 }
23990+*/
23991+int bmcheck(which) int which;
23992+{
23993+  int i = 0;
23994+  int j = 0;
23995+  int x = 0;
23996+  int negate = 0;
23997+  static stralloc bmb = {0};
23998+  static stralloc curregex = {0};
23999+
24000+  if (which == BMCHECK_BMF) {
24001+    if (!stralloc_copy(&bmb,&bmf)) die_nomem();
24002+  } else if (which == BMCHECK_BMFNR) {
24003+    if (!stralloc_copy(&bmb,&bmfnr)) die_nomem();
24004+  } else if (which == BMCHECK_BMT) {
24005+    if (!stralloc_copy(&bmb,&bmt)) die_nomem();
24006+  } else if (which == BMCHECK_BMTNR) {
24007+    if (!stralloc_copy(&bmb,&bmtnr)) die_nomem();
24008+  } else if (which == BMCHECK_BHELO) {
24009+    if (!stralloc_copy(&bmb,&bhelo)) die_nomem();
24010+  } else if (which == BMCHECK_BHELONR) {
24011+    if (!stralloc_copy(&bmb,&bhelonr)) die_nomem();
24012+  } else {
24013+    die_control();
24014+  }
24015+
24016+  while (j < bmb.len) {
24017+    i = j;
24018+    while ((bmb.s[i] != '\0') && (i < bmb.len)) i++;
24019+    if (bmb.s[j] == '!') {
24020+      negate = 1;
24021+      j++;
24022+    }
24023+    if (!stralloc_copyb(&curregex,bmb.s + j,(i - j))) die_nomem();
24024+    if (!stralloc_0(&curregex)) die_nomem();
24025+    if (which == BMCHECK_BHELO) {
24026+      x = matchregex(helohost.s, curregex.s);
24027+    } else {
24028+      x = matchregex(addr.s, curregex.s);
24029+    }
24030+    if ((negate) && (x == 0)) {
24031+      if (!stralloc_copyb(&matchedregex,bmb.s + j - 1,(i - j + 1))) die_nomem();
24032+      if (!stralloc_0(&matchedregex)) die_nomem();
24033+      return 1;
24034+    }
24035+    if (!(negate) && (x > 0)) {
24036+      if (!stralloc_copyb(&matchedregex,bmb.s + j,(i - j))) die_nomem();
24037+      if (!stralloc_0(&matchedregex)) die_nomem();
24038+      return 1;
24039+    }
24040+    j = i + 1;
24041+    negate = 0;
24042+  }
24043+  return 0;
24044+}
24045+/* qregex: end */
24046+
24047+/* validrcptto.cdb: start */
24048+void vrtlog(l,a,b)
24049+int l;
24050+const char *a;
24051+const char *b;
24052+{
24053+/*  if (l <= vrtlog_do)
24054+    strerr_warn6(title.s,"validrcptto [",remoteip,"] ",a,b,0);*/
24055+}
24056+
24057+int vrtcheck()
24058+{
24059+  static char *rcptto = "RCPT TO: ";
24060+  static char *trying = "trying: ";
24061+  static char *found  = "found: ";
24062+  static char *reject = "reject: ";
24063+  char *f = 0;
24064+  int j,k,r;
24065+  uint32 dlen;
24066+  stralloc laddr = {0};
24067+
24068+  stralloc luser = {0};
24069+  stralloc adom = {0};
24070+  stralloc utry = {0};
24071+  stralloc stnoaddr = {0};
24072+  stralloc stnodom = {0};
24073+
24074+  int atfound, okaddr, okdom, noaddr, nodom;
24075+
24076+  /* if both validrcptto and morevalidrcptto.cdb are missing, consider valid the recipient */
24077+  if (!((vrtok) || (vrtfd != -1))) return 1;
24078+
24079+  okaddr = 0; okdom = 0; noaddr = 0; nodom = 0; atfound = 0;
24080+
24081+  /* lowercase whatever we were sent */
24082+  if (!stralloc_copy(&laddr,&addr)) die_nomem() ;
24083+  case_lowerb(laddr.s,laddr.len);
24084+
24085+  /* split user/domain parts, create negated stralloc */
24086+  j = byte_rchr(laddr.s,laddr.len,'@');
24087+  if (j < laddr.len) {
24088+    atfound = 1;
24089+    if (!stralloc_copyb(&luser,laddr.s,j)) die_nomem();
24090+    if (!stralloc_copyb(&adom,laddr.s+j,laddr.len-j-1)) die_nomem();
24091+
24092+    if (!stralloc_copys(&stnodom,"-")) die_nomem();
24093+    if (!stralloc_cat(&stnodom,&adom)) die_nomem();
24094+    if (!stralloc_0(&stnodom)) die_nomem();
24095+
24096+    if (!stralloc_copys(&stnoaddr,"-")) die_nomem();
24097+    if (!stralloc_cat(&stnoaddr,&luser)) die_nomem();
24098+    if (!stralloc_cat(&stnoaddr,&adom)) die_nomem();
24099+    if (!stralloc_0(&stnoaddr)) die_nomem();
24100+  }
24101+  /* validrcptto */
24102+  if (vrtok) {
24103+    vrtlog(rcptto,laddr.s,0);
24104+    if (constmap(&mapvrt,laddr.s,laddr.len - 1)) { okaddr = 1; vrtlog(found,laddr.s,0); }
24105+    if (atfound) {
24106+      if (constmap(&mapvrt,stnoaddr.s,stnoaddr.len-1)) { noaddr= 1; vrtlog(reject,stnoaddr.s,0); }
24107+      if (constmap(&mapvrt,laddr.s+j,laddr.len-j-1)) { okdom = 1; vrtlog(found,laddr.s+j,0); }
24108+      if (constmap(&mapvrt,stnodom.s,stnodom.len-1)) { nodom = 1; vrtlog(reject,stnodom.s,0); }
24109+    }
24110+  }
24111
24112+  /* morevalidrcptto.cdb */
24113+  if ((vrtfd != -1)) {
24114+    vrtlog(rcptto,laddr.s,0);
24115+
24116+    if (cdb_seek(vrtfd,laddr.s,laddr.len-1,&dlen) > 0) { okaddr = 1; vrtlog(found,laddr.s,0); }
24117+    if (atfound) {
24118+      if (cdb_seek(vrtfd,stnoaddr.s,stnoaddr.len-1,&dlen) > 0) { noaddr = 1; vrtlog(reject,stnoaddr.s,0); }
24119+      if (cdb_seek(vrtfd,laddr.s+j,laddr.len-j-1,&dlen) > 0) { okdom = 1; vrtlog(found,laddr.s+j,0); }
24120+      if (cdb_seek(vrtfd,stnodom.s,stnodom.len-1,&dlen) > 0) { nodom = 1; vrtlog(reject,stnodom.s,0); }
24121+    }
24122+  }
24123+
24124+  if (okaddr) return 1;
24125+  else if (noaddr) return -1;
24126+  else if (okdom) return 1;
24127+  else if (nodom) return -1;
24128+  else return 0;
24129+}
24130+/* validrcptto.cdb: end */
24131+
24132+/* rbl: start */
24133+void rbl(char *base)
24134+{
24135+  int i,j;
24136+  int whitelisted = 0;
24137+  int altmustbounce = 0;
24138+  char *altreply = 0;
24139+  strsalloc ssa = {0};
24140+
24141+  if (!str_len(base)) return;
24142+  if (!stralloc_copys(&rbltext,"")) die_nomem();
24143+  if (!stralloc_copys(&rblhost,"")) die_nomem();
24144+  if (!stralloc_copys(&rblserver,"")) die_nomem();
24145+
24146+  if (!stralloc_copy(&rblhost,&ip_reverse)) die_nomem();
24147+  i = str_chr(base, ':');
24148+  if (base[i]) {
24149+    if (base[i+1] == '-') { /* if reply begins with '-', message must bounce (check rblsmtpd man page) */
24150+      altreply = base+i+2;
24151+      altmustbounce = 1;
24152+    }
24153+    else altreply = base+i+1;
24154+  }
24155+
24156+  if (base[0] == '+') { /* entries beginning with '+' are for whitelistedlists */
24157+    whitelisted = 1;
24158+    if (!stralloc_catb(&rblhost,base+1,i-1)) die_nomem();
24159+    if (!stralloc_catb(&rblserver,base+1,i-1)) die_nomem();
24160+  }
24161+  else if (base[0] == '-') { /* force bounce (553 error message), instead of default reject (451) */
24162+    whitelisted = 0;
24163+    if (!stralloc_catb(&rblhost,base+1,i-1)) die_nomem();
24164+    if (!stralloc_catb(&rblserver,base+1,i-1)) die_nomem();
24165+    altmustbounce = 1;
24166+  }
24167+  else {
24168+    if (!stralloc_catb(&rblhost,base,i)) die_nomem();
24169+    if (!stralloc_catb(&rblserver,base,i)) die_nomem();
24170+  }
24171+  if (!stralloc_0(&rblhost)) die_nomem();
24172+  if (!stralloc_0(&rblserver)) die_nomem();
24173+
24174+  rblhosterror = 0; /* set in case of dns errors */
24175+
24176+  if (altreply) { /* if text response is defined in control file, query A records */
24177+    if (dns_ip(&rbltext,&rblhost) == -1) {
24178+      flagmustnotbounce = 1;
24179+      rblhosterror = 1;
24180+      if (flagrblfailclosed) {
24181+        if (!stralloc_copys(&rbltext,"temporary RBL lookup error")) die_nomem();
24182+        if (whitelisted) rbldecision = 1; else rbldecision = 2;
24183+      }
24184+      return;
24185+    }
24186+    if (rbltext.len) {
24187+      if(!stralloc_copys(&rbltext, "")) die_nomem();
24188+      while(*altreply) {
24189+        i = str_chr(altreply, '%');
24190+        if(!stralloc_catb(&rbltext, altreply, i)) die_nomem();
24191+        if(altreply[i] &&
24192+           altreply[i+1]=='I' &&
24193+           altreply[i+2]=='P' &&
24194+           altreply[i+3]=='%') {
24195+          if(!stralloc_catb(&rbltext, ip_env, str_len(ip_env))) die_nomem();
24196+          altreply+=i+4;
24197+        } else if(altreply[i]) {
24198+          if(!stralloc_cats(&rbltext, "%")) die_nomem();
24199+          altreply+=i+1;
24200+        } else {
24201+          altreply+=i;
24202+        }
24203+      }
24204+    }
24205+  } else { /* normal rbl query looks for TXT record */
24206+    if (dns_txt(&ssa,&rblhost) == -1) { /* DNS_SOFT = -1, DNS_HARD = -2, DNS_MEM = -3 */
24207+      flagmustnotbounce = 1;
24208+      rblhosterror = 1;
24209+      if (flagrblfailclosed) {
24210+        if (!stralloc_copys(&rbltext,"temporary RBL lookup error")) die_nomem();
24211+        if (whitelisted) rbldecision = 1; else rbldecision = 2;
24212+      }
24213+      return;
24214+    }
24215+    else {
24216+      /* in case of multiple records, take only the first */
24217+      if (ssa.len > 0)
24218+        if (!stralloc_cat(&rbltext,&ssa.sa[0])) die_nomem();
24219+      /* in case of multiple records, append results to rbltext */
24220+      /*for (j = 0;j < ssa.len;++j) if (!stralloc_cat(&rbltext,&ssa.sa[j])) die_nomem();*/
24221+    }
24222+  }
24223+  if (rbltext.len) {
24224+    if (whitelisted) {
24225+      rbldecision = 1;
24226+    }
24227+    else {
24228+      if (altmustbounce)
24229+        rbldecision = 3;
24230+      else
24231+        rbldecision = 2;
24232+    }
24233+  }
24234+  else rbldecision = 0;
24235+}
24236+
24237+int rblcheck()
24238+{
24239+  char *ch;
24240+  unsigned int i;
24241+  unsigned int j;
24242+  stralloc sar = {0};
24243+
24244+  if (rbldecision) return rbldecision; /* rbldecision already set in case of RBLSMTPD or if rblcheck was executed previously */
24245+  if (!rbllistok) return 0;
24246+
24247+  ip_env = env_get("TCPREMOTEIP");
24248+  if (!ip_env) ip_env = "";
24249+  if (!stralloc_copys(&ip_reverse,"")) die_nomem();
24250+
24251+  i = str_len(ip_env);
24252+  while (i) {
24253+    for (j = i;j > 0;--j) if (ip_env[j - 1] == '.') break;
24254+    if (!stralloc_catb(&ip_reverse,ip_env + j,i - j)) die_nomem();
24255+    if (!stralloc_cats(&ip_reverse,".")) die_nomem();
24256+    if (!j) break;
24257+    i = j - 1;
24258+  }
24259
24260+  ch = rbldnslist.s;
24261+  while (ch < (rbldnslist.s + rbldnslist.len)) {
24262+    rbl(ch);
24263+    /* debug log */
24264+    if (!stralloc_copys(&sar,title.s)) die_nomem();
24265+    if (!stralloc_cats(&sar,"rbl: ip=")) die_nomem();
24266+    if (!stralloc_cats(&sar,ip_env)) die_nomem();
24267+    if (!stralloc_cats(&sar," query=")) die_nomem();
24268+    if (!stralloc_cats(&sar,rblhost.s)) die_nomem();
24269+    if (rblhosterror) {
24270+      if (!stralloc_cats(&sar," result=dnserr")) die_nomem();
24271+    }
24272+    else {
24273+      if (!stralloc_cats(&sar," result=")) die_nomem();
24274+      switch (rbldecision) {
24275+        case 0: if (!stralloc_cats(&sar,"ignore")) die_nomem(); break;
24276+        case 1: if (!stralloc_cats(&sar,"accept")) die_nomem(); break;
24277+        case 2: if (!stralloc_cats(&sar,"delay")) die_nomem(); break;
24278+        case 3: if (!stralloc_cats(&sar,"reject")) die_nomem(); break;
24279+      }
24280+    }
24281+    if (!stralloc_cats(&sar," message='")) die_nomem();
24282+    if (!stralloc_catb(&sar,rbltext.s,rbltext.len)) die_nomem();
24283+    if (!stralloc_cats(&sar,"'")) die_nomem();
24284+    if (!stralloc_0(&sar)) die_nomem();
24285+    strerr_warn1(sar.s,0);
24286+    /* end debug log */
24287+    if (rbldecision) break;
24288+    while (*ch++);
24289+  }
24290+  return rbldecision;
24291+}
24292+/* rbl: end */
24293+
24294+int sizelimit(arg)
24295+char *arg;
24296+{
24297+  int i;
24298+  long r;
24299+  unsigned long sizebytes = 0;
24300+
24301+  i = str_chr(arg,'<');
24302+  if (arg[i])
24303+    arg += i + 1;
24304+  else {
24305+    arg += str_chr(arg,':');
24306+    if (*arg == ':') ++arg;
24307+    while (*arg == ' ') ++arg;
24308+  }
24309+
24310+  arg += str_chr(arg,' ');
24311+  if (*arg == ' ') while (*arg == ' ') ++arg;
24312+  else return 1;
24313+
24314+  i = str_chr(arg,'=');
24315+  arg[i] = 0;
24316+  if (case_equals(arg,"SIZE")) {
24317+    arg += i;
24318+    while (*++arg && *arg > 47 && *arg < 58) {
24319+      sizebytes *= 10;
24320+      sizebytes += *arg - 48;
24321+    }
24322+    r = databytes - sizebytes;
24323+    if (r < 0) return 0;
24324+  }
24325+  return 1;
24326+}
24327 
24328 int addrallowed()
24329 {
24330   int r;
24331   r = rcpthosts(addr.s,str_len(addr.s));
24332   if (r == -1) die_control();
24333+#ifdef TLS
24334+  if (r == 0) if (tls_verify()) r = -2;
24335+#endif
24336   return r;
24337 }
24338 
24339+/* rejectrelaytest: start */
24340+int addrrelay()
24341+{
24342+  if (!rejectrelaytest) { return 0; }
24343+  else
24344+  {
24345+    int j;
24346+    j = addr.len;
24347+    while(--j >= 0)
24348+      if (addr.s[j] == '@') break;
24349+    if (j < 0) j = addr.len;
24350+    while(--j >= 0) {
24351+      if (addr.s[j] == '@') return 1;
24352+      if (addr.s[j] == '%') return 1;
24353+      if (addr.s[j] == '!') return 1;
24354+    }
24355+    return 0;
24356+  }
24357+}
24358+/* rejectrelaytest: end */
24359 
24360+int seenauth = 0;
24361 int seenmail = 0;
24362-int flagbarf; /* defined if seenmail */
24363+int rcptcount = 0;
24364+
24365+/* qregex: start */
24366+/*
24367+int flagbarf;
24368+*/
24369+int flagbarfbmf; /* defined if seenmail */
24370+int flagbarfbmt;
24371+int flagbarfbhelo;
24372+/* qregex: end */
24373+
24374+int flagsize;
24375+int flagbarfspf;
24376+stralloc spfbarfmsg = {0};
24377 stralloc mailfrom = {0};
24378 stralloc rcptto = {0};
24379+stralloc fuser = {0};
24380+stralloc mfparms = {0};
24381+stralloc log_buf = {0};
24382+
24383+/* realbadrcpt: start */
24384+int flagvrt; /* defined if valid rcpt */
24385+int brtcount = 0; /* for brtlimit count */
24386+/* realbadrcpt: end */
24387+
24388+/* rcptcheck: start */
24389+int addrvalid()
24390+{
24391+  int pid;
24392+  int wstat;
24393+  int pierr[2] ;
24394+  substdio ss;
24395+  char ssbuf[sizeof(rcptcheck_err)];
24396+  int len = 0 ;
24397+  char ch;
24398+
24399+  if (!rcptcheck[0]) return 1;
24400+  if (pipe(pierr) == -1) die_rcpt2();
24401+
24402+  switch(pid = fork()) {
24403+    case -1:
24404+      close(pierr[0]);
24405+      close(pierr[1]);
24406+      die_fork();
24407+    case 0:
24408+      if (!env_put2("SENDER",mailfrom.s)) die_nomem();
24409+      if (!env_put2("RECIPIENT",addr.s)) die_nomem();
24410+      if (!env_put2("HELO",helohost.s)) die_nomem();
24411+      if (!env_put2("USE_FD4","1")) die_nomem();
24412+      close(1);
24413+      dup2(2,1);
24414+      close(pierr[0]);
24415+      if (fd_move(4,pierr[1]) == -1) die_rcpt2();
24416+      execv(*rcptcheck,rcptcheck);
24417+      _exit(120);
24418+  }
24419+
24420+  close(pierr[1]);
24421+  if (wait_pid(&wstat,pid) == -1) die_rcpt2();
24422+  if (wait_crashed(wstat)) die_rcpt2();
24423+
24424+  substdio_fdbuf(&ss,read,pierr[0],ssbuf,sizeof(ssbuf));
24425+  while ( substdio_bget(&ss,&ch,1) && len < (sizeof(ssbuf)-3) )
24426+    rcptcheck_err[len++] = ch;
24427+  close(pierr[0]);
24428+
24429+  while (len&&((rcptcheck_err[len-1]=='\n')||(rcptcheck_err[len-1]=='\r')))
24430+    len -- ;
24431+  if (len) {
24432+    rcptcheck_err[len] = '\0';
24433+    strerr_warn3(title.s,"RCPTCHECK error: ",rcptcheck_err,0);
24434+    rcptcheck_err[len++] = '\r';
24435+    rcptcheck_err[len++] = '\n';
24436+  }
24437+  rcptcheck_err[len] = '\0';
24438+
24439+  switch(wait_exitcode(wstat)) {
24440+    case 100:
24441+      return 0;
24442+    case 111:
24443+      die_rcpt();
24444+    case 112:
24445+      return 2; // ignore
24446+    case 113:
24447+      return 3; // overlimit
24448+    case 120:
24449+      die_rcpt2();
24450+  }
24451+  return 1;
24452+}
24453+/* rcptcheck: end */
24454+
24455+int checkrcptcount() {
24456+  if (maxrcpt == -1) {return 0;}
24457+  else if (rcptcount > maxrcpt) {return 1;}
24458+  else {return 0;}
24459+}
24460+
24461+void logit(message) const char* message;
24462+{
24463+  if (!stralloc_copys(&log_buf, "qmail-smtpd: ")) die_nomem();
24464+  if (!stralloc_cats(&log_buf, message)) die_nomem();
24465+  if (!stralloc_catb(&log_buf, ": ", 2)) die_nomem();
24466+  if (mailfrom.s) {
24467+      if (!stralloc_catb(&log_buf, mailfrom.s, mailfrom.len-1)) die_nomem();
24468+  } else
24469+      if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24470+  if (!stralloc_catb(&log_buf, " from ", 6)) die_nomem();
24471+  if (!stralloc_cats(&log_buf, remoteip)) die_nomem();
24472+  if (!stralloc_catb(&log_buf, " to ", 4)) die_nomem();
24473+  if (addr.s) {
24474+      if (!stralloc_catb(&log_buf, addr.s, addr.len-1)) die_nomem();
24475+  } else
24476+      if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24477+  if (!stralloc_catb(&log_buf, " helo ", 6)) die_nomem();
24478+  if (helohost.s) {
24479+      if (!stralloc_catb(&log_buf, helohost.s, helohost.len-1)) die_nomem();
24480+  } else
24481+      if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24482+  if (!stralloc_catb(&log_buf, "\n", 1)) die_nomem();
24483+  substdio_putflush(&sserr, log_buf);
24484+}
24485+
24486+void logit2(message, reason)
24487+const char* message;
24488+const char* reason;
24489+{
24490+  if (!stralloc_copys(&log_buf,"qmail-smtpd: ")) die_nomem();
24491+  if (!stralloc_cats(&log_buf, message)) die_nomem();
24492+  if (!stralloc_cats(&log_buf, " (")) die_nomem();
24493+  if (!stralloc_cats(&log_buf, reason)) die_nomem();
24494+  if (!stralloc_cats(&log_buf, "): ")) die_nomem();
24495+  if (mailfrom.s) {
24496+      if (!stralloc_catb(&log_buf, mailfrom.s, mailfrom.len-1)) die_nomem();
24497+  } else
24498+      if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24499+  if (!stralloc_cats(&log_buf," from ")) die_nomem();
24500+  if (!stralloc_cats(&log_buf, remoteip)) die_nomem();
24501+  if (!stralloc_cats(&log_buf, " to ")) die_nomem();
24502+  if (addr.s) {
24503+      if (!stralloc_catb(&log_buf, addr.s, addr.len-1)) die_nomem();
24504+  } else
24505+      if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24506+  if (!stralloc_cats(&log_buf, " helo ")) die_nomem();
24507+  if (helohost.s) {
24508+      if (!stralloc_catb(&log_buf, helohost.s, helohost.len-1)) die_nomem();
24509+  } else
24510+      if (!stralloc_catb(&log_buf, "(null)", 6)) die_nomem();
24511+  if (!stralloc_catb(&log_buf, "\n", 1)) die_nomem();
24512+  substdio_putflush(&sserr, log_buf);
24513+}
24514+
24515+int mailfrom_size(arg) char *arg;
24516+{
24517+  long r;
24518+  unsigned long sizebytes = 0;
24519+
24520+  scan_ulong(arg,&r);
24521+  sizebytes = r;
24522+  if (databytes) if (sizebytes > databytes) return 1;
24523+  return 0;
24524+}
24525+
24526+void mailfrom_auth(arg,len)
24527+char *arg;
24528+int len;
24529+{
24530+  if (!stralloc_copys(&fuser,"")) die_nomem();
24531+  if (case_starts(arg,"<>")) { if (!stralloc_cats(&fuser,"unknown")) die_nomem(); }
24532+  else
24533+    while (len) {
24534+      if (*arg == '+') {
24535+        if (case_starts(arg,"+3D")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"=")) die_nomem(); }
24536+        if (case_starts(arg,"+2B")) { arg=arg+2; len=len-2; if (!stralloc_cats(&fuser,"+")) die_nomem(); }
24537+      }
24538+      else
24539+        if (!stralloc_catb(&fuser,arg,1)) die_nomem();
24540+      arg++; len--;
24541+    }
24542+  if(!stralloc_0(&fuser)) die_nomem();
24543+  if (!remoteinfo) {
24544+    remoteinfo = fuser.s;
24545+    if (!env_unset("TCPREMOTEINFO")) die_read();
24546+    if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
24547+  }
24548+}
24549+
24550+void mailfrom_parms(arg) char *arg;
24551+{
24552+  int i;
24553+  int len;
24554+
24555+    len = str_len(arg);
24556+    if (!stralloc_copys(&mfparms,"")) die_nomem();
24557+    i = byte_chr(arg,len,'>');
24558+    if (i > 4 && i < len) {
24559+      while (len) {
24560+        arg++; len--;
24561+        if (*arg == ' ' || *arg == '\0' ) {
24562+           if (case_starts(mfparms.s,"SIZE=")) if (mailfrom_size(mfparms.s+5)) { flagsize = 1; return; }
24563+           if (case_starts(mfparms.s,"AUTH=")) mailfrom_auth(mfparms.s+5,mfparms.len-5);
24564+           if (!stralloc_copys(&mfparms,"")) die_nomem();
24565+        }
24566+        else
24567+           if (!stralloc_catb(&mfparms,arg,1)) die_nomem();
24568+      }
24569+    }
24570+}
24571 
24572 void smtp_helo(arg) char *arg;
24573 {
24574+  envelopepos = 1;
24575   smtp_greet("250 "); out("\r\n");
24576   seenmail = 0; dohelo(arg);
24577+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
24578+  if ((!flagbarfbhelo) && (bhelonrok) && (!relayclient)) flagbarfbhelo = bmcheck(BMCHECK_BHELONR);
24579+}
24580+char size_buf[FMT_ULONG];
24581+void smtp_size()
24582+{
24583+  size_buf[fmt_ulong(size_buf,(unsigned long) databytes)] = 0;
24584+  out("250 SIZE "); out(size_buf); out("\r\n");
24585 }
24586+
24587+/* ESMTP extensions are published here */
24588 void smtp_ehlo(arg) char *arg;
24589 {
24590-  smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
24591+  char size[FMT_ULONG];
24592+#ifdef TLS
24593+  struct stat st;
24594+#endif
24595+  size[fmt_ulong(size,(unsigned int) databytes)] = 0;
24596+  envelopepos = 1;
24597+  smtp_greet("250-");
24598+  #ifdef TLS
24599+  if (!disabletls && !ssl && (stat("control/servercert.pem",&st) == 0))
24600+  out("\r\n250-STARTTLS");
24601+  #endif
24602+  out("\r\n250-PIPELINING\r\n250-8BITMIME\r\n");
24603+#ifdef TLS
24604+  if (!forcetls || ssl) {
24605+#endif
24606+  if (smtpauth == 1 || smtpauth == 11) out("250-AUTH LOGIN PLAIN\r\n");
24607+  if (smtpauth == 3 || smtpauth == 13) out("250-AUTH LOGIN PLAIN CRAM-MD5\r\n");
24608+  if (smtpauth == 2 || smtpauth == 12) out("250-AUTH CRAM-MD5\r\n");
24609+#ifdef TLS
24610+  }
24611+#endif
24612+  smtp_size();
24613   seenmail = 0; dohelo(arg);
24614+  if (bhelook) flagbarfbhelo = bmcheck(BMCHECK_BHELO);
24615+  if ((!flagbarfbhelo) && (bhelonrok) && (!relayclient)) flagbarfbhelo = bmcheck(BMCHECK_BHELONR);
24616 }
24617 void smtp_rset(arg) char *arg;
24618 {
24619-  seenmail = 0;
24620+  seenmail = 0; /* seenauth = 0; RFC 5321: retain authentication */
24621+  mailfrom.len = 0; rcptto.len = 0;
24622   out("250 flushed\r\n");
24623 }
24624+
24625 void smtp_mail(arg) char *arg;
24626 {
24627+  int r;
24628+
24629+  envelopepos = 2;
24630+  if (smtpauth)
24631+    if (smtpauth > 10 && !seenauth) { err_submission(); return; }
24632   if (!addrparse(arg)) { err_syntax(); return; }
24633+  if (databytes && !sizelimit(arg)) { err_size(); return; }
24634+/* start chkuser code */
24635+  switch (chkuser_sender (&addr)) {
24636+    case CHKUSER_OK:
24637+       break;
24638+    case CHKUSER_ERR_MUSTAUTH:
24639+       qlogenvelope("rejected","chkusersender","mustauth","530");
24640+       return;
24641+       break;
24642+    case CHKUSER_ERR_SENDER_FORMAT:
24643+       qlogenvelope("rejected","chkusersender","senderformat","553");
24644+       return;
24645+       break;
24646+    case CHKUSER_ERR_SENDER_MX:
24647+       qlogenvelope("rejected","chkusersender","sendermxinvalid","550");
24648+       return;
24649+       break;
24650+    case CHKUSER_ERR_SENDER_MX_TMP:
24651+       qlogenvelope("rejected","chkusersender","sendermxdnstmpfail","451");
24652+       return;
24653+       break;
24654+    default:
24655+       qlogenvelope("rejected","chkusersender","invalid","550");
24656+       return;
24657+       break;
24658+   }
24659+/* end chkuser code */
24660+/* authtlsvariables: start */
24661+    /* if it is authenticated but MAIL FROM and AUTH USER are different */
24662+    if (smtpauth && seenauth && forceauthmailfrom) {
24663+      if (strcmp(addr.s,user.s)) { err_authmismatch(); return; }
24664+    }
24665+/* authtlsvariables: end */
24666+/* rejectnullsenders: start */
24667+  if ((rejnsmf) && (addr.len <= 1)) { die_nullsender(); return; }
24668+/* rejectnullsenders: end */
24669+/* qregex: start */
24670+  /*
24671+  flagbarf = bmfcheck();
24672+  */
24673+  flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */
24674+  if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF);
24675+  if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) {
24676+    flagbarfbmf = bmcheck(BMCHECK_BMFNR);
24677+  }
24678+/* qregex: end */
24679+  flagsize = 0;
24680+  mailfrom_parms(arg);
24681+  if (flagsize) { err_size(); return; }
24682+
24683+/* qregex: start */
24684+  /*
24685   flagbarf = bmfcheck();
24686+  */
24687+  flagbarfbmf = 0; /* bmcheck is skipped for empty envelope senders */
24688+  if ((bmfok) && (addr.len != 1)) flagbarfbmf = bmcheck(BMCHECK_BMF);
24689+  if ((!flagbarfbmf) && (bmfnrok) && (addr.len != 1) && (!relayclient)) {
24690+    flagbarfbmf = bmcheck(BMCHECK_BMFNR);
24691+  }
24692+/* qregex: end */
24693+
24694+  flagbarfspf = 0;
24695+  if (spfbehavior && !relayclient)
24696+   {
24697+    switch(r = spfcheck(remoteip4)) {
24698+    case SPF_OK: env_put2("SPFRESULT","pass"); break;
24699+    case SPF_NONE: env_put2("SPFRESULT","none"); break;
24700+    case SPF_UNKNOWN: env_put2("SPFRESULT","unknown"); break;
24701+    case SPF_NEUTRAL: env_put2("SPFRESULT","neutral"); break;
24702+    case SPF_SOFTFAIL: env_put2("SPFRESULT","softfail"); break;
24703+    case SPF_FAIL: env_put2("SPFRESULT","fail"); break;
24704+    case SPF_ERROR: env_put2("SPFRESULT","error"); break;
24705+    }
24706+    switch (r) {
24707+    case SPF_NOMEM:
24708+      die_nomem();
24709+    case SPF_ERROR:
24710+      if (spfbehavior < 2) break;
24711+      qlogenvelope("rejected","spf","lookupfailure","451");
24712+      out("451 SPF lookup failure (#4.3.0)\r\n");
24713+      return;
24714+    case SPF_NONE:
24715+    case SPF_UNKNOWN:
24716+      if (spfbehavior < 6) break;
24717+    case SPF_NEUTRAL:
24718+      if (spfbehavior < 5) break;
24719+    case SPF_SOFTFAIL:
24720+      if (spfbehavior < 4) break;
24721+    case SPF_FAIL:
24722+      if (spfbehavior < 3) break;
24723+      if (!spfexplanation(&spfbarfmsg)) die_nomem();
24724+      if (!stralloc_0(&spfbarfmsg)) die_nomem();
24725+      flagbarfspf = 1;
24726+    }
24727+   }
24728+  else
24729+   env_unset("SPFRESULT");
24730   seenmail = 1;
24731   if (!stralloc_copys(&rcptto,"")) die_nomem();
24732   if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();
24733   if (!stralloc_0(&mailfrom)) die_nomem();
24734   out("250 ok\r\n");
24735 }
24736+
24737+void err_spf() {
24738+  int i,j;
24739+
24740+  for(i = 0; i < spfbarfmsg.len; i = j + 1) {
24741+    j = byte_chr(spfbarfmsg.s + i, spfbarfmsg.len - i, '\n') + i;
24742+    if (j < spfbarfmsg.len) {
24743+      out("550-");
24744+      spfbarfmsg.s[j] = 0;
24745+      out(spfbarfmsg.s);
24746+      spfbarfmsg.s[j] = '\n';
24747+      out("\r\n");
24748+    } else {
24749+      out("550 ");
24750+      out(spfbarfmsg.s);
24751+      out(" (#5.7.1)\r\n");
24752+    }
24753+  }
24754+}
24755+
24756+int flagdnsbl = 0;
24757+stralloc dnsblhost = {0};
24758+
24759 void smtp_rcpt(arg) char *arg; {
24760+  int flagrcptmatch = 0; /* 0 undefined, 1 validrcptto, 2 chkuser, 3 chkuserrelay, 4 rcptcheck */
24761+/* added by empf patch */
24762+  int ret = 0;
24763+/* end of empf pacth  */
24764+  envelopepos = 3;
24765   if (!seenmail) { err_wantmail(); return; }
24766   if (!addrparse(arg)) { err_syntax(); return; }
24767+/* rejectrelaytest: start */
24768+  if (addrrelay()) { err_relay(); return; }
24769+/* rejectrelaytest: end */
24770+  if (addr.len) addrinrcpthosts = addrallowed();
24771+  else addrinrcpthosts = 0;
24772+/* qregex: start */
24773+  /*
24774   if (flagbarf) { err_bmf(); return; }
24775-  if (relayclient) {
24776+  */
24777+  if (flagbarfbhelo) {
24778+    if (logregex) {
24779+      strerr_warn5(title.s,"badhelo: <",helohost.s,"> matches pattern: ",matchedregex.s,0);
24780+    } else {
24781+      strerr_warn5(title.s,"badhelo: <",helohost.s,"> at ",remoteip,0);
24782+    }
24783+    qlogenvelope("rejected","qregexbhelo",matchedregex.s,"553");
24784+    err_bhelo();
24785+    return;
24786+  }
24787+
24788+  if (flagbarfbmf) {
24789+    if (logregex) {
24790+      strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> matches pattern: ",matchedregex.s,0);
24791+    } else {
24792+      strerr_warn5(title.s,"badmailfrom: <",mailfrom.s,"> at ",remoteip,0);
24793+    }
24794+    qlogenvelope("rejected","qregexbmf",matchedregex.s,"553");
24795+    err_bmf();
24796+    return;
24797+  }
24798+/* qregex: end */
24799+
24800+  if (flagbarfspf) { qlogenvelope("rejected","spf",env_get("SPFRESULT"),"550"); err_spf(); return; }
24801+
24802+/* dnsbl: start */
24803+/*
24804+  if (!(relayclient || dnsblskip || flagdnsbl))
24805+    if (dnsblcheck()) die_dnsbl(dnsblhost.s);
24806+*/
24807+/* dnsbl: end */
24808+/* start chkuser code */
24809+/*  if (relayclient) {
24810     --addr.len;
24811     if (!stralloc_cats(&addr,relayclient)) die_nomem();
24812     if (!stralloc_0(&addr)) die_nomem();
24813   }
24814   else
24815     if (!addrallowed()) { err_nogateway(); return; }
24816+*/
24817+
24818+/* qregex: start */
24819+    if (brtlimit && (brtcount >= brtlimit)) {
24820+      strerr_warn3(title.s,"badrcptto: excessive rcptto violations hanging up on ",remoteip,0);
24821+      die_brtlimit();
24822+    }
24823+
24824+    if (bmtok) flagbarfbmt = bmcheck(BMCHECK_BMT);
24825+    if ((!flagbarfbmt) && (bmtnrok) && (!relayclient)) {
24826+      flagbarfbmt = bmcheck(BMCHECK_BMTNR);
24827+    }
24828+    if (flagbarfbmt) {
24829+      if (logregex) {
24830+        strerr_warn5(title.s,"badrcptto: <",addr.s,"> matches pattern: ",matchedregex.s,0);
24831+      } else {
24832+        strerr_warn5(title.s,"badrcptto: <",addr.s,"> at ",remoteip,0);
24833+      }
24834+      qlogenvelope("rejected","qregexbmt",matchedregex.s,"553");
24835+      ++brtcount;
24836+      err_bmt();
24837+      return;
24838+    }
24839+/* qregex: end */
24840+
24841+/* realbadrcpt: start */
24842+  if (!relayclient) {  /* if relayclient is defined, skip valid recipient checking */
24843+    /* validrcptto */
24844+    flagvrt = 0;
24845+    int vrtres = 0;
24846+    if ((vrtok) || (vrtfd != -1)) {  /* run check only if validrcptto or morevalidrcptto.cdb exist */
24847+      vrtres = vrtcheck();
24848+      if (vrtres > 0) {
24849+        flagvrt = 1;
24850+       flagrcptmatch = 1;
24851+        strerr_warn5(title.s,"validrcptto: accepted address <",addr.s,"> at ",remoteip,0);
24852+      }
24853+      else if (vrtres < 0) {
24854+        strerr_warn5(title.s,"validrcptto: drop address <",addr.s,"> at ",remoteip,0);
24855+        ++brtcount;
24856+        err_vrt();
24857+        /*err_rcpt();*/
24858+        return;
24859+      }
24860+    }
24861+
24862+    if (!flagvrt) {
24863+      switch (chkuser_realrcpt (&mailfrom, &addr)) {
24864+         case CHKUSER_OK:
24865+               flagrcptmatch = 2;
24866+                break;
24867+         case CHKUSER_RELAYING:
24868+                --addr.len;
24869+                if (!stralloc_cats(&addr,relayclient)) die_nomem();
24870+                if (!stralloc_0(&addr)) die_nomem();
24871+               flagrcptmatch = 3;
24872+                break;
24873+         case CHKUSER_NORCPTHOSTS:
24874+                qlogenvelope("rejected","chkuser","notinrcpthosts","553");
24875+                ++brtcount;
24876+                return;
24877+                break;
24878+         case CHKUSER_KO:
24879+                qlogenvelope("rejected","chkuser","nomailbox","550");
24880+               ++brtcount;
24881+                return;
24882+                break;
24883+         case CHKUSER_ERR_AUTH_RESOURCE:
24884+                qlogenvelope("rejected","chkuser","noauthresource","451");
24885+                return;
24886+                break;
24887+         case CHKUSER_ERR_MUSTAUTH:
24888+                qlogenvelope("rejected","chkuser","mustauth","530");
24889+                return;
24890+                break;
24891+         case CHKUSER_ERR_MBXFULL:
24892+                qlogenvelope("rejected","chkuser","mailboxfull","552");
24893+                return;
24894+                break;
24895+         case CHKUSER_ERR_MAXRCPT:
24896+                qlogenvelope("rejected","chkuser","maxrcpt","550");
24897+                return;
24898+                break;
24899+         case CHKUSER_ERR_MAXWRONGRCPT:
24900+                qlogenvelope("rejected","chkuser","maxwrongrcpt","550");
24901+                return;
24902+                break;
24903+         case CHKUSER_ERR_INTRUSION_THRESHOLD:
24904+                qlogenvelope("rejected","chkuser","instrusionthreshold","550");
24905+                ++brtcount;
24906+                return;
24907+                break;
24908+         case CHKUSER_ERR_DOMAIN_MISSING:
24909+                qlogenvelope("rejected","chkuser","domainmissing","550");
24910+               ++brtcount;
24911+                return;
24912+                break;
24913+         case CHKUSER_ERR_RCPT_FORMAT:
24914+                qlogenvelope("rejected","chkuser","rcptformat","553");
24915+                ++brtcount;
24916+                return;
24917+                break;
24918+         case CHKUSER_ERR_RCPT_MX:
24919+                qlogenvelope("rejected","chkuser","rcptmxinvalid","550");
24920+               ++brtcount;
24921+                return;
24922+                break;
24923+         case CHKUSER_ERR_RCPT_MX_TMP:
24924+                qlogenvelope("rejected","chkuser","rcptmxdnstmpfail","451");
24925+                return;
24926+                break;
24927+         default:
24928+                qlogenvelope("rejected","chkuser","invalid","550");
24929+                return;
24930+                break;
24931+      }
24932+    }
24933+  } // if (!relayclient)
24934+
24935+
24936+  /* rcptcheck */
24937+  if ( (rcptcheck[0]) && (!relayclient || rcptcheckrelayclient) ) { // if RCPTCHECK is not defined, addrvalid returns 1 (rcpt ok),check before calling
24938+    strerr_warn5(title.s,"rcptcheck: checking <",addr.s,"> at ",remoteip,0);
24939+    if (flagrcptmatch) {
24940+      if (!env_put2("RCPTFOUND","1")) die_nomem();
24941+    }
24942+    else {
24943+      if (!env_unset("RCPTFOUND")) die_nomem();
24944+    }
24945+    if (addrinrcpthosts) {
24946+      if (!env_put2("RCPTHOSTS","1")) die_nomem();
24947+    }
24948+    else {
24949+      if (!env_unset("RCPTHOSTS")) die_nomem();
24950+    }
24951+
24952+    int rcres = 0;
24953+    rcres = addrvalid();
24954+
24955+    char smtperrcode[4];
24956+    char *smtperrstrptr;
24957+    long smtperrcodenum = 0;
24958+    int len = 0;
24959+    int closesession = 0;
24960+
24961+    if ((rcptcheck_err[0]) && (sizeof(rcptcheck_err) > 3)) {
24962+      strncpy(smtperrcode,rcptcheck_err,3);
24963+      smtperrcode[3] = '\0';
24964+      smtperrcodenum = strtoul(smtperrcode, &smtperrstrptr, 10);
24965+      if ((smtperrcodenum >= 400) && (smtperrcodenum <=599)) {
24966+        if (smtperrcodenum == 421) closesession = 1;
24967+      }
24968+      else {
24969+        len = str_copy(rcptcheck_err,"451 temporary problem (#4.4.2)\r\n");
24970+        rcptcheck_err[len] = '\0' ;
24971+      }
24972+      qlogenvelope("rejected","rcptcheck","custom",smtperrcode);
24973+    }
24974+    else {
24975+      switch (rcres) {
24976+        case 0:
24977+          strerr_warn5(title.s,"rcptcheck: drop address <",addr.s,"> at ",remoteip,0);
24978+          qlogenvelope("rejected","rcptcheck","nomailbox","550");
24979+          len = str_copy(rcptcheck_err,"550 sorry, no mailbox here by that name. (#5.1.1)\r\n");
24980+          rcptcheck_err[len] = '\0';
24981+          break;
24982+        case 1:
24983+          strerr_warn5(title.s,"rcptcheck: accepted address <",addr.s,"> at ",remoteip,0);
24984+          flagrcptmatch = 4;
24985+          break;
24986+        case 2:
24987+          strerr_warn5(title.s,"rcptcheck: ignore address <",addr.s,"> at ",remoteip,0);
24988+          break;
24989+        case 3:
24990+          strerr_warn5(title.s,"rcptcheck: overlimit sender <",addr.s,"> at ",remoteip,0);
24991+          qlogenvelope("rejected","rcptcheck","overlimit","421");
24992+          len = str_copy(rcptcheck_err,"421 you have exceeded your messaging limits (#4.3.0)\r\n");
24993+          rcptcheck_err[len] = '\0';
24994+          closesession = 1;
24995+          break;
24996+      }
24997+    }
24998+
24999+    if ( (rcres == 0) || (rcres == 3) ) {
25000+      out(rcptcheck_err); flush();
25001+      if (closesession) {
25002+        _exit(1);
25003+      }
25004+      return;
25005+    }
25006+  } // if rcptcheck[0]
25007+/* realbadrcpt: end */
25008+
25009+/* end chkuser code */
25010+/* rbl: start */
25011+  if ((rblok) && !(relayclient || seenauth || dnsblskip || flagrbldns)) {
25012+    flagrbldns = 1;
25013+    rblcheck();
25014+  }
25015+  if (rbldecision >= 2) {
25016+    if (!stralloc_ready(&rblmessage,0)) die_nomem();
25017+    if (flagmustnotbounce || (rbldecision == 2)) {
25018+      if (!stralloc_copys(&rblmessage,"451 ")) die_nomem();
25019+    }
25020+    else
25021+      if (!stralloc_copys(&rblmessage,"553 ")) die_nomem();
25022+        if (rbltext.len > 500) rbltext.len = 500;
25023+    if (!stralloc_cat(&rblmessage,&rbltext)) die_nomem();
25024+    int i;
25025+    for (i = 0;i < rblmessage.len;++i)
25026+      if ((rblmessage.s[i] < 32) || (rblmessage.s[i] > 126))
25027+              rblmessage.s[i] = '?';
25028+        if (!stralloc_cats(&rblmessage,"\r\n")) die_nomem();
25029+    if (flagmustnotbounce || (rbldecision == 2)) die_rbldelay();
25030+    else err_rblreject();
25031+    return;
25032+  }
25033+/* rbl: end */
25034+
25035+/* start empf code */
25036+  ret = policy_check();
25037+
25038+  if (ret == 1) {
25039+    if (!stralloc_cats(&rcptto,"T")) die_nomem();
25040+    if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
25041+    if (!stralloc_0(&rcptto)) die_nomem();
25042+    rcptcount++;
25043+    if (checkrcptcount() == 1) { err_maxrcpt(); return; }
25044+    if (flagrcptmatch == 1) { qlogenvelope("accepted","rcptto","validrcptto","250"); }
25045+    else if (flagrcptmatch == 2) { qlogenvelope("accepted","rcptto","chkuser","250"); }
25046+    else if (flagrcptmatch == 3) { qlogenvelope("accepted","rcptto","chkuserrelay","250"); }
25047+    else if (flagrcptmatch == 4) { qlogenvelope("accepted","rcptto","rcptcheck","250"); }
25048+    else {
25049+      if (relayclient) { qlogenvelope("accepted","relayclient","","250"); }
25050+      else { qlogenvelope("accepted","rcpthosts","","250"); }
25051+    }
25052+    out("250 ok\r\n");
25053+  }
25054+
25055+  else if (ret == 0) {
25056+    qlogenvelope("rejected","empf","","550");
25057+    out("550 cannot message ");
25058+    out(addr.s);
25059+    out(" (#5.0.0 denied by policy)\r\n");
25060+  }
25061+
25062+  else {
25063+    qlogenvelope("rejected","empf","","454");
25064+    out("454 cannot message ");
25065+    out(addr.s);
25066+    out(" (#4.3.0 broken policy)\r\n");
25067+ }
25068+/* end of empf code */
25069+
25070+/*
25071+ * code substituted by empf code
25072   if (!stralloc_cats(&rcptto,"T")) die_nomem();
25073   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
25074   if (!stralloc_0(&rcptto)) die_nomem();
25075+  rcptcount++;
25076+  if (checkrcptcount() == 1) { err_maxrcpt(); return; }
25077   out("250 ok\r\n");
25078+ */
25079 }
25080 
25081-
25082 int saferead(fd,buf,len) int fd; char *buf; int len;
25083 {
25084   int r;
25085   flush();
25086+#ifdef TLS
25087+  if (ssl && fd == ssl_rfd)
25088+    r = ssl_timeoutread(timeout, ssl_rfd, ssl_wfd, ssl, buf, len);
25089+  else
25090+#endif
25091   r = timeoutread(timeout,fd,buf,len);
25092   if (r == -1) if (errno == error_timeout) die_alarm();
25093   if (r <= 0) die_read();
25094@@ -277,6 +1709,9 @@
25095 
25096 char ssinbuf[1024];
25097 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
25098+#ifdef TLS
25099+void flush_io() { ssin.p = 0; flush(); }
25100+#endif
25101 
25102 struct qmail qqt;
25103 unsigned int bytestooverflow = 0;
25104@@ -300,7 +1735,7 @@
25105   int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
25106   int flagmaybey; /* 1 if this line might match \r\n, if fih */
25107   int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
25108-
25109+
25110   state = 1;
25111   *hops = 0;
25112   flaginheader = 1;
25113@@ -322,17 +1757,16 @@
25114     }
25115     switch(state) {
25116       case 0:
25117-        if (ch == '\n') straynewline();
25118+        if (ch == '\n') { state = 1; break; }
25119         if (ch == '\r') { state = 4; continue; }
25120         break;
25121       case 1: /* \r\n */
25122-        if (ch == '\n') straynewline();
25123         if (ch == '.') { state = 2; continue; }
25124         if (ch == '\r') { state = 4; continue; }
25125-        state = 0;
25126+        if (ch != '\n') state = 0;
25127         break;
25128       case 2: /* \r\n + . */
25129-        if (ch == '\n') straynewline();
25130+        if (ch == '\n') return;        /* this is what sendmail-8.8.4 does -djg */
25131         if (ch == '\r') { state = 3; continue; }
25132         state = 0;
25133         break;
25134@@ -351,10 +1785,73 @@
25135   }
25136 }
25137 
25138+void spfreceived()
25139+{
25140+  stralloc sa = {0};
25141+  stralloc rcvd_spf = {0};
25142+
25143+  if (!spfbehavior || relayclient) return;
25144+
25145+  if (!stralloc_copys(&rcvd_spf, "Received-SPF: ")) die_nomem();
25146+  if (!spfinfo(&sa)) die_nomem();
25147+  if (!stralloc_cat(&rcvd_spf, &sa)) die_nomem();
25148+  if (!stralloc_append(&rcvd_spf, "\n")) die_nomem();
25149+  if (bytestooverflow) {
25150+    bytestooverflow -= rcvd_spf.len;
25151+    if (bytestooverflow <= 0) qmail_fail(&qqt);
25152+  }
25153+  qmail_put(&qqt,rcvd_spf.s,rcvd_spf.len);
25154+}
25155+
25156+/* rbl: start */
25157+/*
25158+int dnsblcheck()
25159+{
25160+  char *ch;
25161+  static stralloc dnsblbyte = {0};
25162+  static stralloc dnsblrev = {0};
25163+  static ipalloc dnsblip = {0};
25164+  static stralloc dnsbllist = {0};
25165+
25166+  ch = remoteip;
25167+  if(control_readfile(&dnsbllist,"control/dnsbllist",0) != 1) return 0;
25168+
25169+  if (!stralloc_copys(&dnsblrev,"")) return 0;
25170+  for (;;) {
25171+    if (!stralloc_copys(&dnsblbyte,"")) return 0;
25172+    while (ch[0] && (ch[0] != '.')) {
25173+      if (!stralloc_append(&dnsblbyte,ch)) return 0;
25174+      ch++;
25175+    }
25176+    if (!stralloc_append(&dnsblbyte,".")) return 0;
25177+    if (!stralloc_cat(&dnsblbyte,&dnsblrev)) return 0;
25178+    if (!stralloc_copy(&dnsblrev,&dnsblbyte)) return 0;
25179+
25180+    if (!ch[0]) break;
25181+    ch++;
25182+  }
25183+
25184+  flagdnsbl = 1;
25185+  ch = dnsbllist.s;
25186+  while (ch < (dnsbllist.s + dnsbllist.len)) {
25187+    if (!stralloc_copy(&dnsblhost,&dnsblrev)) return 0;
25188+    if (!stralloc_cats(&dnsblhost,ch)) return 0;
25189+    if (!stralloc_0(&dnsblhost)) return 0;
25190+
25191+    if (!dns_ip(&dnsblip,&dnsblhost)) return 1;
25192+    while (*ch++);
25193+  }
25194+
25195+  return 0;
25196+}
25197+*/
25198+/* rbl:end */
25199+
25200 char accept_buf[FMT_ULONG];
25201 void acceptmessage(qp) unsigned long qp;
25202 {
25203   datetime_sec when;
25204+  strnum[fmt_uint(strnum,(unsigned int) getpid())] = 0;
25205   when = now();
25206   out("250 ok ");
25207   accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;
25208@@ -363,22 +1860,32 @@
25209   accept_buf[fmt_ulong(accept_buf,qp)] = 0;
25210   out(accept_buf);
25211   out("\r\n");
25212+  substdio_puts(&sslog, "mail recv: pid ");
25213+  substdio_puts(&sslog, strnum);
25214+  substdio_puts(&sslog, " from <");
25215+  substdio_puts(&sslog, mailfrom.s);
25216+  substdio_puts(&sslog, "> qp ");
25217+  substdio_puts(&sslog, accept_buf);
25218+  substdio_putsflush(&sslog, "\n");
25219 }
25220 
25221 void smtp_data(arg) char *arg; {
25222   int hops;
25223   unsigned long qp;
25224   char *qqx;
25225-
25226+
25227   if (!seenmail) { err_wantmail(); return; }
25228   if (!rcptto.len) { err_wantrcpt(); return; }
25229+  envelopepos = 4;
25230   seenmail = 0;
25231   if (databytes) bytestooverflow = databytes + 1;
25232   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
25233   qp = qmail_qp(&qqt);
25234+  strnumqp[fmt_ulong(strnumqp,qp)] = 0; /* qp for qlog */
25235   out("354 go ahead\r\n");
25236 
25237-  received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);
25238+  received(&qqt,protocol,local,remoteip,remotehost,remoteinfo,fakehelo);
25239+  spfreceived();
25240   blast(&hops);
25241   hops = (hops >= MAXHOPS);
25242   if (hops) qmail_fail(&qqt);
25243@@ -386,34 +1893,631 @@
25244   qmail_put(&qqt,rcptto.s,rcptto.len);
25245 
25246   qqx = qmail_close(&qqt);
25247-  if (!*qqx) { acceptmessage(qp); return; }
25248-  if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }
25249-  if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }
25250-  if (*qqx == 'D') out("554 "); else out("451 ");
25251+  if (!*qqx) { acceptmessage(qp); logit("message accepted"); qlogreceived("accepted","queueaccept","","250"); return; }
25252+  if (hops) {
25253+    out("554 too many hops, this message is looping (#5.4.6)\r\n");
25254+    logit("message looping");
25255+    qlogreceived("rejected","mailloop","","554");
25256+    return;
25257+  }
25258+  if (databytes) if (!bytestooverflow) {
25259+    err_size();
25260+    logit("message too big");
25261+    return;
25262+  }
25263+  if (*qqx == 'D') {
25264+    out("554 ");
25265+    qlogreceived("rejected","queuereject",qqx + 1,"554");
25266+    logit2("message rejected", qqx + 1);
25267+  } else {
25268+    out("451 ");
25269+    qlogreceived("rejected","queuedelay",qqx + 1,"451");
25270+    logit2("message delayed", qqx + 1);
25271+  }
25272   out(qqx + 1);
25273   out("\r\n");
25274 }
25275 
25276+
25277+int authgetl(void) {
25278+  int i;
25279+
25280+  if (!stralloc_copys(&authin,"")) die_nomem();
25281+  for (;;) {
25282+    if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
25283+    i = substdio_get(&ssin,authin.s + authin.len,1);
25284+    if (i != 1) die_read();
25285+    if (authin.s[authin.len] == '\n') break;
25286+    ++authin.len;
25287+  }
25288+
25289+  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
25290+  authin.s[authin.len] = 0;
25291+  if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
25292+  if (authin.len == 0) { return err_input(); }
25293+  return authin.len;
25294+}
25295+
25296+int authenticate(void)
25297+{
25298+  int child;
25299+  int wstat;
25300+  int pi[2];
25301+
25302+  if (!stralloc_0(&user)) die_nomem();
25303+  if (!stralloc_0(&pass)) die_nomem();
25304+  if (!stralloc_0(&chal)) die_nomem();
25305+
25306+  if (pipe(pi) == -1) return err_pipe();
25307+  switch(child = fork()) {
25308+    case -1:
25309+      return err_fork();
25310+    case 0:
25311+      close(pi[1]);
25312+      if(fd_copy(3,pi[0]) == -1) return err_pipe();
25313+      sig_pipedefault();
25314+        execvp(*childargs, childargs);
25315+      _exit(1);
25316+  }
25317+  close(pi[0]);
25318+
25319+  substdio_fdbuf(&ssauth,write,pi[1],ssauthbuf,sizeof ssauthbuf);
25320+  if (substdio_put(&ssauth,user.s,user.len) == -1) return err_write();
25321+  if (substdio_put(&ssauth,pass.s,pass.len) == -1) return err_write();
25322+  if (smtpauth == 2 || smtpauth == 3 || smtpauth == 12 || smtpauth == 13)
25323+    if (substdio_put(&ssauth,chal.s,chal.len) == -1) return err_write();
25324+  if (substdio_flush(&ssauth) == -1) return err_write();
25325+
25326+  close(pi[1]);
25327+  if (!stralloc_copys(&chal,"")) die_nomem();
25328+  if (!stralloc_copys(&slop,"")) die_nomem();
25329+  byte_zero(ssauthbuf,sizeof ssauthbuf);
25330+  if (wait_pid(&wstat,child) == -1) return err_child();
25331+  if (wait_crashed(wstat)) return err_child();
25332+  if (wait_exitcode(wstat)) { sleep(AUTHSLEEP); return 1; } /* no */
25333+  return 0; /* yes */
25334+}
25335+
25336+int auth_login(arg) char *arg;
25337+{
25338+  int r;
25339+
25340+  if (*arg) {
25341+    if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
25342+  }
25343+  else {
25344+    out("334 VXNlcm5hbWU6\r\n"); flush();       /* Username: */
25345+    if (authgetl() < 0) return -1;
25346+    if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
25347+  }
25348+  if (r == -1) die_nomem();
25349+
25350+  out("334 UGFzc3dvcmQ6\r\n"); flush();         /* Password: */
25351+
25352+  if (authgetl() < 0) return -1;
25353+  if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
25354+  if (r == -1) die_nomem();
25355+
25356+  if (!user.len || !pass.len) return err_input();
25357+  return authenticate();
25358+}
25359+
25360+int auth_plain(arg) char *arg;
25361+{
25362+  int r, id = 0;
25363+
25364+  if (*arg) {
25365+    if (r = b64decode(arg,str_len(arg),&resp) == 1) return err_input();
25366+  }
25367+  else {
25368+    out("334 \r\n"); flush();
25369+    if (authgetl() < 0) return -1;
25370+    if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
25371+  }
25372+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
25373+  while (resp.s[id]) id++;                       /* "authorize-id\0userid\0passwd\0" */
25374+
25375+  if (resp.len > id + 1)
25376+    if (!stralloc_copys(&user,resp.s + id + 1)) die_nomem();
25377+  if (resp.len > id + user.len + 2)
25378+    if (!stralloc_copys(&pass,resp.s + id + user.len + 2)) die_nomem();
25379+
25380+  if (!user.len || !pass.len) return err_input();
25381+  return authenticate();
25382+}
25383+
25384+int auth_cram()
25385+{
25386+  int i, r;
25387+  char *s;
25388+
25389+  s = unique;                                           /* generate challenge */
25390+  s += fmt_uint(s,getpid());
25391+  *s++ = '.';
25392+  s += fmt_ulong(s,(unsigned long) now());
25393+  *s++ = '@';
25394+  *s++ = 0;
25395+  if (!stralloc_copys(&chal,"<")) die_nomem();
25396+  if (!stralloc_cats(&chal,unique)) die_nomem();
25397+  if (!stralloc_cats(&chal,local)) die_nomem();
25398+  if (!stralloc_cats(&chal,">")) die_nomem();
25399+  if (b64encode(&chal,&slop) < 0) die_nomem();
25400+  if (!stralloc_0(&slop)) die_nomem();
25401+
25402+  out("334 ");                                          /* "334 base64_challenge \r\n" */
25403+  out(slop.s);
25404+  out("\r\n");
25405+  flush();
25406+
25407+  if (authgetl() < 0) return -1;                        /* got response */
25408+  if (r = b64decode(authin.s,authin.len,&resp) == 1) return err_input();
25409+  if (r == -1 || !stralloc_0(&resp)) die_nomem();
25410+
25411+  i = str_rchr(resp.s,' ');
25412+  s = resp.s + i;
25413+  while (*s == ' ') ++s;
25414+  resp.s[i] = 0;
25415+  if (!stralloc_copys(&user,resp.s)) die_nomem();       /* userid */
25416+  if (!stralloc_copys(&pass,s)) die_nomem();            /* digest */
25417+
25418+  if (!user.len || !pass.len) return err_input();
25419+  return authenticate();
25420+}
25421+
25422+struct authcmd {
25423+  char *text;
25424+  int (*fun)();
25425+} authcmds[] = {
25426+  { "login",auth_login }
25427+, { "plain",auth_plain }
25428+, { "cram-md5",auth_cram }
25429+, { 0,err_noauth }
25430+};
25431+
25432+void smtp_auth(arg)
25433+char *arg;
25434+{
25435+  int i;
25436+  char *cmd = arg;
25437+  if (!smtpauth || !*childargs) { out("503 auth not available (#5.3.3)\r\n"); return; }
25438+  if (seenauth) { err_authd(); return; }
25439+  if (seenmail) { err_authmail(); return; }
25440+#ifdef TLS
25441+  if (forcetls && !ssl) { out("538 auth not available without TLS (#5.3.3)\r\n"); return; }
25442+#endif
25443+
25444+  if (!stralloc_copys(&user,"")) die_nomem();
25445+  if (!stralloc_copys(&pass,"")) die_nomem();
25446+  if (!stralloc_copys(&resp,"")) die_nomem();
25447+  if (!stralloc_copys(&chal,"")) die_nomem();
25448+
25449+  i = str_chr(cmd,' ');
25450+  arg = cmd + i;
25451+  while (*arg == ' ') ++arg;
25452+  cmd[i] = 0;
25453+
25454+  for (i = 0;authcmds[i].text;++i)
25455+    if (case_equals(authcmds[i].text,cmd)) break;
25456+
25457+  switch (authcmds[i].fun(arg)) {
25458+    case 0:
25459+      seenauth = 1;
25460+      protocol = "ESMTPA";
25461+      relayclient = "";
25462+      remoteinfo = user.s;
25463+      if (!env_unset("TCPREMOTEINFO")) die_read();
25464+      if (!env_put2("TCPREMOTEINFO",remoteinfo)) die_nomem();
25465+      if (!env_put2("RELAYCLIENT",relayclient)) die_nomem();
25466+
25467+      if (!env_unset("SMTPAUTHMETHOD")) die_read();
25468+      if (!env_put2("SMTPAUTHMETHOD", authcmds[i].text)) die_nomem();
25469+      if (!env_unset("SMTPAUTHUSER")) die_read();
25470+      if (!env_put2("SMTPAUTHUSER",user.s)) die_nomem();
25471+      if (!env_unset("SMTP_AUTH_USER")) die_read();
25472+      if (!env_put2("SMTP_AUTH_USER",user.s)) die_nomem();
25473+
25474+      strerr_warn4(title.s,"auth: auth-success type=login user=<",user.s,">",0);
25475+      out("235 ok, go ahead (#2.0.0)\r\n");
25476+      break;
25477+    case 1:
25478+      strerr_warn4(title.s,"auth: auth-failed type=login user=<",user.s,">",0);
25479+      err_authfail(user.s,authcmds[i].text);
25480+  }
25481+}
25482+
25483+#ifdef TLS
25484+stralloc proto = {0};
25485+int ssl_verified = 0;
25486+const char *ssl_verify_err = 0;
25487+
25488+void smtp_tls(char *arg)
25489+{
25490+  if (ssl || disabletls) err_unimpl();
25491+  else if (*arg) out("501 Syntax error (no parameters allowed) (#5.5.4)\r\n");
25492+  else tls_init();
25493+}
25494+
25495+RSA *tmp_rsa_cb(SSL *ssl, int export, int keylen)
25496+{
25497+  if (!export) keylen = 2048;
25498+  if (keylen == 2048) {
25499+    FILE *in = fopen("control/rsa2048.pem", "r");
25500+    if (in) {
25501+      RSA *rsa = PEM_read_RSAPrivateKey(in, NULL, NULL, NULL);
25502+      fclose(in);
25503+      if (rsa) return rsa;
25504+    }
25505+  }
25506+  return RSA_generate_key(keylen, RSA_F4, NULL, NULL);
25507+}
25508+
25509+DH *tmp_dh_cb(SSL *ssl, int export, int keylen)
25510+{
25511+  if (!export) keylen = 2048;
25512+  if (keylen == 2048) {
25513+    FILE *in = fopen("control/dh2048.pem", "r");
25514+    if (in) {
25515+      DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
25516+      fclose(in);
25517+      if (dh) return dh;
25518+    }
25519+  }
25520+  if (keylen == 1024) {
25521+    FILE *in = fopen("control/dh1024.pem", "r");
25522+    if (in) {
25523+      DH *dh = PEM_read_DHparams(in, NULL, NULL, NULL);
25524+      fclose(in);
25525+      if (dh) return dh;
25526+    }
25527+  }
25528+  return DH_generate_parameters(keylen, DH_GENERATOR_2, NULL, NULL);
25529+}
25530+
25531+/* don't want to fail handshake if cert isn't verifiable */
25532+int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { return 1; }
25533+
25534+void tls_nogateway()
25535+{
25536+  /* there may be cases when relayclient is set */
25537+  if (!ssl || relayclient) return;
25538+  out("; no valid cert for gatewaying");
25539+  if (ssl_verify_err) { out(": "); out(ssl_verify_err); }
25540+}
25541+void tls_out(const char *s1, const char *s2)
25542+{
25543+  out("454 TLS "); out(s1);
25544+  if (s2) { out(": "); out(s2); }
25545+  out(" (#4.3.0)\r\n"); flush();
25546+}
25547+void tls_err(const char *s) { tls_out(s, ssl_error()); if (smtps) die_read(); }
25548+
25549+# define CLIENTCA "control/clientca.pem"
25550+# define CLIENTCRL "control/clientcrl.pem"
25551+# define SERVERCERT "control/servercert.pem"
25552+
25553+int tls_verify()
25554+{
25555+  stralloc clients = {0};
25556+  struct constmap mapclients;
25557+
25558+  if (!ssl || relayclient || ssl_verified) return 0;
25559+  ssl_verified = 1; /* don't do this twice */
25560+
25561+  /* request client cert to see if it can be verified by one of our CAs
25562+   * and the associated email address matches an entry in tlsclients */
25563+  switch (control_readfile(&clients, "control/tlsclients", 0))
25564+  {
25565+  case 1:
25566+    if (constmap_init(&mapclients, clients.s, clients.len, 0)) {
25567+      /* if CLIENTCA contains all the standard root certificates, a
25568+       * 0.9.6b client might fail with SSL_R_EXCESSIVE_MESSAGE_SIZE;
25569+       * it is probably due to 0.9.6b supporting only 8k key exchange
25570+       * data while the 0.9.6c release increases that limit to 100k */
25571+      STACK_OF(X509_NAME) *sk = SSL_load_client_CA_file(CLIENTCA);
25572+      if (sk) {
25573+        SSL_set_client_CA_list(ssl, sk);
25574+        SSL_set_verify(ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
25575+        break;
25576+      }
25577+      constmap_free(&mapclients);
25578+    }
25579+  case 0: alloc_free(clients.s); return 0;
25580+  case -1: die_control();
25581+  }
25582+
25583+  if (ssl_timeoutrehandshake(timeout, ssl_rfd, ssl_wfd, ssl) <= 0) {
25584+    const char *err = ssl_error_str();
25585+    tls_out("rehandshake failed", err); die_read();
25586+  }
25587+
25588+  do { /* one iteration */
25589+    X509 *peercert;
25590+    X509_NAME *subj;
25591+    stralloc email = {0};
25592+
25593+    int n = SSL_get_verify_result(ssl);
25594+    if (n != X509_V_OK)
25595+      { ssl_verify_err = X509_verify_cert_error_string(n); break; }
25596+    peercert = SSL_get_peer_certificate(ssl);
25597+    if (!peercert) break;
25598+
25599+    subj = X509_get_subject_name(peercert);
25600+    n = X509_NAME_get_index_by_NID(subj, NID_pkcs9_emailAddress, -1);
25601+    if (n >= 0) {
25602+      const ASN1_STRING *s = X509_NAME_get_entry(subj, n)->value;
25603+      if (s) { email.len = s->length; email.s = s->data; }
25604+    }
25605+
25606+    if (email.len <= 0)
25607+      ssl_verify_err = "contains no email address";
25608+    else if (!constmap(&mapclients, email.s, email.len))
25609+      ssl_verify_err = "email address not in my list of tlsclients";
25610+    else {
25611+      /* add the cert email to the proto if it helped allow relaying */
25612+      --proto.len;
25613+      if (!stralloc_cats(&proto, "\n  (cert ") /* continuation line */
25614+        || !stralloc_catb(&proto, email.s, email.len)
25615+        || !stralloc_cats(&proto, ")")
25616+        || !stralloc_0(&proto)) die_nomem();
25617+      relayclient = "";
25618+      /* also inform qmail-queue */
25619+      if (!env_put("RELAYCLIENT=")) die_nomem();
25620+      protocol = proto.s;
25621+    }
25622+
25623+    X509_free(peercert);
25624+  } while (0);
25625+  constmap_free(&mapclients); alloc_free(clients.s);
25626+
25627+  /* we are not going to need this anymore: free the memory */
25628+  SSL_set_client_CA_list(ssl, NULL);
25629+  SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
25630+
25631+  return relayclient ? 1 : 0;
25632+}
25633+
25634+void tls_init()
25635+{
25636+  SSL *myssl;
25637+  SSL_CTX *ctx;
25638+  const char *ciphers;
25639+  stralloc saciphers = {0};
25640+  X509_STORE *store;
25641+  X509_LOOKUP *lookup;
25642+
25643+  SSL_library_init();
25644+
25645+  /* a new SSL context with the bare minimum of options */
25646+  ctx = SSL_CTX_new(SSLv23_server_method());
25647+  if (!ctx) { tls_err("unable to initialize ctx"); return; }
25648+
25649+  /* POODLE vulnerability */
25650+  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
25651+
25652+  if (!SSL_CTX_use_certificate_chain_file(ctx, SERVERCERT))
25653+    { SSL_CTX_free(ctx); tls_err("missing certificate"); return; }
25654+  SSL_CTX_load_verify_locations(ctx, CLIENTCA, NULL);
25655+
25656+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
25657+  /* crl checking */
25658+  store = SSL_CTX_get_cert_store(ctx);
25659+  if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) &&
25660+      (X509_load_crl_file(lookup, CLIENTCRL, X509_FILETYPE_PEM) == 1))
25661+    X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
25662+                                X509_V_FLAG_CRL_CHECK_ALL);
25663+#endif
25664+
25665+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
25666+  /* support ECDH */
25667+  SSL_CTX_set_ecdh_auto(ctx,1);
25668+#endif
25669+
25670+  /* set the callback here; SSL_set_verify didn't work before 0.9.6c */
25671+  SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_cb);
25672+
25673+  /* a new SSL object, with the rest added to it directly to avoid copying */
25674+  myssl = SSL_new(ctx);
25675+  SSL_CTX_free(ctx);
25676+  if (!myssl) { tls_err("unable to initialize ssl"); return; }
25677+
25678+  /* this will also check whether public and private keys match */
25679+  if (!SSL_use_RSAPrivateKey_file(myssl, SERVERCERT, SSL_FILETYPE_PEM))
25680+    { SSL_free(myssl); tls_err("no valid RSA private key"); return; }
25681+
25682+  ciphers = env_get("TLSCIPHERS");
25683+  if (!ciphers) {
25684+    if (control_readfile(&saciphers, "control/tlsserverciphers", 0) == -1)
25685+      { SSL_free(myssl); die_control(); }
25686+    if (saciphers.len) { /* convert all '\0's except the last one to ':' */
25687+      int i;
25688+      for (i = 0; i < saciphers.len - 1; ++i)
25689+        if (!saciphers.s[i]) saciphers.s[i] = ':';
25690+      ciphers = saciphers.s;
25691+    }
25692+  }
25693+  if (!ciphers || !*ciphers) ciphers = "DEFAULT";
25694+  SSL_set_cipher_list(myssl, ciphers);
25695+  alloc_free(saciphers.s);
25696+
25697+  SSL_set_tmp_rsa_callback(myssl, tmp_rsa_cb);
25698+  SSL_set_tmp_dh_callback(myssl, tmp_dh_cb);
25699+  SSL_set_rfd(myssl, ssl_rfd = substdio_fileno(&ssin));
25700+  SSL_set_wfd(myssl, ssl_wfd = substdio_fileno(&ssout));
25701+
25702+  if (!smtps) { flagtls = 1; out("220 ready for tls\r\n"); flush(); }
25703+
25704+  if (ssl_timeoutaccept(timeout, ssl_rfd, ssl_wfd, myssl) <= 0) {
25705+    /* neither cleartext nor any other response here is part of a standard */
25706+    const char *err = ssl_error_str();
25707+    ssl_free(myssl); tls_out("connection failed", err); die_read();
25708+  }
25709+  ssl = myssl;
25710+
25711+  /* populate the protocol string, used in Received */
25712+  if (!stralloc_copys(&proto, "ESMTPS (")
25713+    || !stralloc_cats(&proto, SSL_get_cipher(ssl))
25714+    || !stralloc_cats(&proto, " encrypted)")) die_nomem();
25715+  if (!stralloc_0(&proto)) die_nomem();
25716+  protocol = proto.s;
25717+
25718+  /* have to discard the pre-STARTTLS HELO/EHLO argument, if any */
25719+  dohelo(remotehost);
25720+}
25721+
25722+# undef SERVERCERT
25723+# undef CLIENTCA
25724+
25725+#endif
25726+
25727 struct commands smtpcommands[] = {
25728   { "rcpt", smtp_rcpt, 0 }
25729 , { "mail", smtp_mail, 0 }
25730 , { "data", smtp_data, flush }
25731+, { "auth", smtp_auth, flush }
25732 , { "quit", smtp_quit, flush }
25733 , { "helo", smtp_helo, flush }
25734 , { "ehlo", smtp_ehlo, flush }
25735 , { "rset", smtp_rset, 0 }
25736 , { "help", smtp_help, flush }
25737+#ifdef TLS
25738+, { "starttls", smtp_tls, flush_io }
25739+#endif
25740 , { "noop", err_noop, flush }
25741 , { "vrfy", err_vrfy, flush }
25742-, { 0, err_unimpl, flush }
25743+, { 0, err_unrecog, flush }
25744 } ;
25745 
25746-void main()
25747+/* qsmtpdlog: start */
25748+void qsmtpdlog(const char *head, const char *result, const char *reason, const char *detail, const char *statuscode) {
25749+  char *x;
25750+  char *ch;
25751+  int i, r;
25752+  stralloc lst = {0};
25753+  int isenvelope = 0;
25754
25755+  void outqlog(char *s, unsigned int n) {
25756+    while (n > 0) {
25757+      substdio_put(&sslog,((*s > 32) && (*s <= 126)) ? s : "_",1);
25758+      --n;
25759+      ++s;
25760+    }
25761+  }
25762+  void outsqlog(s) char *s; { outqlog(s,str_len(s)); }
25763+
25764+  stralloc_copys(&lst,head);
25765+  if (stralloc_starts(&lst,"qlogenvelope")) isenvelope = 1;
25766+  substdio_puts(&sslog, head);
25767+  substdio_puts(&sslog, ":");
25768+
25769+  substdio_puts(&sslog, " result="); if (result) outsqlog(result);
25770+  substdio_puts(&sslog, " code="); if (detail) outsqlog(statuscode);
25771+  substdio_puts(&sslog, " reason="); if (reason) outsqlog(reason);
25772+  substdio_puts(&sslog, " detail="); if (detail) outsqlog(detail);
25773+  substdio_puts(&sslog, " helo="); if (helohost.len) outsqlog(helohost.s);
25774+  substdio_puts(&sslog, " mailfrom=");
25775+  if (mailfrom.len) outsqlog(mailfrom.s);
25776+  else if ( (envelopepos==2) && (addr.len) ) outsqlog(addr.s); // qlog called in smtp_mail() doesn't have mailfrom.s defined yet
25777+
25778+  substdio_puts(&sslog, " rcptto=");
25779+  if ((rcptto.len) && (!isenvelope)) {
25780+    ch = rcptto.s;
25781+    outsqlog(ch+1);
25782+    while (*ch++);
25783+    while (ch < (rcptto.s + rcptto.len)) {
25784+      outsqlog(",");
25785+      outsqlog(ch+1);
25786+      while (*ch++);
25787+    }
25788+  }
25789+  else if ( (envelopepos==3) && (addr.len) ) outsqlog(addr.s); // qlog was probably called at the beginning of smtp_rcpt and addr.s contains the recipient
25790+
25791+  substdio_puts(&sslog, " relay="); if (relayclient) outsqlog("yes"); else outsqlog("no");
25792+
25793+  // only log rcpthosts value in smtp_rcpt(), that is for a single recipient, this field is meaningless for multiple recipients
25794+  substdio_puts(&sslog, " rcpthosts="); if (isenvelope && addr.len && (envelopepos==3)) { if (addrinrcpthosts) outsqlog("yes"); else outsqlog("no"); }
25795+
25796+  substdio_puts(&sslog, " size=");
25797+  if (bytestooverflow) {
25798+    char *p,text[20];
25799+    if ((databytes - bytestooverflow) >= 0)
25800+      sprintf(text,"%d",databytes - bytestooverflow);
25801+    else
25802+      sprintf(text,"");
25803+    p = text;
25804+    outsqlog(p);
25805+  }
25806+
25807+  substdio_puts(&sslog, " authuser="); if (user.len) outsqlog(user.s);
25808+  substdio_puts(&sslog, " authtype="); x = env_get("SMTPAUTHMETHOD"); if (x) outsqlog(x);
25809+  substdio_puts(&sslog, " encrypted="); if (smtps) outsqlog("ssl"); else if (flagtls) outsqlog("tls");
25810+
25811+  substdio_puts(&sslog, " sslverified=");
25812+#ifdef TLS
25813+  if (ssl_verified) outsqlog("yes"); else outsqlog("no");
25814+#endif
25815+/*
25816+  substdio_puts(&sslog, " sslproto=");
25817+#ifdef TLS
25818+  if (proto.len) outsqlog(proto.s);
25819+#endif
25820+*/
25821+  substdio_puts(&sslog, " localip="); x = env_get("TCPLOCALIP"); if (x) outsqlog(x);
25822+  substdio_puts(&sslog, " localport="); x = env_get("TCPLOCALPORT"); if (x) outsqlog(x);
25823+  substdio_puts(&sslog, " remoteip="); x = env_get("TCPREMOTEIP"); if (x) outsqlog(x);
25824+  substdio_puts(&sslog, " remoteport="); x = env_get("TCPREMOTEPORT"); if (x) outsqlog(x);
25825+  substdio_puts(&sslog, " remotehost="); x = env_get("TCPREMOTEHOST"); if (x) outsqlog(x);
25826+  substdio_puts(&sslog, " qp="); if (strnumqp) outsqlog(strnumqp);
25827+  substdio_puts(&sslog, " pid="); if (strnumpid) outsqlog(strnumpid);
25828+  substdio_putsflush(&sslog, "\n");
25829+}
25830+/* qsmtpdlog: end */
25831+
25832+void main(argc,argv)
25833+int argc;
25834+char **argv;
25835 {
25836+  int n, m;
25837+  childargs = argv + 1;
25838   sig_pipeignore();
25839   if (chdir(auto_qmail) == -1) die_control();
25840+
25841+  pid_buf[fmt_ulong(pid_buf,getpid())]=0;
25842+  if (!stralloc_copys(&title,"qmail-smtpd[")) die_nomem();
25843+  if (!stralloc_cats(&title,pid_buf)) die_nomem();
25844+  if (!stralloc_cats(&title,"]: ")) die_nomem();
25845+  if (!stralloc_0(&title)) die_nomem();
25846+
25847   setup();
25848   if (ipme_init() != 1) die_ipme();
25849+  if (!relayclient && greetdelay) {
25850+    if (drop_pre_greet) {
25851+      n = timeoutread(greetdelay ? greetdelay : 1, 0, ssinbuf, sizeof(ssinbuf));
25852+      if(n == -1) {
25853+        if (errno != error_timeout)
25854+          strerr_die3sys(1, "GREETDELAY from ", remoteip, ": ");
25855+      } else if (n == 0) {
25856+        strerr_die3x(1, "GREETDELAY from ", remoteip, ": client disconnected");
25857+      } else {
25858+        strerr_warn3("GREETDELAY from ", remoteip, ": client sent data before greeting", 0);
25859+        die_pre_greet();
25860+      }
25861+    }
25862+    else {
25863+      strerr_warn3("GREETDELAY: ", greetdelay, "s", 0);
25864+      sleep(greetdelay);
25865+      m = 0;
25866+      for (;;) {
25867+        n = timeoutread(0, 0, ssinbuf, sizeof(ssinbuf));
25868+        if (n <= 0)
25869+          break;
25870+        if (n > 0 && m == 0) {
25871+          strerr_warn3("GREETDELAY from ", remoteip, ": client sent data before greeting. ignoring", 0);
25872+          m = 1;
25873+        }
25874+      }
25875+    }
25876+  }
25877+
25878   smtp_greet("220 ");
25879   out(" ESMTP\r\n");
25880   if (commands(&ssin,&smtpcommands) == 0) die_read();
25881diff -ruN ../netqmail-1.06-original/qmail-start.c netqmail-1.06/qmail-start.c
25882--- ../netqmail-1.06-original/qmail-start.c     1998-06-15 12:53:16.000000000 +0200
25883+++ netqmail-1.06/qmail-start.c 2016-11-22 21:03:57.125527665 +0100
25884@@ -8,6 +8,9 @@
25885 char *(qcargs[]) = { "qmail-clean", 0 };
25886 char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 };
25887 char *(qrargs[]) = { "qmail-rspawn", 0 };
25888+#ifdef EXTERNAL_TODO
25889+char *(qtargs[]) = { "qmail-todo", 0};
25890+#endif
25891 
25892 void die() { _exit(111); }
25893 
25894@@ -18,13 +21,28 @@
25895 int pi4[2];
25896 int pi5[2];
25897 int pi6[2];
25898-
25899-void close23456() { close(2); close(3); close(4); close(5); close(6); }
25900+#ifdef EXTERNAL_TODO
25901+int pi7[2];
25902+int pi8[2];
25903+int pi9[2];
25904+int pi10[2];
25905+#endif
25906+
25907+void close23456() {
25908+  close(2); close(3); close(4); close(5); close(6);
25909+#ifdef EXTERNAL_TODO
25910+  close(7); close(8);
25911+#endif
25912+}
25913 
25914 void closepipes() {
25915   close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]);
25916   close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]);
25917   close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]);
25918+#ifdef EXTERNAL_TODO
25919+  close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]);
25920+       close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]);
25921+#endif
25922 }
25923 
25924 void main(argc,argv)
25925@@ -40,6 +58,10 @@
25926   if (fd_copy(4,0) == -1) die();
25927   if (fd_copy(5,0) == -1) die();
25928   if (fd_copy(6,0) == -1) die();
25929+#ifdef EXTERNAL_TODO
25930+  if (fd_copy(7,0) == -1) die();
25931+  if (fd_copy(8,0) == -1) die();
25932+#endif
25933 
25934   if (argv[1]) {
25935     qlargs[1] = argv[1];
25936@@ -70,6 +92,12 @@
25937   if (pipe(pi4) == -1) die();
25938   if (pipe(pi5) == -1) die();
25939   if (pipe(pi6) == -1) die();
25940+#ifdef EXTERNAL_TODO
25941+  if (pipe(pi7) == -1) die();
25942+  if (pipe(pi8) == -1) die();
25943+  if (pipe(pi9) == -1) die();
25944+  if (pipe(pi10) == -1) die();
25945+#endif
25946 
25947   switch(fork()) {
25948     case -1: die();
25949@@ -106,6 +134,34 @@
25950       die();
25951   }
25952 
25953+#ifdef EXTERNAL_TODO
25954+  switch(fork()) {
25955+    case -1: die();
25956+    case 0:
25957+      if (prot_uid(auto_uids) == -1) die();
25958+      if (fd_copy(0,pi7[0]) == -1) die();
25959+      if (fd_copy(1,pi8[1]) == -1) die();
25960+      close23456();
25961+      if (fd_copy(2,pi9[1]) == -1) die();
25962+      if (fd_copy(3,pi10[0]) == -1) die();
25963+      closepipes();
25964+      execvp(*qtargs,qtargs);
25965+      die();
25966+  }
25967+
25968+  switch(fork()) {
25969+    case -1: die();
25970+    case 0:
25971+      if (prot_uid(auto_uidq) == -1) die();
25972+      if (fd_copy(0,pi9[0]) == -1) die();
25973+      if (fd_copy(1,pi10[1]) == -1) die();
25974+      close23456();
25975+      closepipes();
25976+      execvp(*qcargs,qcargs);
25977+      die();
25978+  }
25979+#endif
25980+
25981   if (prot_uid(auto_uids) == -1) die();
25982   if (fd_copy(0,1) == -1) die();
25983   if (fd_copy(1,pi1[1]) == -1) die();
25984@@ -114,6 +170,10 @@
25985   if (fd_copy(4,pi4[0]) == -1) die();
25986   if (fd_copy(5,pi5[1]) == -1) die();
25987   if (fd_copy(6,pi6[0]) == -1) die();
25988+#ifdef EXTERNAL_TODO
25989+  if (fd_copy(7,pi7[1]) == -1) die();
25990+  if (fd_copy(8,pi8[0]) == -1) die();
25991+#endif
25992   closepipes();
25993   execvp(*qsargs,qsargs);
25994   die();
25995diff -ruN ../netqmail-1.06-original/qmail-todo.c netqmail-1.06/qmail-todo.c
25996--- ../netqmail-1.06-original/qmail-todo.c      1970-01-01 01:00:00.000000000 +0100
25997+++ netqmail-1.06/qmail-todo.c  2016-11-22 21:03:57.126527632 +0100
25998@@ -0,0 +1,703 @@
25999+#include <sys/types.h>
26000+#include <sys/stat.h>
26001+#include "alloc.h"
26002+#include "auto_qmail.h"
26003+#include "byte.h"
26004+#include "constmap.h"
26005+#include "control.h"
26006+#include "direntry.h"
26007+#include "error.h"
26008+#include "exit.h"
26009+#include "fmt.h"
26010+#include "fmtqfn.h"
26011+#include "getln.h"
26012+#include "open.h"
26013+#include "ndelay.h"
26014+#include "now.h"
26015+#include "readsubdir.h"
26016+#include "readwrite.h"
26017+#include "scan.h"
26018+#include "select.h"
26019+#include "str.h"
26020+#include "stralloc.h"
26021+#include "substdio.h"
26022+#include "trigger.h"
26023+
26024+/* critical timing feature #1: if not triggered, do not busy-loop */
26025+/* critical timing feature #2: if triggered, respond within fixed time */
26026+/* important timing feature: when triggered, respond instantly */
26027+#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */
26028+#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */
26029+#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */
26030+#define SLEEP_SYSFAIL 123
26031+
26032+stralloc percenthack = {0};
26033+struct constmap mappercenthack;
26034+stralloc locals = {0};
26035+struct constmap maplocals;
26036+stralloc vdoms = {0};
26037+struct constmap mapvdoms;
26038+stralloc envnoathost = {0};
26039+
26040+char strnum[FMT_ULONG];
26041+
26042+/* XXX not good, if qmail-send.c changes this has to be updated */
26043+#define CHANNELS 2
26044+char *chanaddr[CHANNELS] = { "local/", "remote/" };
26045+
26046+datetime_sec recent;
26047+
26048+void log1(char *x);
26049+void log3(char* x, char* y, char* z);
26050+
26051+int flagstopasap = 0;
26052+void sigterm(void)
26053+{
26054+  if (flagstopasap == 0)
26055+    log1("status: qmail-todo stop processing asap\n");
26056+  flagstopasap = 1;
26057+}
26058+
26059+int flagreadasap = 0; void sighup(void) { flagreadasap = 1; }
26060+int flagsendalive = 1; void senddied(void) { flagsendalive = 0; }
26061+
26062+void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); }
26063+void pausedir(dir) char *dir;
26064+{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); }
26065+
26066+void cleandied()
26067+{
26068+  log1("alert: qmail-todo: oh no! lost qmail-clean connection! dying...\n");
26069+  flagstopasap = 1;
26070+}
26071+
26072+
26073+/* this file is not so long ------------------------------------- FILENAMES */
26074+
26075+stralloc fn = {0};
26076+
26077+void fnmake_init(void)
26078+{
26079+ while (!stralloc_ready(&fn,FMTQFN)) nomem();
26080+}
26081+
26082+void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); }
26083+void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,1); }
26084+void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); }
26085+void fnmake_chanaddr(unsigned long id, int c)
26086+{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
26087+
26088+
26089+/* this file is not so long ------------------------------------- REWRITING */
26090+
26091+stralloc rwline = {0};
26092+
26093+/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. */
26094+/* may trash recip. must set up rwline, between a T and a \0. */
26095+int rewrite(char *recip)
26096+{
26097+  int i;
26098+  int j;
26099+  char *x;
26100+  static stralloc addr = {0};
26101+  int at;
26102+
26103+  if (!stralloc_copys(&rwline,"T")) return 0;
26104+  if (!stralloc_copys(&addr,recip)) return 0;
26105+
26106+  i = byte_rchr(addr.s,addr.len,'@');
26107+  if (i == addr.len) {
26108+    if (!stralloc_cats(&addr,"@")) return 0;
26109+    if (!stralloc_cat(&addr,&envnoathost)) return 0;
26110+  }
26111+
26112+  while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) {
26113+    j = byte_rchr(addr.s,i,'%');
26114+    if (j == i) break;
26115+    addr.len = i;
26116+    i = j;
26117+    addr.s[i] = '@';
26118+  }
26119+
26120+  at = byte_rchr(addr.s,addr.len,'@');
26121+
26122+  if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) {
26123+    if (!stralloc_cat(&rwline,&addr)) return 0;
26124+    if (!stralloc_0(&rwline)) return 0;
26125+    return 1;
26126+  }
26127+
26128+  for (i = 0;i <= addr.len;++i)
26129+    if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.')))
26130+      if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) {
26131+        if (!*x) break;
26132+        if (!stralloc_cats(&rwline,x)) return 0;
26133+        if (!stralloc_cats(&rwline,"-")) return 0;
26134+        if (!stralloc_cat(&rwline,&addr)) return 0;
26135+        if (!stralloc_0(&rwline)) return 0;
26136+        return 1;
26137+      }
26138+
26139+  if (!stralloc_cat(&rwline,&addr)) return 0;
26140+  if (!stralloc_0(&rwline)) return 0;
26141+  return 2;
26142+}
26143+
26144+/* this file is not so long --------------------------------- COMMUNICATION */
26145+
26146+substdio sstoqc; char sstoqcbuf[1024];
26147+substdio ssfromqc; char ssfromqcbuf[1024];
26148+stralloc comm_buf = {0};
26149+int comm_pos;
26150+int fdout = -1;
26151+int fdin = -1;
26152+
26153+void comm_init(void)
26154+{
26155+ substdio_fdbuf(&sstoqc,write,2,sstoqcbuf,sizeof(sstoqcbuf));
26156+ substdio_fdbuf(&ssfromqc,read,3,ssfromqcbuf,sizeof(ssfromqcbuf));
26157+
26158+ fdout = 1; /* stdout */
26159+ fdin = 0;  /* stdin */
26160+ if (ndelay_on(fdout) == -1)
26161+ /* this is so stupid: NDELAY semantics should be default on write */
26162+   senddied(); /* drastic, but better than risking deadlock */
26163+
26164+ while (!stralloc_ready(&comm_buf,1024)) nomem();
26165+}
26166+
26167+int comm_canwrite(void)
26168+{
26169+ /* XXX: could allow a bigger buffer; say 10 recipients */
26170+ /* XXX: returns true if there is something in the buffer */
26171+ if (!flagsendalive) return 0;
26172+ if (comm_buf.s && comm_buf.len) return 1;
26173+ return 0;
26174+}
26175+
26176+void log1(char* x)
26177+{
26178+  int pos;
26179
26180+  pos = comm_buf.len;
26181+  if (!stralloc_cats(&comm_buf,"L")) goto fail;
26182+  if (!stralloc_cats(&comm_buf,x)) goto fail;
26183+  if (!stralloc_0(&comm_buf)) goto fail;
26184+  return;
26185
26186+fail:
26187+  /* either all or nothing */
26188+  comm_buf.len = pos;
26189+}
26190+
26191+void log3(char* x, char *y, char *z)
26192+{
26193+  int pos;
26194
26195+  pos = comm_buf.len;
26196+  if (!stralloc_cats(&comm_buf,"L")) goto fail;
26197+  if (!stralloc_cats(&comm_buf,x)) goto fail;
26198+  if (!stralloc_cats(&comm_buf,y)) goto fail;
26199+  if (!stralloc_cats(&comm_buf,z)) goto fail;
26200+  if (!stralloc_0(&comm_buf)) goto fail;
26201+  return;
26202
26203+fail:
26204+  /* either all or nothing */
26205+  comm_buf.len = pos;
26206+}
26207+
26208+void comm_write(unsigned long id, int local, int remote)
26209+{
26210+  int pos;
26211+  char *s;
26212
26213+  if(local && remote) s="B";
26214+  else if(local) s="L";
26215+  else if(remote) s="R";
26216+  else s="X";
26217
26218+  pos = comm_buf.len;
26219+  strnum[fmt_ulong(strnum,id)] = 0;
26220+  if (!stralloc_cats(&comm_buf,"D")) goto fail;
26221+  if (!stralloc_cats(&comm_buf,s)) goto fail;
26222+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26223+  if (!stralloc_0(&comm_buf)) goto fail;
26224+  return;
26225
26226+fail:
26227+  /* either all or nothing */
26228+  comm_buf.len = pos;
26229+}
26230+
26231+static int issafe(char ch)
26232+{
26233+ if (ch == '%') return 0; /* general principle: allman's code is crap */
26234+ if (ch < 33) return 0;
26235+ if (ch > 126) return 0;
26236+ return 1;
26237+}
26238+
26239+void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid)
26240+{
26241+  int pos;
26242+  int i;
26243
26244+  pos = comm_buf.len;
26245+  if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto fail;
26246+  strnum[fmt_ulong(strnum,id)] = 0;
26247+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26248+  if (!stralloc_cats(&comm_buf,": bytes ")) goto fail;
26249+  strnum[fmt_ulong(strnum,size)] = 0;
26250+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26251+  if (!stralloc_cats(&comm_buf," from <")) goto fail;
26252+  i = comm_buf.len;
26253+  if (!stralloc_cats(&comm_buf,from)) goto fail;
26254+  for (;i < comm_buf.len;++i)
26255+    if (comm_buf.s[i] == '\n')
26256+      comm_buf.s[i] = '/';
26257+    else
26258+      if (!issafe(comm_buf.s[i]))
26259+       comm_buf.s[i] = '_';
26260+  if (!stralloc_cats(&comm_buf,"> qp ")) goto fail;
26261+  strnum[fmt_ulong(strnum,pid)] = 0;
26262+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26263+  if (!stralloc_cats(&comm_buf," uid ")) goto fail;
26264+  strnum[fmt_ulong(strnum,uid)] = 0;
26265+  if (!stralloc_cats(&comm_buf,strnum)) goto fail;
26266+  if (!stralloc_cats(&comm_buf,"\n")) goto fail;
26267+  if (!stralloc_0(&comm_buf)) goto fail;
26268+  return;
26269
26270+fail:
26271+  /* either all or nothing */
26272+  comm_buf.len = pos;
26273+}
26274+
26275+void comm_exit(void)
26276+{
26277+  int w;
26278
26279+  /* if it fails exit, we have already stoped */
26280+  if (!stralloc_cats(&comm_buf,"X")) _exit(1);
26281+  if (!stralloc_0(&comm_buf)) _exit(1);
26282+}
26283+
26284+void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds)
26285+{
26286+  if (flagsendalive) {
26287+    if (flagstopasap && comm_canwrite() == 0)
26288+      comm_exit();
26289+    if (comm_canwrite()) {
26290+      FD_SET(fdout,wfds);
26291+      if (*nfds <= fdout)
26292+       *nfds = fdout + 1;
26293+    }
26294+    FD_SET(fdin,rfds);
26295+    if (*nfds <= fdin)
26296+      *nfds = fdin + 1;
26297+  }
26298+}
26299+
26300+void comm_do(fd_set *wfds, fd_set *rfds)
26301+{
26302+  /* first write then read */
26303+  if (flagsendalive)
26304+    if (comm_canwrite())
26305+      if (FD_ISSET(fdout,wfds)) {
26306+       int w;
26307+       int len;
26308+       len = comm_buf.len;
26309+       w = write(fdout,comm_buf.s + comm_pos,len - comm_pos);
26310+       if (w <= 0) {
26311+         if ((w == -1) && (errno == error_pipe))
26312+           senddied();
26313+       } else {
26314+         comm_pos += w;
26315+         if (comm_pos == len) {
26316+           comm_buf.len = 0;
26317+           comm_pos = 0;
26318+         }
26319+       }
26320+      }
26321+  if (flagsendalive)
26322+    if (FD_ISSET(fdin,rfds)) {
26323+      /* there are only two messages 'H' and 'X' */
26324+      char c;
26325+      int r;
26326+      r = read(fdin, &c, 1);
26327+      if (r <= 0) {
26328+       if ((r == -1) && (errno != error_intr))
26329+         senddied();
26330+      } else {
26331+       switch(c) {
26332+         case 'H':
26333+           sighup();
26334+           break;
26335+         case 'X':
26336+           sigterm();
26337+           break;
26338+         default:
26339+           log1("warning: qmail-todo: qmail-send speaks an obscure dialect\n");
26340+           break;
26341+       }
26342+      }
26343+    }
26344+}
26345+
26346+/* this file is not so long ------------------------------------------ TODO */
26347+
26348+datetime_sec nexttodorun;
26349+/* DIR *tododir;  if 0, have to opendir again */
26350+int flagtododir = 0; /* if 0, have to readsubdir_init again */
26351+readsubdir todosubdir;
26352+stralloc todoline = {0};
26353+char todobuf[SUBSTDIO_INSIZE];
26354+char todobufinfo[512];
26355+char todobufchan[CHANNELS][1024];
26356+
26357+void todo_init(void)
26358+{
26359+/*  tododir = 0; */
26360+ flagtododir = 0;
26361+ nexttodorun = now();
26362+ trigger_set();
26363+}
26364+
26365+void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup)
26366+{
26367+ if (flagstopasap) return;
26368+ trigger_selprep(nfds,rfds);
26369+/*  if (tododir) *wakeup = 0; */
26370+ if (flagtododir) *wakeup = 0;
26371+ if (*wakeup > nexttodorun) *wakeup = nexttodorun;
26372+}
26373+
26374+void todo_do(fd_set *rfds)
26375+{
26376+ struct stat st;
26377+ substdio ss; int fd;
26378+ substdio ssinfo; int fdinfo;
26379+ substdio sschan[CHANNELS];
26380+ int fdchan[CHANNELS];
26381+ int flagchan[CHANNELS];
26382+ char ch;
26383+ int match;
26384+ unsigned long id;
26385+/* unsigned int len;
26386+ direntry *d; */
26387+ int z;
26388+ int c;
26389+ unsigned long uid;
26390+ unsigned long pid;
26391+
26392+ fd = -1;
26393+ fdinfo = -1;
26394+ for (c = 0;c < CHANNELS;++c) fdchan[c] = -1;
26395+
26396+ if (flagstopasap) return;
26397+
26398+/* if (!tododir) */
26399+ if (!flagtododir)
26400+  {
26401+   if (!trigger_pulled(rfds))
26402+     if (recent < nexttodorun)
26403+       return;
26404+   trigger_set();
26405+/*   tododir = opendir("todo");
26406+   if (!tododir)
26407+    {
26408+     pausedir("todo");
26409+     return;
26410+    } */
26411+   readsubdir_init(&todosubdir, "todo", pausedir);
26412+   flagtododir = 1;
26413+   nexttodorun = recent + SLEEP_TODO;
26414+  }
26415+
26416+/* d = readdir(tododir);
26417+ if (!d) */
26418+ switch(readsubdir_next(&todosubdir, &id))
26419+  {
26420+/*   closedir(tododir);
26421+   tododir = 0;
26422+   return; */
26423+       case 1:
26424+        break;
26425+       case 0:
26426+       flagtododir = 0;
26427+       default:
26428+        return;
26429+  }
26430+/* if (str_equal(d->d_name,".")) return;
26431+ if (str_equal(d->d_name,"..")) return;
26432+ len = scan_ulong(d->d_name,&id);
26433+ if (!len || d->d_name[len]) return;
26434+*/
26435+ fnmake_todo(id);
26436+
26437+ fd = open_read(fn.s);
26438+ if (fd == -1) { log3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; }
26439+
26440+ fnmake_mess(id);
26441+ /* just for the statistics */
26442+ if (stat(fn.s,&st) == -1)
26443+  { log3("warning: qmail-todo: unable to stat ",fn.s,"\n"); goto fail; }
26444+
26445+ for (c = 0;c < CHANNELS;++c)
26446+  {
26447+   fnmake_chanaddr(id,c);
26448+   if (unlink(fn.s) == -1) if (errno != error_noent)
26449+    { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; }
26450+  }
26451+
26452+ fnmake_info(id);
26453+ if (unlink(fn.s) == -1) if (errno != error_noent)
26454+  { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; }
26455+
26456+ fdinfo = open_excl(fn.s);
26457+ if (fdinfo == -1)
26458+  { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; }
26459+
26460+ strnum[fmt_ulong(strnum,id)] = 0;
26461+ log3("new msg ",strnum,"\n");
26462+
26463+ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0;
26464+
26465+ substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf));
26466+ substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo));
26467+
26468+ uid = 0;
26469+ pid = 0;
26470+
26471+ for (;;)
26472+  {
26473+   if (getln(&ss,&todoline,&match,'\0') == -1)
26474+    {
26475+     /* perhaps we're out of memory, perhaps an I/O error */
26476+     fnmake_todo(id);
26477+     log3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto fail;
26478+    }
26479+   if (!match) break;
26480+
26481+   switch(todoline.s[0])
26482+    {
26483+     case 'u':
26484+       scan_ulong(todoline.s + 1,&uid);
26485+       break;
26486+     case 'p':
26487+       scan_ulong(todoline.s + 1,&pid);
26488+       break;
26489+     case 'F':
26490+       if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1)
26491+       {
26492+        fnmake_info(id);
26493+         log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail;
26494+       }
26495+       comm_info(id, (unsigned long) st.st_size, todoline.s + 1, pid, uid);
26496+       break;
26497+     case 'T':
26498+       switch(rewrite(todoline.s + 1))
26499+       {
26500+        case 0: nomem(); goto fail;
26501+        case 2: c = 1; break;
26502+        default: c = 0; break;
26503+        }
26504+       if (fdchan[c] == -1)
26505+       {
26506+        fnmake_chanaddr(id,c);
26507+        fdchan[c] = open_excl(fn.s);
26508+        if (fdchan[c] == -1)
26509+          { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; }
26510+        substdio_fdbuf(&sschan[c]
26511+          ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c]));
26512+        flagchan[c] = 1;
26513+       }
26514+       if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1)
26515+        {
26516+        fnmake_chanaddr(id,c);
26517+         log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail;
26518+        }
26519+       break;
26520+     default:
26521+       fnmake_todo(id);
26522+       log3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto fail;
26523+    }
26524+  }
26525+
26526+ close(fd); fd = -1;
26527+
26528+ fnmake_info(id);
26529+ if (substdio_flush(&ssinfo) == -1)
26530+  { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; }
26531+ if (fsync(fdinfo) == -1)
26532+  { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; }
26533+ close(fdinfo); fdinfo = -1;
26534+
26535+ for (c = 0;c < CHANNELS;++c)
26536+   if (fdchan[c] != -1)
26537+    {
26538+     fnmake_chanaddr(id,c);
26539+     if (substdio_flush(&sschan[c]) == -1)
26540+      { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; }
26541+     if (fsync(fdchan[c]) == -1)
26542+      { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; }
26543+     close(fdchan[c]); fdchan[c] = -1;
26544+    }
26545+
26546+ fnmake_todo(id);
26547+ if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; }
26548+ if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; }
26549+ if (ch != '+')
26550+  {
26551+   log3("warning: qmail-clean unable to clean up ",fn.s,"\n");
26552+   return;
26553+  }
26554+
26555+ comm_write(id, flagchan[0], flagchan[1]);
26556+
26557+ return;
26558+
26559+ fail:
26560+ if (fd != -1) close(fd);
26561+ if (fdinfo != -1) close(fdinfo);
26562+ for (c = 0;c < CHANNELS;++c)
26563+   if (fdchan[c] != -1) close(fdchan[c]);
26564+}
26565+
26566+/* this file is too long ---------------------------------------------- MAIN */
26567+
26568+int getcontrols(void)
26569+{
26570+ if (control_init() == -1) return 0;
26571+ if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
26572+ if (control_readfile(&locals,"control/locals",1) != 1) return 0;
26573+ if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
26574+ switch(control_readfile(&percenthack,"control/percenthack",0))
26575+  {
26576+   case -1: return 0;
26577+   case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
26578+   case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
26579+  }
26580+ switch(control_readfile(&vdoms,"control/virtualdomains",0))
26581+  {
26582+   case -1: return 0;
26583+   case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
26584+   case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
26585+  }
26586+ return 1;
26587+}
26588+
26589+stralloc newlocals = {0};
26590+stralloc newvdoms = {0};
26591+
26592+void regetcontrols(void)
26593+{
26594+ int r;
26595+
26596+ if (control_readfile(&newlocals,"control/locals",1) != 1)
26597+  { log1("alert: qmail-todo: unable to reread control/locals\n"); return; }
26598+ r = control_readfile(&newvdoms,"control/virtualdomains",0);
26599+ if (r == -1)
26600+  { log1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; }
26601+
26602+ constmap_free(&maplocals);
26603+ constmap_free(&mapvdoms);
26604+
26605+ while (!stralloc_copy(&locals,&newlocals)) nomem();
26606+ while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
26607+
26608+ if (r)
26609+  {
26610+   while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
26611+   while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
26612+  }
26613+ else
26614+   while (!constmap_init(&mapvdoms,"",0,1)) nomem();
26615+}
26616+
26617+void reread(void)
26618+{
26619+ if (chdir(auto_qmail) == -1)
26620+  {
26621+   log1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n");
26622+   return;
26623+  }
26624+ regetcontrols();
26625+ while (chdir("queue") == -1)
26626+  {
26627+   log1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n");
26628+   sleep(10);
26629+  }
26630+}
26631+
26632+void main()
26633+{
26634+ datetime_sec wakeup;
26635+ fd_set rfds;
26636+ fd_set wfds;
26637+ int nfds;
26638+ struct timeval tv;
26639+ int r;
26640+ char c;
26641+
26642+ if (chdir(auto_qmail) == -1)
26643+  { log1("alert: qmail-todo: cannot start: unable to switch to home directory\n"); _exit(111); }
26644+ if (!getcontrols())
26645+  { log1("alert: qmail-todo: cannot start: unable to read controls\n"); _exit(111); }
26646+ if (chdir("queue") == -1)
26647+  { log1("alert: qmail-todo: cannot start: unable to switch to queue directory\n"); _exit(111); }
26648+ sig_pipeignore();
26649+ umask(077);
26650+
26651+ fnmake_init();
26652+
26653+ todo_init();
26654+ comm_init();
26655+
26656+ do {
26657+   r = read(fdin, &c, 1);
26658+   if ((r == -1) && (errno != error_intr))
26659+     _exit(100); /* read failed probably qmail-send died */
26660+ } while (r =! 1); /* we assume it is a 'S' */
26661+
26662+ for (;;)
26663+  {
26664+   recent = now();
26665+
26666+   if (flagreadasap) { flagreadasap = 0; reread(); }
26667+   if (!flagsendalive) {
26668+     /* qmail-send finaly exited, so do the same. */
26669+     if (flagstopasap) _exit(0);
26670+     /* qmail-send died. We can not log and we can not work therefor _exit(1). */
26671+     _exit(1);
26672+   }
26673+
26674+   wakeup = recent + SLEEP_FOREVER;
26675+   FD_ZERO(&rfds);
26676+   FD_ZERO(&wfds);
26677+   nfds = 1;
26678+
26679+   todo_selprep(&nfds,&rfds,&wakeup);
26680+   comm_selprep(&nfds,&wfds,&rfds);
26681+
26682+   if (wakeup <= recent) tv.tv_sec = 0;
26683+   else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
26684+   tv.tv_usec = 0;
26685+
26686+   if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1)
26687+     if (errno == error_intr)
26688+       ;
26689+     else
26690+       log1("warning: qmail-todo: trouble in select\n");
26691+   else
26692+    {
26693+     recent = now();
26694+
26695+     todo_do(&rfds);
26696+     comm_do(&wfds, &rfds);
26697+    }
26698+  }
26699+  /* NOTREACHED */
26700+}
26701+
26702diff -ruN ../netqmail-1.06-original/qmail.c netqmail-1.06/qmail.c
26703--- ../netqmail-1.06-original/qmail.c   2007-11-30 21:22:54.000000000 +0100
26704+++ netqmail-1.06/qmail.c       2016-11-22 21:03:57.126527632 +0100
26705@@ -23,22 +23,32 @@
26706 {
26707   int pim[2];
26708   int pie[2];
26709+  int pic[2], errfd;
26710+  char *x;
26711 
26712   setup_qqargs();
26713 
26714   if (pipe(pim) == -1) return -1;
26715   if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
26716-
26717+  if (pipe(pic) == -1) { close(pim[0]); close(pim[1]); close(pie[0]); close(pie[1]); return -1; }
26718+
26719   switch(qq->pid = vfork()) {
26720     case -1:
26721       close(pim[0]); close(pim[1]);
26722       close(pie[0]); close(pie[1]);
26723-      return -1;
26724+      close(pic[0]); close(pic[1]);
26725+    return -1;
26726     case 0:
26727       close(pim[1]);
26728       close(pie[1]);
26729+      close(pic[0]); /*- we want to receive data */
26730       if (fd_move(0,pim[0]) == -1) _exit(120);
26731       if (fd_move(1,pie[0]) == -1) _exit(120);
26732+      if (!(x = env_get("ERROR_FD")))
26733+        errfd = CUSTOM_ERR_FD;
26734+      else
26735+        scan_int(x, &errfd);
26736+      if (fd_move(errfd, pic[1]) == -1) _exit(120);
26737       if (chdir(auto_qmail) == -1) _exit(61);
26738       execv(*binqqargs,binqqargs);
26739       _exit(120);
26740@@ -46,6 +56,7 @@
26741 
26742   qq->fdm = pim[1]; close(pim[0]);
26743   qq->fde = pie[1]; close(pie[0]);
26744+  qq->fdc = pic[0]; close(pic[1]);
26745   substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
26746   qq->flagerr = 0;
26747   return 0;
26748@@ -93,11 +104,27 @@
26749 {
26750   int wstat;
26751   int exitcode;
26752+  char ch;
26753+  static char errstr[256];
26754+  int len = 0;
26755 
26756   qmail_put(qq,"",1);
26757   if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
26758   close(qq->fde);
26759 
26760+  substdio_fdbuf(&qq->ss, read, qq->fdc, qq->buf, sizeof(qq->buf));
26761+
26762+  while (substdio_bget(&qq->ss, &ch, 1) && len < 255)
26763+  {
26764+    errstr[len]=ch;
26765+    len++;
26766+  }
26767+
26768+  if (len > 0) errstr[len]='\0'; /* add str-term */
26769+ /* dkim +     errstr[len] = 0; */  /* add str-term */
26770+
26771+  close(qq->fdc);
26772+
26773   if (wait_pid(&wstat,qq->pid) != qq->pid)
26774     return "Zqq waitpid surprise (#4.3.0)";
26775   if (wait_crashed(wstat))
26776@@ -108,6 +135,7 @@
26777     case 115: /* compatibility */
26778     case 11: return "Denvelope address too long for qq (#5.1.3)";
26779     case 31: return "Dmail server permanently rejected message (#5.3.0)";
26780+    case 32: return "DPrivate key file does not exist (#5.3.5)";
26781     case 51: return "Zqq out of memory (#4.3.0)";
26782     case 52: return "Zqq timeout (#4.3.0)";
26783     case 53: return "Zqq write error or disk full (#4.3.0)";
26784@@ -127,10 +155,14 @@
26785     case 74: return "Zcommunication with mail server failed (#4.4.2)";
26786     case 91: /* fall through */
26787     case 81: return "Zqq internal bug (#4.3.0)";
26788+    case 82: /*- simscan exits with 82 */
26789+       case 88: /*- custom error */
26790+                if (len > 2)
26791+                       return errstr;
26792     case 120: return "Zunable to exec qq (#4.3.0)";
26793     default:
26794       if ((exitcode >= 11) && (exitcode <= 40))
26795-       return "Dqq permanent problem (#5.3.0)";
26796+        return "Dqq permanent problem (#5.3.0)";
26797       return "Zqq temporary problem (#4.3.0)";
26798   }
26799 }
26800diff -ruN ../netqmail-1.06-original/qmail.h netqmail-1.06/qmail.h
26801--- ../netqmail-1.06-original/qmail.h   1998-06-15 12:53:16.000000000 +0200
26802+++ netqmail-1.06/qmail.h       2016-11-22 21:03:57.126527632 +0100
26803@@ -3,11 +3,13 @@
26804 
26805 #include "substdio.h"
26806 
26807+#define CUSTOM_ERR_FD 4
26808 struct qmail {
26809   int flagerr;
26810   unsigned long pid;
26811   int fdm;
26812   int fde;
26813+  int fdc;
26814   substdio ss;
26815   char buf[1024];
26816 } ;
26817diff -ruN ../netqmail-1.06-original/qregex.c netqmail-1.06/qregex.c
26818--- ../netqmail-1.06-original/qregex.c  1970-01-01 01:00:00.000000000 +0100
26819+++ netqmail-1.06/qregex.c      2016-11-22 21:03:57.126527632 +0100
26820@@ -0,0 +1,239 @@
26821+/*
26822+ * $Log: qregex.c,v $
26823+ * Revision 1.13  2007-12-20 13:51:04+05:30  Cprogrammer
26824+ * removed compiler warning
26825+ *
26826+ * Revision 1.12  2005-08-23 17:41:36+05:30  Cprogrammer
26827+ * regex to be turned on only of QREGEX is defined to non-zero value
26828+ *
26829+ * Revision 1.11  2005-04-02 19:07:25+05:30  Cprogrammer
26830+ * use internal wildmat version
26831+ *
26832+ * Revision 1.10  2005-01-22 00:39:04+05:30  Cprogrammer
26833+ * added missing error handling
26834+ *
26835+ * Revision 1.9  2004-10-22 20:29:45+05:30  Cprogrammer
26836+ * added RCS id
26837+ *
26838+ * Revision 1.8  2004-09-21 23:48:18+05:30  Cprogrammer
26839+ * made matchregex() visible
26840+ * introduced dotChar (configurable dot char)
26841+ *
26842+ * Revision 1.7  2004-02-05 18:48:48+05:30  Cprogrammer
26843+ * changed curregex to static
26844+ *
26845+ * Revision 1.6  2003-12-23 23:22:53+05:30  Cprogrammer
26846+ * implicitly use wildcard if address starts with '@'
26847+ *
26848+ * Revision 1.5  2003-12-22 18:33:12+05:30  Cprogrammer
26849+ * added address_match()
26850+ *
26851+ * Revision 1.4  2003-12-22 13:21:08+05:30  Cprogrammer
26852+ * added text and pattern as part of error message
26853+ *
26854+ * Revision 1.3  2003-12-22 10:04:04+05:30  Cprogrammer
26855+ * conditional compilation of qregex
26856+ *
26857+ * Revision 1.2  2003-12-21 15:32:18+05:30  Cprogrammer
26858+ * added regerror
26859+ *
26860+ * Revision 1.1  2003-12-20 13:17:16+05:30  Cprogrammer
26861+ * Initial revision
26862+ *
26863+ * qregex (v2)
26864+ * $Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $
26865+ *
26866+ * Author  : Evan Borgstrom (evan at unixpimps dot org)
26867+ * Created : 2001/12/14 23:08:16
26868+ * Modified: $Date: 2007-12-20 13:51:04+05:30 $
26869+ * Revision: $Revision: 1.13 $
26870+ *
26871+ * Do POSIX regex matching on addresses for anti-relay / spam control.
26872+ * It logs to the maillog
26873+ * See the qregex-readme file included with this tarball.
26874+ * If you didn't get this file in a tarball please see the following URL:
26875+ *  http://www.unixpimps.org/software/qregex
26876+ *
26877+ * qregex.c is released under a BSD style copyright.
26878+ * See http://www.unixpimps.org/software/qregex/copyright.html
26879+ *
26880+ * Note: this revision follows the coding guidelines set forth by the rest of
26881+ *       the qmail code and that described at the following URL.
26882+ *       http://cr.yp.to/qmail/guarantee.html
26883+ *
26884+ */
26885+#include "case.h"
26886+#include "scan.h"
26887+#include "stralloc.h"
26888+#include "constmap.h"
26889+#include "substdio.h"
26890+#include "byte.h"
26891+#include "env.h"
26892+#include <sys/types.h>
26893+#include <regex.h>
26894+#include <unistd.h>
26895+
26896+static int      wildmat_match(stralloc *, int, struct constmap *, int, stralloc *);
26897+static int      regex_match(stralloc *, int, stralloc *);
26898+int             wildmat_internal(char *, char *);
26899+
26900+static char     sserrbuf[512];
26901+static substdio sserr = SUBSTDIO_FDBUF(write, 2, sserrbuf, sizeof(sserrbuf));
26902+static char     dotChar = '@';
26903+
26904+int
26905+address_match(stralloc *addr, int bhfok, stralloc *bhf,
26906+       struct constmap *mapbhf, int bhpok, stralloc *bhp)
26907+{
26908+       char           *ptr;
26909+       int             x = 0;
26910+
26911+       case_lowerb(addr->s, addr->len); /*- convert into lower case */
26912+       if ((ptr = env_get("QREGEX")))
26913+               scan_int(ptr, &x);
26914+       if (ptr && x)
26915+               return (regex_match(addr, bhfok, bhf));
26916+       else
26917+               return (wildmat_match(addr, bhfok, mapbhf, bhpok, bhp));
26918+}
26919+
26920+int
26921+matchregex(char *text, char *regex)
26922+{
26923+       regex_t         qreg;
26924+       char            errbuf[512];
26925+       int             retval = 0;
26926+
26927+#define REGCOMP(X,Y)    regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
26928+       /*- build the regex */
26929+       if ((retval = REGCOMP(qreg, regex)) != 0)
26930+       {
26931+               regerror(retval, &qreg, errbuf, sizeof(errbuf));
26932+               regfree(&qreg);
26933+               if (substdio_puts(&sserr, text) == -1)
26934+                       return (-retval);
26935+               if (substdio_puts(&sserr, ": ") == -1)
26936+                       return (-retval);
26937+               if (substdio_puts(&sserr, regex) == -1)
26938+                       return (-retval);
26939+               if (substdio_puts(&sserr, ": ") == -1)
26940+                       return (-retval);
26941+               if (substdio_puts(&sserr, errbuf) == -1)
26942+                       return (-retval);
26943+               if (substdio_puts(&sserr, "\n") == -1)
26944+                       return (-retval);
26945+               if (substdio_flush(&sserr) == -1)
26946+                       return (-retval);
26947+               return (-retval);
26948+       }
26949+       /*- execute the regex */
26950+#define REGEXEC(X,Y)    regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0)
26951+       retval = REGEXEC(qreg, text);
26952+       regfree(&qreg);
26953+       return (retval == REG_NOMATCH ? 0 : 1);
26954+}
26955+
26956+static int
26957+wildmat_match(stralloc * addr, int mapfile, struct constmap *ptrmap, int patfile, stralloc *wildcard)
26958+{
26959+       int             i = 0;
26960+       int             j = 0;
26961+       int             k = 0;
26962+       char            subvalue;
26963+
26964+       if (mapfile)
26965+       {
26966+               if (constmap(ptrmap, addr->s, addr->len - 1))
26967+                       return 1;
26968+               if ((j = byte_rchr(addr->s, addr->len, dotChar)) < addr->len)
26969+               {
26970+                       if (constmap(ptrmap, addr->s + j, addr->len - j - 1))
26971+                               return 1;
26972+               }
26973+       }
26974+       /*- Include control file control/xxxxpatterns and evaluate with Wildmat check */
26975+       if (patfile && wildcard)
26976+       {
26977+               i = 0;
26978+               for (j = 0; j < wildcard->len; ++j)
26979+               {
26980+                       if (!wildcard->s[j])
26981+                       {
26982+                               subvalue = wildcard->s[i] != '!';
26983+                               if (!subvalue)
26984+                                       i++;
26985+                               if ((k != subvalue) && wildmat_internal(addr->s, wildcard->s + i))
26986+                                       k = subvalue;
26987+                               i = j + 1;
26988+                       }
26989+               }
26990+               return k;
26991+       }
26992+       return (0);
26993+}
26994+
26995+static int
26996+regex_match(stralloc * addr, int mapfile, stralloc *map)
26997+{
26998+       int             i = 0;
26999+       int             j = 0;
27000+       int             k = 0;
27001+       int             negate = 0, match;
27002+       static stralloc curregex = { 0 };
27003+
27004+       match = 0;
27005+       if (mapfile)
27006+       {
27007+               while (j < map->len)
27008+               {
27009+                       i = j;
27010+                       while ((map->s[i] != '\0') && (i < map->len))
27011+                               i++;
27012+                       if (map->s[j] == '!')
27013+                       {
27014+                               negate = 1;
27015+                               j++;
27016+                       }
27017+                       if (*(map->s + j) == dotChar)
27018+                       {
27019+                               if (!stralloc_copys(&curregex, ".*"))
27020+                                       return(-1);
27021+                               if (!stralloc_catb(&curregex, map->s + j, (i - j)))
27022+                                       return(-1);
27023+                       } else
27024+                       if (!stralloc_copyb(&curregex, map->s + j, (i - j)))
27025+                               return(-1);
27026+                       if (!stralloc_0(&curregex))
27027+                               return(-1);
27028+                       if((k = matchregex(addr->s, curregex.s)) == 1)
27029+                       {
27030+                               if (negate)
27031+                                       return(0);
27032+                               match = 1;
27033+                       }
27034+                       j = i + 1;
27035+                       negate = 0;
27036+               }
27037+       }
27038+       return (match);
27039+}
27040+
27041+void
27042+setdotChar(c)
27043+       char            c;
27044+{
27045+       dotChar = c;
27046+       return;
27047+}
27048+
27049+void
27050+getversion_qregex_c()
27051+{
27052+       static char    *x = "$Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $";
27053+
27054+#ifdef INDIMAIL
27055+       x = sccsidh;
27056+#else
27057+       x++;
27058+#endif
27059+}
27060diff -ruN ../netqmail-1.06-original/qregex.h netqmail-1.06/qregex.h
27061--- ../netqmail-1.06-original/qregex.h  1970-01-01 01:00:00.000000000 +0100
27062+++ netqmail-1.06/qregex.h      2016-11-22 21:03:57.126527632 +0100
27063@@ -0,0 +1,24 @@
27064+/*
27065+ * $Log: qregex.h,v $
27066+ * Revision 1.3  2004-09-21 23:49:02+05:30  Cprogrammer
27067+ * added matchregex() and setdotChar()
27068+ *
27069+ * Revision 1.2  2003-12-22 18:35:26+05:30  Cprogrammer
27070+ * added address_match() function
27071+ *
27072+ * Revision 1.1  2003-12-20 13:17:45+05:30  Cprogrammer
27073+ * Initial revision
27074+ *
27075+ */
27076+/*
27077+ * simple header file for the matchregex prototype
27078+ */
27079+#ifndef _QREGEX_H_
27080+#define _QREGEX_H_
27081+#include "constmap.h"
27082+#include "stralloc.h"
27083+
27084+int             address_match(stralloc *, int, stralloc *, struct constmap *, int, stralloc *);
27085+int             matchregex(char *, char *);
27086+void            setdotChar(char);
27087+#endif
27088diff -ruN ../netqmail-1.06-original/readwrite.h netqmail-1.06/readwrite.h
27089--- ../netqmail-1.06-original/readwrite.h       1998-06-15 12:53:16.000000000 +0200
27090+++ netqmail-1.06/readwrite.h   2016-11-22 21:03:57.126527632 +0100
27091@@ -1,7 +1,6 @@
27092 #ifndef READWRITE_H
27093 #define READWRITE_H
27094 
27095-extern int read();
27096-extern int write();
27097+#include <unistd.h>
27098 
27099 #endif
27100diff -ruN ../netqmail-1.06-original/received.c netqmail-1.06/received.c
27101--- ../netqmail-1.06-original/received.c        1998-06-15 12:53:16.000000000 +0200
27102+++ netqmail-1.06/received.c    2016-11-22 21:03:57.127527599 +0100
27103@@ -21,6 +21,9 @@
27104   return 0;
27105 }
27106 
27107+char *relayclient;
27108+int relayclientlen;
27109+
27110 void safeput(qqt,s)
27111 struct qmail *qqt;
27112 char *s;
27113@@ -58,9 +61,12 @@
27114   qmail_puts(qqt," (");
27115   if (remoteinfo) {
27116     safeput(qqt,remoteinfo);
27117-    qmail_puts(qqt,"@");
27118   }
27119-  safeput(qqt,remoteip);
27120+  relayclient = env_get("RELAYCLIENT");
27121+  if (!relayclient) {
27122+    if (remoteinfo) { qmail_puts(qqt,"@"); }
27123+     safeput(qqt,remoteip);
27124+  }
27125   qmail_puts(qqt,")\n  by ");
27126   safeput(qqt,local);
27127   qmail_puts(qqt," with ");
27128diff -ruN ../netqmail-1.06-original/remoteinfo.c netqmail-1.06/remoteinfo.c
27129--- ../netqmail-1.06-original/remoteinfo.c      1998-06-15 12:53:16.000000000 +0200
27130+++ netqmail-1.06/remoteinfo.c  2016-11-22 21:03:57.127527599 +0100
27131@@ -44,12 +44,12 @@
27132   s = socket(AF_INET,SOCK_STREAM,0);
27133   if (s == -1) return 0;
27134 
27135-  byte_zero(&sin,sizeof(sin));
27136+/*  byte_zero(&sin,sizeof(sin));
27137   sin.sin_family = AF_INET;
27138   byte_copy(&sin.sin_addr,4,ipl);
27139   sin.sin_port = 0;
27140-  if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; }
27141-  if (timeoutconn(s,ipr,113,timeout) == -1) { close(s); return 0; }
27142+  if (bind(s,(struct sockaddr *) &sin,sizeof(sin)) == -1) { close(s); return 0; } */
27143+  if (timeoutconn(s,ipr,ipl,113,timeout) == -1) { close(s); return 0; }
27144   fcntl(s,F_SETFL,fcntl(s,F_GETFL,0) & ~O_NDELAY);
27145 
27146   len = 0;
27147diff -ruN ../netqmail-1.06-original/scan.h netqmail-1.06/scan.h
27148--- ../netqmail-1.06-original/scan.h    1998-06-15 12:53:16.000000000 +0200
27149+++ netqmail-1.06/scan.h        2018-03-31 14:54:40.767441567 +0200
27150@@ -2,6 +2,7 @@
27151 #define SCAN_H
27152 
27153 extern unsigned int scan_uint();
27154+extern unsigned int scan_int();
27155 extern unsigned int scan_xint();
27156 extern unsigned int scan_nbbint();
27157 extern unsigned int scan_ushort();
27158diff -ruN ../netqmail-1.06-original/scan_ulong.c netqmail-1.06/scan_ulong.c
27159--- ../netqmail-1.06-original/scan_ulong.c      1998-06-15 12:53:16.000000000 +0200
27160+++ netqmail-1.06/scan_ulong.c  2016-11-22 21:03:57.127527599 +0100
27161@@ -9,3 +9,43 @@
27162     { result = result * 10 + c; ++pos; }
27163   *u = result; return pos;
27164 }
27165+
27166+unsigned int
27167+scan_int(s, i)
27168+       register char  *s;
27169+       register int   *i;
27170+{
27171+       register unsigned int pos;
27172+       register int result;
27173+       register unsigned char c;
27174+       int             sign;
27175+
27176+       pos = 0;
27177+       result = 0;
27178+       sign = 1;
27179+       /*-
27180+        * determine sign of the number
27181+        */
27182+       switch (s[0])
27183+       {
27184+               case '\0':
27185+                       return 0;
27186+               case '-':
27187+                       ++pos;
27188+                       sign = -1;
27189+                       break;
27190+               case '+':
27191+                       ++pos;
27192+                       sign = 1;
27193+                       break;
27194+               default:
27195+                       break;
27196+       }
27197+       while ((c = (unsigned char)(s[pos] - '0')) < 10)
27198+       {
27199+               result = result * 10 + c;
27200+               ++pos;
27201+       }
27202+       *i = result * sign;
27203+       return pos;
27204+}
27205diff -ruN ../netqmail-1.06-original/scan_xlong.c netqmail-1.06/scan_xlong.c
27206--- ../netqmail-1.06-original/scan_xlong.c      1970-01-01 01:00:00.000000000 +0100
27207+++ netqmail-1.06/scan_xlong.c  2016-11-22 21:03:57.127527599 +0100
27208@@ -0,0 +1,47 @@
27209+/*
27210+ * $Log: scan_xlong.c,v $
27211+ * Revision 1.2  2005-06-15 22:35:48+05:30  Cprogrammer
27212+ * added RCS version information
27213+ *
27214+ * Revision 1.1  2005-06-15 22:11:59+05:30  Cprogrammer
27215+ * Initial revision
27216+ *
27217+ */
27218+#include "scan.h"
27219+
27220+static int
27221+fromhex(unsigned char c)
27222+{
27223+       if (c >= '0' && c <= '9')
27224+               return c - '0';
27225+       else
27226+       if (c >= 'A' && c <= 'F')
27227+               return c - 'A' + 10;
27228+       else
27229+       if (c >= 'a' && c <= 'f')
27230+               return c - 'a' + 10;
27231+       return -1;
27232+}
27233+
27234+unsigned int
27235+scan_xlong(char *src, unsigned long *dest)
27236+{
27237+       register const char *tmp = src;
27238+       register int    l = 0;
27239+       register unsigned char c;
27240+       while ((c = fromhex(*tmp)) < 16)
27241+       {
27242+               l = (l << 4) + c;
27243+               ++tmp;
27244+       }
27245+       *dest = l;
27246+       return tmp - src;
27247+}
27248+
27249+void
27250+getversion_scan_xlong_c()
27251+{
27252+       static char    *x = "$Id: scan_xlong.c,v 1.2 2005-06-15 22:35:48+05:30 Cprogrammer Stab mbhangui $";
27253+
27254+       x++;
27255+}
27256diff -ruN ../netqmail-1.06-original/select.h2 netqmail-1.06/select.h2
27257--- ../netqmail-1.06-original/select.h2 1998-06-15 12:53:16.000000000 +0200
27258+++ netqmail-1.06/select.h2     2016-11-22 21:03:57.127527599 +0100
27259@@ -1,6 +1,12 @@
27260 #ifndef SELECT_H
27261 #define SELECT_H
27262 
27263+#include <features.h>
27264+#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2)
27265+# include <bits/types.h>
27266+# undef __FD_SETSIZE
27267+# define __FD_SETSIZE 131077
27268+#endif
27269 #include <sys/types.h>
27270 #include <sys/time.h>
27271 #include <sys/select.h>
27272diff -ruN ../netqmail-1.06-original/socket_v4mappedprefix.c netqmail-1.06/socket_v4mappedprefix.c
27273--- ../netqmail-1.06-original/socket_v4mappedprefix.c   1970-01-01 01:00:00.000000000 +0100
27274+++ netqmail-1.06/socket_v4mappedprefix.c       2016-11-22 21:03:57.127527599 +0100
27275@@ -0,0 +1,9 @@
27276+/*
27277+ * $Log: socket_v4mappedprefix.c,v $
27278+ * Revision 1.1  2005-06-15 22:12:51+05:30  Cprogrammer
27279+ * Initial revision
27280+ *
27281+ */
27282+#ifdef IPV6
27283+unsigned char V4mappedprefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
27284+#endif
27285diff -ruN ../netqmail-1.06-original/socket_v6any.c netqmail-1.06/socket_v6any.c
27286--- ../netqmail-1.06-original/socket_v6any.c    1970-01-01 01:00:00.000000000 +0100
27287+++ netqmail-1.06/socket_v6any.c        2016-11-22 21:03:57.127527599 +0100
27288@@ -0,0 +1,9 @@
27289+/*
27290+ * $Log: socket_v6loopback.c,v $
27291+ * Revision 1.1  2005-06-15 22:13:07+05:30  Cprogrammer
27292+ * Initial revision
27293+ *
27294+ */
27295+#ifdef IPV6
27296+const unsigned char V6any[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
27297+#endif
27298diff -ruN ../netqmail-1.06-original/softwarelicense1-1.html netqmail-1.06/softwarelicense1-1.html
27299--- ../netqmail-1.06-original/softwarelicense1-1.html   1970-01-01 01:00:00.000000000 +0100
27300+++ netqmail-1.06/softwarelicense1-1.html       2016-11-22 21:03:57.128527565 +0100
27301@@ -0,0 +1,59 @@
27302+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
27303+<html>
27304+<head>
27305+<title>Yahoo! DomainKeys Public License Agreement v1.0</title>
27306+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
27307+</head>
27308+
27309+<body>
27310+Yahoo! DomainKeys Public License Agreement v1.1<br>
27311+(this &quot;Agreement&quot;)
27312+<p>Copyright (c) 2004, Yahoo! Inc.<br>
27313+ All rights reserved.</p>
27314+
27315+<br>
27316+<a href=http://domainkeys.sourceforge.net/license/softwarelicense1-1.html>(Available online)</a>
27317+<br>
27318+
27319+
27320+<p>This Agreement is between Licensor and You.  You agree to be bound by all the terms and conditions set forth below, and, subject to those terms and conditions, You may use the intellectual property described below. </p>
27321+<p>1.    LICENSE GRANT. </p>
27322+<p>1.1. Subject to the terms and conditions of this Agreement, each DomainKeys Developer hereby grants You a royalty-free, perpetual, worldwide, sublicensable, non-exclusive license to use, reproduce, modify, publicly display, publicly perform, and distribute the Licensed Code.</p>
27323+<p>1.2. Subject to the terms and conditions of this Agreement, Licensor hereby grants You a royalty-free, perpetual, worldwide, sublicensable, non-exclusive license under its rights to the Yahoo! Patent Claims to make, use, sell, offer for sale, and/or import the Licensed Code for the sole purpose of implementing a sender verification solution in connection with e-mail. </p>
27324+<p>2.    DEFINITIONS. </p>
27325+<p>2.1. &quot;Contributions&quot; means any modifications to the Licensed Code, and/or any portions thereof, that are distributed under this Agreement.  A Contribution includes, without limitation, any addition to or deletion from the contents of a file containing any Licensed Code, or any new file that contains any part of the Licensed Code.</p>
27326+<p>2.2. &quot;DomainKeys Developer(s)&quot; means Yahoo, Inc. (&quot;Yahoo!&quot;), Licensor, and/or any other individual or entity who distributes code under this Agreement.</p>
27327+<p>2.3. &quot;Licensed Code&quot; means the Original Code, any Contributions (whether made by You or any DomainKeys Developer other than You), and the combination of Original Code and any such Contributions.</p>
27328+<p>2.4. &quot;Licensor&quot; means Yahoo! or any other individual or entity that elects to use this Agreement to license intellectual property to any licensee.</p>
27329+<p>2.5. &quot;Original Code&quot; means the source code and binary code that is based on the Specifications and distributed by or on behalf of Yahoo! under this Agreement for the sole purpose of implementing a sender verification solution in connection with e-mail, including any updates or upgrades to such code made available by Yahoo!.</p>
27330+<p>2.6. &quot;Specifications&quot; means the specification having submission ID &quot;draft-delany-domainkeys-base-01.txt&quot; dated Aug 2004 published through the IETF (Internet Engineering Task Force). The Specifications may be found at the following link: <br>
27331+<a href="http://antispam.yahoo.com/domainkeys/draft-delany-domainkeys-base-02.txt">http://antispam.yahoo.com/domainkeys/draft-delany-domainkeys-base-02.txt</a></p>
27332+<p>
27333+ 2.7. &quot;Yahoo! Patent Claims&quot; shall mean those claims of all Yahoo! foreign and domestic patents and patent applications that base their priority on U.S. Provisional Patent Application Ser. Nos. 60/497,794, filed Aug. 26, 2003, or 60/553,300, filed Mar. 15, 2004, or U.S. Patent Application Ser. Nos. 10/671,319, filed Sep. 24, 2003, or 10/805,181, filed Mar. 19, 2004.  </p>
27334+<p>2.8. &quot;You&quot; or &quot;Your&quot; means an individual, company, or other legal entity exercising any rights under this Agreement.  Any individual who accepts the terms and conditions of this Agreement on behalf of a company or other legal entity represents and warrants that the individual has the authority to enter into this Agreement on behalf of the company or other legal entity. </p>
27335+<p>3.    TERMS. </p>
27336+<p>3.1. You agree not to assert against Yahoo!, any other DomainKeys Developer or any of their respective licensees under Section 3.4, a patent infringement claim based on the manufacture, use, sale, offer for sale and/or importation of any of the specific portions of a hardware or software implementation expressly required to be compliant with the Specifications for the sole purpose of implementing a sender verification solution in connection with e-mail (&quot;Licensed Code IP Claim&quot;).</p>
27337+<p>3.2. To indicate your assent to the terms and conditions of this Agreement and in order to obtain a license to use, reproduce, modify, publicly display, publicly perform, distribute, and sublicense Licensed Code, You must:</p>
27338+<p>(a) include, attach or preserve the following prominently displayed statement in the Licensed Code: &quot;This code incorporates intellectual property owned by Yahoo! and licensed pursuant to the Yahoo! DomainKeys Public License Agreement.&quot;; </p>
27339+<p>(b) preserve the copyright and other proprietary notices and disclaimers of DomainKeys Developers as they appear in the Licensed Code; and  </p>
27340+<p>(c) if the Licensed Code developed by You is distributed in source form, You must identify Yourself, in the source code of such Licensed Code, as the originator of any modifications in a manner that reasonably allows subsequent DomainKeys Developers or their licensees to identify the originator of the modifications.</p>
27341+<p>3.3. You will not use the name of Yahoo! to endorse or promote any products, services, or Licensed Code without specific prior written permission of Yahoo!.  &quot;DomainKeys&quot; is a trademark of Yahoo!.  However, You may state Your Licensed Code is &quot;DomainKeys compliant&quot;, &quot;supports DomainKeys&quot;, or is &quot;DomainKeys-enabled&quot;, without citation to Yahoo!.  You must create Your own product or service names or trademarks for Your Licensed Code and You agree not to use the term &quot;DomainKeys&quot; in or as part of a name or trademark for Your Licensed Code.</p>
27342+<p>3.4. You may choose to distribute Licensed Code or modifications under this Agreement or a different agreement, provided that:</p>
27343+<p>(a) a copy of this Agreement or the different agreement is included with each copy of the Licensed Code or modifications along with the following prominently displayed statement: &quot;By using, reproducing, modifying, publicly displaying, publicly performing, distributing, and/or sublicensing this code as permitted, you agree to the terms and conditions of the Yahoo! DomainKeys Public License Agreement or other agreement contained herein.&quot;; and</p>
27344+<p>(b) if distributed under a different agreement, such different agreement contains terms and conditions that (i) provide no fewer rights, privileges and immunities to DomainKeys Developers than the terms and conditions of this Agreement, including, without limitation, Sections 1.2, 3.1, 3.4, 3.7, 4.1, 4.2, and 4.3, except that You may alter the terms and conditions of Section 1.1 and (ii) apply such terms and conditions to the Licensed Code and/or modifications made by You.</p>
27345+<p>3.5. You acknowledge that Licensed Code may be subject to U.S. export restriction and other applicable national and international laws.  You agree to comply with all export, re-export, or import restrictions, laws, or regulations. </p>
27346+<p>3.6. Yahoo!, and only Yahoo!, may, from time to time and at its sole discretion, update or modify the terms of this Agreement.  If Yahoo! makes any such modifications, You may continue under the terms and conditions of this Agreement or agree to the updated or modified terms and conditions.  For the most recent version of this Agreement please contact Yahoo!.</p>
27347+<p>3.7. This Agreement and the rights hereunder will terminate: <br>
27348+ (a) automatically without notice from Yahoo!, if You at any time during the term of this Agreement assert any Licensed Code IP Claim against Yahoo!; </p>
27349+<p>(b) upon written notice from Yahoo!, if You at any time during the term of this Agreement assert any Licensed Code IP Claim against any DomainKeys Developer (other than Yahoo!) or any licensees of any DomainKeys Developer; or </p>
27350+<p>(c) where (a) or (b) do not apply, automatically without notice from Yahoo!, if You fail to comply with any term(s) of this Agreement and fail to cure such breach within 30 days of You becoming aware of such breach. </p>
27351+<p>3.8. This Agreement constitutes the entire agreement between the parties with respect to the subject matter hereof.  This Agreement shall be governed by and construed under the laws of the United States and the State of California without giving effect to California conflict of law provisions or to construction provisions favoring either party.  All actions arising out of or relating to this Agreement that involve Yahoo! as a party will be adjudicated exclusively by the Superior Court of the State of California for the County of Santa Clara or the United States District Court for the Northern District of California. </p>
27352+<p>3.9. In the event that any provision of this Agreement is deemed to be invalid, such invalidity shall not affect the remainder of this Agreement.</p>
27353+<p>4.    <b>LEGAL DISCLAIMERS. </b></p>
27354+<p>4.1. <b>THE YAHOO! PATENT CLAIMS, THIS AGREEMENT, LICENSED CODE, THE DOMAINKEYS TRADEMARK, AND THE SPECIFICATIONS ARE PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.</b>  You are solely responsible for determining the appropriateness of exercising any rights under this Agreement and using the Specifications, Licensed Code, and the DomainKeys trademark and assume all risks associated in connection therewith, including, but not limited to, the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. </p>
27355+<p>4.2. You expressly acknowledge and agree that no assurances are provided by DomainKeys Developers with respect to the validity of the Yahoo! Patent Claims or that the Specifications, Licensed Code, the DomainKeys trademark or any implementations related to the Specifications, Licensed Code or the DomainKeys trademark do not infringe or misappropriate the patent, trademark or other intellectual property rights of any other entity.  DomainKeys Developers disclaim any liability to You for claims brought by any other person or entity based on infringement or misappropriation of intellectual property rights or otherwise.  As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed. </p>
27356+<p>4.3. <b>DOMAINKEYS DEVELOPERS SHALL NOT HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE EXERCISE OF ANY RIGHTS UNDER THIS AGREEMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, AND EVEN IF THE REMEDIES PROVIDED FOR IN THIS AGREEMENT FAIL OF THEIR ESSENTIAL PURPOSE.</b></p>
27357+
27358+</body>
27359+</html>
27360+
27361diff -ruN ../netqmail-1.06-original/spawn-filter.9 netqmail-1.06/spawn-filter.9
27362--- ../netqmail-1.06-original/spawn-filter.9    1970-01-01 01:00:00.000000000 +0100
27363+++ netqmail-1.06/spawn-filter.9        2016-11-22 21:03:57.128527565 +0100
27364@@ -0,0 +1,103 @@
27365+.TH spawn-filter 8
27366+.SH NAME
27367+spawn-filter \- Helper for running filters for qmail-local and qmail-remote
27368+.SH SYNOPSIS
27369+.B spawn-filter args
27370+.SH DESCRIPTION
27371+.B spawn-filter
27372+is a utility to help qmail run any filter during local or remote delivery. It
27373+can run any filter which expects to read mess on fd 0 and writes back the message on fd 1.
27374+The filter can be turned on individually for local and remote mails by defining
27375+.B QMAILLOCAL
27376+and
27377+.B QMAILREMOTE
27378+environment variables respectively in
27379+.B qmail-send
27380+supervise or rc script. If spawn-filter is invoked as qmail-local, it executes the
27381+original
27382+.B qmail-local
27383+after runing the mail through the filter. If spawn-filter is invoked as qmail-remote, it
27384+executes the original
27385+.B qmail-remote
27386+after running the mail through the filter. Hence QMAILLOCAL should be set as QMAILHOME/bin/spawn-filter
27387+for filtering local mails and QMAILREMOTE as QMAILHOME/bin/spawn-filter for filtering
27388+remote mails.
27389+
27390+Filters can be run by setting the environment variable
27391+.B FILTERARGS
27392+or by using a control file
27393+.BR filterargs.
27394+The environment variable overrides the control file.
27395+.B spawn-filter
27396+uses /bin/sh to run the filter (with arguments) specified by the FILTERARGS environment variable or the control file
27397+.BR filterargs .
27398+The environment variable FILTERARGS apply to both local and remote mails. For individual domain level control,
27399+it is best to set using the control file filterargs.
27400+
27401+.TP 5
27402+.I filterargs
27403+The format of this file is of the form
27404+.B domain:args
27405+for both local and remote mails.
27406+.B domain:remote:args
27407+for remote mails and
27408+.B domain:local:args
27409+for local mails.
27410+
27411+.EX
27412+indimail.org:remote:QMAILHOME/bin/dk-filter
27413+.EE
27414+
27415+.TP 0
27416+The sequence in which the filter program is run is given below
27417+
27418+.TP 5
27419+1. create two pipes and fork
27420+.TP 5
27421+2. dup write end of the first pipe to descriptor 1, dup write end of the second pipe to descriptor 2 in the child and exec the filter program
27422+.TP 5
27423+3. dup read end of the pipe to descriptor 0 in parent and exec qmail-local for local mails and qmail-remote for remote mails.
27424+.TP 5
27425+4. Wait for filter to exit and read read end of second pipe for any error messages.
27426+.TP 5
27427+5. Report success or failure
27428+.TP 0
27429+
27430+This gives the ability for the any filter program to read the mail message from descriptor 0 before
27431+passing it to qmail-local/qmail-remote through the pipe.
27432+
27433+.B spawn-filter
27434+will attempt to make the descriptor 0 seekable if the environment variable MAKE_SEEKABLE
27435+is defined. This may be necessary for certain filter programs which could do lseek().
27436+
27437+.B spawn-filter
27438+sets the environment variable
27439+.B DOMAIN
27440+to the recipient domain. This can be conveniently used in programs/scripts which get invoked by
27441+setting
27442+.B FILTERARGS
27443+environment variable or by rules in the control file
27444+.BR filterargs .
27445+
27446+.SH "EXIT CODES"
27447+.B spawn-filter
27448+exits 111 for any error or if it is not able to exec
27449+QMAILHOME/bin/qmail-local (for local mails) or
27450+QMAILHOME/bin/qmail-remote (for remote mails).
27451+
27452+.SH "SEE ALSO"
27453+qmail-lspawn(8),
27454+qmail-rspawn(8),
27455+qmail-local(8),
27456+qmail-remote(8),
27457+qmail-smtpd(8),
27458+qmail-control(5),
27459+qmail-queue(8)
27460+
27461+.SH "AUTHORS"
27462+
27463+Manvendra Bhangui.
27464+.SH PROBLEMS
27465+Problems with
27466+.B spawn-filter
27467+should be forwarded to "Manvendra Bhangui" <mbhangui@gmail.com>
27468diff -ruN ../netqmail-1.06-original/spawn-filter.c netqmail-1.06/spawn-filter.c
27469--- ../netqmail-1.06-original/spawn-filter.c    1970-01-01 01:00:00.000000000 +0100
27470+++ netqmail-1.06/spawn-filter.c        2016-11-22 21:03:57.128527565 +0100
27471@@ -0,0 +1,546 @@
27472+/*
27473+ * netqmail-version without spam filter
27474+ *
27475+ * $Log: spawn-filter.c,v $
27476+ * Revision 1.41  2009-04-03 11:42:48+05:30  Cprogrammer
27477+ * create pipe for error messages
27478+ *
27479+ * Revision 1.40  2009-04-02 15:17:54+05:30  Cprogrammer
27480+ * unset QMAILLOCAL in qmail-remote and unset QMAILREMOTE in qmail-local
27481+ *
27482+ * Revision 1.39  2008-06-12 08:40:55+05:30  Cprogrammer
27483+ * added rulesfile argument
27484+ *
27485+ * Revision 1.38  2008-05-25 17:16:43+05:30  Cprogrammer
27486+ * made message more readable by adding a blank space
27487+ *
27488+ * Revision 1.37  2007-12-20 13:51:54+05:30  Cprogrammer
27489+ * avoid loops with FILTERARGS, SPAMFILTERARGS
27490+ * removed compiler warning
27491+ *
27492+ * Revision 1.36  2006-06-07 14:11:28+05:30  Cprogrammer
27493+ * added SPAMEXT, SPAMHOST, SPAMSENDER, QQEH environment variable
27494+ * unset FILTERARGS before calling filters
27495+ *
27496+ * Revision 1.35  2006-01-22 10:14:45+05:30  Cprogrammer
27497+ * BUG fix for spam mails wrongly getting blackholed
27498+ *
27499+ * Revision 1.34  2005-08-23 17:36:48+05:30  Cprogrammer
27500+ * gcc 4 compliance
27501+ * delete sender in spam notification
27502+ *
27503+ * Revision 1.33  2005-04-02 19:07:47+05:30  Cprogrammer
27504+ * use internal wildmat version
27505+ *
27506+ * Revision 1.32  2004-11-22 19:50:53+05:30  Cprogrammer
27507+ * include regex.h after sys/types.h to avoid compilation prob on RH 7.3
27508+ *
27509+ * Revision 1.31  2004-10-22 20:30:35+05:30  Cprogrammer
27510+ * added RCS id
27511+ *
27512+ * Revision 1.30  2004-10-21 21:56:21+05:30  Cprogrammer
27513+ * change for two additional arguments to strerr_die()
27514+ *
27515+ * Revision 1.29  2004-10-11 14:06:14+05:30  Cprogrammer
27516+ * use control_readulong instead of control_readint
27517+ *
27518+ * Revision 1.28  2004-09-22 23:14:20+05:30  Cprogrammer
27519+ * replaced atoi() with scan_int()
27520+ *
27521+ * Revision 1.27  2004-09-08 10:54:49+05:30  Cprogrammer
27522+ * incorrect exit code in report() function for remote
27523+ * mails. Caused qmail-rspawn to report "Unable to run qmail-remote"
27524+ *
27525+ * Revision 1.26  2004-07-17 21:23:31+05:30  Cprogrammer
27526+ * change qqeh code in qmail-remote
27527+ *
27528+ * Revision 1.25  2004-07-15 23:40:46+05:30  Cprogrammer
27529+ * fixed compilation warning
27530+ *
27531+ * Revision 1.24  2004-07-02 16:15:25+05:30  Cprogrammer
27532+ * override control files rejectspam, spamredirect by
27533+ * environment variables REJECTSPAM and SPAMREDIRECT
27534+ * allow patterns in domain specification in the control files
27535+ * spamfilterargs, filterargs, rejectspam and spamredirect
27536+ *
27537+ * Revision 1.23  2004-06-03 22:58:34+05:30  Cprogrammer
27538+ * fixed compilation problem without indimail
27539+ *
27540+ * Revision 1.22  2004-05-23 22:18:17+05:30  Cprogrammer
27541+ * added envrules filename as argument
27542+ *
27543+ * Revision 1.21  2004-05-19 23:15:07+05:30  Cprogrammer
27544+ * added comments
27545+ *
27546+ * Revision 1.20  2004-05-12 22:37:47+05:30  Cprogrammer
27547+ * added check DATALIMIT check
27548+ *
27549+ * Revision 1.19  2004-05-03 22:17:36+05:30  Cprogrammer
27550+ * use QUEUE_BASE instead of auto_qmail
27551+ *
27552+ * Revision 1.18  2004-02-13 14:51:24+05:30  Cprogrammer
27553+ * added envrules
27554+ *
27555+ * Revision 1.17  2004-01-20 06:56:56+05:30  Cprogrammer
27556+ * unset FILTERARGS for notifications
27557+ *
27558+ * Revision 1.16  2004-01-20 01:52:08+05:30  Cprogrammer
27559+ * report string length corrected
27560+ *
27561+ * Revision 1.15  2004-01-10 09:44:36+05:30  Cprogrammer
27562+ * added comment for exit codes of bogofilter
27563+ *
27564+ * Revision 1.14  2004-01-08 00:32:49+05:30  Cprogrammer
27565+ * use TMPDIR environment variable for temporary directory
27566+ * send spam reports to central spam logger
27567+ *
27568+ * Revision 1.13  2003-12-30 00:44:42+05:30  Cprogrammer
27569+ * set argv[0] from spamfilterprog
27570+ *
27571+ * Revision 1.12  2003-12-22 18:34:25+05:30  Cprogrammer
27572+ * replaced spfcheck() with address_match()
27573+ *
27574+ * Revision 1.11  2003-12-20 01:35:06+05:30  Cprogrammer
27575+ * added wait_pid to prevent zombies
27576+ *
27577+ * Revision 1.10  2003-12-17 23:33:39+05:30  Cprogrammer
27578+ * improved logic for getting remote/local tokens
27579+ *
27580+ * Revision 1.9  2003-12-16 10:38:24+05:30  Cprogrammer
27581+ * fixed incorrect address being returned if filterargs contained local: or
27582+ * remote: directives
27583+ *
27584+ * Revision 1.8  2003-12-15 20:46:19+05:30  Cprogrammer
27585+ * added case 100 to bounce mail
27586+ *
27587+ * Revision 1.7  2003-12-15 13:51:44+05:30  Cprogrammer
27588+ * code to run additional filters using /bin/sh
27589+ *
27590+ * Revision 1.6  2003-12-14 11:36:18+05:30  Cprogrammer
27591+ * added option to blackhole spammers
27592+ *
27593+ * Revision 1.5  2003-12-13 21:08:46+05:30  Cprogrammer
27594+ * extensive rewrite
27595+ * common report() function for local/remote mails to report errors
27596+ *
27597+ * Revision 1.4  2003-12-12 20:20:55+05:30  Cprogrammer
27598+ * use -a option to prevent using header addresses
27599+ *
27600+ * Revision 1.3  2003-12-09 23:37:16+05:30  Cprogrammer
27601+ * change for spawn-filter to be called as qmail-local or qmail-remote
27602+ *
27603+ * Revision 1.2  2003-12-08 23:48:23+05:30  Cprogrammer
27604+ * new function getDomainToken() to retrieve domain specific values
27605+ * read rejectspam and spamredirect only if SPAMEXITCODE is set
27606+ *
27607+ * Revision 1.1  2003-12-07 13:02:00+05:30  Cprogrammer
27608+ * Initial revision
27609+ *
27610+ */
27611+#include "fmt.h"
27612+#include "str.h"
27613+#include "strerr.h"
27614+#include "env.h"
27615+#include "substdio.h"
27616+#include "subfd.h"
27617+#include "stralloc.h"
27618+#include "error.h"
27619+#include "control.h"
27620+#include "wait.h"
27621+#include "qregex.h"
27622+#include "auto_qmail.h"
27623+#include <regex.h>
27624+#include <unistd.h>
27625+#include <sys/stat.h>
27626+#include <sys/types.h>
27627+#include <fcntl.h>
27628+
27629+#define REGCOMP(X,Y)    regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
27630+#define REGEXEC(X,Y)    regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0)
27631+
27632+static int      mkTempFile(int);
27633+static void     report(int, char *, char *, char *, char *, char *, char *);
27634+char           *getDomainToken(char *, stralloc *);
27635+static int      run_mailfilter(char *, char *, char **);
27636+int             wildmat_internal(char *, char *);
27637+static int      check_size(char *);
27638+
27639+static int      remotE;
27640+stralloc        sender = { 0 };
27641+stralloc        recipient = { 0 };
27642+
27643+static int
27644+check_size(char *size)
27645+{
27646+       char           *x;
27647+       unsigned long   databytes = -1, msgsize;
27648+
27649+       if (!(x = env_get("DATABYTES")))
27650+       {
27651+               if (control_readulong(&databytes, "databytes") == -1)
27652+                       report(111, "spawn-filter: Unable to read databytes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27653+       } else
27654+               scan_ulong(x, &databytes);
27655+       if (databytes == -1)
27656+               return (0);
27657+       scan_ulong(size, &msgsize);
27658+       if (msgsize > databytes)
27659+               return(1);
27660+       else
27661+               return(0);
27662+}
27663+
27664+static void
27665+report(int errCode, char *s1, char *s2, char *s3, char *s4, char *s5, char *s6)
27666+{
27667+       if (!remotE) /*- strerr_die does not return */
27668+               strerr_die(errCode, s1, s2, s3, s4, s5, s6, 0, 0, (struct strerr *) 0);
27669+       /*- h - hard, s - soft */
27670+       if (substdio_put(subfdoutsmall, errCode == 111 ? "s" : "h", 1) == -1)
27671+               _exit(111);
27672+       if (s1 && substdio_puts(subfdoutsmall, s1) == -1)
27673+               _exit(111);
27674+       if (s2 && substdio_puts(subfdoutsmall, s2) == -1)
27675+               _exit(111);
27676+       if (s3 && substdio_puts(subfdoutsmall, s3) == -1)
27677+               _exit(111);
27678+       if (s4 && substdio_puts(subfdoutsmall, s4) == -1)
27679+               _exit(111);
27680+       if (s5 && substdio_puts(subfdoutsmall, s5) == -1)
27681+               _exit(111);
27682+       if (s6 && substdio_puts(subfdoutsmall, s6) == -1)
27683+               _exit(111);
27684+       if (substdio_put(subfdoutsmall, "\0", 1) == -1)
27685+               _exit(111);
27686+       if (substdio_puts(subfdoutsmall,
27687+               errCode == 111 ?  "Zspawn-filter said: Message deferred" : "DGiving up on spawn-filter\n") == -1)
27688+               _exit(111);
27689+       if (substdio_put(subfdoutsmall, "\0", 1) == -1)
27690+               _exit(111);
27691+       substdio_flush(subfdoutsmall);
27692+       /*- For qmail-rspawn to stop complaining unable to run qmail-remote */
27693+       _exit(0);
27694+}
27695+
27696+void
27697+set_environ(char *host, char *sender, char *recipient)
27698+{
27699+       if (!env_put2("DOMAIN", host))
27700+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27701+       if (!env_put2("_SENDER", sender))
27702+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27703+       if (!env_put2("_RECIPIENT", recipient))
27704+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27705+       return;
27706+}
27707+
27708+static int
27709+run_mailfilter(char *domain, char *mailprog, char **argv)
27710+{
27711+       char            strnum[FMT_ULONG];
27712+       pid_t           filt_pid;
27713+       int             pipefd[2], pipefe[2];
27714+       int             wstat, filt_exitcode, len = 0;
27715+       char           *filterargs;
27716+       static stralloc filterdefs = { 0 };
27717+       static char     errstr[1024];
27718+       char            inbuf[1024];
27719+       char            ch;
27720+       static substdio errbuf;
27721+
27722+       if (!(filterargs = env_get("FILTERARGS")))
27723+       {
27724+               if (control_readfile(&filterdefs, "control/filterargs", 0) == -1)
27725+                       report(111, "spawn-filter: Unable to read filterargs: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27726+               filterargs = getDomainToken(domain, &filterdefs);
27727+       }
27728+       if (!filterargs)
27729+       {
27730+               execv(mailprog, argv);
27731+               report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
27732+               _exit(111); /*- To make compiler happy */
27733+       }
27734+       if (pipe(pipefd) == -1)
27735+               report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27736+       if (pipe(pipefe) == -1)
27737+               report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27738+       switch ((filt_pid = fork()))
27739+       {
27740+       case -1:
27741+               report(111, "spawn-filter: Trouble creating child filter: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27742+       case 0: /*- Filter Program */
27743+               set_environ(domain, sender.s, recipient.s);
27744+               /*- Mail content read from fd 0 */
27745+               if (mkTempFile(0))
27746+                       report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27747+               /*- stdout will go here */
27748+               if (dup2(pipefd[1], 1) == -1 || close(pipefd[0]) == -1)
27749+                       report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27750+               if (pipefd[1] != 1)
27751+                       close(pipefd[1]);
27752+               /*- stderr will go here */
27753+               if (dup2(pipefe[1], 2) == -1 || close(pipefe[0]) == -1)
27754+                       report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27755+               if (pipefe[1] != 2)
27756+                       close(pipefe[1]);
27757+               /*- Avoid loop if program(s) defined by FILTERARGS call qmail-inject, etc */
27758+               if (!env_unset("FILTERARGS") || !env_unset("SPAMFILTER"))
27759+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27760+               execl("/bin/sh", "IndiMailfilter", "-c", filterargs, (char *) 0);
27761+               report(111, "spawn-filter: could not exec /bin/sh: ",  filterargs, ": ", error_str(errno), ". (#4.3.0)", 0);
27762+       default:
27763+               close(pipefe[1]);
27764+               close(pipefd[1]);
27765+               if (dup2(pipefd[0], 0))
27766+               {
27767+                       close(pipefd[0]);
27768+                       close(pipefe[0]);
27769+                       wait_pid(&wstat, filt_pid);
27770+                       report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27771+               }
27772+               if (pipefd[0] != 0)
27773+                       close(pipefd[0]);
27774+               if (mkTempFile(0))
27775+               {
27776+                       close(0);
27777+                       close(pipefe[0]);
27778+                       wait_pid(&wstat, filt_pid);
27779+                       report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27780+               }
27781+               break;
27782+       }
27783+       /*- Process message if exit code is 0, bounce if 100 */
27784+       if (wait_pid(&wstat, filt_pid) != filt_pid)
27785+       {
27786+               close(0);
27787+               close(pipefe[0]);
27788+               report(111, "spawn-filter: waitpid surprise: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27789+       }
27790+       if (wait_crashed(wstat))
27791+       {
27792+               close(0);
27793+               close(pipefe[0]);
27794+               report(111, "spawn-filter: filter crashed: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27795+       }
27796+       switch (filt_exitcode = wait_exitcode(wstat))
27797+       {
27798+       case 0:
27799+               execv(mailprog, argv);
27800+               report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
27801+       case 100:
27802+               report(100, "Mail Rejected (#5.7.1)", 0, 0, 0, 0, 0);
27803+       default:
27804+               substdio_fdbuf(&errbuf, read, pipefe[0], inbuf, sizeof(inbuf));
27805+               for (len = 0; substdio_bget(&errbuf, &ch, 1) && len < (sizeof(errstr) - 1); len++)
27806+                       errstr[len] = ch;
27807+               errstr[len] = 0;
27808+               strnum[fmt_ulong(strnum, filt_exitcode)] = 0;
27809+               report(111, filterargs, ": (spawn-filter) exit code: ", strnum, *errstr ? ": " : 0, *errstr ? errstr : 0, ". (#4.3.0)");
27810+       }
27811+       /*- Not reached */
27812+       return(111);
27813+}
27814+
27815+char           *
27816+getDomainToken(char *domain, stralloc *sa)
27817+{
27818+       regex_t         qreg;
27819+       int             len, n, retval;
27820+       char           *ptr, *p;
27821+       char            errbuf[512];
27822+
27823+       for (len = 0, ptr = sa->s;len < sa->len;)
27824+       {
27825+               len += ((n = str_len(ptr)) + 1);
27826+               for (p = ptr;*p && *p != ':';p++);
27827+               if (*p == ':')
27828+               {
27829+                       *p = 0;
27830+                       /*- build the regex */
27831+                       if ((retval = str_diff(ptr, domain)))
27832+                       {
27833+                               if (env_get("QREGEX"))
27834+                               {
27835+                                       if ((retval = REGCOMP(qreg, ptr)) != 0)
27836+                                       {
27837+                                               regerror(retval, &qreg, errbuf, sizeof(errbuf));
27838+                                               regfree(&qreg);
27839+                                               report(111, "spawn-filter: ", ptr, ": ", errbuf, ". (#4.3.0)", 0);
27840+                                       }
27841+                                       retval = REGEXEC(qreg, domain);
27842+                                       regfree(&qreg);
27843+                               } else
27844+                                       retval = !wildmat_internal(domain, ptr);
27845+                       }
27846+                       *p = ':';
27847+                       if (!retval) /*- match occurred for domain or wildcard */
27848+                       {
27849+                               /* check for local/remote directives */
27850+                               if (remotE)
27851+                               {
27852+                                       if (!str_diffn(p + 1, "remote:", 7))
27853+                                               return (p + 8);
27854+                                       if (!str_diffn(p + 1, "local:", 6))
27855+                                       {
27856+                                               ptr = sa->s + len;
27857+                                               continue; /*- skip local directives for remote mails */
27858+                                       }
27859+                               } else
27860+                               {
27861+                                       if (!str_diffn(p + 1, "local:", 6))
27862+                                               return (p + 7);
27863+                                       if (!str_diffn(p + 1, "remote:", 7))
27864+                                       {
27865+                                               ptr = sa->s + len;
27866+                                               continue; /*- skip remote directives for remote mails */
27867+                                       }
27868+                               }
27869+                               return (p + 1);
27870+                       }
27871+               }
27872+               ptr = sa->s + len;
27873+       }
27874+       return ((char *) 0);
27875+}
27876+
27877+int
27878+mkTempFile(int seekfd)
27879+{
27880+       char            inbuf[2048], outbuf[2048], strnum[FMT_ULONG];
27881+       char           *tmpdir;
27882+       static stralloc tmpFile = {0};
27883+       struct substdio _ssin;
27884+       struct substdio _ssout;
27885+       int             fd;
27886+
27887+       if (lseek(seekfd, 0, SEEK_SET) == 0)
27888+               return (0);
27889+       if (errno == EBADF)
27890+       {
27891+               strnum[fmt_ulong(strnum, seekfd)] = 0;
27892+               report(111, "spawn-filter: fd ", strnum, ": ", error_str(errno), ". (#4.3.0)", 0);
27893+       }
27894+       if (!(tmpdir = env_get("TMPDIR")))
27895+               tmpdir = "/tmp";
27896+       if (!stralloc_copys(&tmpFile, tmpdir))
27897+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27898+       if (!stralloc_cats(&tmpFile, "/qmailFilterXXX"))
27899+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27900+       if (!stralloc_catb(&tmpFile, strnum, fmt_ulong(strnum, (unsigned long) getpid())))
27901+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27902+       if (!stralloc_0(&tmpFile))
27903+               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27904+       if ((fd = open(tmpFile.s, O_RDWR | O_EXCL | O_CREAT, 0600)) == -1)
27905+               report(111, "spawn-filter: ", tmpFile.s, ": ", error_str(errno), ". (#4.3.0)", 0);
27906+       unlink(tmpFile.s);
27907+       substdio_fdbuf(&_ssout, write, fd, outbuf, sizeof(outbuf));
27908+       substdio_fdbuf(&_ssin, read, seekfd, inbuf, sizeof(inbuf));
27909+       switch (substdio_copy(&_ssout, &_ssin))
27910+       {
27911+       case -2: /*- read error */
27912+               report(111, "spawn-filter: read error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27913+       case -3: /*- write error */
27914+               report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27915+       }
27916+       if (substdio_flush(&_ssout) == -1)
27917+               report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27918+       if (dup2(fd, seekfd) == -1)
27919+               report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27920+       if (lseek(seekfd, 0, SEEK_SET) != 0)
27921+               report(111, "spawn-filter: lseek: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27922+       return (0);
27923+}
27924+
27925+int
27926+main(int argc, char **argv)
27927+{
27928+       char           *ptr, *mailprog, *domain, *errStr = 0, *size = "0", *qqeh, *ext;
27929+       char            sizebuf[FMT_ULONG];
27930+       struct stat     statbuf;
27931+       int             len;
27932+
27933+       len = str_len(argv[0]);
27934+       for (ptr = argv[0] + len;*ptr != '/' && ptr != argv[0];ptr--);
27935+       if (*ptr && *ptr == '/')
27936+               ptr++;
27937+       ptr += 6;
27938+       if (*ptr == 'l') /*- qmail-local Filter */
27939+       {
27940+               mailprog = "bin/qmail-local";
27941+               domain = argv[7];
27942+               ext = argv[6];
27943+               qqeh = argv[10];
27944+               remotE = 0;
27945+               if (!env_unset("QMAILREMOTE"))
27946+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27947+               if (!fstat(0, &statbuf))
27948+               {
27949+                       sizebuf[fmt_ulong(sizebuf, statbuf.st_size)] = 0;
27950+                       size = sizebuf;
27951+               } else
27952+                       size = "0";
27953+               /*- sender */
27954+               if (!stralloc_copys(&sender, argv[8]))
27955+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27956+               if (!stralloc_0(&sender))
27957+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27958+               if (!env_unset("QMAILREMOTE"))
27959+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27960+               /*- recipient */
27961+               if (*ext) /*- EXT */
27962+               {
27963+                       if (!stralloc_copys(&recipient, ext))
27964+                               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27965+               } else /*- user */
27966+                       if (!stralloc_copys(&recipient, argv[2]))
27967+                               report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27968+               if (!stralloc_cats(&recipient, "@"))
27969+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27970+               if (!stralloc_cats(&recipient, domain))
27971+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27972+               if (!stralloc_0(&recipient))
27973+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27974+       } else
27975+       if (*ptr == 'r') /*- qmail-remote Filter */
27976+       {
27977+               mailprog = "bin/qmail-remote";
27978+               domain = argv[1];
27979+               ext = argv[5];
27980+               qqeh = argv[3];
27981+               size = argv[4];
27982+               remotE = 1;
27983+               if (!env_unset("QMAILLOCAL"))
27984+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27985+               /*- sender */
27986+               if (!stralloc_copys(&sender, argv[2]))
27987+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27988+               if (!stralloc_0(&sender))
27989+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27990+               /*- recipient */
27991+               if (!stralloc_copys(&recipient, argv[5]))
27992+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27993+               if (!stralloc_0(&recipient))
27994+                       report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
27995+       } else
27996+       {
27997+               report(111, "spawn-filter: Incorrect usage. ", argv[0], " (#4.3.0)", 0, 0, 0);
27998+               _exit(111);
27999+       }
28000+       if (chdir(auto_qmail) == -1)
28001+               report(111, "spawn-filter: Unable to switch to ", auto_qmail, ": ", error_str(errno), ". (#4.3.0)", 0);
28002+       /*- DATABYTES Check */
28003+       if (check_size(size))
28004+               report(100, "sorry, that message size exceeds my databytes limit (#5.3.4)", 0, 0, 0, 0, 0);
28005+       run_mailfilter(domain, mailprog, argv);
28006+       report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
28007+       /*- Not reached */
28008+       return(0);
28009+}
28010+
28011+void
28012+getversion_qmail_spawn_filter_c()
28013+{
28014+       static char    *x = "$Id: spawn-filter.c,v 1.41 2009-04-03 11:42:48+05:30 Cprogrammer Stab mbhangui $";
28015+
28016+       x++;
28017+}
28018diff -ruN ../netqmail-1.06-original/spawn.c netqmail-1.06/spawn.c
28019--- ../netqmail-1.06-original/spawn.c   2007-11-30 21:22:54.000000000 +0100
28020+++ netqmail-1.06/spawn.c       2016-11-22 21:03:57.128527565 +0100
28021@@ -1,4 +1,4 @@
28022-#include <sys/types.h>
28023+#include "select.h"
28024 #include <sys/stat.h>
28025 #include "sig.h"
28026 #include "wait.h"
28027@@ -7,7 +7,6 @@
28028 #include "str.h"
28029 #include "alloc.h"
28030 #include "stralloc.h"
28031-#include "select.h"
28032 #include "exit.h"
28033 #include "coe.h"
28034 #include "open.h"
28035@@ -64,7 +63,7 @@
28036 int flagreading = 1;
28037 char outbuf[1024]; substdio ssout;
28038 
28039-int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */
28040+int stage = 0; /* reading 0:delnum 1:delnum2 2:messid 3:sender 4:recip */
28041 int flagabort = 0; /* if 1, everything except delnum is garbage */
28042 int delnum;
28043 stralloc messid = {0};
28044@@ -74,6 +73,7 @@
28045 void err(s) char *s;
28046 {
28047  char ch; ch = delnum; substdio_put(&ssout,&ch,1);
28048+ ch = delnum >> 8; substdio_put(&ssout,&ch,1);
28049  substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1);
28050 }
28051 
28052@@ -156,16 +156,19 @@
28053     {
28054      case 0:
28055        delnum = (unsigned int) (unsigned char) ch;
28056-       messid.len = 0; stage = 1; break;
28057+       stage = 1; break;
28058      case 1:
28059+       delnum += (unsigned int) ((unsigned int) ch) << 8;
28060+       messid.len = 0; stage = 2; break;
28061+     case 2:
28062        if (!stralloc_append(&messid,&ch)) flagabort = 1;
28063        if (ch) break;
28064-       sender.len = 0; stage = 2; break;
28065-     case 2:
28066+       sender.len = 0; stage = 3; break;
28067+     case 3:
28068        if (!stralloc_append(&sender,&ch)) flagabort = 1;
28069        if (ch) break;
28070-       recip.len = 0; stage = 3; break;
28071-     case 3:
28072+       recip.len = 0; stage = 4; break;
28073+     case 4:
28074        if (!stralloc_append(&recip,&ch)) flagabort = 1;
28075        if (ch) break;
28076        docmd();
28077@@ -202,7 +205,8 @@
28078 
28079  initialize(argc,argv);
28080 
28081- ch = auto_spawn; substdio_putflush(&ssout,&ch,1);
28082+ ch = auto_spawn; substdio_put(&ssout,&ch,1);
28083+ ch = auto_spawn >> 8; substdio_putflush(&ssout,&ch,1);
28084 
28085  for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; }
28086 
28087@@ -237,7 +241,8 @@
28088           continue; /* read error on a readable pipe? be serious */
28089         if (r == 0)
28090          {
28091-           ch = i; substdio_put(&ssout,&ch,1);
28092+           char ch; ch = i; substdio_put(&ssout,&ch,1);
28093+           ch = i >> 8; substdio_put(&ssout,&ch,1);
28094           report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len);
28095           substdio_put(&ssout,"",1);
28096           substdio_flush(&ssout);
28097diff -ruN ../netqmail-1.06-original/spf.c netqmail-1.06/spf.c
28098--- ../netqmail-1.06-original/spf.c     1970-01-01 01:00:00.000000000 +0100
28099+++ netqmail-1.06/spf.c 2016-11-22 21:03:57.129527532 +0100
28100@@ -0,0 +1,877 @@
28101+#include "stralloc.h"
28102+#include "strsalloc.h"
28103+#include "alloc.h"
28104+#include "ip.h"
28105+#include "ipalloc.h"
28106+#include "ipme.h"
28107+#include "str.h"
28108+#include "fmt.h"
28109+#include "scan.h"
28110+#include "byte.h"
28111+#include "now.h"
28112+#include "dns.h"
28113+#include "case.h"
28114+#include "spf.h"
28115+
28116+#define SPF_EXT    -1
28117+#define SPF_SYNTAX -2
28118+
28119+#define WSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n')
28120+#define NXTOK(b, p, a) do { (b) = (p); \
28121+          while((p) < (a)->len && !WSPACE((a)->s[(p)])) ++(p); \
28122+          while((p) < (a)->len && WSPACE((a)->s[(p)])) (a)->s[(p)++] = 0; \
28123+        } while(0)
28124+
28125+/* this table and macro came from wget more or less */
28126+/* and was in turn stolen by me from libspf as is :) */
28127+const static unsigned char urlchr_table[256] =
28128+{
28129+  1,  1,  1,  1,   1,  1,  1,  1,   /* NUL SOH STX ETX  EOT ENQ ACK BEL */
28130+  1,  1,  1,  1,   1,  1,  1,  1,   /* BS  HT  LF  VT   FF  CR  SO  SI  */
28131+  1,  1,  1,  1,   1,  1,  1,  1,   /* DLE DC1 DC2 DC3  DC4 NAK SYN ETB */
28132+  1,  1,  1,  1,   1,  1,  1,  1,   /* CAN EM  SUB ESC  FS  GS  RS  US  */
28133+  1,  0,  1,  1,   0,  1,  1,  0,   /* SP  !   "   #    $   %   &   '   */
28134+  0,  0,  0,  1,   0,  0,  0,  1,   /* (   )   *   +    ,   -   .   /   */
28135+  0,  0,  0,  0,   0,  0,  0,  0,   /* 0   1   2   3    4   5   6   7   */
28136+  0,  0,  1,  1,   1,  1,  1,  1,   /* 8   9   :   ;    <   =   >   ?   */
28137+  1,  0,  0,  0,   0,  0,  0,  0,   /* @   A   B   C    D   E   F   G   */
28138+  0,  0,  0,  0,   0,  0,  0,  0,   /* H   I   J   K    L   M   N   O   */
28139+  0,  0,  0,  0,   0,  0,  0,  0,   /* P   Q   R   S    T   U   V   W   */
28140+  0,  0,  0,  1,   1,  1,  1,  0,   /* X   Y   Z   [    \   ]   ^   _   */
28141+  1,  0,  0,  0,   0,  0,  0,  0,   /* `   a   b   c    d   e   f   g   */
28142+  0,  0,  0,  0,   0,  0,  0,  0,   /* h   i   j   k    l   m   n   o   */
28143+  0,  0,  0,  0,   0,  0,  0,  0,   /* p   q   r   s    t   u   v   w   */
28144+  0,  0,  0,  1,   1,  1,  1,  1,   /* x   y   z   {    |   }   ~   DEL */
28145+
28146+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28147+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28148+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28149+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28150+
28151+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28152+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28153+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28154+  1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,   1,  1,  1,  1,
28155+};
28156+
28157+
28158+extern stralloc addr;
28159+extern stralloc helohost;
28160+extern char *local;
28161+
28162+extern stralloc spflocal;
28163+extern stralloc spfguess;
28164+extern stralloc spfexp;
28165+
28166+static stralloc sender_fqdn = {0};
28167+static stralloc explanation = {0};
28168+static stralloc expdomain = {0};
28169+static stralloc errormsg = {0};
28170+static char *received;
28171+
28172+static int recursion;
28173+static struct ip_address ip;
28174+
28175+static void hdr_pass() { received = "pass (%{xr}: %{xs} designates %{i} as permitted sender)"; };
28176+static void hdr_softfail() { received = "softfail (%{xr}: transitioning %{xs} does not designate %{i} as permitted sender)"; };
28177+static void hdr_fail() { received = "fail (%{xr}: %{xs} does not designate %{i} as permitted sender)"; };
28178+static void hdr_unknown() { received = "unknown (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
28179+static void hdr_neutral() { received = "neutral (%{xr}: %{i} is neither permitted nor denied by %{xs})"; };
28180+static void hdr_none() { received = "none (%{xr}: domain at %{d} does not designate permitted sender hosts)"; };
28181+static void hdr_unknown_msg(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown (%{xr}: %{xe})"; };
28182+static void hdr_ext(e) char *e; { stralloc_copys(&errormsg, e); received = "unknown %{xe} (%{xr}: %{xs} uses mechanism not recognized by this client)"; };
28183+static void hdr_syntax() { received = "unknown (%{xr}: parse error in %{xs})"; };
28184+static void hdr_error(e) char *e; { stralloc_copys(&errormsg, e); received = "error (%{xr}: error in processing during lookup of %{d}: %{xe})"; };
28185+static void hdr_dns() { hdr_error("DNS problem"); }
28186+
28187+
28188+static int matchip(struct ip_address *net, int mask, struct ip_address *ip)
28189+{
28190+       int j;
28191+       int bytemask;
28192+
28193+       for (j = 0; j < 4 && mask > 0; ++j) {
28194+               if (mask > 8) bytemask = 8; else bytemask = mask;
28195+               mask -= bytemask;
28196+
28197+               if ((net->d[j] ^ ip->d[j]) & (0x100 - (1 << (8 - bytemask))))
28198+                       return 0;
28199+       }
28200+       return 1;
28201+}
28202+
28203+static int getipmask(char *mask, int ipv6) {
28204+       unsigned long r;
28205+       int pos;
28206+
28207+       if (!mask) return 32;
28208+
28209+       pos = scan_ulong(mask, &r);
28210+       if (!pos || (mask[pos] && !(mask[pos] == '/' && ipv6))) return -1;
28211+       if (r > 32) return -1;
28212+
28213+       return r;
28214+}
28215+
28216+int spfget(stralloc *spf, stralloc *domain)
28217+{
28218+       strsalloc ssa = {0};
28219+       int j;
28220+       int begin, pos, i;
28221+       int r = SPF_NONE;
28222+
28223+       spf->len = 0;
28224+
28225+       switch(dns_txt(&ssa, domain)) {
28226+               case DNS_MEM: return SPF_NOMEM;
28227+               case DNS_SOFT: hdr_dns(); return SPF_ERROR;
28228+               case DNS_HARD: return SPF_NONE;
28229+       }
28230+
28231+       for (j = 0;j < ssa.len;++j) {
28232+               pos = 0;
28233+
28234+               NXTOK(begin, pos, &ssa.sa[j]);
28235+               if (str_len(ssa.sa[j].s + begin) < 6) continue;
28236+               if (!byte_equal(ssa.sa[j].s + begin,6,"v=spf1")) continue;
28237+               if (ssa.sa[j].s[begin + 6]) {
28238+                       /* check for subversion */
28239+                       if (ssa.sa[j].s[begin + 6] != '.') continue;
28240+                       for(i = begin + 7;;++i)
28241+                               if (!(ssa.sa[j].s[i] >= '0' && ssa.sa[j].s[i] <= '9')) break;
28242+                       if (i == (begin + 7)) continue;
28243+                       if (ssa.sa[j].s[i]) continue;
28244+               }
28245+
28246+               if (spf->len > 0) {
28247+                       spf->len = 0;
28248+                       hdr_unknown_msg("Multiple SPF records returned");
28249+                       r = SPF_UNKNOWN;
28250+                       break;
28251+               }
28252+               if (!stralloc_0(&ssa.sa[j])) return SPF_NOMEM;
28253+               if (!stralloc_copys(spf,ssa.sa[j].s + pos)) return SPF_NOMEM;
28254+               r = SPF_OK;
28255+       }
28256+
28257+       for (j = 0;j < ssa.len;++j)
28258+               alloc_free(ssa.sa[j].s);
28259+       alloc_free(ssa.sa);
28260+       return r;
28261+}
28262+
28263+static int spf_ptr(char *spec, char *mask);
28264+
28265+int spfsubst(stralloc *expand, char *spec, char *domain)
28266+{
28267+       static char hexdigits[] = "0123456789abcdef";
28268+       stralloc sa = {0};
28269+       char ch;
28270+       int digits = -1;
28271+       int urlencode = 0;
28272+       int reverse = 0;
28273+       int start = expand->len;
28274+       int i, pos;
28275+       char *split = ".";
28276+
28277+       if (!stralloc_readyplus(&sa,0)) return 0;
28278+
28279+       if (*spec == 'x') { i = 1; ++spec; } else i = 0;
28280+       ch = *spec++;
28281+       if (!ch) { alloc_free(sa.s); return 1; }
28282+       if (ch >= 'A' && ch <= 'Z') { ch += 32; urlencode = 1; }
28283+       if (i) ch -= 32;
28284+       while(*spec >= '0' && *spec <= '9') {
28285+               if (digits < 0) digits = 0;
28286+               if (digits >= 1000000) { digits = 10000000; continue; }
28287+               digits = (digits * 10) + (*spec - '0');
28288+               spec++;
28289+       }
28290+
28291+       while((*spec >= 'a' && *spec <= 'z') || (*spec >= 'A' && *spec <= 'Z')) {
28292+               if (*spec == 'r') reverse = 1;
28293+               spec++;
28294+       }
28295+
28296+       if (*spec) split = spec;
28297+
28298+       switch(ch) {
28299+               case 'l':
28300+                       pos = byte_rchr(addr.s, addr.len, '@');
28301+                       if (pos < addr.len) {
28302+                               if (!stralloc_copyb(&sa, addr.s, pos)) return 0;
28303+                       } else
28304+                               if (!stralloc_copys(&sa, "postmaster")) return 0;
28305+                       break;
28306+               case 's':
28307+                       if (!stralloc_copys(&sa, addr.s)) return 0;
28308+                       break;
28309+               case 'o':
28310+                       pos = byte_rchr(addr.s, addr.len, '@') + 1;
28311+                       if (pos > addr.len) break;
28312+                       if (!stralloc_copys(&sa, addr.s + pos)) return 0;
28313+                       break;
28314+               case 'd':
28315+                       if (!stralloc_copys(&sa, domain)) return 0;
28316+                       break;
28317+               case 'i':
28318+                       if (!stralloc_ready(&sa, IPFMT)) return 0;
28319+                       sa.len = ip_fmt(sa.s, &ip);
28320+                       break;
28321+               case 't':
28322+                       if (!stralloc_ready(&sa, FMT_ULONG)) return 0;
28323+                       sa.len = fmt_ulong(sa.s, (unsigned long)now());
28324+                       break;
28325+               case 'p':
28326+                       if (!sender_fqdn.len)
28327+                               spf_ptr(domain, 0);
28328+                       if (sender_fqdn.len) {
28329+                               if (!stralloc_copy(&sa, &sender_fqdn)) return 0;
28330+                       } else
28331+                               if (!stralloc_copys(&sa, "unknown")) return 0;
28332+                       break;
28333+               case 'v':
28334+                       if (!stralloc_copys(&sa, "in-addr")) return 0;
28335+                       break;
28336+               case 'h':
28337+                       if (!stralloc_copys(&sa, helohost.s)) return 0; /* FIXME: FQDN? */
28338+                       break;
28339+               case 'E':
28340+                       if (errormsg.len && !stralloc_copy(&sa, &errormsg)) return 0;
28341+                       break;
28342+               case 'R':
28343+                       if (!stralloc_copys(&sa, local)) return 0;
28344+                       break;
28345+               case 'S':
28346+                       if (expdomain.len > 0) {
28347+                               if (!stralloc_copys(&sa, "SPF record at ")) return 0;
28348+                               if (!stralloc_cats(&sa, expdomain.s)) return 0;
28349+                       } else {
28350+                               if (!stralloc_copys(&sa, "local policy")) return 0;
28351+                       }
28352+                       break;
28353+       }
28354+
28355+       if (reverse) {
28356+               for(pos = 0; digits; ++pos) {
28357+                       pos += byte_cspn(sa.s + pos, sa.len - pos, split);
28358+                       if (pos >= sa.len) break;
28359+                       if (!--digits) break;
28360+               }
28361+
28362+               for(; pos > 0; pos = i - 1) {
28363+                       i = byte_rcspn(sa.s, pos, split) + 1;
28364+                       if (i > pos) i = 0;
28365+                       if (!stralloc_catb(expand, sa.s + i, pos - i)) return 0;
28366+                       if (i > 0 && !stralloc_append(expand, ".")) return 0;
28367+               }
28368+       } else {
28369+               for(pos = sa.len; digits; --pos) {
28370+                       i = byte_rcspn(sa.s, pos, split) + 1;
28371+                       if (i > pos) { pos = 0; break; }
28372+                       pos = i;
28373+                       if (!--digits) break;
28374+               }
28375+
28376+               if (!stralloc_catb(expand, sa.s + pos, sa.len - pos)) return 0;
28377+               if (split[0] != '.' || split[1])
28378+                       for(pos = 0; pos < expand->len; pos++) {
28379+                               pos += byte_cspn(expand->s + pos, expand->len - pos, split);
28380+                               if (pos < expand->len)
28381+                                       expand->s[pos] = '.';
28382+                       }
28383+       }
28384+
28385+       if (urlencode) {
28386+               stralloc_copyb(&sa, expand->s + start, expand->len - start);
28387+               expand->len = start;
28388+
28389+               for(pos = 0; pos < sa.len; ++pos) {
28390+                       ch = sa.s[pos];
28391+                       if (urlchr_table[(unsigned char)ch]) {
28392+                               if (!stralloc_readyplus(expand, 3)) return 0;
28393+                               expand->s[expand->len++] = '%';
28394+                               expand->s[expand->len++] = hexdigits[(unsigned char)ch >> 4];
28395+                               expand->s[expand->len++] = hexdigits[(unsigned char)ch & 15];
28396+                       } else
28397+                               if (!stralloc_append(expand, &ch)) return 0;
28398+               }
28399+       }
28400+
28401+       alloc_free(sa.s);
28402+       return 1;
28403+}
28404+
28405+int spfexpand(stralloc *sa, char *spec, char *domain)
28406+{
28407+       char *p;
28408+       char append;
28409+       int pos;
28410+
28411+       if (!stralloc_readyplus(sa, 0)) return 0;
28412+       sa->len = 0;
28413+
28414+       for(p = spec; *p; p++) {
28415+               append = *p;
28416+               if (*p == '%') {
28417+                       p++;
28418+                       switch(*p) {
28419+                               case '%': break;
28420+                               case '_': append = ' '; break;
28421+                               case '-': if (!stralloc_cats(sa, "%20")) return 0; continue;
28422+                               case '{':
28423+                                       pos = str_chr(p, '}');
28424+                                       if (p[pos] != '}') { p--; break; }
28425+                                       p[pos] = 0;
28426+                                       if (!spfsubst(sa, p + 1, domain)) return 0;
28427+                                       p += pos;
28428+                                       continue;
28429+                               default: p--;
28430+                       }
28431+               }
28432+               if (!stralloc_append(sa, &append)) return 0;
28433+       }
28434+
28435+       return 1;
28436+}
28437+
28438+static int spflookup(stralloc *domain);
28439+
28440+static int spf_include(char *spec, char *mask)
28441+{
28442+       stralloc sa = {0};
28443+       int r;
28444+
28445+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28446+       r = spflookup(&sa);
28447+       alloc_free(sa.s);
28448+
28449+       switch(r) {
28450+               case SPF_NONE:
28451+                       hdr_unknown();
28452+                       r = SPF_UNKNOWN;
28453+                       break;
28454+               case SPF_SYNTAX:
28455+                       r = SPF_UNKNOWN;
28456+                       break;
28457+               case SPF_NEUTRAL:
28458+               case SPF_SOFTFAIL:
28459+               case SPF_FAIL:
28460+                       r = SPF_NONE;
28461+                       break;
28462+       }
28463+
28464+       return r;
28465+}
28466+
28467+static int spf_a(char *spec, char *mask)
28468+{
28469+       stralloc sa = {0};
28470+       ipalloc ia = {0};
28471+       int ipmask = getipmask(mask, 1);
28472+       int r;
28473+       int j;
28474+
28475+       if (ipmask < 0) return SPF_SYNTAX;
28476+
28477+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28478+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28479+
28480+       switch(dns_ip(&ia, &sa)) {
28481+               case DNS_MEM: return SPF_NOMEM;
28482+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28483+               case DNS_HARD: r = SPF_NONE; break;
28484+               default:
28485+                       r = SPF_NONE;
28486+                       for(j = 0; j < ia.len; ++j)
28487+                               if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
28488+                                       r = SPF_OK;
28489+                                       break;
28490+                               }
28491+       }
28492+
28493+       alloc_free(sa.s);
28494+       alloc_free(ia.ix);
28495+       return r;
28496+}
28497+
28498+static int spf_mx(char *spec, char *mask)
28499+{
28500+       stralloc sa = {0};
28501+       ipalloc ia = {0};
28502+       int ipmask = getipmask(mask, 1);
28503+       int random = now() + (getpid() << 16);
28504+       int r;
28505+       int j;
28506+
28507+       if (ipmask < 0) return SPF_SYNTAX;
28508+
28509+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28510+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28511+
28512+       switch(dns_mxip(&ia, &sa, random)) {
28513+               case DNS_MEM: return SPF_NOMEM;
28514+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28515+               case DNS_HARD: r = SPF_NONE; break;
28516+               default:
28517+                       r = SPF_NONE;
28518+                       for(j = 0; j < ia.len; ++j)
28519+                               if (matchip(&ia.ix[j].ip, ipmask, &ip)) {
28520+                                       r = SPF_OK;
28521+                                       break;
28522+                               }
28523+       }
28524+
28525+       alloc_free(sa.s);
28526+       alloc_free(ia.ix);
28527+       return r;
28528+}
28529+
28530+static int spf_ptr(char *spec, char *mask)
28531+{
28532+       strsalloc ssa = {0};
28533+       ipalloc ia = {0};
28534+       int len = str_len(spec);
28535+       int r;
28536+       int j, k;
28537+       int pos;
28538+
28539+       /* we didn't find host with the matching ip before */
28540+       if (sender_fqdn.len == 7 && str_equal(sender_fqdn.s, "unknown"))
28541+               return SPF_NONE;
28542+
28543+       /* the hostname found will probably be the same as before */
28544+       while (sender_fqdn.len) {
28545+               pos = sender_fqdn.len - len;
28546+               if (pos < 0) break;
28547+               if (pos > 0 && sender_fqdn.s[pos - 1] != '.') break;
28548+               if (case_diffb(sender_fqdn.s + pos, len, spec)) break;
28549+
28550+               return SPF_OK;
28551+       }
28552+
28553+       /* ok, either it's the first test or it's a very weird setup */
28554+
28555+       if (!stralloc_readyplus(&ssa, 0)) return SPF_NOMEM;
28556+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28557+
28558+       switch(dns_ptr(&ssa, &ip)) {
28559+               case DNS_MEM: return SPF_NOMEM;
28560+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28561+               case DNS_HARD: r = SPF_NONE; break;
28562+               default:
28563+                       r = SPF_NONE;
28564+                       for(j = 0; j < ssa.len; ++j) {
28565+                               switch(dns_ip(&ia, &ssa.sa[j])) {
28566+                                       case DNS_MEM: return SPF_NOMEM;
28567+                                       case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28568+                                       case DNS_HARD: break;
28569+                                       default:
28570+                                               for(k = 0; k < ia.len; ++k)
28571+                                                       if (matchip(&ia.ix[k].ip, 32, &ip)) {
28572+                                                               if (!sender_fqdn.len)
28573+                                                                       if (!stralloc_copy(&sender_fqdn, &ssa.sa[j])) return SPF_NOMEM;
28574+
28575+                                                               pos = ssa.sa[j].len - len;
28576+                                                               if (pos < 0) continue;
28577+                                                               if (pos > 0 && ssa.sa[j].s[pos - 1] != '.') continue;
28578+                                                               if (case_diffb(ssa.sa[j].s + pos, len, spec)) continue;
28579+
28580+                                                               stralloc_copy(&sender_fqdn, &ssa.sa[j]);
28581+                                                               r = SPF_OK;
28582+                                                               break;
28583+                                                       }
28584+                               }
28585+
28586+                               if (r == SPF_ERROR) break;
28587+                       }
28588+       }
28589+
28590+       for(j = 0;j < ssa.len;++j)
28591+               alloc_free(ssa.sa[j].s);
28592+
28593+       alloc_free(ssa.sa);
28594+       alloc_free(ia.ix);
28595+
28596+       if (!sender_fqdn.len)
28597+               if (!stralloc_copys(&sender_fqdn, "unknown")) return SPF_NOMEM;
28598+
28599+       return r;
28600+}
28601+
28602+static int spf_ip(char *spec, char *mask)
28603+{
28604+       struct ip_address net;
28605+       int ipmask = getipmask(mask, 0);
28606+
28607+       if (ipmask < 0) return SPF_SYNTAX;
28608+       if (!ip_scan(spec, &net)) return SPF_SYNTAX;
28609+
28610+       if (matchip(&net, ipmask, &ip)) return SPF_OK;
28611+
28612+       return SPF_NONE;
28613+}
28614+
28615+static int spf_exists(char *spec, char *mask)
28616+{
28617+       stralloc sa = {0};
28618+       ipalloc ia = {0};
28619+       int r;
28620+
28621+       if (!stralloc_copys(&sa, spec)) return SPF_NOMEM;
28622+       if (!stralloc_readyplus(&ia, 0)) return SPF_NOMEM;
28623+
28624+       switch(dns_ip(&ia, &sa)) {
28625+               case DNS_MEM: return SPF_NOMEM;
28626+               case DNS_SOFT: hdr_dns(); r = SPF_ERROR; break;
28627+               case DNS_HARD: r = SPF_NONE; break;
28628+               default: r = SPF_OK;
28629+       }
28630+
28631+       alloc_free(sa.s);
28632+       alloc_free(ia.ix);
28633+       return r;
28634+}
28635+
28636+static struct mechanisms {
28637+  char *mechanism;
28638+  int (*func)(char *spec, char *mask);
28639+  unsigned int takes_spec  : 1;
28640+  unsigned int takes_mask  : 1;
28641+  unsigned int expands     : 1;
28642+  unsigned int filldomain  : 1;
28643+  int defresult            : 4;
28644+} mechanisms[] = {
28645+  { "all",      0,          0,0,0,0,SPF_OK   }
28646+, { "include",  spf_include,1,0,1,0,0        }
28647+, { "a",        spf_a,      1,1,1,1,0        }
28648+, { "mx",       spf_mx,     1,1,1,1,0        }
28649+, { "ptr",      spf_ptr,    1,0,1,1,0        }
28650+, { "ip4",      spf_ip,     1,1,0,0,0        }
28651+, { "ip6",      0,          1,1,0,0,SPF_NONE }
28652+, { "exists",   spf_exists, 1,0,1,0,0        }
28653+, { "extension",0,          1,1,0,0,SPF_EXT  }
28654+, { 0,          0,          1,1,0,0,SPF_EXT  }
28655+};
28656+
28657+static int spfmech(char *mechanism, char *spec, char *mask, char *domain)
28658+{
28659+       struct mechanisms *mech;
28660+       stralloc sa = {0};
28661+       int r;
28662+       int pos;
28663+
28664+       for(mech = mechanisms; mech->mechanism; mech++)
28665+               if (str_equal(mech->mechanism, mechanism)) break;
28666+
28667+       if (mech->takes_spec && !spec && mech->filldomain) spec = domain;
28668+       if (!mech->takes_spec != !spec) return SPF_SYNTAX;
28669+       if (!mech->takes_mask && mask) return SPF_SYNTAX;
28670+       if (!mech->func) return mech->defresult;
28671+
28672+       if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
28673+       if (mech->expands && spec != domain) {
28674+               if (!spfexpand(&sa, spec, domain)) return SPF_NOMEM;
28675+               for (pos = 0; (sa.len - pos) > 255;) {
28676+                       pos += byte_chr(sa.s + pos, sa.len - pos, '.');
28677+                       if (pos < sa.len) pos++;
28678+               }
28679+               sa.len -= pos;
28680+               if (pos > 0) byte_copy(sa.s, sa.len, sa.s + pos);
28681+               stralloc_0(&sa);
28682+               spec = sa.s;
28683+       }
28684+
28685+       r = mech->func(spec, mask);
28686+
28687+       alloc_free(sa.s);
28688+       return r;
28689+}
28690+
28691+static struct default_aliases {
28692+  char *alias;
28693+  int defret;
28694+} default_aliases[] = {
28695+  { "allow",   SPF_OK }
28696+, { "pass",    SPF_OK }
28697+, { "deny",    SPF_FAIL }
28698+, { "softdeny",SPF_SOFTFAIL }
28699+, { "fail",    SPF_FAIL }
28700+, { "softfail",SPF_SOFTFAIL }
28701+, { "unknown", SPF_NEUTRAL }
28702+, { 0,         SPF_UNKNOWN }
28703+};
28704+
28705+static int spflookup(stralloc *domain)
28706+{
28707+       stralloc spf = {0};
28708+       stralloc sa = {0};
28709+       struct default_aliases *da;
28710+       int main = !recursion;
28711+       int local_pos = -1;
28712+       int r, q;
28713+       int begin, pos;
28714+       int i;
28715+       int prefix;
28716+       int done;
28717+       int guessing = 0;
28718+       char *p;
28719+
28720+       if (!stralloc_readyplus(&spf, 0)) return SPF_NOMEM;
28721+       if (!stralloc_readyplus(&sa, 0)) return SPF_NOMEM;
28722+
28723+       /* fallthrough result */
28724+       if (main) hdr_none();
28725+
28726+redirect:
28727+       if (++recursion > 20) {
28728+               alloc_free(spf.s);
28729+               alloc_free(sa.s);
28730+               hdr_unknown_msg("Maximum nesting level exceeded, possible loop");
28731+               return SPF_SYNTAX;
28732+       }
28733+
28734+       if (!stralloc_0(domain)) return SPF_NOMEM;
28735+       if (!stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
28736+
28737+       r = spfget(&spf, domain);
28738+       if (r == SPF_NONE) {
28739+               if (!main) { alloc_free(spf.s); return r; }
28740+
28741+               if (spfguess.len) {
28742+                       /* try to guess */
28743+                       guessing = 1;
28744+                       if (!stralloc_copys(&spf, spfguess.s)) return SPF_NOMEM;
28745+                       if (!stralloc_append(&spf, " ")) return SPF_NOMEM;
28746+               } else
28747+                       spf.len = 0;
28748+
28749+               /* append local rulest */
28750+               if (spflocal.len) {
28751+                       local_pos = spf.len;
28752+                       if (!stralloc_cats(&spf, spflocal.s)) return SPF_NOMEM;
28753+               }
28754+               if (!stralloc_0(&spf)) return SPF_NOMEM;
28755+
28756+               expdomain.len = 0;
28757+       } else if (r == SPF_OK) {
28758+               if (!stralloc_0(&spf)) return SPF_NOMEM;
28759+               if (main) hdr_neutral();
28760+               r = SPF_NEUTRAL;
28761+
28762+               /* try to add local rules before fail all mechs */
28763+               if (main && spflocal.len) {
28764+                       pos = 0;
28765+                       p = (char *) 0;
28766+                       while(pos < spf.len) {
28767+                               NXTOK(begin, pos, &spf);
28768+                               if (!spf.s[begin]) continue;
28769+
28770+                               if (p && spf.s[begin] != *p) p = (char *) 0;
28771+                               if (!p && (spf.s[begin] == '-' || spf.s[begin] == '~' ||
28772+                                          spf.s[begin] == '?')) p = &spf.s[begin];
28773+
28774+                               if (p && p > spf.s && str_equal(spf.s + begin + 1, "all")) {
28775+                                       /* ok, we can insert the local rules at p */
28776+                                       local_pos = p - spf.s;
28777+
28778+                                       stralloc_readyplus(&spf, spflocal.len);
28779+                                       p = spf.s + local_pos;
28780+                                       byte_copyr(p + spflocal.len, spf.len - local_pos, p);
28781+                                       byte_copy(p, spflocal.len, spflocal.s);
28782+                                       spf.len += spflocal.len;
28783+
28784+                                       pos += spflocal.len;
28785+                                       break;
28786+                               }
28787+                       }
28788+
28789+                       if (pos >= spf.len) pos = spf.len - 1;
28790+                       for(i = 0; i < pos; i++)
28791+                               if (!spf.s[i]) spf.s[i] = ' ';
28792+               }
28793+       } else {
28794+               alloc_free(spf.s);
28795+               return r;
28796+       }
28797+
28798+       pos = 0;
28799+       done = 0;
28800+       while(pos < spf.len) {
28801+               NXTOK(begin, pos, &spf);
28802+               if (!spf.s[begin]) continue;
28803+
28804+               /* in local ruleset? */
28805+               if (!done && local_pos >= 0 && begin >= local_pos) {
28806+                       if (begin < (local_pos + spflocal.len))
28807+                               expdomain.len = 0;
28808+                       else
28809+                               if (!stralloc_copy(&expdomain, domain))
28810+                                       return SPF_NOMEM;
28811+               }
28812+
28813+               for (p = spf.s + begin;*p;++p)
28814+                       if (*p == ':' || *p == '/' || *p == '=') break;
28815+
28816+               if (*p == '=') {
28817+                       *p++ = 0;
28818+
28819+                       /* modifiers are simply handled here */
28820+                       if (str_equal(spf.s + begin, "redirect")) {
28821+                               if (done) continue;
28822+
28823+                               if (!spfexpand(&sa, p, domain->s)) return SPF_NOMEM;
28824+                               stralloc_copy(domain, &sa);
28825+
28826+                               hdr_unknown();
28827+                               r = SPF_UNKNOWN;
28828+
28829+                               goto redirect;
28830+                       } else if (str_equal(spf.s + begin, "default")) {
28831+                               if (done) continue;
28832+
28833+                               for(da = default_aliases; da->alias; ++da)
28834+                                       if (str_equal(da->alias, p)) break;
28835+
28836+                               r = da->defret;
28837+                       } else if (str_equal(spf.s + begin, "exp")) {
28838+                               strsalloc ssa = {0};
28839+
28840+                               if (!main) continue;
28841+
28842+                               if (!stralloc_copys(&sa, p)) return SPF_NOMEM;
28843+                               switch(dns_txt(&ssa, &sa)) {
28844+                                       case DNS_MEM: return SPF_NOMEM;
28845+                                       case DNS_SOFT: continue; /* FIXME... */
28846+                                       case DNS_HARD: continue;
28847+                               }
28848+
28849+                               explanation.len = 0;
28850+                               for(i = 0; i < ssa.len; i++) {
28851+                                       if (!stralloc_cat(&explanation, &ssa.sa[i])) return SPF_NOMEM;
28852+                                       if (i < (ssa.len - 1))
28853+                                               if (!stralloc_append(&explanation, "\n")) return SPF_NOMEM;
28854+
28855+                                       alloc_free(ssa.sa[i].s);
28856+                               }
28857+                               if (!stralloc_0(&explanation)) return SPF_NOMEM;
28858+                       } /* and unknown modifiers are ignored */
28859+               } else if (!done) {
28860+                       if (!stralloc_copys(&sa, spf.s + begin)) return SPF_NOMEM;
28861+                       if (!stralloc_0(&sa)) return SPF_NOMEM;
28862+
28863+                       switch(spf.s[begin]) {
28864+                               case '-': begin++; prefix = SPF_FAIL; break;
28865+                               case '~': begin++; prefix = SPF_SOFTFAIL; break;
28866+                               case '+': begin++; prefix = SPF_OK; break;
28867+                               case '?': begin++; prefix = SPF_NEUTRAL; break;
28868+                               default: prefix = SPF_OK;
28869+                       }
28870+
28871+                       if (*p == '/') {
28872+                               *p++ = 0;
28873+                               q = spfmech(spf.s + begin, 0, p, domain->s);
28874+                       } else {
28875+                               if (*p) *p++ = 0;
28876+                               i = str_chr(p, '/');
28877+                               if (p[i] == '/') {
28878+                                       p[i++] = 0;
28879+                                       q = spfmech(spf.s + begin, p, p + i, domain->s);
28880+                               } else if (i > 0)
28881+                                       q = spfmech(spf.s + begin, p, 0, domain->s);
28882+                               else
28883+                                       q = spfmech(spf.s + begin, 0, 0, domain->s);
28884+                       }
28885+
28886+                       if (q == SPF_OK) q = prefix;
28887+
28888+                       switch(q) {
28889+                               case SPF_OK: hdr_pass(); break;
28890+                               case SPF_NEUTRAL: hdr_neutral(); break;
28891+                               case SPF_SYNTAX: hdr_syntax(); break;
28892+                               case SPF_SOFTFAIL: hdr_softfail(); break;
28893+                               case SPF_FAIL: hdr_fail(); break;
28894+                               case SPF_EXT: hdr_ext(sa.s); break;
28895+                               case SPF_ERROR:
28896+                                       if (!guessing)
28897+                                               break;
28898+                                       if (local_pos >= 0 && begin >= local_pos)
28899+                                               break;
28900+                                       hdr_none();
28901+                                       q = SPF_NONE;
28902+                                       break;
28903+                               case SPF_NONE: continue;
28904+                       }
28905+
28906+                       r = q;
28907+                       done = 1; /* we're done, no more mechanisms */
28908+               }
28909+       }
28910+
28911+       /* we fell through, no local rule applied */
28912+       if (!done && !stralloc_copy(&expdomain, domain)) return SPF_NOMEM;
28913+
28914+       alloc_free(spf.s);
28915+       alloc_free(sa.s);
28916+       return r;
28917+}
28918+
28919+int spfcheck(char *remoteip)
28920+{
28921+       stralloc domain = {0};
28922+       int pos;
28923+       int r;
28924+
28925+       pos = byte_rchr(addr.s, addr.len, '@') + 1;
28926+       if (pos < addr.len) {
28927+               if (!stralloc_copys(&domain, addr.s + pos)) return SPF_NOMEM;
28928+       } else {
28929+               pos = str_rchr(helohost.s, '@');
28930+               if (helohost.s[pos]) {
28931+                       if (!stralloc_copys(&domain, helohost.s + pos + 1)) return SPF_NOMEM;
28932+               } else
28933+                       if (!stralloc_copys(&domain, helohost.s)) return SPF_NOMEM;
28934+       }
28935+       if (!stralloc_copys(&explanation, spfexp.s)) return SPF_NOMEM;
28936+       if (!stralloc_0(&explanation)) return SPF_NOMEM;
28937+       recursion = 0;
28938+
28939+       if (!remoteip || !ip_scan(remoteip, &ip)) {
28940+               hdr_unknown_msg("No IP address in conversation");
28941+               return SPF_UNKNOWN;
28942+       }
28943+
28944+       if (!stralloc_readyplus(&expdomain, 0)) return SPF_NOMEM;
28945+       if (!stralloc_readyplus(&errormsg, 0)) return SPF_NOMEM;
28946+       expdomain.len = 0;
28947+       errormsg.len = 0;
28948+       sender_fqdn.len = 0;
28949+       received = (char *) 0;
28950+
28951+       if ((ip.d[0] == 127 && ip.d[1] == 0 && ip.d[2] == 0 && ip.d[3] == 1) || ipme_is(&ip))
28952+               { hdr_pass(); r = SPF_OK; }
28953+       else
28954+               r = spflookup(&domain);
28955+
28956+       if (r < 0) r = SPF_UNKNOWN;
28957+
28958+       alloc_free(domain.s);
28959+       return r;
28960+}
28961+
28962+int spfexplanation(sa)
28963+stralloc *sa;
28964+{
28965+       return spfexpand(sa, explanation.s, expdomain.s);
28966+}
28967+
28968+int spfinfo(sa)
28969+stralloc *sa;
28970+{
28971+       stralloc tmp = {0};
28972+       if (!stralloc_copys(&tmp, received)) return 0;
28973+       if (!stralloc_0(&tmp)) return 0;
28974+       if (!spfexpand(sa, tmp.s, expdomain.s)) return 0;
28975+       alloc_free(tmp.s);
28976+       return 1;
28977+}
28978diff -ruN ../netqmail-1.06-original/spf.h netqmail-1.06/spf.h
28979--- ../netqmail-1.06-original/spf.h     1970-01-01 01:00:00.000000000 +0100
28980+++ netqmail-1.06/spf.h 2016-11-22 21:03:57.129527532 +0100
28981@@ -0,0 +1,20 @@
28982+#ifndef SPF_H
28983+#define SPF_H
28984+
28985+#define SPF_OK       0
28986+#define SPF_NONE     1
28987+#define SPF_UNKNOWN  2
28988+#define SPF_NEUTRAL  3
28989+#define SPF_SOFTFAIL 4
28990+#define SPF_FAIL     5
28991+#define SPF_ERROR    6
28992+#define SPF_NOMEM    7
28993+
28994+#define SPF_DEFEXP   "See http://spf.pobox.com/" \
28995+                     "why.html?sender=%{S}&ip=%{I}&receiver=%{xR}"
28996+
28997+extern int spfcheck();
28998+extern int spfexplanation();
28999+extern int spfinfo();
29000+
29001+#endif
29002diff -ruN ../netqmail-1.06-original/spfquery.c netqmail-1.06/spfquery.c
29003--- ../netqmail-1.06-original/spfquery.c        1970-01-01 01:00:00.000000000 +0100
29004+++ netqmail-1.06/spfquery.c    2016-11-22 21:03:57.129527532 +0100
29005@@ -0,0 +1,84 @@
29006+#include "substdio.h"
29007+#include "subfd.h"
29008+#include "stralloc.h"
29009+#include "alloc.h"
29010+#include "spf.h"
29011+#include "exit.h"
29012+
29013+void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); }
29014+void die_usage() { die(100,"fatal: invalid usage\nusage: spfquery <sender-ip> <sender-helo/ehlo> <envelope-from> [<local rules>] [<best guess rules>]\n"); }
29015+void die_nomem() { die(111,"fatal: out of memory\n"); }
29016+
29017+stralloc addr = {0};
29018+stralloc helohost = {0};
29019+char *remoteip;
29020+char *local;
29021+
29022+stralloc spflocal = {0};
29023+stralloc spfguess = {0};
29024+stralloc spfexp = {0};
29025+
29026+void main(argc,argv)
29027+int argc;
29028+char **argv;
29029+{
29030+       stralloc sa = {0};
29031+       int r;
29032+
29033+       if (argc < 4) die_usage();
29034+
29035+       remoteip = (char *)strdup(argv[1]);
29036+       local = "localhost";
29037+
29038+       if (!stralloc_copys(&helohost, argv[2])) die_nomem();
29039+       if (!stralloc_0(&helohost)) die_nomem();
29040+
29041+       if (!stralloc_copys(&addr, argv[3])) die_nomem();
29042+       if (!stralloc_0(&addr)) die_nomem();
29043+
29044+       if (argc > 4) {
29045+               if (!stralloc_copys(&spflocal, argv[4])) die_nomem();
29046+               if (spflocal.len && !stralloc_0(&spflocal)) die_nomem();
29047+       }
29048+
29049+       if (argc > 5) {
29050+               if (!stralloc_copys(&spfguess, argv[5])) die_nomem();
29051+               if (spfguess.len && !stralloc_0(&spfguess)) die_nomem();
29052+       }
29053+
29054+       if (argc > 6) {
29055+               if (!stralloc_copys(&spfexp, argv[6])) die_nomem();
29056+       } else
29057+               if (!stralloc_copys(&spfexp, SPF_DEFEXP)) die_nomem();
29058+       if (spfexp.len && !stralloc_0(&spfexp)) die_nomem();
29059+
29060+       dns_init(0);
29061+       r = spfcheck();
29062+       if (r == SPF_NOMEM) die_nomem();
29063+
29064+       substdio_puts(subfdout,"result=");
29065+       switch(r) {
29066+               case SPF_OK: substdio_puts(subfdout,"pass"); break;
29067+               case SPF_NONE: substdio_puts(subfdout,"none"); break;
29068+               case SPF_UNKNOWN: substdio_puts(subfdout,"unknown"); break;
29069+               case SPF_NEUTRAL: substdio_puts(subfdout,"neutral"); break;
29070+               case SPF_SOFTFAIL: substdio_puts(subfdout,"softfail"); break;
29071+               case SPF_FAIL: substdio_puts(subfdout,"fail"); break;
29072+               case SPF_ERROR: substdio_puts(subfdout,"error"); break;
29073+       }
29074+
29075+       if (r == SPF_FAIL) {
29076+               substdio_puts(subfdout,": ");
29077+               if (!spfexplanation(&sa)) die_nomem();
29078+               substdio_put(subfdout,sa.s,sa.len);
29079+       }
29080+
29081+       substdio_putsflush(subfdout,"\n");
29082+
29083+       substdio_puts(subfdout,"Received-SPF: ");
29084+       if (!spfinfo(&sa)) die_nomem();
29085+       substdio_put(subfdout,sa.s,sa.len);
29086+       substdio_putsflush(subfdout,"\n");
29087+
29088+       _exit(0);
29089+}
29090diff -ruN ../netqmail-1.06-original/srs.c netqmail-1.06/srs.c
29091--- ../netqmail-1.06-original/srs.c     1970-01-01 01:00:00.000000000 +0100
29092+++ netqmail-1.06/srs.c 2016-11-22 21:03:57.129527532 +0100
29093@@ -0,0 +1,166 @@
29094+#include <sys/types.h>
29095+#include <sys/stat.h>
29096+#include </usr/local/include/srs2.h>
29097+#include "auto_qmail.h"
29098+#include "stralloc.h"
29099+#include "srs.h"
29100+
29101+static stralloc srs_domain = {0};
29102+static stralloc srs_secrets = {0};
29103+static unsigned int srs_maxage = 0;
29104+static unsigned int srs_hashlength = 0;
29105+static unsigned int srs_hashmin = 0;
29106+static unsigned int srs_alwaysrewrite = 0;
29107+static stralloc srs_separator = {0};
29108+
29109+stralloc srs_result = {0};
29110+stralloc srs_error = {0};
29111+
29112+static int setup_ok = 0;
29113+static int srs_secrets_ok = 0;
29114+
29115+static int setup(int with_rcpthosts) {
29116+
29117+  if (setup_ok == 1) return 1;
29118+
29119+  if (chdir(auto_qmail) == -1) return -1;
29120+  if (control_init() == -1) return -1;
29121+
29122+  if (control_readline(&srs_domain,"control/srs_domain") == -1) return -1;
29123+  if (srs_domain.len) {
29124+    if (!stralloc_0(&srs_domain)) return -2;
29125+  } else {
29126+    return 0;
29127+  }
29128+
29129+  srs_secrets_ok = control_readfile(&srs_secrets,"control/srs_secrets",0);
29130+  if (srs_secrets_ok == -1) return -1;
29131
29132+  if (control_readint(&srs_maxage,"control/srs_maxage") == -1) return 0;
29133+  if (control_readint(&srs_hashlength,"control/srs_hashlength") == -1) return 0;
29134+  if (control_readint(&srs_hashmin,"control/srs_hashmin") == -1) return 0;
29135+  if (srs_hashmin > srs_hashlength) srs_hashmin = srs_hashlength;
29136
29137+  if (control_readint(&srs_alwaysrewrite,"control/srs_alwaysrewrite") == -1) return 0;
29138
29139+  if (control_readline(&srs_separator,"control/srs_separator") == -1) return -1;
29140+  if (srs_separator.len && !stralloc_0(&srs_separator)) return -2;
29141+  if (srs_separator.len && srs_separator.s[0] != '-' && srs_separator.s[0] != '+' && srs_separator.s[0] != '=') {
29142+    if (!stralloc_copys(&srs_separator,"")) return -2;
29143+  }
29144
29145+  if (!srs_alwaysrewrite) {
29146+    if (with_rcpthosts && rcpthosts_init() == -1) return -1;
29147+  }
29148+
29149+  setup_ok = 1;
29150+  return 1;
29151
29152+}
29153+
29154+static int srs_error_str(int code) {
29155+  if (!stralloc_copys(&srs_error,"SRS: ")) return -2;
29156+  if (!stralloc_cats(&srs_error,srs_strerror(code))) return -2;
29157+  if (!stralloc_0(&srs_error)) return -2;   
29158+  return -3;
29159+}
29160+
29161+
29162+int srsforward(char *address) {
29163+  int x = 0;
29164+
29165+  /* Return if setup was unsucessfull */
29166+  x = setup(1);
29167+  if (x < 1) return(x);
29168
29169+  /* Return zero if null-sender */
29170+  x = str_len(address);
29171+  if (x <= 1) return 0;
29172+
29173+  /* Return zero if local address */
29174+  if (!srs_alwaysrewrite && rcpthosts(address,x) == 1) return 0; 
29175
29176+  /* Now it's time to rewrite the envelope */
29177+  char srsaddress[1000];
29178+
29179+  srs_t *srs;
29180+  srs = srs_new();
29181+  if (srs_maxage > 0) srs->maxage = srs_maxage;
29182+  if (srs_hashlength > 0) srs->hashlength = srs_hashlength;
29183+  if (srs_hashmin > 0) srs->hashmin = srs_hashmin;
29184
29185+  if (srs_alwaysrewrite){ 
29186+    x = srs_set_alwaysrewrite(srs, TRUE);
29187+    if (x != SRS_SUCCESS) return srs_error_str(x);
29188+  }
29189
29190+  if (srs_separator.len) {
29191+    x = srs_set_separator(srs, srs_separator.s[0]);
29192+    if (x != SRS_SUCCESS) return srs_error_str(x);
29193+  }
29194
29195+  int i = 0;
29196+  int j = 0;
29197+  for (j = 0;j < srs_secrets.len;++j)
29198+    if (!srs_secrets.s[j]) {
29199+      x = srs_add_secret(srs, srs_secrets.s + i);
29200+      if (x != SRS_SUCCESS) return srs_error_str(x);
29201+      i = j + 1;
29202+    }
29203
29204+  x = srs_forward(srs, srsaddress, 1000, address, srs_domain.s);
29205+  if (x != SRS_SUCCESS) return srs_error_str(x);
29206
29207+  if (!stralloc_copys(&srs_result,srsaddress)) return -2;
29208+  if (!stralloc_0(&srs_result)) return -2;
29209
29210+  srs_free(srs);
29211
29212+  return 1;
29213+}
29214+
29215+int srsreverse(char *srsaddress) {
29216+  int x = 0;
29217+
29218+  /* Return if setup was unsucessfull */
29219+  x = setup(0);
29220+  if (x < 1) return(x);
29221
29222+  /* Return error if null-sender */
29223+  x = str_len(srsaddress);
29224+  if (x <= 1) return -3;
29225
29226+  /* Now it's time to rewrite the envelope */
29227+  char address[1000];
29228+
29229+  srs_t *srs;
29230+  srs = srs_new();
29231+  if (srs_maxage > 0) srs->maxage = srs_maxage;
29232+  if (srs_hashlength > 0) srs->hashlength = srs_hashlength;
29233+  if (srs_hashmin > 0) srs->hashmin = srs_hashmin;
29234
29235+  if (srs_separator.len) {
29236+    x = srs_set_separator(srs, srs_separator.s[0]);
29237+    if (x != SRS_SUCCESS) return srs_error_str(x);
29238+  }
29239+
29240+  int i = 0;
29241+  int j = 0;
29242+  for (j = 0;j < srs_secrets.len;++j)
29243+    if (!srs_secrets.s[j]) {
29244+      x = srs_add_secret(srs, srs_secrets.s + i);
29245+      if (x != SRS_SUCCESS) return srs_error_str(x);
29246+      i = j + 1;
29247+    }
29248+   
29249+  x = srs_reverse(srs, address, 1000, srsaddress);
29250+  if (x != SRS_SUCCESS) return srs_error_str(x);
29251
29252+  if (!stralloc_copys(&srs_result,address)) return -2;
29253+  if (!stralloc_0(&srs_result)) return -2;
29254
29255+  srs_free(srs);
29256
29257+  return 1;
29258+}
29259+
29260diff -ruN ../netqmail-1.06-original/srs.h netqmail-1.06/srs.h
29261--- ../netqmail-1.06-original/srs.h     1970-01-01 01:00:00.000000000 +0100
29262+++ netqmail-1.06/srs.h 2016-11-22 21:03:57.129527532 +0100
29263@@ -0,0 +1,9 @@
29264+#ifndef SRS_H
29265+#define SRS_H
29266+
29267+extern stralloc srs_result;
29268+extern stralloc srs_error;
29269+extern int srsforward(char *);
29270+extern int srsreverse(char *);
29271+
29272+#endif
29273diff -ruN ../netqmail-1.06-original/srsfilter.c netqmail-1.06/srsfilter.c
29274--- ../netqmail-1.06-original/srsfilter.c       1970-01-01 01:00:00.000000000 +0100
29275+++ netqmail-1.06/srsfilter.c   2016-11-22 21:03:57.129527532 +0100
29276@@ -0,0 +1,137 @@
29277+#include "sig.h"
29278+#include "readwrite.h"
29279+#include "exit.h"
29280+#include "env.h"
29281+#include "qmail.h"
29282+#include "strerr.h"
29283+#include "substdio.h"
29284+#include "fmt.h"
29285+#include "stralloc.h"
29286+#include "srs.h"
29287+
29288+#define FATAL "srsfilter: fatal: "
29289+#define IGNORE "srsfilter: ignore: "
29290+
29291+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
29292+
29293+struct qmail qqt;
29294+
29295+stralloc line = {0};
29296+int flagbody = 0;
29297+int flagnewline = 0;
29298+int flagto = 0;
29299+int seento = 0;
29300+
29301+void newheader() {
29302+  if (!stralloc_copyb(&line,"To: ",4)) die_nomem();
29303+  if (!stralloc_cat(&line,&srs_result)) die_nomem();
29304+  ++flagto; ++seento;
29305+}
29306+
29307+void skipheader() {
29308+  if (!stralloc_copys(&line,"")) die_nomem();
29309+}
29310+
29311+void printheader() {
29312+  qmail_put(&qqt, line.s, line.len);
29313+  qmail_put(&qqt,"\n",1);
29314+  if (!stralloc_copys(&line,"")) die_nomem();
29315+}
29316+
29317+int mywrite(fd,buf,len) int fd; char *buf; int len;
29318+{
29319+  int i;
29320+  if (flagbody) {
29321+    qmail_put(&qqt,buf,len);
29322+    return len;
29323+  } else {
29324+    i = 0;
29325+    while (buf[i]) {
29326+      if (buf[i] == '\n') {
29327+        if (flagnewline) {
29328+          if (!seento) { newheader(); printheader(); }
29329+          qmail_put(&qqt,"\n",1); i++; flagbody = 1; continue;
29330+        }
29331+        if (flagto && (line.s[0] == ' ' || line.s[0] == '\t')) {
29332+          skipheader(); i++; continue;
29333+        }
29334+        if (line.len > 2 && line.s[2] == ':' && (line.s[1] == 'o' ||
29335+        line.s[1] == 'O') && (line.s[0] == 'T' || line.s[0] == 't')) {
29336+          if (seento) { skipheader(); i++; continue; }
29337+          newheader();
29338+        } else { flagto = 0; }
29339+        printheader();
29340+        flagnewline = 1;
29341+      } else {
29342+        if (!stralloc_append(&line,&buf[i])) die_nomem();
29343+        flagnewline = 0;
29344+      }
29345+      ++i;
29346+    }
29347+    return len;
29348+  }
29349+}
29350+
29351+char inbuf[SUBSTDIO_INSIZE];
29352+char outbuf[1];
29353+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof inbuf);
29354+substdio ssout = SUBSTDIO_FDBUF(mywrite,-1,outbuf,sizeof outbuf);
29355+
29356+char num[FMT_ULONG];
29357+
29358+void main(argc,argv)
29359+int argc;
29360+char **argv;
29361+{
29362+  char *ext2;
29363+  char *host;
29364+  char *sender;
29365+  char *qqx;
29366+
29367+  sig_pipeignore();
29368+
29369+  sender = env_get("SENDER");
29370+  if (!sender)
29371+    strerr_die2x(100,FATAL,"SENDER not set");
29372+  if (str_len(sender)) {
29373+    /* Return zero, the message will not bounce back */
29374+    strerr_die2x(0,IGNORE,"SENDER must be empty");
29375+  }
29376+  ext2 = env_get("EXT2");
29377+  if (!ext2)
29378+    strerr_die2x(100,FATAL,"EXT2 not set");
29379+  host = env_get("HOST");
29380+  if (!host)
29381+    strerr_die2x(100,FATAL,"HOST not set");
29382+   
29383+  switch(srsreverse(ext2)) {
29384+    case -3: strerr_die2x(100,FATAL,srs_error.s); break;
29385+    case -2: die_nomem(); break;
29386+    case -1: strerr_die2x(111,FATAL,"unable to read controls"); break;
29387+    case 0: strerr_die2x(100,FATAL,"unable to rewrite envelope"); break;
29388+  }
29389+
29390+  if (qmail_open(&qqt) == -1)
29391+    strerr_die2x(111,FATAL,"unable to fork");
29392+  if (substdio_copy(&ssout,&ssin) != 0)
29393+    strerr_die2x(111,FATAL,"unable to read message");
29394+  substdio_flush(&ssout);
29395
29396+  if (!flagbody) {
29397+    qmail_fail(&qqt);
29398+    strerr_die2x(100,FATAL,"unable to read message body");
29399+  }
29400+
29401+  num[fmt_ulong(num,qmail_qp(&qqt))] = 0;
29402+
29403+  /* Always from nullsender */
29404+  qmail_from(&qqt,"");
29405
29406+  qmail_to(&qqt,srs_result.s);
29407
29408+  qqx = qmail_close(&qqt);
29409+  if (*qqx) strerr_die2x(*qqx == 'D' ? 100 : 111,FATAL,qqx + 1);
29410+  strerr_die2x(0,"srsfilter: qp ",num);
29411+
29412+}
29413+
29414diff -ruN ../netqmail-1.06-original/ssl_timeoutio.c netqmail-1.06/ssl_timeoutio.c
29415--- ../netqmail-1.06-original/ssl_timeoutio.c   1970-01-01 01:00:00.000000000 +0100
29416+++ netqmail-1.06/ssl_timeoutio.c       2016-11-22 21:03:57.129527532 +0100
29417@@ -0,0 +1,95 @@
29418+#include "select.h"
29419+#include "error.h"
29420+#include "ndelay.h"
29421+#include "now.h"
29422+#include "ssl_timeoutio.h"
29423+
29424+int ssl_timeoutio(int (*fun)(),
29425+  int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
29426+{
29427+  int n;
29428+  const datetime_sec end = (datetime_sec)t + now();
29429+
29430+  do {
29431+    fd_set fds;
29432+    struct timeval tv;
29433+
29434+    const int r = buf ? fun(ssl, buf, len) : fun(ssl);
29435+    if (r > 0) return r;
29436+
29437+    t = end - now();
29438+    if (t < 0) break;
29439+    tv.tv_sec = (time_t)t; tv.tv_usec = 0;
29440+
29441+    FD_ZERO(&fds);
29442+    switch (SSL_get_error(ssl, r))
29443+    {
29444+    default: return r; /* some other error */
29445+    case SSL_ERROR_WANT_READ:
29446+      FD_SET(rfd, &fds); n = select(rfd + 1, &fds, NULL, NULL, &tv);
29447+      break;
29448+    case SSL_ERROR_WANT_WRITE:
29449+      FD_SET(wfd, &fds); n = select(wfd + 1, NULL, &fds, NULL, &tv);
29450+      break;
29451+    }
29452+
29453+    /* n is the number of descriptors that changed status */
29454+  } while (n > 0);
29455+
29456+  if (n != -1) errno = error_timeout;
29457+  return -1;
29458+}
29459+
29460+int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl)
29461+{
29462+  int r;
29463+
29464+  /* if connection is established, keep NDELAY */
29465+  if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
29466+  r = ssl_timeoutio(SSL_accept, t, rfd, wfd, ssl, NULL, 0);
29467+
29468+  if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
29469+  else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
29470+
29471+  return r;
29472+}
29473+
29474+int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl)
29475+{
29476+  int r;
29477+
29478+  /* if connection is established, keep NDELAY */
29479+  if (ndelay_on(rfd) == -1 || ndelay_on(wfd) == -1) return -1;
29480+  r = ssl_timeoutio(SSL_connect, t, rfd, wfd, ssl, NULL, 0);
29481+
29482+  if (r <= 0) { ndelay_off(rfd); ndelay_off(wfd); }
29483+  else SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
29484+
29485+  return r;
29486+}
29487+
29488+int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl)
29489+{
29490+  int r;
29491+
29492+  SSL_renegotiate(ssl);
29493+  r = ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
29494+  if (r <= 0 || ssl->type == SSL_ST_CONNECT) return r;
29495+
29496+  /* this is for the server only */
29497+  ssl->state = SSL_ST_ACCEPT;
29498+  return ssl_timeoutio(SSL_do_handshake, t, rfd, wfd, ssl, NULL, 0);
29499+}
29500+
29501+int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
29502+{
29503+  if (!buf) return 0;
29504+  if (SSL_pending(ssl)) return SSL_read(ssl, buf, len);
29505+  return ssl_timeoutio(SSL_read, t, rfd, wfd, ssl, buf, len);
29506+}
29507+
29508+int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len)
29509+{
29510+  if (!buf) return 0;
29511+  return ssl_timeoutio(SSL_write, t, rfd, wfd, ssl, buf, len);
29512+}
29513diff -ruN ../netqmail-1.06-original/ssl_timeoutio.h netqmail-1.06/ssl_timeoutio.h
29514--- ../netqmail-1.06-original/ssl_timeoutio.h   1970-01-01 01:00:00.000000000 +0100
29515+++ netqmail-1.06/ssl_timeoutio.h       2016-11-22 21:03:57.130527499 +0100
29516@@ -0,0 +1,21 @@
29517+#ifndef SSL_TIMEOUTIO_H
29518+#define SSL_TIMEOUTIO_H
29519+
29520+#include <openssl/ssl.h>
29521+
29522+/* the version is like this: 0xMNNFFPPS: major minor fix patch status */
29523+#if OPENSSL_VERSION_NUMBER < 0x00906000L
29524+# error "Need OpenSSL version at least 0.9.6"
29525+#endif
29526+
29527+int ssl_timeoutconn(int t, int rfd, int wfd, SSL *ssl);
29528+int ssl_timeoutaccept(int t, int rfd, int wfd, SSL *ssl);
29529+int ssl_timeoutrehandshake(int t, int rfd, int wfd, SSL *ssl);
29530+
29531+int ssl_timeoutread(int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
29532+int ssl_timeoutwrite(int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
29533+
29534+int ssl_timeoutio(
29535+  int (*fun)(), int t, int rfd, int wfd, SSL *ssl, char *buf, int len);
29536+
29537+#endif
29538diff -ruN ../netqmail-1.06-original/str.h netqmail-1.06/str.h
29539--- ../netqmail-1.06-original/str.h     1998-06-15 12:53:16.000000000 +0200
29540+++ netqmail-1.06/str.h 2016-11-22 21:03:57.130527499 +0100
29541@@ -2,6 +2,11 @@
29542 #define STR_H
29543 
29544 extern unsigned int str_copy();
29545+
29546+/* DKIM 1.10
29547+extern unsigned int str_copyb();
29548+   end DKIM 1.10 */
29549+
29550 extern int str_diff();
29551 extern int str_diffn();
29552 extern unsigned int str_len();
29553@@ -9,6 +14,11 @@
29554 extern unsigned int str_rchr();
29555 extern int str_start();
29556 
29557+/* DKIM 1.10 */
29558+#include <sys/types.h>
29559+extern size_t str_cspn();
29560+/* end DKIM 1.10 */
29561+
29562 #define str_equal(s,t) (!str_diff((s),(t)))
29563 
29564 #endif
29565diff -ruN ../netqmail-1.06-original/str_cpyb.c netqmail-1.06/str_cpyb.c
29566--- ../netqmail-1.06-original/str_cpyb.c        1970-01-01 01:00:00.000000000 +0100
29567+++ netqmail-1.06/str_cpyb.c    2016-11-22 21:03:57.130527499 +0100
29568@@ -0,0 +1,53 @@
29569+/*
29570+ * $Log: str_cpyb.c,v $
29571+ * Revision 1.2  2004-10-22 20:30:54+05:30  Cprogrammer
29572+ * added RCS id
29573+ *
29574+ * Revision 1.1  2004-08-15 19:52:35+05:30  Cprogrammer
29575+ * Initial revision
29576+ *
29577+ */
29578+#include "str.h"
29579+
29580+unsigned int
29581+str_copyb(s, t, max)
29582+      register char  *s;
29583+      register char  *t;
29584+      unsigned int    max;
29585+{
29586+      register int    len;
29587+
29588+      len = 0;
29589+      while (max-- > 0)
29590+      {
29591+              if (!(*s = *t))
29592+                      return len;
29593+              ++s;
29594+              ++t;
29595+              ++len;
29596+              if (!(*s = *t))
29597+                      return len;
29598+              ++s;
29599+              ++t;
29600+              ++len;
29601+              if (!(*s = *t))
29602+                     return len;
29603+              ++s;
29604+              ++t;
29605+              ++len;
29606+              if (!(*s = *t))
29607+                      return len;
29608+              ++s;
29609+              ++t;
29610+              ++len;
29611+      }
29612+      return len;
29613+}
29614+
29615+void
29616+getversion_str_cpyb_c()
29617+{
29618+      static char    *x = "$Id: str_cpyb.c,v 1.2 2004-10-22 20:30:54+05:30 Cprogrammer Stab mbhangui $";
29619+      x++;
29620+}
29621+
29622diff -ruN ../netqmail-1.06-original/str_cspn.c netqmail-1.06/str_cspn.c
29623--- ../netqmail-1.06-original/str_cspn.c        1970-01-01 01:00:00.000000000 +0100
29624+++ netqmail-1.06/str_cspn.c    2016-11-22 21:03:57.130527499 +0100
29625@@ -0,0 +1,40 @@
29626+/*
29627+ * $Log: str_cspn.c,v $
29628+ * Revision 1.1  2011-07-12 20:42:00+05:30  Cprogrammer
29629+ * Initial revision
29630+ *
29631+ */
29632+#include "str.h"
29633+/*
29634+ * Span the complement of string s2.
29635+ */
29636+size_t
29637+str_cspn(s1, s2)
29638+       const char *s1;
29639+       register const char *s2;
29640+{
29641+       register const char *p, *spanp;
29642+       register char c, sc;
29643+
29644+       /*
29645+        * Stop as soon as we find any character from s2.  Note that there
29646+        * must be a NUL in s2; it suffices to stop when we find that, too.
29647+        */
29648+       for (p = s1;;) {
29649+               c = *p++;
29650+               spanp = s2;
29651+               do {
29652+                       if ((sc = *spanp++) == c)
29653+                               return (p - 1 - s1);
29654+               } while (sc != 0);
29655+       }
29656+       /* NOTREACHED */
29657+}
29658+
29659+void
29660+getversion_str_cspn_c()
29661+{
29662+       static char    *x = "$Id: str_cspn.c,v 1.1 2011-07-12 20:42:00+05:30 Cprogrammer Exp mbhangui $";
29663+
29664+       x++;
29665+}
29666diff -ruN ../netqmail-1.06-original/strpidt.c netqmail-1.06/strpidt.c
29667--- ../netqmail-1.06-original/strpidt.c 1970-01-01 01:00:00.000000000 +0100
29668+++ netqmail-1.06/strpidt.c     2016-11-22 21:03:57.130527499 +0100
29669@@ -0,0 +1,26 @@
29670+/*
29671+** Copyright 1998 - 2000 Double Precision, Inc.
29672+** See COPYING for distribution information.
29673+*/
29674+
29675+#if    HAVE_CONFIG_H
29676+#include       "config.h"
29677+#endif
29678+#include       "numlib.h"
29679+#include       <string.h>
29680+
29681+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
29682+
29683+char *str_pid_t(pid_t t, char *arg)
29684+{
29685+char   buf[NUMBUFSIZE];
29686+char   *p=buf+sizeof(buf)-1;
29687+
29688+       *p=0;
29689+       do
29690+       {
29691+               *--p= '0' + (t % 10);
29692+               t=t / 10;
29693+       } while(t);
29694+       return (strcpy(arg, p));
29695+}
29696diff -ruN ../netqmail-1.06-original/strsalloc.c netqmail-1.06/strsalloc.c
29697--- ../netqmail-1.06-original/strsalloc.c       1970-01-01 01:00:00.000000000 +0100
29698+++ netqmail-1.06/strsalloc.c   2016-11-22 21:03:57.130527499 +0100
29699@@ -0,0 +1,7 @@
29700+#include "alloc.h"
29701+#include "gen_allocdefs.h"
29702+#include "stralloc.h"
29703+#include "strsalloc.h"
29704+
29705+GEN_ALLOC_readyplus(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus)
29706+GEN_ALLOC_append(strsalloc,stralloc,sa,len,a,i,n,x,10,strsalloc_readyplus,strsalloc_append)
29707diff -ruN ../netqmail-1.06-original/strsalloc.h netqmail-1.06/strsalloc.h
29708--- ../netqmail-1.06-original/strsalloc.h       1970-01-01 01:00:00.000000000 +0100
29709+++ netqmail-1.06/strsalloc.h   2016-11-22 21:03:57.130527499 +0100
29710@@ -0,0 +1,12 @@
29711+#ifndef STRSALLOC_H
29712+#define STRSALLOC_H
29713+
29714+#include "stralloc.h"
29715+
29716+#include "gen_alloc.h"
29717+
29718+GEN_ALLOC_typedef(strsalloc,stralloc,sa,len,a)
29719+extern int strsalloc_readyplus();
29720+extern int strsalloc_append();
29721+
29722+#endif
29723diff -ruN ../netqmail-1.06-original/strtimet.c netqmail-1.06/strtimet.c
29724--- ../netqmail-1.06-original/strtimet.c        1970-01-01 01:00:00.000000000 +0100
29725+++ netqmail-1.06/strtimet.c    2016-11-22 21:03:57.130527499 +0100
29726@@ -0,0 +1,26 @@
29727+/*
29728+** Copyright 1998 - 2000 Double Precision, Inc.
29729+** See COPYING for distribution information.
29730+*/
29731+
29732+#if    HAVE_CONFIG_H
29733+#include       "config.h"
29734+#endif
29735+#include       "numlib.h"
29736+#include       <string.h>
29737+
29738+static const char rcsid[]="$Id: qmail-maildir++.patch,v 1.1.1.1.2.1 2005/01/19 23:35:23 tomcollins Exp $";
29739+
29740+char *str_time_t(time_t t, char *arg)
29741+{
29742+char   buf[NUMBUFSIZE];
29743+char   *p=buf+sizeof(buf)-1;
29744+
29745+       *p=0;
29746+       do
29747+       {
29748+               *--p= '0' + (t % 10);
29749+               t=t / 10;
29750+       } while(t);
29751+       return (strcpy(arg, p));
29752+}
29753diff -ruN ../netqmail-1.06-original/surblfilter.9 netqmail-1.06/surblfilter.9
29754--- ../netqmail-1.06-original/surblfilter.9     1970-01-01 01:00:00.000000000 +0100
29755+++ netqmail-1.06/surblfilter.9 2016-11-22 21:03:57.130527499 +0100
29756@@ -0,0 +1,80 @@
29757+.TH surblfilter 8
29758+.SH NAME
29759+surblfilter \- qmail SURBL blocklist interface
29760+.SH SYNOPSIS
29761+.B surblfilter
29762+[
29763+.B \-v
29764+]
29765+[
29766+.B \-c
29767+]
29768+[
29769+.B \-t
29770+]
29771+.SH DESCRIPTION
29772+\fBSURBL\fRs are lists of web sites that have appeared in unsolicited messages. Unlike most lists,
29773+SURBLs are not lists of message senders.
29774+
29775+Web sites seen in unsolicited messages tend to be more stable than the rapidly changing botnet
29776+IP addresses used to send the vast majority of them. Sender lists like zen.spamhaus.org can be
29777+used in a first stage filter to help identify 80% to 90% of unsolicited messages. SURBLs can
29778+help find about 75% of the otherwise difficult, remaining unsolicited messages in a second
29779+stage filter. Used together with sender lists, SURBLs have proven to be a highly-effective way
29780+to detect 95% of unsolicited messages.
29781+
29782+\fBsurblfilter\fR reads an rfc822 email on stdin, extracts URL and checks them against SURBL.
29783+\fBsurblfilter\fR can be used as a filter using \fBqmail-qfilter(1)\fR. It can also be used
29784+as a qmail-queue(8) frontend by setting QMAILQUEUE environment variable to a wrapper
29785+QMAILHOME/bin/surblqueue. You can define \fBSURBLQUEUE\fR environment variable to make
29786+\fBsurblqueue\fR execute something other than \fBqmail-queue\fR(8).
29787+
29788+\fBsurblfilter\fR uses \fBQMAILRCPTS\fR environment variable to get the recipient list. You can
29789+whitelist recipients by having the email addresses in \fIsurblrcpt\fR control file. You can
29790+change the name of this control file by setting \fBSURBLRCPT\fR environment variable.
29791+.PP
29792+\fBsurblfilter\fR uses the control file \fIsurbldomainwhite\fR to whitelist a domain.
29793+
29794+.PP
29795+The default SURBL list that is used is \fBmulti.surbl.org\fR. You can use a different list
29796+by setting the \fIsurbldomain\fR control file.
29797+
29798+.PP
29799+\fBsurblfilter\fR caches results in QMAILHOME/control/cache. The filename represents the domain.
29800+If a file as permission 0600, it means that the domain was blacklisted. The entries are
29801+cached for a default of 300 seconds. You can change this by setting \fIcachelifetime\fR
29802+control file. This directory should be owned by the uid set for running qmail-smtpd(8).
29803+
29804+\fBsurblfilter\fR removes all leading host names, subdomains, www., randomized subdomains, etc. In
29805+order to determine the level of domain check, it uses the control files \fBlevel3-tlds\fR and
29806+\fBlevel2-tlds\fR. For any domain found in \fBlevel3-tlds\fR, it checks the domain at level4. For any
29807+domain found in \fBlevel2-tlds\fR, it checks the domain at level3. For all domains not found in
29808+these two control files, it checks the domain at level2. Please look at http://www.surbl.org/guidelines
29809+
29810+for more details.
29811+
29812+.SH OPTIONS
29813+.PP
29814+.TP
29815+\fB-v\fR
29816+Use debug mode
29817+
29818+.TP
29819+\fB-c\fR
29820+Do not cache results
29821+
29822+.TP
29823+\fB-t\fR
29824+Do dns text query to get the reason. This option will slow \fBsurblfilter\fR. Remember that
29825+last octet of the IP address obtained for the domain gives you the reason for the block
29826+
29827+.SH RETURN VALUE
29828+\fBsurbfilter\fR returns 88 if the domain is blocked and prints the reason on standard error.
29829+This allows for qmail-smtpd(8) to print a permanent error during a SMTP session.
29830+It returns 111 for all temporary errors. It returns 0 if the message does not contain any
29831+domain blocked by SURBL
29832+
29833+.SH "SEE ALSO"
29834+qmail-smtpd(8),
29835+qmail-queue(8),
29836+qmail-qfilter(1)
29837diff -ruN ../netqmail-1.06-original/surblfilter.c netqmail-1.06/surblfilter.c
29838--- ../netqmail-1.06-original/surblfilter.c     1970-01-01 01:00:00.000000000 +0100
29839+++ netqmail-1.06/surblfilter.c 2017-05-10 21:54:26.719100696 +0200
29840@@ -0,0 +1,881 @@
29841+/*
29842+ * $Log: surblfilter.c,v $
29843+ * Revision 1.3  2011-07-13 22:11:13+05:30  Cprogrammer
29844+ * skip surblrcpt if QMAILRCPTS is not defined
29845+ *
29846+ * Revision 1.2  2011-07-13 22:02:13+05:30  Cprogrammer
29847+ * added surblrcpt functionality
29848+ *
29849+ * Revision 1.1  2011-07-13 20:56:34+05:30  Cprogrammer
29850+ * Initial revision
29851+ *
29852+ */
29853+#include <unistd.h>
29854+#include <fcntl.h>
29855+#include <errno.h>
29856+#include <ctype.h>
29857+#include <time.h>
29858+#include <sys/stat.h>
29859+
29860+#include <sys/socket.h>
29861+#include <netinet/in.h>
29862+#include <arpa/inet.h>
29863+#ifdef DARWIN
29864+#include <nameser8_compat.h>
29865+#endif
29866+#include <arpa/nameser.h>
29867+#include <resolv.h>
29868+#include <netdb.h>
29869+
29870+#include "alloc.h"
29871+#include "sgetopt.h"
29872+#include "error.h"
29873+#include "scan.h"
29874+#include "str.h"
29875+#include "case.h"
29876+#include "constmap.h"
29877+#include "auto_qmail.h"
29878+#include "stralloc.h"
29879+#include "env.h"
29880+#include "control.h"
29881+#include "strerr.h"
29882+#include "substdio.h"
29883+#include "getln.h"
29884+#include "byte.h"
29885+#include "dns.h"
29886+#include "ip.h"
29887+#include "ipalloc.h"
29888+#include "mess822.h"
29889+#include "base64.h"
29890+
29891+#define FATAL "surblfilter: fatal: "
29892+
29893+char           *dns_text(char *);
29894+
29895+stralloc        line = { 0 };
29896+int             debug = 0, do_text = 0, do_cache = 1;
29897+static int      cachelifetime = 300;
29898+stralloc        whitelist = { 0 };
29899+stralloc        surbldomain = { 0 };
29900+
29901+/*- SURBL: RCPT whitelist. */
29902+stralloc        srw = { 0 };
29903+int             srwok = 0;
29904+struct constmap mapsrw;
29905+
29906+/*- 2 level tld */
29907+stralloc        l2 = { 0 };
29908+int             l2ok = 0;
29909+struct constmap mapl2;
29910+/*- 3 level tld */
29911+stralloc        l3 = { 0 };
29912+int             l3ok = 0;
29913+struct constmap mapl3;
29914+
29915+static char     ssinbuf[1024];
29916+static substdio ssin = SUBSTDIO_FDBUF(read, 0, ssinbuf, sizeof ssinbuf);
29917+static char     ssoutbuf[512];
29918+static substdio ssout = SUBSTDIO_FDBUF(write, 1, ssoutbuf, sizeof ssoutbuf);
29919+static char     sserrbuf[512];
29920+static substdio sserr = SUBSTDIO_FDBUF(write, 2, sserrbuf, sizeof(sserrbuf));
29921+
29922+void
29923+out(char *str)
29924+{
29925+       if (!str || !*str)
29926+               return;
29927+       if (substdio_puts(&ssout, str) == -1)
29928+               strerr_die2sys(111, FATAL, "write: ");
29929+       return;
29930+}
29931+
29932+void
29933+print_debug(char *arg1, char *arg2, char *arg3)
29934+{
29935+       if (!debug)
29936+               return;
29937+       if (arg1 && substdio_puts(&sserr, arg1) == -1)
29938+               _exit(1);
29939+       if (arg2 && substdio_puts(&sserr, arg2) == -1)
29940+               _exit(1);
29941+       if (arg3 && substdio_puts(&sserr, arg3) == -1)
29942+               _exit(1);
29943+       if ((arg1 || arg2 || arg3) && substdio_puts(&sserr, "\n"))
29944+               _exit(1);
29945+       if (substdio_flush(&sserr) == -1)
29946+               _exit(1);
29947+}
29948+
29949+void
29950+die_write()
29951+{
29952+       strerr_die2sys(111, FATAL, "write: ");
29953+       return;
29954+}
29955+
29956+void
29957+flush()
29958+{
29959+       if (substdio_flush(&ssout) == -1)
29960+               strerr_die2sys(111, FATAL, "write: ");
29961+       return;
29962+}
29963+
29964+void
29965+logerr(char *s)
29966+{
29967+       if (substdio_puts(&sserr, s) == -1)
29968+               _exit(1);
29969+}
29970+
29971+void
29972+logerrf(char *s)
29973+{
29974+       if (substdio_puts(&sserr, s) == -1)
29975+               _exit(1);
29976+       if (substdio_flush(&sserr) == -1)
29977+               _exit(1);
29978+}
29979+
29980+void
29981+my_error(char *s1, char *s2, int exit_val)
29982+{
29983+       logerr(s1);
29984+       if (s2) {
29985+               logerr(": ");
29986+               logerr(s2);
29987+       }
29988+       if (exit_val > 0) {
29989+               logerr(": ");
29990+               logerr(error_str(errno));
29991+       }
29992+       logerrf("\n");
29993+       _exit(exit_val > 0 ? exit_val : 0 - exit_val);
29994+}
29995+
29996+void
29997+die_nomem()
29998+{
29999+       substdio_flush(&ssout);
30000+       substdio_puts(&sserr, "surblfilter: out of memory\n");
30001+       substdio_flush(&sserr);
30002+       _exit(1);
30003+}
30004+
30005+void
30006+die_soft()
30007+{
30008+       substdio_flush(&ssout);
30009+       substdio_puts(&sserr, "surblfilter: DNS temporary failure\n");
30010+       substdio_flush(&sserr);
30011+       _exit(1);
30012+}
30013+
30014+void
30015+die_hard()
30016+{
30017+       substdio_flush(&ssout);
30018+       substdio_puts(&sserr, "surblfilter: DNS permanent failure\n");
30019+       substdio_flush(&sserr);
30020+       _exit(1);
30021+}
30022+
30023+void
30024+die_control()
30025+{
30026+       substdio_flush(&ssout);
30027+       substdio_puts(&sserr, "surblfilter: unable to read controls\n");
30028+       substdio_flush(&sserr);
30029+       _exit(1);
30030+}
30031+
30032+static unsigned short
30033+getshort(unsigned char *cp)
30034+{
30035+       return (cp[0] << 8) | cp[1];
30036+}
30037+
30038+static char *
30039+strdup(const char *str)
30040+{
30041+       size_t siz;
30042+       char *copy;
30043+
30044+       siz = str_len((char *) str) + 1;
30045+       if (!(copy = alloc(siz)))
30046+               return((char *) 0);
30047+       byte_copy(copy, siz, (char *) str);
30048+       return(copy);
30049+}
30050+
30051+/*
30052+ * we always return a null-terminated string which has been malloc'ed.  The string
30053+ * is always in the tag=value form.  If a temporary or permanent error occurs,
30054+ * the string will be exactly "e=perm;" or "e=temp;".
30055+ * Note that it never returns NULL.
30056+ */
30057+char           *
30058+dns_text(char *dn)
30059+{
30060+        u_char          response[PACKETSZ + PACKETSZ + 1];      /* response */
30061+       int             responselen;                    /* buffer length */
30062+       int             rc;                             /* misc variables */
30063+       int             ancount, qdcount;               /* answer count and query count */
30064+       u_short         type, rdlength;                 /* fields of records returned */
30065+       u_char         *eom, *cp;
30066+       u_char          buf[PACKETSZ + 1];              /* we're storing a TXT record here, not just a DNAME */
30067+       u_char         *bufptr;
30068+
30069+        for (rc = 0, responselen = PACKETSZ;rc < 2;rc++) {
30070+                if ((responselen = res_query(dn, C_IN, T_TXT, response, responselen)) < 0) {
30071+                        if (h_errno == TRY_AGAIN)
30072+                                return strdup("e=temp;");
30073+                        else
30074+                                return strdup("e=perm;");
30075+                }
30076+                if (responselen <= PACKETSZ)
30077+                        break;
30078+               else
30079+                if (responselen >= (2 * PACKETSZ))
30080+                       return strdup("e=perm;");
30081+       }
30082+       qdcount = getshort(response + 4);       /* http://crynwr.com/rfc1035/rfc1035.html#4.1.1. */
30083+       ancount = getshort(response + 6);
30084+       eom = response + responselen;
30085+       cp = response + HFIXEDSZ;
30086+       while (qdcount-- > 0 && cp < eom) {
30087+               rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME);
30088+               if (rc < 0)
30089+                       return strdup("e=perm;");
30090+               cp += rc + QFIXEDSZ;
30091+       }
30092+       while (ancount-- > 0 && cp < eom) {
30093+               if ((rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME)) < 0)
30094+                       return strdup("e=perm;");
30095+               cp += rc;
30096+               if (cp + RRFIXEDSZ >= eom)
30097+                       return strdup("e=perm;");
30098+               type = getshort(cp + 0);        /* http://crynwr.com/rfc1035/rfc1035.html#4.1.3. */
30099+               rdlength = getshort(cp + 8);
30100+               cp += RRFIXEDSZ;
30101+               if (type != T_TXT) {
30102+                       cp += rdlength;
30103+                       continue;
30104+               }
30105+               bufptr = buf;
30106+               while (rdlength && cp < eom) {
30107+                       unsigned int    cnt;
30108+
30109+                       cnt = *cp++;            /* http://crynwr.com/rfc1035/rfc1035.html#3.3.14. */
30110+                        if (bufptr - buf + cnt + 1 >= (2 * PACKETSZ))
30111+                               return strdup("e=perm;");
30112+                       if (cp + cnt > eom)
30113+                               return strdup("e=perm;");
30114+                       byte_copy((char *) bufptr, cnt, (char *) cp);
30115+                       rdlength -= cnt + 1;
30116+                       bufptr += cnt;
30117+                       cp += cnt;
30118+                       *bufptr = '\0';
30119+               }
30120+               return (char *) strdup((char *) buf);
30121+       }
30122+       return strdup("e=perm;");
30123+}
30124+
30125+static char    *
30126+uri_decode(char *str, size_t str_len, char **strend)
30127+{
30128+       size_t          i = 0, j = 0, found;
30129+       int             pasthostname = 0;
30130+       char           *str_bits = "\r\n\t \'\"<>()";
30131+
30132+       for (i = 0; i < str_len; i++, j++) {
30133+               if (str[i] == '%' || (!pasthostname && str[i] == '=')) {
30134+                       if (i + 2 < str_len) {
30135+                               if (isxdigit(str[i + 1]) && isxdigit(str[i + 2])) {
30136+                                       int             c1 = str[i + 1];
30137+                                       int             c2 = str[i + 2];
30138+                                       int             num = ( /* first character */
30139+                                                                                         ((c1 & 0xF)   /* take right half */
30140+                                                                                          +(9 * (c1 >> 6)))    /* add 9 if character is a-f or A-F */
30141+                                                                                         <<4   /* pack into the left half of the byte */
30142+                                               ) | (   /* second character */
30143+                                                               (c2 & 0xF)
30144+                                                               + (9 * (c2 >> 6))
30145+                                               );              /* leave it as the left half */
30146+                                       str[j] = tolower(num);
30147+                                       i += 2;
30148+                                       continue;
30149+                               }
30150+                       }
30151+               }
30152+               if (!pasthostname && (str[i] == '?' || str[i] == '/' || str[i] == '\\'))
30153+                       pasthostname = 1;
30154+               if (i + 1 < str_len) {
30155+                       if (str[i] == '=' && str[i + 1] == '\n') {
30156+                               j -= 1;
30157+                               i += 1;
30158+                               continue;
30159+                       }
30160+               }
30161+               if (i + 2 < str_len) {
30162+                       if (str[i] == '=' && str[i + 1] == '\r' && str[i + 2] == '\n') {
30163+                               j -= 1;
30164+                               i += 2;
30165+                               continue;
30166+                       }
30167+               }
30168+               found = str_chr(str_bits, str[i]);
30169+               if (str_bits[found])
30170+                       break;
30171+               str[j] = tolower(str[i]);
30172+       }
30173+       str[j] = '\0';
30174+       *strend = str + j + 1;
30175+       return str;
30176+}
30177+
30178+/*
30179+ * Returns:
30180+ * -1 on error
30181+ *  0 if domain wasn't cached
30182+ *  1 if domain was cached, and not blacklisted
30183+ *  2 if domain was cached, and blacklisted.
30184+ *
30185+ * text != NULL: host blacklisted, text == reason.
30186+ */
30187+static int
30188+cachefunc(char *uri, size_t urilen, char **text, int flag)
30189+{
30190+       static char     inbuf[2048];
30191+       static stralloc cachefile = { 0 }, reason = { 0 };
30192+       int             fd, i, n, textlen, match;
30193+       struct stat     st;
30194+       substdio        ss;
30195+
30196+       if (!do_cache)
30197+               return (0);
30198+       if (uri[i = str_chr(uri, '/')]) {
30199+               errno = EINVAL;
30200+               return (-1);
30201+       }
30202+       if (!stralloc_copys(&cachefile, "control/cache", 13))
30203+               die_nomem();
30204+       if (!stralloc_0(&cachefile))
30205+               die_nomem();
30206+       if (access(cachefile.s, F_OK))
30207+               return (0);
30208+       cachefile.len--;
30209+       if (!stralloc_append(&cachefile, "/"))
30210+               die_nomem();
30211+       if (!stralloc_cats(&cachefile, uri))
30212+               die_nomem();
30213+       if (!stralloc_0(&cachefile))
30214+               die_nomem();
30215+       if (flag) { /*- add the cache */
30216+               if (!access(cachefile.s, F_OK))
30217+                       return (0);
30218+               if ((fd = open(cachefile.s, O_CREAT|O_WRONLY, *text ? 0600 : 0644)) == -1)
30219+                       my_error(cachefile.s, 0, 2);
30220+               if (*text) {
30221+                       textlen = str_len(*text);
30222+                       if ((n = write(fd, *text, textlen)) == -1) {
30223+                               close(fd);
30224+                               my_error("write", 0, 1);
30225+                       }
30226+               }
30227+               if (close(fd))
30228+                       my_error(cachefile.s, 0, 1);
30229+       } else {
30230+               if (stat(cachefile.s, &st) == -1) {
30231+                       if (errno == ENOENT)
30232+                               return (0);
30233+                       my_error("stat", 0, 1);
30234+                       return -1;
30235+               }
30236+               if (time(0) > st.st_mtime + cachelifetime) {
30237+                       if (unlink(cachefile.s)) {
30238+                               my_error("unlink", 0, 1);
30239+                               return -1;
30240+                       }
30241+                       return (0);
30242+               }
30243+               if ((fd = open(cachefile.s, O_RDONLY)) == -1)
30244+                       my_error(cachefile.s, 0, 2);
30245+               substdio_fdbuf(&ss, read, fd, inbuf, sizeof(inbuf));
30246+               if (getln(&ss, &reason, &match, '\n') == -1) {
30247+                       close(fd);
30248+                       return -1;
30249+               }
30250+               *text = reason.s;
30251+               close(fd);
30252+               return (((st.st_mode & 07777) == 0600) ? 2 : 1);
30253+       }
30254+       return (0);
30255+}
30256+
30257+static int
30258+getdnsip(stralloc *ip, stralloc *domain, int *code)
30259+{
30260+       char            x[IPFMT];
30261+       ipalloc         ia = { 0 };
30262+       int             len;
30263+
30264+       if (!stralloc_copys(ip, ""))
30265+               die_nomem();
30266+       switch(dns_ip(&ia, domain))
30267+       {
30268+       case DNS_MEM:
30269+               die_nomem();
30270+       case DNS_SOFT:
30271+               die_soft();
30272+       case DNS_HARD:
30273+               return 0;
30274+       case 1:
30275+               if (ia.len <= 0)
30276+                       die_soft();
30277+       }
30278+       if (code)
30279+               *code = *(&ia.ix->ip.d[3]);
30280+       len = ip_fmt(x, &ia.ix->ip);
30281+       if (!stralloc_copyb(ip, x, len))
30282+               die_nomem();
30283+       return 0;
30284+}
30285+
30286+/*- SURBL: Check surbl rcpt whitelist.  */
30287+int
30288+srwcheck(char *arg, int len)
30289+{
30290+       int             j;
30291+
30292+       if (!srwok)
30293+               return 0;
30294+       if (constmap(&mapsrw, arg, len))
30295+               return 1;
30296+       if ((j = byte_rchr(arg, len, '@')) < (len - 1)) {
30297+               if (constmap(&mapsrw, arg + j, len - j))
30298+                       return 1;
30299+       }
30300+       return 0;
30301+}
30302+
30303+int
30304+l2check(char *arg, int len)
30305+{
30306+       if (!l2ok)
30307+               return (0);
30308+       if (constmap(&mapl2, arg, len))
30309+               return 1;
30310+       return (0);
30311+}
30312+
30313+int
30314+l3check(char *arg, int len)
30315+{
30316+       if (!l3ok)
30317+               return (0);
30318+       if (constmap(&mapl3, arg, len))
30319+               return 1;
30320+       return (0);
30321+}
30322+
30323+/*
30324+ * Returns -1 on error.
30325+ * Returns 0 if host does not exist.
30326+ * Returns 1 if host exists.
30327+ */
30328+static int
30329+checkwhitelist(char *hostname, int hostlen)
30330+{
30331+       int             len;
30332+       char           *ptr;
30333+
30334+       for (ptr = whitelist.s, len = 0;len < whitelist.len;) {
30335+               if (!str_diffn(hostname, ptr, hostlen))
30336+                       return (1);
30337+               len += (str_len(ptr) + 1);
30338+               ptr = whitelist.s + len;
30339+       }
30340+       return (0);
30341+}
30342+
30343+static int
30344+getreason(int code, char **text)
30345+{
30346+       static stralloc reason = { 0 };
30347+
30348+       if (!stralloc_copyb(&reason, "blacklisted by ", 15))
30349+               die_nomem();
30350+       if (code & 64 && !stralloc_cats(&reason, debug ? "prolocation/jwspamspy" : "[jp]"))
30351+               die_nomem();
30352+       if (code & 32 && !stralloc_cats(&reason, debug ? "abusebutler " : "[ab]"))
30353+               die_nomem();
30354+       if (code & 16 && !stralloc_cats(&reason, debug ? "outblaze " : "[ob]"))
30355+               die_nomem();
30356+       if (code & 8 && !stralloc_cats(&reason, debug ? "phising " : "[ph]"))
30357+               die_nomem();
30358+       if (code & 2 && !stralloc_cats(&reason, debug ? "spamcop " : "[sc]"))
30359+               die_nomem();
30360+       if (code & 4 && !stralloc_cats(&reason, debug ? "w.stearns " : "[ws]"))
30361+               die_nomem();
30362+       if (!stralloc_0(&reason))
30363+               die_nomem();
30364+       *text = reason.s;
30365+       return (code >= 2);
30366+}
30367+
30368+static int
30369+checksurbl(char *uri, int urilen, char *surbldomain, char **text)
30370+{
30371+       static stralloc ip = { 0 };
30372+       static stralloc host = { 0 };
30373+       int             i, code = 0;
30374+
30375+       if ((i = checkwhitelist(uri, urilen)) == -1)
30376+               return -1;
30377+       else
30378+       if (i)
30379+               return (0);
30380+       if (stralloc_copys(&host, uri) == 0)
30381+               die_nomem();
30382+       if (stralloc_append(&host, ".") == 0)
30383+               die_nomem();
30384+       if (stralloc_cats(&host, surbldomain) == 0)
30385+               die_nomem();
30386+       if (!stralloc_0(&host))
30387+               die_nomem();
30388+       if (getdnsip(&ip, &host, &code) == -1)
30389+               return -1;
30390+       if (do_text && ip.len > 0) {
30391+               if (text) {
30392+                       if ((*text = dns_text(host.s)))
30393+                               return 2;
30394+               }
30395+               return 1;
30396+       }
30397+       if (code > 1)
30398+               return (getreason(code, text) ? 2 : 0);
30399+       return 0;
30400+}
30401+
30402+static int
30403+num_domains(const char *s)
30404+{
30405+       int             r = *s ? 1 : 0;
30406+
30407+       while (*s) {
30408+               if (*s++ == '.')
30409+                       ++r;
30410+       }
30411+       return r;
30412+}
30413+
30414+static char *
30415+remove_subdomains(char *orig, int output_domains)
30416+{
30417+       char           *s = orig + str_len((char *) orig);
30418+       int             dots = 0;
30419+
30420+       while (s > orig) {
30421+               if (*s == '.')
30422+                       ++dots;
30423+               if (dots == output_domains) {
30424+                       ++s;
30425+                       break;
30426+               }
30427+               --s;
30428+       }
30429+       return s;
30430+}
30431+
30432+/*
30433+ * Returns 0 if URI was erronous.
30434+ *         1 if URI was not blacklisted.
30435+ *         2 if URI was blacklisted.
30436+ */
30437+static int
30438+checkuri(char **ouri, char **text, size_t textlen)
30439+{
30440+       char           *uri = *ouri, *uriend, *ptr;
30441+       char            ipuri[IPFMT];
30442+       size_t          urilen = 0;
30443+       ip_addr         ip;
30444+       int             cached, blacklisted, i, level;
30445+
30446+       if (case_diffb(uri, 4, "http"))
30447+               return 0;
30448+       uri += 4;
30449+
30450+       /*- Check and skip http[s]?:[/\\][/\\]?  */
30451+       if (*uri == 's')
30452+               uri++;
30453+       if (*uri == ':' && (uri[1] == '/' || uri[1] == '\\'))
30454+               uri += 2;
30455+       else
30456+               return 0;
30457+       if (*uri == '/' || *uri == '\\')
30458+               uri++;
30459+       if (!isalpha(*uri) && !isdigit(*uri))
30460+               return 0;
30461+       uri_decode(uri, textlen, &uriend);
30462+       *ouri = uriend;
30463+       print_debug("Full    URI: ", uri, 0);
30464+       uri[(urilen = str_cspn(uri, "/\\?"))] = '\0';
30465+       if (uri[i = str_chr(uri, '@')])
30466+               uri += (i + 1);
30467+       uri[i = str_chr(uri, ':')] = 0;
30468+       if (ip_scan(uri, &ip)) {
30469+               ip_fmt(ipuri, &ip);
30470+               uri = ipuri;
30471+               print_debug("Proper IP: ", uri, 0);
30472+       } else {
30473+               urilen = str_len(uri);
30474+               print_debug("Full domain: ", uri, 0);
30475+               level = num_domains(uri);
30476+               if (level > 2) {
30477+                       ptr = remove_subdomains(uri, 3);
30478+                       if (l3check(ptr, str_len(ptr)))
30479+                               uri = remove_subdomains(uri, 4);
30480+                       else {
30481+                               ptr = remove_subdomains(uri, 2);
30482+                               if (l2check(ptr, str_len(ptr)))
30483+                                       uri = remove_subdomains(uri, 3);
30484+                               else
30485+                                       uri = remove_subdomains(uri, 2);
30486+                       }
30487+               } else
30488+               if (level > 1) {
30489+                       ptr = remove_subdomains(uri, 2);
30490+                       if (l2check(ptr, str_len(ptr)))
30491+                               uri = remove_subdomains(uri, 3);
30492+                       else
30493+                               uri = remove_subdomains(uri, 2);
30494+               }
30495+               print_debug("       Part: ", uri, 0);
30496+       }
30497+       urilen = str_len(uri);
30498+       cached = 1;
30499+       blacklisted = 0;
30500+       switch (cachefunc(uri, urilen, text, 0))
30501+       {
30502+       case 0:
30503+               cached = 0;
30504+               break;
30505+       case 1:
30506+               blacklisted = 0;
30507+               break;
30508+       case 2:
30509+               blacklisted = 1;
30510+               break;
30511+       }
30512+       if (cached == 0) {
30513+               switch (checksurbl(uri, urilen, surbldomain.s, text))
30514+               {
30515+               case -1:
30516+                       return -1;
30517+               case 0:
30518+                       blacklisted = 0;
30519+                       *text = (char *) 0;
30520+                       print_debug(uri, ": not blacklisted", 0);
30521+                       break;
30522+               case 1:
30523+                       *text = "No reason given";
30524+                       blacklisted = 1;
30525+                       print_debug(uri, ": blacklisted. reason - ", *text);
30526+                       break;
30527+               case 2:
30528+                       blacklisted = 2;
30529+                       print_debug(uri, ": blacklisted. reason - ", *text);
30530+                       break;
30531+               }
30532+               cachefunc(uri, urilen, text, 1);
30533+       }
30534+       return (blacklisted);
30535+}
30536+
30537+#define DEF_SURBL_DOMAIN "multi.surbl.org"
30538+
30539+static int      do_surbl = 1;
30540+
30541+static void
30542+setup()
30543+{
30544+       char           *x, *y, *rcpt;
30545+       int             i;
30546+
30547+       if ((rcpt = env_get("QMAILRCPTS"))) {
30548+               if ((srwok = control_readfile(&srw, "control/surblrcpt", 0)) == -1)
30549+                       die_control();
30550+               if (srwok && !constmap_init(&mapsrw, srw.s, srw.len, 0))
30551+                       die_nomem();
30552+       }
30553+       for (x = y = rcpt, i = 0;rcpt && *x;x++, i++) {
30554+               if (*x == '\n') {
30555+                       *x = 0;
30556+                       if (srwcheck(y, i)) {
30557+                               do_surbl = 0;
30558+                               return;
30559+                       }
30560+                       y = x + 1;
30561+                       *x = '\n';
30562+                       i = 0;
30563+               }
30564+       }
30565+       if ((l2ok = control_readfile(&l2, "control/level2-tlds", 0)) == -1)
30566+               die_control();
30567+       if (l2ok && !constmap_init(&mapl2, l2.s, l2.len, 0))
30568+               die_nomem();
30569+       if ((l3ok = control_readfile(&l3, "control/level3-tlds", 0)) == -1)
30570+               die_control();
30571+       if (l3ok && !constmap_init(&mapl3, l3.s, l3.len, 0))
30572+               die_nomem();
30573+       switch (control_readline(&surbldomain, "control/surbldomain"))
30574+       {
30575+       case -1:
30576+               die_control();
30577+       case 0:
30578+               if (!stralloc_copys(&surbldomain, DEF_SURBL_DOMAIN))
30579+                       die_nomem();
30580+               /*- flow through */
30581+       case 1:
30582+               if (!stralloc_0(&surbldomain))
30583+                       die_nomem();
30584+       }
30585+       if ((x = env_get("CACHELIFETIME")))
30586+               scan_int(x, &cachelifetime);
30587+       else
30588+       if (control_readint(&cachelifetime, "control/cachelifetime") == -1)
30589+               die_control();
30590+       if (control_readfile(&whitelist, "control/surbldomainwhite", 0) == -1)
30591+               die_control();
30592+       return;
30593+}
30594+
30595+int
30596+main(int argc, char **argv)
30597+{
30598+       stralloc        base64out = { 0 }, boundary = { 0 };
30599+       stralloc       *ptr;
30600+       char           *x, *reason = 0;
30601+       int             opt, in_header = 1, i, total_bl = 0, blacklisted, match, html_plain_text,
30602+                                       base64_decode, found_content_type = 0;
30603+
30604+       if (!(x = env_get("SURBL")))
30605+               do_surbl = 0;
30606+       while ((opt = getopt(argc, argv, "vtc")) != opteof) {
30607+               switch (opt) {
30608+               case 'c':
30609+                       do_cache = 0;
30610+                       break;
30611+               case 'v':
30612+                       debug = 1;
30613+                       break;
30614+               case 't':
30615+                       do_text = 1;
30616+                       break;
30617+               }
30618+       }
30619+       if (chdir(auto_qmail) == -1)
30620+               die_control();
30621+       if (do_surbl)
30622+               setup();
30623+       for (html_plain_text = base64_decode = 0;;) {
30624+               if (getln(&ssin, &line, &match, '\n') == -1)
30625+                       my_error("getln: ", 0, 1);
30626+               if (!match && line.len == 0)
30627+                       break;
30628+               if (substdio_put(&ssout, line.s, line.len))
30629+                       die_write();
30630+               if (!do_surbl)
30631+                       continue;
30632+               if (in_header) {
30633+                       if (!str_diffn(line.s, "Content-Type: ", 14)) {
30634+                               found_content_type = 1;
30635+                       }
30636+                       if (found_content_type) {
30637+                               for (i = 0;i < line.len; i++) {
30638+                                       if (case_startb(line.s + i, line.len - i, "boundary=")) {
30639+                                               if (line.s[i + 9] == '\"' && line.s[line.len -2] == '\"')
30640+                                               {
30641+                                                       if (!stralloc_copyb(&boundary, line.s + i + 10, line.len -i - 12))
30642+                                                               die_nomem();
30643+                                               } else
30644+                                               if (!stralloc_copyb(&boundary, line.s + i + 9, line.len - i - 10))
30645+                                                       die_nomem();
30646+                                               if (!stralloc_0(&boundary))
30647+                                                       die_nomem();
30648+                                               boundary.len--;
30649+                                       }
30650+                               }
30651+                       }
30652+                       if (!mess822_ok(&line))
30653+                               in_header = 0;
30654+               } else {
30655+                       if (!str_diffn(line.s, "Content-Type: ", 14)) {
30656+                               if (!str_diffn(line.s + 14, "message/rfc822", 14) ||
30657+                                       !str_diffn(line.s + 14, "text/html", 9) ||
30658+                                       !str_diffn(line.s + 14, "text/plain", 10))
30659+                                               html_plain_text = 1;
30660+                               else
30661+                                               html_plain_text = 0;
30662+                       }
30663+                       if (html_plain_text && !str_diffn(line.s, "Content-Transfer-Encoding: ", 27)) {
30664+                               if (!str_diffn(line.s + 27, "base64", 6))
30665+                                       base64_decode = 1;
30666+                               else
30667+                                       base64_decode = 0;
30668+                       }
30669+                       if (line.len == 1)
30670+                               continue;
30671+                       if (base64_decode) {
30672+                               if (!str_diffn(line.s, "Content-", 8))
30673+                                       continue;
30674+                               if (!str_diffn(line.s + 2, boundary.s, boundary.len)) {
30675+                                       base64_decode = 0;
30676+                                       continue;
30677+                               }
30678+                               if (b64decode((const unsigned char *) line.s, line.len - 1, &base64out) == -1)
30679+                                       die_nomem();
30680+                               ptr = &base64out;
30681+                       } else
30682+                               ptr = &line;
30683+                       for (blacklisted = -1, i = 0;i < ptr->len; i++) {
30684+                               if (case_startb(line.s + i, ptr->len - i, "http:")) {
30685+                                       x = ptr->s + i;
30686+                                       switch (checkuri(&x, &reason, ptr->len - i))
30687+                                       {
30688+                                       case -1:
30689+                                               my_error("checkuri", 0, 111);
30690+                                       case 0: /*- no valid uri in line */
30691+                                               blacklisted = 0;
30692+                                               break;
30693+                                       case 1:
30694+                                       case 2:
30695+                                               blacklisted = 1;
30696+                                               break;
30697+                                       }
30698+                               }
30699+                               if (blacklisted == 1) {
30700+                                       total_bl++;
30701+                                       break;
30702+                               }
30703+                       }
30704+               }
30705+       } /*- for (html_plain_text = base64_decode = 0;;) { */
30706+       if (substdio_flush(&ssout) == -1)
30707+               die_write();
30708+       if (do_surbl && total_bl) {
30709+               logerrf("Dmessage contains an URL listed in SURBL blocklist");
30710+               _exit (88); /*- custom error */
30711+       }
30712+       return (0);
30713+}
30714+
30715+void
30716+getversion_surblfilter_c()
30717+{
30718+       static char    *x = "$Id: surblfilter.c,v 1.4 2011-07-13 22:28:32+05:30 Cprogrammer Exp mbhangui $";
30719+
30720+       x++;
30721+}
30722diff -ruN ../netqmail-1.06-original/surblqueue.sh netqmail-1.06/surblqueue.sh
30723--- ../netqmail-1.06-original/surblqueue.sh     1970-01-01 01:00:00.000000000 +0100
30724+++ netqmail-1.06/surblqueue.sh 2016-11-22 21:03:57.131527465 +0100
30725@@ -0,0 +1,33 @@
30726+#!/bin/sh
30727+# I should be called by qmail-smtpd or anything that calls qmail-queue
30728+#
30729+if [ -f /bin/mktemp ] ; then
30730+       MKTEMP=/bin/mktemp
30731+elif [ -f /usr/bin/mktemp ] ; then
30732+       MKTEMP=/usr/bin/mktemp
30733+else
30734+       MKTEMP=mktemp
30735+fi
30736+out=`$MKTEMP -t surblXXXXXXXXXX`
30737+if [ $? -ne 0 ] ; then
30738+       echo "mktemp: unable to create temp files" 1>&2
30739+       exit 111
30740+fi
30741+#
30742+# Redirect standard error to 4 so that qmail_open() will pick up the error
30743+#
30744+QMAIL/bin/surblfilter > $out 2>&4
30745+status=$?
30746+if [ $status -eq 0 ] ; then
30747+       exec 0<$out
30748+       /bin/rm -f $out
30749+       # use SURBLQUEUE to execute queue program (thanks Roberto Puzzanghera)
30750+       if [ "$SURBLQUEUE" != "" -a -x "$SURBLQUEUE" ]; then
30751+               exec $SURBLQUEUE
30752+       else
30753+               exec QMAIL/bin/qmail-queue
30754+       fi
30755+else
30756+       /bin/rm -f $out
30757+       exit $status
30758+fi
30759diff -ruN ../netqmail-1.06-original/tai.h netqmail-1.06/tai.h
30760--- ../netqmail-1.06-original/tai.h     1970-01-01 01:00:00.000000000 +0100
30761+++ netqmail-1.06/tai.h 2016-11-22 21:03:57.131527465 +0100
30762@@ -0,0 +1,34 @@
30763+/*
30764+ * $Log: tai.h,v $
30765+ * Revision 1.3  2004-10-11 14:15:10+05:30  Cprogrammer
30766+ * added function prototypes
30767+ *
30768+ * Revision 1.2  2004-09-19 22:49:23+05:30  Cprogrammer
30769+ * added tai_unix macro
30770+ *
30771+ * Revision 1.1  2004-06-16 01:20:25+05:30  Cprogrammer
30772+ * Initial revision
30773+ *
30774+ */
30775+#ifndef TAI_H
30776+#define TAI_H
30777+
30778+#include "uint64.h"
30779+
30780+struct tai
30781+{
30782+       uint64          x;
30783+};
30784+
30785+#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u)))
30786+#define tai_approx(t) ((double) ((t)->x))
30787+#define tai_less(t,u) ((t)->x < (u)->x)
30788+#define TAI_PACK 8
30789+
30790+void            tai_now(struct tai *);
30791+void            tai_add();
30792+void            tai_sub(struct tai *, struct tai *, struct tai *);
30793+void            tai_pack(char *, struct tai *);
30794+void            tai_unpack(char *, struct tai *);
30795+
30796+#endif
30797diff -ruN ../netqmail-1.06-original/tcp-env.c netqmail-1.06/tcp-env.c
30798--- ../netqmail-1.06-original/tcp-env.c 1998-06-15 12:53:16.000000000 +0200
30799+++ netqmail-1.06/tcp-env.c     2016-11-22 21:03:57.131527465 +0100
30800@@ -10,6 +10,7 @@
30801 #include "scan.h"
30802 #include "subgetopt.h"
30803 #include "ip.h"
30804+#include "strsalloc.h"
30805 #include "dns.h"
30806 #include "byte.h"
30807 #include "remoteinfo.h"
30808@@ -34,6 +35,7 @@
30809 int argc;
30810 char *argv[];
30811 {
30812+ strsalloc ssa = {0};
30813  int dummy;
30814  char *proto;
30815  int opt;
30816@@ -74,12 +76,13 @@
30817    temp[ip_fmt(temp,&iplocal)] = 0;
30818    if (!env_put2("TCPLOCALIP",temp)) die();
30819 
30820-   switch(dns_ptr(&localname,&iplocal))
30821+   switch(dns_ptr(&ssa,&iplocal))
30822     {
30823      case DNS_MEM: die();
30824      case DNS_SOFT:
30825        if (!stralloc_copys(&localname,"softdnserror")) die();
30826      case 0:
30827+       if (!stralloc_copy(&localname,&ssa.sa[0])) die();
30828        if (!stralloc_0(&localname)) die();
30829        case_lowers(localname.s);
30830        if (!env_put2("TCPLOCALHOST",localname.s)) die();
30831@@ -99,12 +102,13 @@
30832    temp[ip_fmt(temp,&ipremote)] = 0;
30833    if (!env_put2("TCPREMOTEIP",temp)) die();
30834 
30835-   switch(dns_ptr(&remotename,&ipremote))
30836+   switch(dns_ptr(&ssa,&ipremote))
30837     {
30838      case DNS_MEM: die();
30839      case DNS_SOFT:
30840        if (!stralloc_copys(&remotename,"softdnserror")) die();
30841      case 0:
30842+       if (!stralloc_copy(&remotename,&ssa.sa[0])) die();
30843        if (!stralloc_0(&remotename)) die();
30844        case_lowers(remotename.s);
30845        if (!env_put2("TCPREMOTEHOST",remotename.s)) die();
30846diff -ruN ../netqmail-1.06-original/timeoutconn.c netqmail-1.06/timeoutconn.c
30847--- ../netqmail-1.06-original/timeoutconn.c     1998-06-15 12:53:16.000000000 +0200
30848+++ netqmail-1.06/timeoutconn.c 2016-11-22 21:03:57.131527465 +0100
30849@@ -10,9 +10,10 @@
30850 #include "byte.h"
30851 #include "timeoutconn.h"
30852 
30853-int timeoutconn(s,ip,port,timeout)
30854+int timeoutconn(s,ip,outip,port,timeout)
30855 int s;
30856 struct ip_address *ip;
30857+struct ip_address *outip;
30858 unsigned int port;
30859 int timeout;
30860 {
30861@@ -22,6 +23,13 @@
30862   fd_set wfds;
30863   struct timeval tv;
30864 
30865+  /* bind() an outgoing ipaddr */
30866+  byte_zero(&sin,sizeof(sin));
30867+  byte_copy(&sin.sin_addr.s_addr,4,outip);
30868+  sin.sin_family = AF_INET;
30869+
30870+  if (-1 == bind(s,(struct sockaddr *) &sin,sizeof(sin))) return -1;
30871+
30872   byte_zero(&sin,sizeof(sin));
30873   byte_copy(&sin.sin_addr,4,ip);
30874   x = (char *) &sin.sin_port;
30875diff -ruN ../netqmail-1.06-original/tls.c netqmail-1.06/tls.c
30876--- ../netqmail-1.06-original/tls.c     1970-01-01 01:00:00.000000000 +0100
30877+++ netqmail-1.06/tls.c 2016-11-22 21:03:57.131527465 +0100
30878@@ -0,0 +1,25 @@
30879+#include "exit.h"
30880+#include "error.h"
30881+#include <openssl/ssl.h>
30882+#include <openssl/err.h>
30883+
30884+int smtps = 0;
30885+SSL *ssl = NULL;
30886+
30887+void ssl_free(SSL *myssl) { SSL_shutdown(myssl); SSL_free(myssl); }
30888+void ssl_exit(int status) { if (ssl) ssl_free(ssl); _exit(status); }
30889+
30890+const char *ssl_error()
30891+{
30892+  int r = ERR_get_error();
30893+  if (!r) return NULL;
30894+  SSL_load_error_strings();
30895+  return ERR_error_string(r, NULL);
30896+}
30897+const char *ssl_error_str()
30898+{
30899+  const char *err = ssl_error();
30900+  if (err) return err;
30901+  if (!errno) return 0;
30902+  return (errno == error_timeout) ? "timed out" : error_str(errno);
30903+}
30904diff -ruN ../netqmail-1.06-original/tls.h netqmail-1.06/tls.h
30905--- ../netqmail-1.06-original/tls.h     1970-01-01 01:00:00.000000000 +0100
30906+++ netqmail-1.06/tls.h 2016-11-22 21:03:57.132527432 +0100
30907@@ -0,0 +1,16 @@
30908+#ifndef TLS_H
30909+#define TLS_H
30910+
30911+#include <openssl/ssl.h>
30912+
30913+extern int smtps;
30914+extern SSL *ssl;
30915+
30916+void ssl_free(SSL *myssl);
30917+void ssl_exit(int status);
30918+# define _exit ssl_exit
30919+
30920+const char *ssl_error();
30921+const char *ssl_error_str();
30922+
30923+#endif
30924diff -ruN ../netqmail-1.06-original/tryulong64.c netqmail-1.06/tryulong64.c
30925--- ../netqmail-1.06-original/tryulong64.c      1970-01-01 01:00:00.000000000 +0100
30926+++ netqmail-1.06/tryulong64.c  2016-11-22 21:03:57.132527432 +0100
30927@@ -0,0 +1,47 @@
30928+/*
30929+ * $Log: tryulong64.c,v $
30930+ * Revision 1.1  2004-05-14 00:45:23+05:30  Cprogrammer
30931+ * Initial revision
30932+ *
30933+ */
30934+void
30935+main()
30936+{
30937+       unsigned long   u;
30938+       u = 1;
30939+       u += u;
30940+       u += u;
30941+       u += u;
30942+       u += u;
30943+       u += u;
30944+       u += u;
30945+       u += u;
30946+       u += u;
30947+       u += u;
30948+       u += u;
30949+       u += u;
30950+       u += u;
30951+       u += u;
30952+       u += u;
30953+       u += u;
30954+       u += u;
30955+       u += u;
30956+       u += u;
30957+       u += u;
30958+       u += u;
30959+       u += u;
30960+       u += u;
30961+       u += u;
30962+       u += u;
30963+       u += u;
30964+       u += u;
30965+       u += u;
30966+       u += u;
30967+       u += u;
30968+       u += u;
30969+       u += u;
30970+       u += u;
30971+       if (!u)
30972+               _exit(1);
30973+       _exit(0);
30974+}
30975diff -ruN ../netqmail-1.06-original/uint64.h1 netqmail-1.06/uint64.h1
30976--- ../netqmail-1.06-original/uint64.h1 1970-01-01 01:00:00.000000000 +0100
30977+++ netqmail-1.06/uint64.h1     2016-11-22 21:03:57.132527432 +0100
30978@@ -0,0 +1,12 @@
30979+/*
30980+ * $Log: uint64.h1,v $
30981+ * Revision 1.1  2004-10-22 15:00:08+05:30  Cprogrammer
30982+ * Initial revision
30983+ *
30984+ */
30985+#ifndef UINT64_H
30986+#define UINT64_H
30987+
30988+typedef unsigned long uint64;
30989+
30990+#endif
30991diff -ruN ../netqmail-1.06-original/uint64.h2 netqmail-1.06/uint64.h2
30992--- ../netqmail-1.06-original/uint64.h2 1970-01-01 01:00:00.000000000 +0100
30993+++ netqmail-1.06/uint64.h2     2016-11-22 21:03:57.132527432 +0100
30994@@ -0,0 +1,12 @@
30995+/*
30996+ * $Log: uint64.h2,v $
30997+ * Revision 1.1  2004-10-22 15:00:36+05:30  Cprogrammer
30998+ * Initial revision
30999+ *
31000+ */
31001+#ifndef UINT64_H
31002+#define UINT64_H
31003+
31004+typedef unsigned long long uint64;
31005+
31006+#endif
31007diff -ruN ../netqmail-1.06-original/update_tmprsadh.sh netqmail-1.06/update_tmprsadh.sh
31008--- ../netqmail-1.06-original/update_tmprsadh.sh        1970-01-01 01:00:00.000000000 +0100
31009+++ netqmail-1.06/update_tmprsadh.sh    2016-11-22 21:03:57.132527432 +0100
31010@@ -0,0 +1,22 @@
31011+#!/bin/sh
31012+
31013+# Update temporary RSA and DH keys
31014+# Frederik Vermeulen 2004-05-31 GPL
31015+#
31016+# Slightly modified by Roberto Puzzanghera
31017+# to chown the .pem files to vpopmail
31018+
31019+umask 0077 || exit 0
31020+
31021+export PATH="$PATH:/usr/local/bin/ssl:/usr/sbin"
31022+
31023+openssl genrsa -out QMAIL/control/rsa2048.new 2048 &&
31024+chmod 600 QMAIL/control/rsa2048.new &&
31025+chown vpopmail:vchkpw QMAIL/control/rsa2048.new &&
31026+mv -f QMAIL/control/rsa2048.new QMAIL/control/rsa2048.pem
31027+
31028+openssl dhparam -2 -out QMAIL/control/dh2048.new 2048 &&
31029+chmod 600 QMAIL/control/dh2048.new &&
31030+chown vpopmail:vchkpw QMAIL/control/dh2048.new &&
31031+mv -f QMAIL/control/dh2048.new QMAIL/control/dh2048.pem
31032+
31033diff -ruN ../netqmail-1.06-original/wildmat.c netqmail-1.06/wildmat.c
31034--- ../netqmail-1.06-original/wildmat.c 1970-01-01 01:00:00.000000000 +0100
31035+++ netqmail-1.06/wildmat.c     2016-11-22 21:03:57.132527432 +0100
31036@@ -0,0 +1,173 @@
31037+/*-** wildmat.c.orig   Wed Dec  3 11:46:31 1997
31038+ * $Revision: 1.6 $
31039+ * Do shell-style pattern matching for ?, \, [], and * characters.
31040+ * Might not be robust in face of malformed patterns; e.g., "foo[a-"
31041+ * could cause a segmentation violation.  It is 8bit clean.
31042+ *
31043+ * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
31044+ * Rich $alz is now <rsalz@osf.org>.
31045+ * April, 1991:  Replaced mutually-recursive calls with in-line code
31046+ * for the star character.
31047+ *
31048+ * Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code.
31049+ * This can greatly speed up failing wildcard patterns.  For example:
31050+ * pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
31051+ * text 1:  -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
31052+ * text 2:  -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
31053+ * Text 1 matches with 51 calls, while text 2 fails with 54 calls.  Without
31054+ * the ABORT code, it takes 22310 calls to fail.  Ugh.  The following
31055+ * explanation is from Lars:
31056+ * The precondition that must be fulfilled is that DoMatch will consume
31057+ * at least one character in text.  This is true if *p is neither '*' no
31058+ * '\0'.)  The last return has ABORT instead of FALSE to avoid quadratic
31059+ * behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx".  With
31060+ * FALSE, each star-loop has to run to the end of the text; with ABORT
31061+ * only the last one does.
31062+ *
31063+ * Once the control of one instance of DoMatch enters the star-loop, that
31064+ * instance will return either TRUE or ABORT, and any calling instance
31065+ * will therefore return immediately after (without calling recursively
31066+ * again).  In effect, only one star-loop is ever active.  It would be
31067+ * possible to modify the code to maintain this context explicitly,
31068+ * eliminating all recursive calls at the cost of some complication and
31069+ * loss of clarity (and the ABORT stuff seems to be unclear enough by
31070+ * itself).  I think it would be unwise to try to get this into a
31071+ * released version unless you have a good test data base to try it out
31072+ * on.
31073+ */
31074+#define TRUE                    1
31075+#define FALSE                   0
31076+#define ABORT                  -1
31077+
31078+
31079+/*- What character marks an inverted character class?  */
31080+#define NEGATE_CLASS           '^'
31081+/*- Is "*" a common pattern?  */
31082+#define OPTIMIZE_JUST_STAR
31083+/*- Do tar(1) matching rules, which ignore a trailing slash?  */
31084+#undef MATCH_TAR_PATTERN
31085+
31086+
31087+/*- Match text and p, return TRUE, FALSE, or ABORT.  */
31088+static int
31089+DoMatch(text, p)
31090+       register char  *text;
31091+       register char  *p;
31092+{
31093+       register int    last;
31094+       register int    matched;
31095+       register int    reverse;
31096+
31097+       for (; *p; text++, p++)
31098+       {
31099+               if (*text == '\0' && *p != '*')
31100+                       return ABORT;
31101+               switch (*p)
31102+               {
31103+               case '\\': /*- Literal match with following character.  */
31104+                       p++;
31105+                       /*- FALLTHROUGH */
31106+               default:
31107+                       if (*text != *p)
31108+                               return FALSE;
31109+                       continue;
31110+               case '?': /*- Match anything. */
31111+                       continue;
31112+               case '*':
31113+                       /*- Consecutive stars act just like one.  */
31114+                       while (*++p == '*')
31115+                               continue;
31116+                       /*- Trailing star matches everything.  */
31117+                       if (*p == '\0')
31118+                               return TRUE;
31119+                       while (*text)
31120+                               if ((matched = DoMatch(text++, p)) != FALSE)
31121+                                       return matched;
31122+                       return ABORT;
31123+               case '[':
31124+                       reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
31125+                       /*- Inverted character class.  */
31126+                       if (reverse)
31127+                               p++;
31128+                       matched = FALSE;
31129+                       if (p[1] == ']' || p[1] == '-')
31130+                       {
31131+                               if (*++p == *text)
31132+                                       matched = TRUE;
31133+                       }
31134+                       for (last = *p; *++p && *p != ']'; last = *p)
31135+                       {
31136+                               /*- This next line requires a good C compiler.  */
31137+                               if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p)
31138+                                       matched = TRUE;
31139+                       }
31140+                       if (matched == reverse)
31141+                               return FALSE;
31142+                       continue;
31143+               }
31144+       }
31145+
31146+#ifdef MATCH_TAR_PATTERN
31147+       if (*text == '/')
31148+               return TRUE;
31149+#endif /*- MATCH_TAR_ATTERN */
31150+       return *text == '\0';
31151+}
31152+
31153+
31154+/*- User-level routine.  Returns TRUE or FALSE.  */
31155+int
31156+wildmat_internal(text, p)
31157+       char           *text;
31158+       char           *p;
31159+{
31160+#ifdef OPTIMIZE_JUST_STAR
31161+       if (p[0] == '*' && p[1] == '\0')
31162+               return TRUE;
31163+#endif /*- OPTIMIZE_JUST_STAR */
31164+       return DoMatch(text, p) == TRUE;
31165+}
31166+
31167+#if    defined(TEST)
31168+include < stdio.h >
31169+/*- Yes, we use gets not fgets.  Sue me.  */
31170+
31171+int
31172+main()
31173+{
31174+       char            p[80];
31175+       char            text[80];
31176+
31177+       printf("Wildmat tester.  Enter pattern, then strings to test.\n");
31178+       printf("A blank line gets prompts for a new pattern; a blank pattern\n");
31179+       printf("exits the program.\n");
31180+       for (;;)
31181+       {
31182+               printf("\nEnter pattern:  ");
31183+               (void) fflush(stdout);
31184+               if (gets(p) == NULL || p[0] == '\0')
31185+                       break;
31186+               for (;;)
31187+               {
31188+                       printf("Enter text:  ");
31189+                       (void) fflush(stdout);
31190+                       if (gets(text) == NULL)
31191+                               exit(0);
31192+                       /*- Blank line; go back and get a new pattern.  */
31193+                       if (text[0] == '\0')
31194+                               break;
31195+                       printf("      %s\n", wildmat_internal(text, p) ? "YES" : "NO");
31196+               }
31197+       }
31198+       exit(0);
31199+       /*- NOTREACHED */
31200+}
31201+#endif /*- defined(TEST) */
31202+
31203+void
31204+getversion_wildmat_internal_c()
31205+{
31206+       static char    *x = "$Id: wildmat.c,v 1.6 2008-08-03 18:26:33+05:30 Cprogrammer Stab mbhangui $";
31207+       x++;
31208+       x--;
31209+}
Note: See TracBrowser for help on using the repository browser.